Merge "Skip background memory trim if the app is in the foreground state" into main
diff --git a/DREAM_MANAGER_OWNERS b/DREAM_MANAGER_OWNERS
new file mode 100644
index 0000000..48bde60
--- /dev/null
+++ b/DREAM_MANAGER_OWNERS
@@ -0,0 +1 @@
+brycelee@google.com
diff --git a/ProtoLibraries.bp b/ProtoLibraries.bp
index e7adf20..f6213b9 100644
--- a/ProtoLibraries.bp
+++ b/ProtoLibraries.bp
@@ -31,6 +31,7 @@
"&& $(location soong_zip) -jar -o $(out) -C $(genDir)/$(in) -D $(genDir)/$(in)",
srcs: [
+ ":aconfigd_protos",
":ipconnectivity-proto-src",
":libstats_atom_enum_protos",
":libstats_atom_message_protos",
diff --git a/Ravenwood.bp b/Ravenwood.bp
index 74b34fb..412f2b7 100644
--- a/Ravenwood.bp
+++ b/Ravenwood.bp
@@ -104,6 +104,18 @@
],
}
+genrule {
+ name: "framework-minus-apex.ravenwood.keep_all",
+ defaults: ["ravenwood-internal-only-visibility-genrule"],
+ cmd: "cp $(in) $(out)",
+ srcs: [
+ ":framework-minus-apex.ravenwood-base{hoststubgen_keep_all.txt}",
+ ],
+ out: [
+ "hoststubgen_framework-minus-apex_keep_all.txt",
+ ],
+}
+
java_library {
name: "services.core-for-hoststubgen",
installable: false, // host only jar.
@@ -189,6 +201,18 @@
],
}
+genrule {
+ name: "services.core.ravenwood.keep_all",
+ defaults: ["ravenwood-internal-only-visibility-genrule"],
+ cmd: "cp $(in) $(out)",
+ srcs: [
+ ":services.core.ravenwood-base{hoststubgen_keep_all.txt}",
+ ],
+ out: [
+ "hoststubgen_services.core_keep_all.txt",
+ ],
+}
+
java_library {
name: "services.core.ravenwood-jarjar",
installable: false,
diff --git a/apex/jobscheduler/framework/java/android/app/job/JobInfo.java b/apex/jobscheduler/framework/java/android/app/job/JobInfo.java
index c74c48c..5f57c39 100644
--- a/apex/jobscheduler/framework/java/android/app/job/JobInfo.java
+++ b/apex/jobscheduler/framework/java/android/app/job/JobInfo.java
@@ -31,6 +31,7 @@
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.RequiresPermission;
+import android.annotation.SuppressLint;
import android.app.Notification;
import android.compat.Compatibility;
import android.compat.annotation.ChangeId;
@@ -1366,6 +1367,7 @@
* @return This object for method chaining
*/
@FlaggedApi(Flags.FLAG_JOB_DEBUG_INFO_APIS)
+ @SuppressLint("BuilderSetStyle")
@NonNull
public Builder removeDebugTag(@NonNull String tag) {
mDebugTags.remove(tag);
diff --git a/apex/jobscheduler/service/Android.bp b/apex/jobscheduler/service/Android.bp
index 06c7d64..559490c 100644
--- a/apex/jobscheduler/service/Android.bp
+++ b/apex/jobscheduler/service/Android.bp
@@ -33,6 +33,7 @@
"modules-utils-fastxmlserializer",
"service-jobscheduler-alarm.flags-aconfig-java",
"service-jobscheduler-job.flags-aconfig-java",
+ "service-jobscheduler-appidle.flags-aconfig-java",
],
// Rename classes shared with the framework
diff --git a/apex/jobscheduler/service/aconfig/Android.bp b/apex/jobscheduler/service/aconfig/Android.bp
index 859c67a..7b2525c 100644
--- a/apex/jobscheduler/service/aconfig/Android.bp
+++ b/apex/jobscheduler/service/aconfig/Android.bp
@@ -42,3 +42,16 @@
name: "service-jobscheduler-alarm.flags-aconfig-java",
aconfig_declarations: "alarm_flags",
}
+
+// App Idle
+aconfig_declarations {
+ name: "app_idle_flags",
+ package: "com.android.server.usage",
+ container: "system",
+ srcs: ["app_idle.aconfig"],
+}
+
+java_aconfig_library {
+ name: "service-jobscheduler-appidle.flags-aconfig-java",
+ aconfig_declarations: "app_idle_flags",
+}
diff --git a/apex/jobscheduler/service/aconfig/app_idle.aconfig b/apex/jobscheduler/service/aconfig/app_idle.aconfig
new file mode 100644
index 0000000..c8976ca
--- /dev/null
+++ b/apex/jobscheduler/service/aconfig/app_idle.aconfig
@@ -0,0 +1,14 @@
+package: "com.android.server.usage"
+container: "system"
+
+flag {
+ name: "avoid_idle_check"
+ namespace: "backstage_power"
+ description: "Postpone app idle check after boot completed"
+ is_fixed_read_only: true
+ bug: "337864590"
+ metadata {
+ purpose: PURPOSE_BUGFIX
+ }
+}
+
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 384d786..ff73a49 100644
--- a/apex/jobscheduler/service/java/com/android/server/job/JobSchedulerService.java
+++ b/apex/jobscheduler/service/java/com/android/server/job/JobSchedulerService.java
@@ -80,6 +80,7 @@
import android.os.RemoteCallbackList;
import android.os.RemoteException;
import android.os.SystemClock;
+import android.os.Trace;
import android.os.UserHandle;
import android.os.WorkSource;
import android.os.storage.StorageManagerInternal;
@@ -179,6 +180,8 @@
public static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
public static final boolean DEBUG_STANDBY = DEBUG || false;
+ public static final String TRACE_TRACK_NAME = "JobScheduler";
+
/** The maximum number of jobs that we allow an app to schedule */
private static final int MAX_JOBS_PER_APP = 150;
/** The number of the most recently completed jobs to keep track of for debugging purposes. */
@@ -4344,7 +4347,11 @@
final boolean wasConsideredCharging = isConsideredCharging();
mChargingPolicy = newPolicy;
-
+ if (Trace.isTagEnabled(Trace.TRACE_TAG_SYSTEM_SERVER)) {
+ Trace.instantForTrack(Trace.TRACE_TAG_SYSTEM_SERVER,
+ JobSchedulerService.TRACE_TRACK_NAME,
+ "CHARGING POLICY CHANGED#" + mChargingPolicy);
+ }
if (isConsideredCharging() != wasConsideredCharging) {
for (int c = mControllers.size() - 1; c >= 0; --c) {
mControllers.get(c).onBatteryStateChangedLocked();
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 39d50f5..d65a66c 100644
--- a/apex/jobscheduler/service/java/com/android/server/job/JobServiceContext.java
+++ b/apex/jobscheduler/service/java/com/android/server/job/JobServiceContext.java
@@ -535,29 +535,17 @@
sEnqueuedJwiAtJobStart.logSampleWithUid(job.getUid(), job.getWorkCount());
final String sourcePackage = job.getSourcePackageName();
if (Trace.isTagEnabled(Trace.TRACE_TAG_SYSTEM_SERVER)) {
- final String componentPackage = job.getServiceComponent().getPackageName();
- String traceTag = "*job*<" + job.getSourceUid() + ">" + sourcePackage;
- if (!sourcePackage.equals(componentPackage)) {
- traceTag += ":" + componentPackage;
- }
- traceTag += "/" + job.getServiceComponent().getShortClassName();
- if (!componentPackage.equals(job.serviceProcessName)) {
- traceTag += "$" + job.serviceProcessName;
- }
- if (job.getNamespace() != null) {
- traceTag += "@" + job.getNamespace();
- }
- traceTag += "#" + job.getJobId();
-
// Use the context's ID to distinguish traces since there'll only be one job
// running per context.
- Trace.asyncTraceForTrackBegin(Trace.TRACE_TAG_SYSTEM_SERVER, "JobScheduler",
- traceTag, getId());
+ Trace.asyncTraceForTrackBegin(Trace.TRACE_TAG_SYSTEM_SERVER,
+ JobSchedulerService.TRACE_TRACK_NAME, job.computeSystemTraceTag(),
+ getId());
}
if (job.getAppTraceTag() != null) {
// Use the job's ID to distinguish traces since the ID will be unique per app.
- Trace.asyncTraceForTrackBegin(Trace.TRACE_TAG_APP, "JobScheduler",
- job.getAppTraceTag(), job.getJobId());
+ Trace.asyncTraceForTrackBegin(Trace.TRACE_TAG_APP,
+ JobSchedulerService.TRACE_TRACK_NAME, job.getAppTraceTag(),
+ job.getJobId());
}
try {
mBatteryStats.noteJobStart(job.getBatteryName(), job.getSourceUid());
@@ -1605,12 +1593,12 @@
completedJob.getFilteredTraceTag(),
completedJob.getFilteredDebugTags());
if (Trace.isTagEnabled(Trace.TRACE_TAG_SYSTEM_SERVER)) {
- Trace.asyncTraceForTrackEnd(Trace.TRACE_TAG_SYSTEM_SERVER, "JobScheduler",
- getId());
+ Trace.asyncTraceForTrackEnd(Trace.TRACE_TAG_SYSTEM_SERVER,
+ JobSchedulerService.TRACE_TRACK_NAME, getId());
}
if (completedJob.getAppTraceTag() != null) {
- Trace.asyncTraceForTrackEnd(Trace.TRACE_TAG_APP, "JobScheduler",
- completedJob.getJobId());
+ Trace.asyncTraceForTrackEnd(Trace.TRACE_TAG_APP,
+ JobSchedulerService.TRACE_TRACK_NAME, completedJob.getJobId());
}
try {
mBatteryStats.noteJobFinish(mRunningJob.getBatteryName(), mRunningJob.getSourceUid(),
diff --git a/apex/jobscheduler/service/java/com/android/server/job/controllers/JobStatus.java b/apex/jobscheduler/service/java/com/android/server/job/controllers/JobStatus.java
index 7fca867..e3af1d8 100644
--- a/apex/jobscheduler/service/java/com/android/server/job/controllers/JobStatus.java
+++ b/apex/jobscheduler/service/java/com/android/server/job/controllers/JobStatus.java
@@ -572,6 +572,9 @@
/** The reason a job most recently went from ready to not ready. */
private int mReasonReadyToUnready = JobParameters.STOP_REASON_UNDEFINED;
+ /** The system trace tag for this job. */
+ private String mSystemTraceTag;
+
/**
* Core constructor for JobStatus instances. All other ctors funnel down to this one.
*
@@ -1058,6 +1061,38 @@
return job.getTraceTag();
}
+ /** Returns a trace tag using debug information provided by job scheduler service. */
+ @NonNull
+ public String computeSystemTraceTag() {
+ // Guarded by JobSchedulerService.mLock, no need for synchronization.
+ if (mSystemTraceTag != null) {
+ return mSystemTraceTag;
+ }
+
+ mSystemTraceTag = computeSystemTraceTagInner();
+ return mSystemTraceTag;
+ }
+
+ @NonNull
+ private String computeSystemTraceTagInner() {
+ final String componentPackage = getServiceComponent().getPackageName();
+ StringBuilder traceTag = new StringBuilder(128);
+ traceTag.append("*job*<").append(sourceUid).append(">").append(sourcePackageName);
+ if (!sourcePackageName.equals(componentPackage)) {
+ traceTag.append(":").append(componentPackage);
+ }
+ traceTag.append("/").append(getServiceComponent().getShortClassName());
+ if (!componentPackage.equals(serviceProcessName)) {
+ traceTag.append("$").append(serviceProcessName);
+ }
+ if (mNamespace != null && !mNamespace.trim().isEmpty()) {
+ traceTag.append("@").append(mNamespace);
+ }
+ traceTag.append("#").append(getJobId());
+
+ return traceTag.toString();
+ }
+
/** Returns whether this job was scheduled by one app on behalf of another. */
public boolean isProxyJob() {
return mIsProxyJob;
diff --git a/apex/jobscheduler/service/java/com/android/server/job/controllers/QuotaController.java b/apex/jobscheduler/service/java/com/android/server/job/controllers/QuotaController.java
index c240b3f..a1c72fb 100644
--- a/apex/jobscheduler/service/java/com/android/server/job/controllers/QuotaController.java
+++ b/apex/jobscheduler/service/java/com/android/server/job/controllers/QuotaController.java
@@ -50,6 +50,7 @@
import android.os.Looper;
import android.os.Message;
import android.os.RemoteException;
+import android.os.Trace;
import android.os.UserHandle;
import android.provider.DeviceConfig;
import android.util.ArraySet;
@@ -2181,6 +2182,12 @@
}
scheduleCutoff();
}
+ } else {
+ if (Trace.isTagEnabled(Trace.TRACE_TAG_SYSTEM_SERVER)) {
+ Trace.instantForTrack(Trace.TRACE_TAG_SYSTEM_SERVER,
+ JobSchedulerService.TRACE_TRACK_NAME,
+ "QC/- " + mPkg);
+ }
}
}
@@ -2720,6 +2727,11 @@
if (timeRemainingMs <= 50) {
// Less than 50 milliseconds left. Start process of shutting down jobs.
if (DEBUG) Slog.d(TAG, pkg + " has reached its quota.");
+ if (Trace.isTagEnabled(Trace.TRACE_TAG_SYSTEM_SERVER)) {
+ Trace.instantForTrack(Trace.TRACE_TAG_SYSTEM_SERVER,
+ JobSchedulerService.TRACE_TRACK_NAME,
+ pkg + "#" + MSG_REACHED_TIME_QUOTA);
+ }
mStateChangedListener.onControllerStateChanged(
maybeUpdateConstraintForPkgLocked(
sElapsedRealtimeClock.millis(),
@@ -2748,6 +2760,11 @@
pkg.userId, pkg.packageName);
if (timeRemainingMs <= 0) {
if (DEBUG) Slog.d(TAG, pkg + " has reached its EJ quota.");
+ if (Trace.isTagEnabled(Trace.TRACE_TAG_SYSTEM_SERVER)) {
+ Trace.instantForTrack(Trace.TRACE_TAG_SYSTEM_SERVER,
+ JobSchedulerService.TRACE_TRACK_NAME,
+ pkg + "#" + MSG_REACHED_EJ_TIME_QUOTA);
+ }
mStateChangedListener.onControllerStateChanged(
maybeUpdateConstraintForPkgLocked(
sElapsedRealtimeClock.millis(),
@@ -2772,6 +2789,12 @@
Slog.d(TAG, pkg + " has reached its count quota.");
}
+ if (Trace.isTagEnabled(Trace.TRACE_TAG_SYSTEM_SERVER)) {
+ Trace.instantForTrack(Trace.TRACE_TAG_SYSTEM_SERVER,
+ JobSchedulerService.TRACE_TRACK_NAME,
+ pkg + "#" + MSG_REACHED_COUNT_QUOTA);
+ }
+
mStateChangedListener.onControllerStateChanged(
maybeUpdateConstraintForPkgLocked(
sElapsedRealtimeClock.millis(),
@@ -2928,6 +2951,11 @@
}
mTempAllowlistGraceCache.delete(uid);
mTopAppGraceCache.delete(uid);
+ if (Trace.isTagEnabled(Trace.TRACE_TAG_SYSTEM_SERVER)) {
+ Trace.instantForTrack(Trace.TRACE_TAG_SYSTEM_SERVER,
+ JobSchedulerService.TRACE_TRACK_NAME,
+ "<" + uid + ">#" + MSG_END_GRACE_PERIOD);
+ }
final ArraySet<String> packages = mService.getPackagesForUidLocked(uid);
if (packages != null) {
final int userId = UserHandle.getUserId(uid);
diff --git a/apex/jobscheduler/service/java/com/android/server/usage/AppIdleHistory.java b/apex/jobscheduler/service/java/com/android/server/usage/AppIdleHistory.java
index 4d4e340..6265d9b 100644
--- a/apex/jobscheduler/service/java/com/android/server/usage/AppIdleHistory.java
+++ b/apex/jobscheduler/service/java/com/android/server/usage/AppIdleHistory.java
@@ -788,7 +788,13 @@
}
appUsageHistory.nextEstimatedLaunchTime = getLongValue(parser,
ATTR_NEXT_ESTIMATED_APP_LAUNCH_TIME, 0);
- appUsageHistory.lastInformedBucket = -1;
+ if (Flags.avoidIdleCheck()) {
+ // Set lastInformedBucket to the same value with the currentBucket
+ // it should have already been informed.
+ appUsageHistory.lastInformedBucket = appUsageHistory.currentBucket;
+ } else {
+ appUsageHistory.lastInformedBucket = -1;
+ }
userHistory.put(packageName, appUsageHistory);
if (version >= XML_VERSION_ADD_BUCKET_EXPIRY_TIMES) {
diff --git a/apex/jobscheduler/service/java/com/android/server/usage/AppStandbyController.java b/apex/jobscheduler/service/java/com/android/server/usage/AppStandbyController.java
index 410074e..c3fe031 100644
--- a/apex/jobscheduler/service/java/com/android/server/usage/AppStandbyController.java
+++ b/apex/jobscheduler/service/java/com/android/server/usage/AppStandbyController.java
@@ -707,7 +707,7 @@
initializeDefaultsForSystemApps(UserHandle.USER_SYSTEM);
}
- if (mPendingOneTimeCheckIdleStates) {
+ if (!Flags.avoidIdleCheck() && mPendingOneTimeCheckIdleStates) {
postOneTimeCheckIdleStates();
}
@@ -1021,7 +1021,7 @@
== REASON_SUB_DEFAULT_APP_RESTORED)) {
newBucket = getBucketForLocked(packageName, userId, elapsedRealtime);
if (DEBUG) {
- Slog.d(TAG, "Evaluated AOSP newBucket = "
+ Slog.d(TAG, "Evaluated " + packageName + " newBucket = "
+ standbyBucketToString(newBucket));
}
reason = REASON_MAIN_TIMEOUT;
@@ -1990,7 +1990,9 @@
}
}
if (android.app.admin.flags.Flags.disallowUserControlBgUsageFix()) {
- postCheckIdleStates(userId);
+ if (!Flags.avoidIdleCheck()) {
+ postCheckIdleStates(userId);
+ }
}
}
@@ -2392,9 +2394,14 @@
final boolean isHeadLess = !systemLauncherActivities.contains(pkg);
if (updateHeadlessSystemAppCache(pkg, isHeadLess)) {
- mHandler.obtainMessage(MSG_CHECK_PACKAGE_IDLE_STATE,
- UserHandle.USER_SYSTEM, -1, pkg)
- .sendToTarget();
+ if (!Flags.avoidIdleCheck()) {
+ // Checking idle state for the each individual headless system app
+ // during the boot up is not necessary, a full idle check for all
+ // usres will be scheduled after boot completed.
+ mHandler.obtainMessage(MSG_CHECK_PACKAGE_IDLE_STATE,
+ UserHandle.USER_SYSTEM, -1, pkg)
+ .sendToTarget();
+ }
}
}
final long end = SystemClock.uptimeMillis();
@@ -2438,6 +2445,11 @@
@Override
public void dumpState(String[] args, PrintWriter pw) {
+ pw.println("Flags: ");
+ pw.println(" " + Flags.FLAG_AVOID_IDLE_CHECK
+ + ": " + Flags.avoidIdleCheck());
+ pw.println();
+
synchronized (mCarrierPrivilegedLock) {
pw.println("Carrier privileged apps (have=" + mHaveCarrierPrivilegedApps
+ "): " + mCarrierPrivilegedApps);
diff --git a/api/StubLibraries.bp b/api/StubLibraries.bp
index 9b81bd4..13d6ae5 100644
--- a/api/StubLibraries.bp
+++ b/api/StubLibraries.bp
@@ -50,7 +50,7 @@
},
api_lint: {
enabled: true,
- new_since: ":android.api.public.latest",
+ new_since: ":android.api.combined.public.latest",
baseline_file: ":non-updatable-lint-baseline.txt",
},
},
@@ -130,7 +130,7 @@
},
api_lint: {
enabled: true,
- new_since: ":android.api.system.latest",
+ new_since: ":android.api.combined.system.latest",
baseline_file: ":non-updatable-system-lint-baseline.txt",
},
},
@@ -185,7 +185,7 @@
},
api_lint: {
enabled: true,
- new_since: ":android.api.test.latest",
+ new_since: ":android.api.combined.test.latest",
baseline_file: ":non-updatable-test-lint-baseline.txt",
},
},
@@ -269,7 +269,7 @@
},
api_lint: {
enabled: true,
- new_since: ":android.api.module-lib.latest",
+ new_since: ":android.api.combined.module-lib.latest",
baseline_file: ":non-updatable-module-lib-lint-baseline.txt",
},
},
diff --git a/cmds/uiautomator/library/core-src/com/android/uiautomator/core/AccessibilityNodeInfoDumper.java b/cmds/uiautomator/library/core-src/com/android/uiautomator/core/AccessibilityNodeInfoDumper.java
index 488292d..f726361 100644
--- a/cmds/uiautomator/library/core-src/com/android/uiautomator/core/AccessibilityNodeInfoDumper.java
+++ b/cmds/uiautomator/library/core-src/com/android/uiautomator/core/AccessibilityNodeInfoDumper.java
@@ -292,13 +292,17 @@
int childCount = node.getChildCount();
for (int x = 0; x < childCount; x++) {
AccessibilityNodeInfo childNode = node.getChild(x);
-
+ if (childNode == null) {
+ continue;
+ }
if (!safeCharSeqToString(childNode.getContentDescription()).isEmpty()
- || !safeCharSeqToString(childNode.getText()).isEmpty())
+ || !safeCharSeqToString(childNode.getText()).isEmpty()) {
return true;
+ }
- if (childNafCheck(childNode))
+ if (childNafCheck(childNode)) {
return true;
+ }
}
return false;
}
diff --git a/core/api/current.txt b/core/api/current.txt
index 53cf7d5..16de8fe7 100644
--- a/core/api/current.txt
+++ b/core/api/current.txt
@@ -3344,9 +3344,9 @@
public abstract class AccessibilityService extends android.app.Service {
ctor public AccessibilityService();
method public void attachAccessibilityOverlayToDisplay(int, @NonNull android.view.SurfaceControl);
- method @FlaggedApi("android.view.accessibility.a11y_overlay_callbacks") public void attachAccessibilityOverlayToDisplay(int, @NonNull android.view.SurfaceControl, @NonNull java.util.concurrent.Executor, @NonNull java.util.function.IntConsumer);
+ method @FlaggedApi("android.view.accessibility.a11y_overlay_callbacks") public final void attachAccessibilityOverlayToDisplay(int, @NonNull android.view.SurfaceControl, @NonNull java.util.concurrent.Executor, @NonNull java.util.function.IntConsumer);
method public void attachAccessibilityOverlayToWindow(int, @NonNull android.view.SurfaceControl);
- method @FlaggedApi("android.view.accessibility.a11y_overlay_callbacks") public void attachAccessibilityOverlayToWindow(int, @NonNull android.view.SurfaceControl, @NonNull java.util.concurrent.Executor, @NonNull java.util.function.IntConsumer);
+ method @FlaggedApi("android.view.accessibility.a11y_overlay_callbacks") public final void attachAccessibilityOverlayToWindow(int, @NonNull android.view.SurfaceControl, @NonNull java.util.concurrent.Executor, @NonNull java.util.function.IntConsumer);
method public boolean clearCache();
method public boolean clearCachedSubtree(@NonNull android.view.accessibility.AccessibilityNodeInfo);
method public final void disableSelf();
@@ -3354,7 +3354,7 @@
method public android.view.accessibility.AccessibilityNodeInfo findFocus(int);
method @NonNull public final android.accessibilityservice.AccessibilityButtonController getAccessibilityButtonController();
method @NonNull public final android.accessibilityservice.AccessibilityButtonController getAccessibilityButtonController(int);
- method @FlaggedApi("android.view.accessibility.braille_display_hid") @NonNull public android.accessibilityservice.BrailleDisplayController getBrailleDisplayController();
+ method @FlaggedApi("android.view.accessibility.braille_display_hid") @NonNull public final android.accessibilityservice.BrailleDisplayController getBrailleDisplayController();
method @NonNull @RequiresPermission(android.Manifest.permission.USE_FINGERPRINT) public final android.accessibilityservice.FingerprintGestureController getFingerprintGestureController();
method @Nullable public final android.accessibilityservice.InputMethod getInputMethod();
method @NonNull public final android.accessibilityservice.AccessibilityService.MagnificationController getMagnificationController();
@@ -26592,7 +26592,7 @@
method public long getFlags();
method @Nullable public android.media.MediaMetadata getMetadata();
method public String getPackageName();
- method @Nullable public android.media.session.MediaController.PlaybackInfo getPlaybackInfo();
+ method @NonNull public android.media.session.MediaController.PlaybackInfo getPlaybackInfo();
method @Nullable public android.media.session.PlaybackState getPlaybackState();
method @Nullable public java.util.List<android.media.session.MediaSession.QueueItem> getQueue();
method @Nullable public CharSequence getQueueTitle();
diff --git a/core/api/test-current.txt b/core/api/test-current.txt
index 624227d..14ae3f5 100644
--- a/core/api/test-current.txt
+++ b/core/api/test-current.txt
@@ -4260,6 +4260,12 @@
field @NonNull public static final android.os.Parcelable.Creator<android.window.TaskFragmentOrganizerToken> CREATOR;
}
+ public final class TaskFragmentParentInfo implements android.os.Parcelable {
+ method public int describeContents();
+ method public void writeToParcel(@NonNull android.os.Parcel, int);
+ field @NonNull public static final android.os.Parcelable.Creator<android.window.TaskFragmentParentInfo> CREATOR;
+ }
+
public final class TaskFragmentTransaction implements android.os.Parcelable {
ctor public TaskFragmentTransaction();
method public void addChange(@Nullable android.window.TaskFragmentTransaction.Change);
@@ -4284,8 +4290,8 @@
method @Nullable public android.os.IBinder getActivityToken();
method @NonNull public android.os.Bundle getErrorBundle();
method @Nullable public android.os.IBinder getErrorCallbackToken();
- method @Nullable public android.content.res.Configuration getTaskConfiguration();
method @Nullable public android.window.TaskFragmentInfo getTaskFragmentInfo();
+ method @Nullable public android.window.TaskFragmentParentInfo getTaskFragmentParentInfo();
method @Nullable public android.os.IBinder getTaskFragmentToken();
method public int getTaskId();
method public int getType();
@@ -4293,7 +4299,6 @@
method @NonNull public android.window.TaskFragmentTransaction.Change setActivityToken(@NonNull android.os.IBinder);
method @NonNull public android.window.TaskFragmentTransaction.Change setErrorBundle(@NonNull android.os.Bundle);
method @NonNull public android.window.TaskFragmentTransaction.Change setErrorCallbackToken(@Nullable android.os.IBinder);
- method @NonNull public android.window.TaskFragmentTransaction.Change setTaskConfiguration(@NonNull android.content.res.Configuration);
method @NonNull public android.window.TaskFragmentTransaction.Change setTaskFragmentInfo(@NonNull android.window.TaskFragmentInfo);
method @NonNull public android.window.TaskFragmentTransaction.Change setTaskFragmentToken(@NonNull android.os.IBinder);
method @NonNull public android.window.TaskFragmentTransaction.Change setTaskId(int);
diff --git a/core/java/android/accessibilityservice/AccessibilityService.java b/core/java/android/accessibilityservice/AccessibilityService.java
index d70fa19..fd9600c 100644
--- a/core/java/android/accessibilityservice/AccessibilityService.java
+++ b/core/java/android/accessibilityservice/AccessibilityService.java
@@ -3554,8 +3554,8 @@
* @see #OVERLAY_RESULT_INVALID
* @see #OVERLAY_RESULT_INTERNAL_ERROR
*/
- @FlaggedApi("android.view.accessibility.a11y_overlay_callbacks")
- public void attachAccessibilityOverlayToDisplay(
+ @FlaggedApi(android.view.accessibility.Flags.FLAG_A11Y_OVERLAY_CALLBACKS)
+ public final void attachAccessibilityOverlayToDisplay(
int displayId,
@NonNull SurfaceControl sc,
@NonNull @CallbackExecutor Executor executor,
@@ -3627,8 +3627,8 @@
* @see #OVERLAY_RESULT_INVALID
* @see #OVERLAY_RESULT_INTERNAL_ERROR
*/
- @FlaggedApi("android.view.accessibility.a11y_overlay_callbacks")
- public void attachAccessibilityOverlayToWindow(
+ @FlaggedApi(android.view.accessibility.Flags.FLAG_A11Y_OVERLAY_CALLBACKS)
+ public final void attachAccessibilityOverlayToWindow(
int accessibilityWindowId,
@NonNull SurfaceControl sc,
@NonNull @CallbackExecutor Executor executor,
@@ -3645,7 +3645,7 @@
*/
@FlaggedApi(android.view.accessibility.Flags.FLAG_BRAILLE_DISPLAY_HID)
@NonNull
- public BrailleDisplayController getBrailleDisplayController() {
+ public final BrailleDisplayController getBrailleDisplayController() {
BrailleDisplayController.checkApiFlagIsEnabled();
synchronized (mLock) {
if (mBrailleDisplayController == null) {
diff --git a/core/java/android/accounts/AccountManager.java b/core/java/android/accounts/AccountManager.java
index 7ee3413..497d47a 100644
--- a/core/java/android/accounts/AccountManager.java
+++ b/core/java/android/accounts/AccountManager.java
@@ -2015,7 +2015,7 @@
* null for no callback
* @param handler {@link Handler} identifying the callback thread,
* null for the main thread
- * @return An {@link AccountManagerFuture} which resolves to a Boolean indicated wether it
+ * @return An {@link AccountManagerFuture} which resolves to a Boolean indicated whether it
* succeeded.
* @hide
*/
diff --git a/core/java/android/app/Activity.java b/core/java/android/app/Activity.java
index a5dd4a7..79e2bd4 100644
--- a/core/java/android/app/Activity.java
+++ b/core/java/android/app/Activity.java
@@ -1183,6 +1183,7 @@
* @see #setIntent(Intent, ComponentCaller)
*/
@FlaggedApi(android.security.Flags.FLAG_CONTENT_URI_PERMISSION_APIS)
+ @SuppressLint("OnNameExpected")
public @Nullable ComponentCaller getCaller() {
return mCaller;
}
@@ -1203,6 +1204,7 @@
* @see #getCaller
*/
@FlaggedApi(android.security.Flags.FLAG_CONTENT_URI_PERMISSION_APIS)
+ @SuppressLint("OnNameExpected")
public void setIntent(@Nullable Intent newIntent, @Nullable ComponentCaller newCaller) {
internalSetIntent(newIntent, newCaller);
}
@@ -5796,6 +5798,8 @@
* @see #onRequestPermissionsResult
*/
@FlaggedApi(Flags.FLAG_DEVICE_AWARE_PERMISSION_APIS_ENABLED)
+ @SuppressLint("OnNameExpected")
+ // Suppress lint as this is an overload of the original API.
public boolean shouldShowRequestPermissionRationale(@NonNull String permission, int deviceId) {
final PackageManager packageManager = getDeviceId() == deviceId ? getPackageManager()
: createDeviceContext(deviceId).getPackageManager();
@@ -7159,6 +7163,7 @@
* @see ComponentCaller
*/
@FlaggedApi(android.security.Flags.FLAG_CONTENT_URI_PERMISSION_APIS)
+ @SuppressLint("OnNameExpected")
public @NonNull ComponentCaller getInitialCaller() {
return mInitialCaller;
}
@@ -7186,10 +7191,11 @@
* @see #getCaller
*/
@FlaggedApi(android.security.Flags.FLAG_CONTENT_URI_PERMISSION_APIS)
+ @SuppressLint("OnNameExpected")
public @NonNull ComponentCaller getCurrentCaller() {
if (mCurrentCaller == null) {
throw new IllegalStateException("The caller is null because #getCurrentCaller should be"
- + " called within #onNewIntent method");
+ + " called within #onNewIntent or #onActivityResult methods");
}
return mCurrentCaller;
}
@@ -9632,6 +9638,7 @@
* the default behaviour
*/
@FlaggedApi(android.security.Flags.FLAG_ASM_RESTRICTIONS_ENABLED)
+ @SuppressLint("OnNameExpected")
public void setAllowCrossUidActivitySwitchFromBelow(boolean allowed) {
ActivityClient.getInstance().setAllowCrossUidActivitySwitchFromBelow(mToken, allowed);
}
diff --git a/core/java/android/app/ActivityManagerInternal.java b/core/java/android/app/ActivityManagerInternal.java
index d8df447..8e99e46b 100644
--- a/core/java/android/app/ActivityManagerInternal.java
+++ b/core/java/android/app/ActivityManagerInternal.java
@@ -1282,4 +1282,15 @@
*/
public abstract void addStartInfoTimestamp(int key, long timestampNs, int uid, int pid,
int userId);
+
+ /**
+ * It is similar {@link IActivityManager#killApplication(String, int, int, String, int)} but
+ * it immediately stop the package.
+ *
+ * <p>Note: Do not call this method from inside PMS's lock, otherwise it'll run into
+ * watchdog reset.
+ * @hide
+ */
+ public abstract void killApplicationSync(String pkgName, int appId, int userId,
+ String reason, int exitInfoReason);
}
diff --git a/core/java/android/app/ActivityOptions.java b/core/java/android/app/ActivityOptions.java
index 9ea55f5..c6a1546 100644
--- a/core/java/android/app/ActivityOptions.java
+++ b/core/java/android/app/ActivityOptions.java
@@ -103,7 +103,8 @@
@IntDef(prefix = {"MODE_BACKGROUND_ACTIVITY_START_"}, value = {
MODE_BACKGROUND_ACTIVITY_START_SYSTEM_DEFINED,
MODE_BACKGROUND_ACTIVITY_START_ALLOWED,
- MODE_BACKGROUND_ACTIVITY_START_DENIED})
+ MODE_BACKGROUND_ACTIVITY_START_DENIED,
+ MODE_BACKGROUND_ACTIVITY_START_COMPAT})
public @interface BackgroundActivityStartMode {}
/**
* No explicit value chosen. The system will decide whether to grant privileges.
@@ -117,6 +118,13 @@
* Deny the {@link PendingIntent} to use the background activity start privileges.
*/
public static final int MODE_BACKGROUND_ACTIVITY_START_DENIED = 2;
+ /**
+ * Special behavior for compatibility.
+ * Similar to {@link #MODE_BACKGROUND_ACTIVITY_START_SYSTEM_DEFINED}
+ *
+ * @hide
+ */
+ public static final int MODE_BACKGROUND_ACTIVITY_START_COMPAT = -1;
/**
* The package name that created the options.
diff --git a/core/java/android/app/AppCompatTaskInfo.java b/core/java/android/app/AppCompatTaskInfo.java
index 7724c23..92543b1 100644
--- a/core/java/android/app/AppCompatTaskInfo.java
+++ b/core/java/android/app/AppCompatTaskInfo.java
@@ -32,6 +32,11 @@
public boolean topActivityEligibleForLetterboxEducation;
/**
+ * Whether the letterbox education is enabled
+ */
+ public boolean isLetterboxEducationEnabled;
+
+ /**
* Whether the direct top activity is in size compat mode on foreground.
*/
public boolean topActivityInSizeCompat;
@@ -178,6 +183,7 @@
== that.topActivityEligibleForUserAspectRatioButton
&& topActivityEligibleForLetterboxEducation
== that.topActivityEligibleForLetterboxEducation
+ && isLetterboxEducationEnabled == that.isLetterboxEducationEnabled
&& topActivityLetterboxVerticalPosition == that.topActivityLetterboxVerticalPosition
&& topActivityLetterboxHorizontalPosition
== that.topActivityLetterboxHorizontalPosition
@@ -192,6 +198,7 @@
* Reads the AppCompatTaskInfo from a parcel.
*/
void readFromParcel(Parcel source) {
+ isLetterboxEducationEnabled = source.readBoolean();
topActivityInSizeCompat = source.readBoolean();
topActivityEligibleForLetterboxEducation = source.readBoolean();
isLetterboxDoubleTapEnabled = source.readBoolean();
@@ -212,6 +219,7 @@
*/
@Override
public void writeToParcel(Parcel dest, int flags) {
+ dest.writeBoolean(isLetterboxEducationEnabled);
dest.writeBoolean(topActivityInSizeCompat);
dest.writeBoolean(topActivityEligibleForLetterboxEducation);
dest.writeBoolean(isLetterboxDoubleTapEnabled);
@@ -232,6 +240,7 @@
return "AppCompatTaskInfo { topActivityInSizeCompat=" + topActivityInSizeCompat
+ " topActivityEligibleForLetterboxEducation= "
+ topActivityEligibleForLetterboxEducation
+ + "isLetterboxEducationEnabled= " + isLetterboxEducationEnabled
+ " isLetterboxDoubleTapEnabled= " + isLetterboxDoubleTapEnabled
+ " topActivityEligibleForUserAspectRatioButton= "
+ topActivityEligibleForUserAspectRatioButton
diff --git a/core/java/android/app/AppOpsManager.java b/core/java/android/app/AppOpsManager.java
index 2d0f6fc..6865f9c 100644
--- a/core/java/android/app/AppOpsManager.java
+++ b/core/java/android/app/AppOpsManager.java
@@ -482,7 +482,8 @@
UID_STATE_FOREGROUND_SERVICE,
UID_STATE_FOREGROUND,
UID_STATE_BACKGROUND,
- UID_STATE_CACHED
+ UID_STATE_CACHED,
+ UID_STATE_NONEXISTENT
})
public @interface UidState {}
@@ -566,6 +567,12 @@
public static final int MIN_PRIORITY_UID_STATE = UID_STATE_CACHED;
/**
+ * Special uid state: The UID is not running
+ * @hide
+ */
+ public static final int UID_STATE_NONEXISTENT = Integer.MAX_VALUE;
+
+ /**
* Resolves the first unrestricted state given an app op.
* @param op The op to resolve.
* @return The last restricted UID state.
@@ -596,6 +603,7 @@
UID_STATE_FOREGROUND,
UID_STATE_BACKGROUND,
UID_STATE_CACHED
+ // UID_STATE_NONEXISTENT isn't a real UID state, so it is excluded
};
/** @hide */
@@ -615,6 +623,8 @@
return "bg";
case UID_STATE_CACHED:
return "cch";
+ case UID_STATE_NONEXISTENT:
+ return "gone";
default:
return "unknown";
}
@@ -3573,6 +3583,9 @@
return mAttributionTag;
}
+ /**
+ * Persistent device Id of the proxy that noted the op
+ */
@FlaggedApi(Flags.FLAG_DEVICE_ID_IN_OP_PROXY_INFO_ENABLED)
public @Nullable String getDeviceId() { return mDeviceId; }
diff --git a/core/java/android/app/ComponentOptions.java b/core/java/android/app/ComponentOptions.java
index 397477d..0e8e2e3 100644
--- a/core/java/android/app/ComponentOptions.java
+++ b/core/java/android/app/ComponentOptions.java
@@ -18,6 +18,7 @@
import static android.app.ActivityOptions.BackgroundActivityStartMode;
import static android.app.ActivityOptions.MODE_BACKGROUND_ACTIVITY_START_ALLOWED;
+import static android.app.ActivityOptions.MODE_BACKGROUND_ACTIVITY_START_COMPAT;
import static android.app.ActivityOptions.MODE_BACKGROUND_ACTIVITY_START_DENIED;
import static android.app.ActivityOptions.MODE_BACKGROUND_ACTIVITY_START_SYSTEM_DEFINED;
@@ -54,7 +55,7 @@
public static final String KEY_PENDING_INTENT_BACKGROUND_ACTIVITY_ALLOWED_BY_PERMISSION =
"android.pendingIntent.backgroundActivityAllowedByPermission";
- private @Nullable Boolean mPendingIntentBalAllowed = null;
+ private Integer mPendingIntentBalAllowed = MODE_BACKGROUND_ACTIVITY_START_SYSTEM_DEFINED;
private boolean mPendingIntentBalAllowedByPermission = false;
ComponentOptions() {
@@ -65,12 +66,9 @@
// results they want, which is their loss.
opts.setDefusable(true);
- boolean pendingIntentBalAllowedIsSetExplicitly =
- opts.containsKey(KEY_PENDING_INTENT_BACKGROUND_ACTIVITY_ALLOWED);
- if (pendingIntentBalAllowedIsSetExplicitly) {
- mPendingIntentBalAllowed =
- opts.getBoolean(KEY_PENDING_INTENT_BACKGROUND_ACTIVITY_ALLOWED);
- }
+ mPendingIntentBalAllowed =
+ opts.getInt(KEY_PENDING_INTENT_BACKGROUND_ACTIVITY_ALLOWED,
+ MODE_BACKGROUND_ACTIVITY_START_SYSTEM_DEFINED);
setPendingIntentBackgroundActivityLaunchAllowedByPermission(
opts.getBoolean(
KEY_PENDING_INTENT_BACKGROUND_ACTIVITY_ALLOWED_BY_PERMISSION, false));
@@ -85,7 +83,8 @@
* @hide
*/
@Deprecated public void setPendingIntentBackgroundActivityLaunchAllowed(boolean allowed) {
- mPendingIntentBalAllowed = allowed;
+ mPendingIntentBalAllowed = allowed ? MODE_BACKGROUND_ACTIVITY_START_ALLOWED
+ : MODE_BACKGROUND_ACTIVITY_START_DENIED;
}
/**
@@ -98,11 +97,8 @@
* @hide
*/
@Deprecated public boolean isPendingIntentBackgroundActivityLaunchAllowed() {
- if (mPendingIntentBalAllowed == null) {
- // cannot return null, so return the value used up to API level 33 for compatibility
- return true;
- }
- return mPendingIntentBalAllowed;
+ // cannot return all detail, so return the value used up to API level 33 for compatibility
+ return mPendingIntentBalAllowed != MODE_BACKGROUND_ACTIVITY_START_DENIED;
}
/**
@@ -119,16 +115,15 @@
@BackgroundActivityStartMode int state) {
switch (state) {
case MODE_BACKGROUND_ACTIVITY_START_SYSTEM_DEFINED:
- mPendingIntentBalAllowed = null;
- break;
- case MODE_BACKGROUND_ACTIVITY_START_ALLOWED:
- mPendingIntentBalAllowed = true;
- break;
case MODE_BACKGROUND_ACTIVITY_START_DENIED:
- mPendingIntentBalAllowed = false;
+ case MODE_BACKGROUND_ACTIVITY_START_COMPAT:
+ case MODE_BACKGROUND_ACTIVITY_START_ALLOWED:
+ mPendingIntentBalAllowed = state;
break;
default:
- throw new IllegalArgumentException(state + " is not valid");
+ // Assume that future values are some variant of allowing the start.
+ mPendingIntentBalAllowed = MODE_BACKGROUND_ACTIVITY_START_ALLOWED;
+ break;
}
return this;
}
@@ -141,13 +136,7 @@
* @see #setPendingIntentBackgroundActivityStartMode(int)
*/
public @BackgroundActivityStartMode int getPendingIntentBackgroundActivityStartMode() {
- if (mPendingIntentBalAllowed == null) {
- return MODE_BACKGROUND_ACTIVITY_START_SYSTEM_DEFINED;
- } else if (mPendingIntentBalAllowed) {
- return MODE_BACKGROUND_ACTIVITY_START_ALLOWED;
- } else {
- return MODE_BACKGROUND_ACTIVITY_START_DENIED;
- }
+ return mPendingIntentBalAllowed;
}
/**
@@ -170,8 +159,8 @@
/** @hide */
public Bundle toBundle() {
Bundle b = new Bundle();
- if (mPendingIntentBalAllowed != null) {
- b.putBoolean(KEY_PENDING_INTENT_BACKGROUND_ACTIVITY_ALLOWED, mPendingIntentBalAllowed);
+ if (mPendingIntentBalAllowed != MODE_BACKGROUND_ACTIVITY_START_SYSTEM_DEFINED) {
+ b.putInt(KEY_PENDING_INTENT_BACKGROUND_ACTIVITY_ALLOWED, mPendingIntentBalAllowed);
}
if (mPendingIntentBalAllowedByPermission) {
b.putBoolean(KEY_PENDING_INTENT_BACKGROUND_ACTIVITY_ALLOWED_BY_PERMISSION,
diff --git a/core/java/android/app/Dialog.java b/core/java/android/app/Dialog.java
index 0e20138..cd7f1e4 100644
--- a/core/java/android/app/Dialog.java
+++ b/core/java/android/app/Dialog.java
@@ -679,11 +679,13 @@
if (keyCode == KeyEvent.KEYCODE_ESCAPE) {
if (mCancelable) {
cancel();
- } else {
+ event.startTracking();
+ return true;
+ } else if (mWindow.shouldCloseOnTouchOutside()) {
dismiss();
+ event.startTracking();
+ return true;
}
- event.startTracking();
- return true;
}
return false;
diff --git a/core/java/android/app/DreamManager.java b/core/java/android/app/DreamManager.java
index ef6982e..4ac40a1 100644
--- a/core/java/android/app/DreamManager.java
+++ b/core/java/android/app/DreamManager.java
@@ -18,6 +18,7 @@
import static android.Manifest.permission.WRITE_SECURE_SETTINGS;
+import android.annotation.FlaggedApi;
import android.annotation.Nullable;
import android.annotation.RequiresPermission;
import android.annotation.SystemService;
@@ -30,6 +31,7 @@
import android.os.UserHandle;
import android.provider.Settings;
import android.service.dreams.DreamService;
+import android.service.dreams.Flags;
import android.service.dreams.IDreamManager;
/**
@@ -217,4 +219,19 @@
}
return false;
}
+
+ /**
+ * Sets whether the dream is obscured by something.
+ *
+ * @hide
+ */
+ @FlaggedApi(Flags.FLAG_DREAM_HANDLES_BEING_OBSCURED)
+ @RequiresPermission(android.Manifest.permission.WRITE_DREAM_STATE)
+ public void setDreamIsObscured(boolean isObscured) {
+ try {
+ mService.setDreamIsObscured(isObscured);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
}
diff --git a/core/java/android/app/Notification.java b/core/java/android/app/Notification.java
index 9c80659..d8f03b1 100644
--- a/core/java/android/app/Notification.java
+++ b/core/java/android/app/Notification.java
@@ -5704,6 +5704,7 @@
p.headerless(resId == getBaseLayoutResource()
|| resId == getHeadsUpBaseLayoutResource()
|| resId == getCompactHeadsUpBaseLayoutResource()
+ || resId == getMessagingCompactHeadsUpLayoutResource()
|| resId == getMessagingLayoutResource()
|| resId == R.layout.notification_template_material_media);
RemoteViews contentView = new BuilderRemoteViews(mContext.getApplicationInfo(), resId);
@@ -6491,8 +6492,13 @@
// visual regressions.
@SuppressWarnings("AndroidFrameworkCompatChange")
private boolean bigContentViewRequired() {
- if (!Flags.notificationExpansionOptional()
- && mContext.getApplicationInfo().targetSdkVersion >= Build.VERSION_CODES.S) {
+ if (Flags.notificationExpansionOptional()) {
+ // Notifications without a bigContentView, style, or actions do not need to expand
+ boolean exempt = mN.bigContentView == null
+ && mStyle == null && mActions.size() == 0;
+ return !exempt;
+ }
+ if (mContext.getApplicationInfo().targetSdkVersion >= Build.VERSION_CODES.S) {
return true;
}
// Notifications with contentView and without a bigContentView, style, or actions would
@@ -7294,6 +7300,10 @@
return R.layout.notification_template_material_compact_heads_up_base;
}
+ private int getMessagingCompactHeadsUpLayoutResource() {
+ return R.layout.notification_template_material_messaging_compact_heads_up;
+ }
+
private int getBigBaseLayoutResource() {
return R.layout.notification_template_material_big_base;
}
@@ -8882,6 +8892,62 @@
}
}
+ private void fixTitleAndTextForCompactMessaging(StandardTemplateParams p) {
+ Message m = findLatestIncomingMessage();
+ final CharSequence text = (m == null) ? null : m.mText;
+ CharSequence sender = m == null ? null
+ : m.mSender == null || TextUtils.isEmpty(m.mSender.getName())
+ ? mUser.getName() : m.mSender.getName();
+
+ CharSequence conversationTitle = mIsGroupConversation ? mConversationTitle : null;
+
+ // we want to have colon for possible title for conversation.
+ final BidiFormatter bidi = BidiFormatter.getInstance();
+ if (sender != null) {
+ sender = mBuilder.mContext.getString(
+ com.android.internal.R.string.notification_messaging_title_template,
+ bidi.unicodeWrap(sender), "");
+ } else if (conversationTitle != null) {
+ conversationTitle = mBuilder.mContext.getString(
+ com.android.internal.R.string.notification_messaging_title_template,
+ bidi.unicodeWrap(conversationTitle), "");
+ }
+
+ if (Flags.cleanUpSpansAndNewLines()) {
+ conversationTitle = stripStyling(conversationTitle);
+ sender = stripStyling(sender);
+ }
+
+ final boolean showOnlySenderName = showOnlySenderName();
+
+ final CharSequence title;
+ boolean isConversationTitleAvailable = !showOnlySenderName && conversationTitle != null;
+ if (isConversationTitleAvailable) {
+ title = conversationTitle;
+ } else {
+ title = sender;
+ }
+
+ p.title(title);
+ // when the conversation title is available, use headerTextSecondary for sender and
+ // summaryText for text
+ if (isConversationTitleAvailable) {
+ p.headerTextSecondary(sender);
+ p.summaryText(text);
+ } else {
+ // when it is not, use headerTextSecondary for text and don't use summaryText
+ p.headerTextSecondary(text);
+ p.summaryText(null);
+ }
+ }
+
+ /** developer settings to always show sender name */
+ private boolean showOnlySenderName() {
+ return SystemProperties.getBoolean(
+ "persist.compact_heads_up_notification.show_only_sender_name",
+ false);
+ }
+
/**
* @hide
*/
@@ -9166,10 +9232,88 @@
@Nullable
@Override
public RemoteViews makeCompactHeadsUpContentView() {
- // TODO(b/336229954): Apply minimal HUN treatment to Messaging Notifications.
- return makeHeadsUpContentView(false);
+ final boolean isConversationLayout = mConversationType != CONVERSATION_TYPE_LEGACY;
+ Icon conversationIcon = null;
+ Notification.Action remoteInputAction = null;
+ if (isConversationLayout) {
+
+ conversationIcon = mShortcutIcon;
+
+ // conversation icon is m
+ // Extract the conversation icon for one to one conversations from
+ // the latest incoming message since
+ // fixTitleAndTextExtras also uses it as data source for title and text
+ if (conversationIcon == null && !mIsGroupConversation) {
+ final Message message = findLatestIncomingMessage();
+ if (message != null) {
+ final Person sender = message.mSender;
+ if (sender != null) {
+ conversationIcon = sender.getIcon();
+ }
+ }
+ }
+
+ if (Flags.compactHeadsUpNotificationReply()) {
+ // Get the first non-contextual inline reply action.
+ final List<Notification.Action> nonContextualActions =
+ mBuilder.getNonContextualActions();
+ for (int i = 0; i < nonContextualActions.size(); i++) {
+ final Notification.Action action = nonContextualActions.get(i);
+ if (mBuilder.hasValidRemoteInput(action)) {
+ remoteInputAction = action;
+ break;
+ }
+ }
+ }
+ }
+
+ final StandardTemplateParams p = mBuilder.mParams.reset()
+ .viewType(StandardTemplateParams.VIEW_TYPE_HEADS_UP)
+ .highlightExpander(isConversationLayout)
+ .fillTextsFrom(mBuilder)
+ .hideTime(true);
+
+ fixTitleAndTextForCompactMessaging(p);
+ TemplateBindResult bindResult = new TemplateBindResult();
+
+ RemoteViews contentView = mBuilder.applyStandardTemplate(
+ mBuilder.getMessagingCompactHeadsUpLayoutResource(), p, bindResult);
+ contentView.setViewVisibility(R.id.header_text_secondary_divider, View.GONE);
+ contentView.setViewVisibility(R.id.header_text_divider, View.GONE);
+ if (conversationIcon != null) {
+ contentView.setViewVisibility(R.id.icon, View.GONE);
+ contentView.setViewVisibility(R.id.conversation_face_pile, View.GONE);
+ contentView.setViewVisibility(R.id.conversation_icon, View.VISIBLE);
+ contentView.setBoolean(R.id.conversation_icon, "setApplyCircularCrop", true);
+ contentView.setImageViewIcon(R.id.conversation_icon, conversationIcon);
+ } else if (mIsGroupConversation) {
+ contentView.setViewVisibility(R.id.icon, View.GONE);
+ contentView.setViewVisibility(R.id.conversation_icon, View.GONE);
+ contentView.setInt(R.id.status_bar_latest_event_content,
+ "setNotificationBackgroundColor", mBuilder.getBackgroundColor(p));
+ contentView.setInt(R.id.status_bar_latest_event_content, "setLayoutColor",
+ mBuilder.getSmallIconColor(p));
+ contentView.setBundle(R.id.status_bar_latest_event_content, "setGroupFacePile",
+ mBuilder.mN.extras);
+ }
+
+ if (remoteInputAction != null) {
+ contentView.setViewVisibility(R.id.reply_action_container, View.VISIBLE);
+
+ final RemoteViews inlineReplyButton =
+ mBuilder.generateActionButton(remoteInputAction, false, p);
+ // Clear the drawable
+ inlineReplyButton.setInt(R.id.action0, "setBackgroundResource", 0);
+ inlineReplyButton.setTextViewText(R.id.action0,
+ mBuilder.mContext.getString(R.string.notification_compact_heads_up_reply));
+ contentView.addView(R.id.reply_action_container, inlineReplyButton);
+ } else {
+ contentView.setViewVisibility(R.id.reply_action_container, View.GONE);
+ }
+ return contentView;
}
+
/**
* @hide
*/
diff --git a/core/java/android/app/NotificationManager.java b/core/java/android/app/NotificationManager.java
index b82a1e3..e4310c1 100644
--- a/core/java/android/app/NotificationManager.java
+++ b/core/java/android/app/NotificationManager.java
@@ -2972,6 +2972,7 @@
android.Manifest.permission.INTERACT_ACROSS_USERS,
android.Manifest.permission.ACCESS_NOTIFICATIONS})
@FlaggedApi(android.service.notification.Flags.FLAG_CALLSTYLE_CALLBACK_API)
+ @SuppressLint("UserHandle")
public void registerCallNotificationEventListener(@NonNull String packageName,
@NonNull UserHandle userHandle, @NonNull @CallbackExecutor Executor executor,
@NonNull CallNotificationEventListener listener) {
diff --git a/core/java/android/app/OWNERS b/core/java/android/app/OWNERS
index 97c2e43..2e38c06 100644
--- a/core/java/android/app/OWNERS
+++ b/core/java/android/app/OWNERS
@@ -60,6 +60,9 @@
# ComponentCaller
per-file ComponentCaller.java = file:COMPONENT_CALLER_OWNERS
+# DreamManager
+per-file DreamManager.java = file:/DREAM_MANAGER_OWNERS
+
# GrammaticalInflectionManager
per-file *GrammaticalInflection* = file:/services/core/java/com/android/server/grammaticalinflection/OWNERS
per-file grammatical_inflection_manager.aconfig = file:/services/core/java/com/android/server/grammaticalinflection/OWNERS
diff --git a/core/java/android/app/TEST_MAPPING b/core/java/android/app/TEST_MAPPING
index a29c196..0deb842 100644
--- a/core/java/android/app/TEST_MAPPING
+++ b/core/java/android/app/TEST_MAPPING
@@ -378,6 +378,14 @@
}
],
"file_patterns": ["(/|^)ContextImpl.java"]
+ },
+ {
+ "file_patterns": [
+ "(/|^)Activity.*.java",
+ "(/|^)PendingIntent.java",
+ "(/|^)ComtextImpl.java"
+ ],
+ "name": "CtsWindowManagerBackgroundActivityTestCases"
}
],
"postsubmit": [
diff --git a/core/java/android/app/admin/DevicePolicyManager.java b/core/java/android/app/admin/DevicePolicyManager.java
index 69f29f3..c529f7d 100644
--- a/core/java/android/app/admin/DevicePolicyManager.java
+++ b/core/java/android/app/admin/DevicePolicyManager.java
@@ -1698,7 +1698,7 @@
/**
* A boolean extra indicating whether device encryption can be skipped as part of
- * <a href="#managed-provisioning>provisioning</a>.
+ * <a href="#managed-provisioning">provisioning</a>.
*
* <p>Use in an NFC record with {@link #MIME_TYPE_PROVISIONING_NFC} or an intent with action
* {@link #ACTION_PROVISION_MANAGED_DEVICE} that starts device owner provisioning.
@@ -10427,7 +10427,7 @@
@WorkerThread
public void setApplicationRestrictions(@Nullable ComponentName admin, String packageName,
Bundle settings) {
- if (!Flags.dmrhCanSetAppRestriction()) {
+ if (!Flags.dmrhSetAppRestrictions()) {
throwIfParentInstance("setApplicationRestrictions");
}
@@ -11835,7 +11835,7 @@
@WorkerThread
public @NonNull Bundle getApplicationRestrictions(
@Nullable ComponentName admin, String packageName) {
- if (!Flags.dmrhCanSetAppRestriction()) {
+ if (!Flags.dmrhSetAppRestrictions()) {
throwIfParentInstance("getApplicationRestrictions");
}
@@ -14120,7 +14120,7 @@
public @NonNull DevicePolicyManager getParentProfileInstance(@NonNull ComponentName admin) {
throwIfParentInstance("getParentProfileInstance");
try {
- if (Flags.dmrhCanSetAppRestriction()) {
+ if (Flags.dmrhSetAppRestrictions()) {
UserManager um = mContext.getSystemService(UserManager.class);
if (!um.isManagedProfile()) {
throw new SecurityException("The current user does not have a parent profile.");
diff --git a/core/java/android/app/admin/flags/flags.aconfig b/core/java/android/app/admin/flags/flags.aconfig
index 3d6ec19..4154e66 100644
--- a/core/java/android/app/admin/flags/flags.aconfig
+++ b/core/java/android/app/admin/flags/flags.aconfig
@@ -244,10 +244,13 @@
}
flag {
- name: "dmrh_can_set_app_restriction"
+ name: "dmrh_set_app_restrictions"
namespace: "enterprise"
description: "Allow DMRH to set application restrictions (both on the profile and the parent)"
bug: "328758346"
+ metadata {
+ purpose: PURPOSE_BUGFIX
+ }
}
flag {
diff --git a/core/java/android/app/notification.aconfig b/core/java/android/app/notification.aconfig
index 50c7b7f..6ceae17 100644
--- a/core/java/android/app/notification.aconfig
+++ b/core/java/android/app/notification.aconfig
@@ -160,3 +160,10 @@
description: "[Minimal HUN] Enables the compact heads up notification feature"
bug: "270709257"
}
+
+flag {
+ name: "compact_heads_up_notification_reply"
+ namespace: "systemui"
+ description: "[Minimal HUN] Enables the compact heads up notification reply capability for Conversation Notifications"
+ bug: "336229954"
+}
\ No newline at end of file
diff --git a/core/java/android/app/ondeviceintelligence/OnDeviceIntelligenceManager.java b/core/java/android/app/ondeviceintelligence/OnDeviceIntelligenceManager.java
index 5e1c1e0..a37f51b 100644
--- a/core/java/android/app/ondeviceintelligence/OnDeviceIntelligenceManager.java
+++ b/core/java/android/app/ondeviceintelligence/OnDeviceIntelligenceManager.java
@@ -529,7 +529,6 @@
* {@link Bundle}s annotated with this type will be validated that they are in-effect read-only
* when passed via Binder IPC. Following restrictions apply :
* <ul>
- * <li> No Nested Bundles are allowed.</li>
* <li> {@link PersistableBundle}s are allowed.</li>
* <li> Any primitive types or their collections can be added as usual.</li>
* <li>IBinder objects should *not* be added.</li>
diff --git a/core/java/android/app/prediction/AppPredictionContext.java b/core/java/android/app/prediction/AppPredictionContext.java
index 99fa869..1b718d4 100644
--- a/core/java/android/app/prediction/AppPredictionContext.java
+++ b/core/java/android/app/prediction/AppPredictionContext.java
@@ -24,6 +24,8 @@
import android.os.Parcel;
import android.os.Parcelable;
+import java.util.Objects;
+
/**
* Class that provides contextual information about the environment in which the app prediction is
* used, such as package name, UI in which the app targets are shown, and number of targets.
@@ -99,6 +101,13 @@
}
@Override
+ public int hashCode() {
+ int hashCode = Objects.hash(mUiSurface, mPackageName);
+ hashCode = 31 * hashCode + mPredictedTargetCount;
+ return hashCode;
+ }
+
+ @Override
public int describeContents() {
return 0;
}
diff --git a/core/java/android/app/prediction/AppTarget.java b/core/java/android/app/prediction/AppTarget.java
index fef9e70..25c1a59 100644
--- a/core/java/android/app/prediction/AppTarget.java
+++ b/core/java/android/app/prediction/AppTarget.java
@@ -167,6 +167,16 @@
}
@Override
+ public int hashCode() {
+ int hashCode = Objects.hash(mId, mPackageName, mClassName, mUser);
+ if (mShortcutInfo != null) {
+ hashCode = 31 * hashCode + mShortcutInfo.getId().hashCode();
+ }
+ hashCode = 31 * hashCode + mRank;
+ return hashCode;
+ }
+
+ @Override
public int describeContents() {
return 0;
}
diff --git a/core/java/android/app/prediction/AppTargetEvent.java b/core/java/android/app/prediction/AppTargetEvent.java
index 91da8ec..e36d878 100644
--- a/core/java/android/app/prediction/AppTargetEvent.java
+++ b/core/java/android/app/prediction/AppTargetEvent.java
@@ -24,6 +24,7 @@
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
+import java.util.Objects;
/**
* A representation of an app target event.
@@ -116,6 +117,13 @@
}
@Override
+ public int hashCode() {
+ int hashCode = Objects.hash(mTarget, mLocation);
+ hashCode = 31 * hashCode + mAction;
+ return hashCode;
+ }
+
+ @Override
public int describeContents() {
return 0;
}
diff --git a/core/java/android/app/prediction/OWNERS b/core/java/android/app/prediction/OWNERS
index fe012da..73168fb 100644
--- a/core/java/android/app/prediction/OWNERS
+++ b/core/java/android/app/prediction/OWNERS
@@ -1,2 +1,4 @@
+pinyaoting@google.com
+hyunyoungs@google.com
adamcohen@google.com
sunnygoyal@google.com
diff --git a/core/java/android/app/servertransaction/ClientTransactionListenerController.java b/core/java/android/app/servertransaction/ClientTransactionListenerController.java
index cda2867..9b53461 100644
--- a/core/java/android/app/servertransaction/ClientTransactionListenerController.java
+++ b/core/java/android/app/servertransaction/ClientTransactionListenerController.java
@@ -33,11 +33,13 @@
import android.os.IBinder;
import android.util.ArrayMap;
import android.util.ArraySet;
+import android.util.Log;
import android.window.ActivityWindowInfo;
import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.VisibleForTesting;
+import java.util.concurrent.RejectedExecutionException;
import java.util.function.BiConsumer;
/**
@@ -47,6 +49,8 @@
*/
public class ClientTransactionListenerController {
+ private static final String TAG = "ClientTransactionListenerController";
+
private static ClientTransactionListenerController sController;
private final Object mLock = new Object();
@@ -179,10 +183,14 @@
}
// Dispatch the display changed callbacks.
- final int displayCount = configUpdatedDisplayIds.size();
- for (int i = 0; i < displayCount; i++) {
- final int displayId = configUpdatedDisplayIds.valueAt(i);
- onDisplayChanged(displayId);
+ try {
+ final int displayCount = configUpdatedDisplayIds.size();
+ for (int i = 0; i < displayCount; i++) {
+ final int displayId = configUpdatedDisplayIds.valueAt(i);
+ onDisplayChanged(displayId);
+ }
+ } catch (RejectedExecutionException e) {
+ Log.w(TAG, "Failed to notify DisplayListener because the Handler is shutting down");
}
}
@@ -222,7 +230,11 @@
}
if (changedDisplayId != INVALID_DISPLAY) {
- onDisplayChanged(changedDisplayId);
+ try {
+ onDisplayChanged(changedDisplayId);
+ } catch (RejectedExecutionException e) {
+ Log.w(TAG, "Failed to notify DisplayListener because the Handler is shutting down");
+ }
}
}
@@ -235,9 +247,11 @@
/**
* Called when receives a {@link Configuration} changed event that is updating display-related
* window configuration.
+ *
+ * @throws RejectedExecutionException if the display listener handler is closing.
*/
@VisibleForTesting
- public void onDisplayChanged(int displayId) {
+ public void onDisplayChanged(int displayId) throws RejectedExecutionException {
mDisplayManager.handleDisplayChangeFromWindowManager(displayId);
}
}
diff --git a/core/java/android/appwidget/AppWidgetManager.java b/core/java/android/appwidget/AppWidgetManager.java
index 57b5c13..3213b40 100644
--- a/core/java/android/appwidget/AppWidgetManager.java
+++ b/core/java/android/appwidget/AppWidgetManager.java
@@ -1041,6 +1041,7 @@
*/
public AppWidgetProviderInfo getAppWidgetInfo(int appWidgetId) {
if (mService == null) {
+ Log.e(TAG, "Service wasn't initialized, appWidgetId=" + appWidgetId);
return null;
}
try {
@@ -1048,6 +1049,9 @@
if (info != null) {
// Converting complex to dp.
info.updateDimensions(mDisplayMetrics);
+ } else {
+ Log.e(TAG, "App widget provider info is null. PackageName=" + mPackageName
+ + " appWidgetId-" + appWidgetId);
}
return info;
} catch (RemoteException e) {
diff --git a/core/java/android/companion/virtual/flags.aconfig b/core/java/android/companion/virtual/flags.aconfig
index 3e23762..b29b52d 100644
--- a/core/java/android/companion/virtual/flags.aconfig
+++ b/core/java/android/companion/virtual/flags.aconfig
@@ -127,4 +127,15 @@
metadata {
purpose: PURPOSE_BUGFIX
}
+}
+
+flag {
+ name: "impulse_velocity_strategy_for_touch_navigation"
+ is_exported: true
+ namespace: "virtual_devices"
+ description: "Use impulse velocity strategy during conversion of touch navigation flings into Dpad events"
+ bug: "338426241"
+ metadata {
+ purpose: PURPOSE_BUGFIX
+ }
}
\ No newline at end of file
diff --git a/core/java/android/content/Intent.java b/core/java/android/content/Intent.java
index 9e316a2..c8cae82 100644
--- a/core/java/android/content/Intent.java
+++ b/core/java/android/content/Intent.java
@@ -4353,13 +4353,6 @@
"android.intent.extra.BRIGHTNESS_DIALOG_IS_FULL_WIDTH";
/**
- * Activity Action: Shows the contrast setting dialog.
- * @hide
- */
- public static final String ACTION_SHOW_CONTRAST_DIALOG =
- "com.android.intent.action.SHOW_CONTRAST_DIALOG";
-
- /**
* Broadcast Action: A global button was pressed. Includes a single
* extra field, {@link #EXTRA_KEY_EVENT}, containing the key event that
* caused the broadcast.
diff --git a/core/java/android/content/IntentSender.java b/core/java/android/content/IntentSender.java
index b074e8b..2e252c1 100644
--- a/core/java/android/content/IntentSender.java
+++ b/core/java/android/content/IntentSender.java
@@ -60,6 +60,10 @@
* {@link android.app.PendingIntent#getIntentSender() PendingIntent.getIntentSender()}.
*/
public class IntentSender implements Parcelable {
+ private static final Bundle SEND_INTENT_DEFAULT_OPTIONS =
+ ActivityOptions.makeBasic().setPendingIntentBackgroundActivityStartMode(
+ ActivityOptions.MODE_BACKGROUND_ACTIVITY_START_COMPAT).toBundle();
+
@UnsupportedAppUsage
private final IIntentSender mTarget;
IBinder mWhitelistToken;
@@ -161,7 +165,8 @@
*/
public void sendIntent(Context context, int code, Intent intent,
OnFinished onFinished, Handler handler) throws SendIntentException {
- sendIntent(context, code, intent, onFinished, handler, null, null /* options */);
+ sendIntent(context, code, intent, onFinished, handler, null,
+ SEND_INTENT_DEFAULT_OPTIONS);
}
/**
@@ -194,7 +199,7 @@
OnFinished onFinished, Handler handler, String requiredPermission)
throws SendIntentException {
sendIntent(context, code, intent, onFinished, handler, requiredPermission,
- null /* options */);
+ SEND_INTENT_DEFAULT_OPTIONS);
}
/**
diff --git a/core/java/android/content/TEST_MAPPING b/core/java/android/content/TEST_MAPPING
index a2cfbf5..41a4288 100644
--- a/core/java/android/content/TEST_MAPPING
+++ b/core/java/android/content/TEST_MAPPING
@@ -56,6 +56,10 @@
}
],
"file_patterns": ["(/|^)Context.java", "(/|^)ContextWrapper.java"]
+ },
+ {
+ "name": "CtsWindowManagerBackgroundActivityTestCases",
+ "file_patterns": ["(/|^)IntentSender.java"]
}
],
"ravenwood-presubmit": [
@@ -63,5 +67,7 @@
"name": "CtsContentTestCasesRavenwood",
"host": true
}
+ ],
+ "postsubmit": [
]
}
diff --git a/core/java/android/content/pm/PackageManager.java b/core/java/android/content/pm/PackageManager.java
index 83285e0..c506c97 100644
--- a/core/java/android/content/pm/PackageManager.java
+++ b/core/java/android/content/pm/PackageManager.java
@@ -270,11 +270,20 @@
/**
* Application level {@link android.content.pm.PackageManager.Property PackageManager
* .Property} for a app to inform the installer that a file containing the app's android
- * safety label data is bundled into the APK at the given path.
+ * safety label data is bundled into the APK as a raw resource.
+ *
+ * <p>For example:
+ * <pre>
+ * <application>
+ * <property
+ * android:name="android.content.PROPERTY_ANDROID_SAFETY_LABEL"
+ * android:resource="@raw/app-metadata"/>
+ * </application>
+ * </pre>
* @hide
*/
- public static final String PROPERTY_ANDROID_SAFETY_LABEL_PATH =
- "android.content.SAFETY_LABEL_PATH";
+ public static final String PROPERTY_ANDROID_SAFETY_LABEL =
+ "android.content.PROPERTY_ANDROID_SAFETY_LABEL";
/**
* A property value set within the manifest.
diff --git a/core/java/android/content/pm/PackageParser.java b/core/java/android/content/pm/PackageParser.java
index 4b579e7..1f6730b 100644
--- a/core/java/android/content/pm/PackageParser.java
+++ b/core/java/android/content/pm/PackageParser.java
@@ -2628,6 +2628,15 @@
return Build.VERSION_CODES.CUR_DEVELOPMENT;
}
+ // STOPSHIP: hack for the pre-release SDK
+ if (platformSdkCodenames.length == 0
+ && Build.VERSION.KNOWN_CODENAMES.stream().max(String::compareTo).orElse("").equals(
+ targetCode)) {
+ Slog.w(TAG, "Package requires development platform " + targetCode
+ + ", returning current version " + Build.VERSION.SDK_INT);
+ return Build.VERSION.SDK_INT;
+ }
+
// Otherwise, we're looking at an incompatible pre-release SDK.
if (platformSdkCodenames.length > 0) {
outError[0] = "Requires development platform " + targetCode
@@ -2699,6 +2708,15 @@
return Build.VERSION_CODES.CUR_DEVELOPMENT;
}
+ // STOPSHIP: hack for the pre-release SDK
+ if (platformSdkCodenames.length == 0
+ && Build.VERSION.KNOWN_CODENAMES.stream().max(String::compareTo).orElse("").equals(
+ minCode)) {
+ Slog.w(TAG, "Package requires min development platform " + minCode
+ + ", returning current version " + Build.VERSION.SDK_INT);
+ return Build.VERSION.SDK_INT;
+ }
+
// Otherwise, we're looking at an incompatible pre-release SDK.
if (platformSdkCodenames.length > 0) {
outError[0] = "Requires development platform " + minCode
diff --git a/core/java/android/content/pm/multiuser.aconfig b/core/java/android/content/pm/multiuser.aconfig
index 83742eb..e2a131c 100644
--- a/core/java/android/content/pm/multiuser.aconfig
+++ b/core/java/android/content/pm/multiuser.aconfig
@@ -247,3 +247,13 @@
description: "Allow MAIN user to access blocked number provider"
bug: "338579331"
}
+
+flag {
+ name: "restrict_quiet_mode_credential_bug_fix_to_managed_profiles"
+ namespace: "profile_experiences"
+ description: "Use user states to check the state of quiet mode for managed profiles only"
+ bug: "332812630"
+ metadata {
+ purpose: PURPOSE_BUGFIX
+ }
+}
diff --git a/core/java/android/content/pm/parsing/FrameworkParsingPackageUtils.java b/core/java/android/content/pm/parsing/FrameworkParsingPackageUtils.java
index 153dd9a..c7403c0 100644
--- a/core/java/android/content/pm/parsing/FrameworkParsingPackageUtils.java
+++ b/core/java/android/content/pm/parsing/FrameworkParsingPackageUtils.java
@@ -316,6 +316,15 @@
return input.success(Build.VERSION_CODES.CUR_DEVELOPMENT);
}
+ // STOPSHIP: hack for the pre-release SDK
+ if (platformSdkCodenames.length == 0
+ && Build.VERSION.KNOWN_CODENAMES.stream().max(String::compareTo).orElse("").equals(
+ minCode)) {
+ Slog.w(TAG, "Parsed package requires min development platform " + minCode
+ + ", returning current version " + Build.VERSION.SDK_INT);
+ return input.success(Build.VERSION.SDK_INT);
+ }
+
// Otherwise, we're looking at an incompatible pre-release SDK.
if (platformSdkCodenames.length > 0) {
return input.error(PackageManager.INSTALL_FAILED_OLDER_SDK,
@@ -368,19 +377,27 @@
return input.success(targetVers);
}
+ // If it's a pre-release SDK and the codename matches this platform, it
+ // definitely targets this SDK.
+ if (matchTargetCode(platformSdkCodenames, targetCode)) {
+ return input.success(Build.VERSION_CODES.CUR_DEVELOPMENT);
+ }
+
+ // STOPSHIP: hack for the pre-release SDK
+ if (platformSdkCodenames.length == 0
+ && Build.VERSION.KNOWN_CODENAMES.stream().max(String::compareTo).orElse("").equals(
+ targetCode)) {
+ Slog.w(TAG, "Parsed package requires development platform " + targetCode
+ + ", returning current version " + Build.VERSION.SDK_INT);
+ return input.success(Build.VERSION.SDK_INT);
+ }
+
try {
if (allowUnknownCodenames && UnboundedSdkLevel.isAtMost(targetCode)) {
return input.success(Build.VERSION_CODES.CUR_DEVELOPMENT);
}
} catch (IllegalArgumentException e) {
- // isAtMost() throws it when encountering an older SDK codename
- return input.error(PackageManager.INSTALL_FAILED_OLDER_SDK, e.getMessage());
- }
-
- // If it's a pre-release SDK and the codename matches this platform, it
- // definitely targets this SDK.
- if (matchTargetCode(platformSdkCodenames, targetCode)) {
- return input.success(Build.VERSION_CODES.CUR_DEVELOPMENT);
+ return input.error(PackageManager.INSTALL_FAILED_OLDER_SDK, "Bad package SDK");
}
// Otherwise, we're looking at an incompatible pre-release SDK.
diff --git a/core/java/android/content/res/Configuration.java b/core/java/android/content/res/Configuration.java
index 885f4c5..982224b 100644
--- a/core/java/android/content/res/Configuration.java
+++ b/core/java/android/content/res/Configuration.java
@@ -806,7 +806,7 @@
*
* <aside class="note"><b>Note:</b> If the app targets
* {@link android.os.Build.VERSION_CODES#VANILLA_ICE_CREAM}
- * or after, The width measurement reflects the window size without excluding insets.
+ * or after, the width measurement reflects the window size without excluding insets.
* Otherwise, the measurement excludes window insets even when the app is displayed edge to edge
* using {@link android.view.Window#setDecorFitsSystemWindows(boolean)
* Window#setDecorFitsSystemWindows(boolean)}.</aside>
diff --git a/core/java/android/database/sqlite/SQLiteDatabase.java b/core/java/android/database/sqlite/SQLiteDatabase.java
index d683d72..1eb466c 100644
--- a/core/java/android/database/sqlite/SQLiteDatabase.java
+++ b/core/java/android/database/sqlite/SQLiteDatabase.java
@@ -731,6 +731,7 @@
* commits, or is rolled back, either explicitly or by a call to
* {@link #yieldIfContendedSafely}.
*/
+ // TODO(340874899) Provide an Executor overload
public void beginTransactionWithListener(
@Nullable SQLiteTransactionListener transactionListener) {
beginTransaction(transactionListener, true);
@@ -760,6 +761,7 @@
* transaction begins, commits, or is rolled back, either
* explicitly or by a call to {@link #yieldIfContendedSafely}.
*/
+ // TODO(340874899) Provide an Executor overload
public void beginTransactionWithListenerNonExclusive(
@Nullable SQLiteTransactionListener transactionListener) {
beginTransaction(transactionListener, false);
@@ -785,6 +787,8 @@
* }
* </pre>
*/
+ // TODO(340874899) Provide an Executor overload
+ @SuppressLint("ExecutorRegistration")
@FlaggedApi(Flags.FLAG_SQLITE_APIS_35)
public void beginTransactionWithListenerReadOnly(
@Nullable SQLiteTransactionListener transactionListener) {
diff --git a/core/java/android/hardware/camera2/CameraManager.java b/core/java/android/hardware/camera2/CameraManager.java
index 2b7d8f1..708f8a1 100644
--- a/core/java/android/hardware/camera2/CameraManager.java
+++ b/core/java/android/hardware/camera2/CameraManager.java
@@ -39,7 +39,6 @@
import android.content.pm.PackageManager;
import android.graphics.Point;
import android.hardware.CameraExtensionSessionStats;
-import android.hardware.CameraIdRemapping;
import android.hardware.CameraStatus;
import android.hardware.ICameraService;
import android.hardware.ICameraServiceListener;
@@ -1996,17 +1995,6 @@
}
/**
- * Remaps Camera Ids in the CameraService.
- *
- * @hide
- */
- @RequiresPermission(android.Manifest.permission.CAMERA_INJECT_EXTERNAL_CAMERA)
- public void remapCameraIds(@NonNull CameraIdRemapping cameraIdRemapping)
- throws CameraAccessException, SecurityException, IllegalArgumentException {
- CameraManagerGlobal.get().remapCameraIds(cameraIdRemapping);
- }
-
- /**
* Injects session params into existing clients in the CameraService.
*
* @param cameraId The camera id of client to inject session params into.
@@ -2117,13 +2105,6 @@
private final Object mLock = new Object();
- /**
- * The active CameraIdRemapping. This will be used to refresh the cameraIdRemapping state
- * in the CameraService every time we connect to it, including when the CameraService
- * Binder dies and we reconnect to it.
- */
- @Nullable private CameraIdRemapping mActiveCameraIdRemapping;
-
// Access only through getCameraService to deal with binder death
private ICameraService mCameraService;
private boolean mHasOpenCloseListenerPermission = false;
@@ -2275,41 +2256,6 @@
} catch (RemoteException e) {
// Camera service died in all probability
}
-
- if (mActiveCameraIdRemapping != null) {
- try {
- cameraService.remapCameraIds(mActiveCameraIdRemapping);
- } catch (ServiceSpecificException e) {
- // Unexpected failure, ignore and continue.
- Log.e(TAG, "Unable to remap camera Ids in the camera service");
- } catch (RemoteException e) {
- // Camera service died in all probability
- }
- }
- }
-
- /** Updates the cameraIdRemapping state in the CameraService. */
- public void remapCameraIds(@NonNull CameraIdRemapping cameraIdRemapping)
- throws CameraAccessException, SecurityException {
- synchronized (mLock) {
- ICameraService cameraService = getCameraService();
- if (cameraService == null) {
- throw new CameraAccessException(
- CameraAccessException.CAMERA_DISCONNECTED,
- "Camera service is currently unavailable.");
- }
-
- try {
- cameraService.remapCameraIds(cameraIdRemapping);
- mActiveCameraIdRemapping = cameraIdRemapping;
- } catch (ServiceSpecificException e) {
- throw ExceptionUtils.throwAsPublicException(e);
- } catch (RemoteException e) {
- throw new CameraAccessException(
- CameraAccessException.CAMERA_DISCONNECTED,
- "Camera service is currently unavailable.");
- }
- }
}
/** Injects session params into an existing client for cameraid. */
diff --git a/core/java/android/hardware/camera2/CameraMetadata.java b/core/java/android/hardware/camera2/CameraMetadata.java
index 7754e32..de26384 100644
--- a/core/java/android/hardware/camera2/CameraMetadata.java
+++ b/core/java/android/hardware/camera2/CameraMetadata.java
@@ -2359,7 +2359,10 @@
* FPS.</p>
* <p>If the session configuration is not supported, the AE mode reported in the
* CaptureResult will be 'ON' instead of 'ON_LOW_LIGHT_BOOST_BRIGHTNESS_PRIORITY'.</p>
- * <p>The application can observe the CapturerResult field
+ * <p>When this AE mode is enabled, the CaptureResult field
+ * {@link CaptureResult#CONTROL_LOW_LIGHT_BOOST_STATE android.control.lowLightBoostState} will be present and not null. Otherwise, the
+ * {@link CaptureResult#CONTROL_LOW_LIGHT_BOOST_STATE android.control.lowLightBoostState} field will not be present in the CaptureResult.</p>
+ * <p>The application can observe the CaptureResult field
* {@link CaptureResult#CONTROL_LOW_LIGHT_BOOST_STATE android.control.lowLightBoostState} to determine when low light boost is 'ACTIVE' or
* 'INACTIVE'.</p>
* <p>The low light boost is 'ACTIVE' once the scene lighting condition is less than the
diff --git a/core/java/android/hardware/camera2/CaptureResult.java b/core/java/android/hardware/camera2/CaptureResult.java
index 5765a73..1460515 100644
--- a/core/java/android/hardware/camera2/CaptureResult.java
+++ b/core/java/android/hardware/camera2/CaptureResult.java
@@ -2819,6 +2819,8 @@
* <p>When low light boost is enabled by setting the AE mode to
* 'ON_LOW_LIGHT_BOOST_BRIGHTNESS_PRIORITY', it can dynamically apply a low light
* boost when the light level threshold is exceeded.</p>
+ * <p>This field is present in the CaptureResult when the AE mode is set to
+ * 'ON_LOW_LIGHT_BOOST_BRIGHTNESS_PRIORITY'. Otherwise, the field is not present.</p>
* <p>This state indicates when low light boost is 'ACTIVE' and applied. Similarly, it can
* indicate when it is not being applied by returning 'INACTIVE'.</p>
* <p>This key will be absent from the CaptureResult if AE mode is not set to
diff --git a/core/java/android/hardware/face/FaceManager.java b/core/java/android/hardware/face/FaceManager.java
index d340f3f..3f2ef84 100644
--- a/core/java/android/hardware/face/FaceManager.java
+++ b/core/java/android/hardware/face/FaceManager.java
@@ -732,6 +732,10 @@
/**
* Get statically configured sensor properties.
+ * @deprecated Generally unsafe to use, use
+ * {@link FaceManager#addAuthenticatorsRegisteredCallback} API instead.
+ * In most cases this method will work as expected, but during early boot up, it will be
+ * null/empty and there is no way for the caller to know when it's actual value is ready.
* @hide
*/
@RequiresPermission(USE_BIOMETRIC_INTERNAL)
diff --git a/core/java/android/hardware/fingerprint/FingerprintManager.java b/core/java/android/hardware/fingerprint/FingerprintManager.java
index 25bfb2a..2ded615 100644
--- a/core/java/android/hardware/fingerprint/FingerprintManager.java
+++ b/core/java/android/hardware/fingerprint/FingerprintManager.java
@@ -1189,6 +1189,10 @@
/**
* Get statically configured sensor properties.
+ * @deprecated Generally unsafe to use, use
+ * {@link FingerprintManager#addAuthenticatorsRegisteredCallback} API instead.
+ * In most cases this method will work as expected, but during early boot up, it will be
+ * null/empty and there is no way for the caller to know when it's actual value is ready.
* @hide
*/
@RequiresPermission(USE_BIOMETRIC_INTERNAL)
diff --git a/core/java/android/hardware/input/IInputManager.aidl b/core/java/android/hardware/input/IInputManager.aidl
index 45b316a..40d4fb6 100644
--- a/core/java/android/hardware/input/IInputManager.aidl
+++ b/core/java/android/hardware/input/IInputManager.aidl
@@ -55,7 +55,6 @@
int[] getInputDeviceIds();
// Enable/disable input device.
- boolean isInputDeviceEnabled(int deviceId);
void enableInputDevice(int deviceId);
void disableInputDevice(int deviceId);
diff --git a/core/java/android/hardware/input/InputManager.java b/core/java/android/hardware/input/InputManager.java
index 7527aa7..9eabc8d 100644
--- a/core/java/android/hardware/input/InputManager.java
+++ b/core/java/android/hardware/input/InputManager.java
@@ -333,19 +333,6 @@
}
/**
- * Returns true if an input device is enabled. Should return true for most
- * situations. Some system apps may disable an input device, for
- * example to prevent unwanted touch events.
- *
- * @param id The input device Id.
- *
- * @hide
- */
- public boolean isInputDeviceEnabled(int id) {
- return mGlobal.isInputDeviceEnabled(id);
- }
-
- /**
* Enables an InputDevice.
* <p>
* Requires {@link android.Manifest.permission#DISABLE_INPUT_DEVICE}.
diff --git a/core/java/android/hardware/input/InputManagerGlobal.java b/core/java/android/hardware/input/InputManagerGlobal.java
index fcd5a3e..7b47180 100644
--- a/core/java/android/hardware/input/InputManagerGlobal.java
+++ b/core/java/android/hardware/input/InputManagerGlobal.java
@@ -411,18 +411,6 @@
}
/**
- * @see InputManager#isInputDeviceEnabled(int)
- */
- public boolean isInputDeviceEnabled(int id) {
- try {
- return mIm.isInputDeviceEnabled(id);
- } catch (RemoteException ex) {
- Log.w(TAG, "Could not check enabled status of input device with id = " + id);
- throw ex.rethrowFromSystemServer();
- }
- }
-
- /**
* @see InputManager#enableInputDevice(int)
*/
public void enableInputDevice(int id) {
diff --git a/core/java/android/hardware/usb/DeviceFilter.java b/core/java/android/hardware/usb/DeviceFilter.java
index 66b0a42..3a271b4 100644
--- a/core/java/android/hardware/usb/DeviceFilter.java
+++ b/core/java/android/hardware/usb/DeviceFilter.java
@@ -18,6 +18,7 @@
import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.hardware.usb.flags.Flags;
import android.service.usb.UsbDeviceFilterProto;
import android.util.Slog;
@@ -57,9 +58,12 @@
public final String mProductName;
// USB device serial number string (or null for unspecified)
public final String mSerialNumber;
+ // USB interface name (or null for unspecified). This will be used when matching devices using
+ // the available interfaces.
+ public final String mInterfaceName;
public DeviceFilter(int vid, int pid, int clasz, int subclass, int protocol,
- String manufacturer, String product, String serialnum) {
+ String manufacturer, String product, String serialnum, String interfaceName) {
mVendorId = vid;
mProductId = pid;
mClass = clasz;
@@ -68,6 +72,7 @@
mManufacturerName = manufacturer;
mProductName = product;
mSerialNumber = serialnum;
+ mInterfaceName = interfaceName;
}
public DeviceFilter(UsbDevice device) {
@@ -79,6 +84,7 @@
mManufacturerName = device.getManufacturerName();
mProductName = device.getProductName();
mSerialNumber = device.getSerialNumber();
+ mInterfaceName = null;
}
public DeviceFilter(@NonNull DeviceFilter filter) {
@@ -90,6 +96,7 @@
mManufacturerName = filter.mManufacturerName;
mProductName = filter.mProductName;
mSerialNumber = filter.mSerialNumber;
+ mInterfaceName = filter.mInterfaceName;
}
public static DeviceFilter read(XmlPullParser parser)
@@ -102,7 +109,7 @@
String manufacturerName = null;
String productName = null;
String serialNumber = null;
-
+ String interfaceName = null;
int count = parser.getAttributeCount();
for (int i = 0; i < count; i++) {
String name = parser.getAttributeName(i);
@@ -114,6 +121,8 @@
productName = value;
} else if ("serial-number".equals(name)) {
serialNumber = value;
+ } else if ("interface-name".equals(name)) {
+ interfaceName = value;
} else {
int intValue;
int radix = 10;
@@ -144,7 +153,7 @@
}
return new DeviceFilter(vendorId, productId,
deviceClass, deviceSubclass, deviceProtocol,
- manufacturerName, productName, serialNumber);
+ manufacturerName, productName, serialNumber, interfaceName);
}
public void write(XmlSerializer serializer) throws IOException {
@@ -173,13 +182,25 @@
if (mSerialNumber != null) {
serializer.attribute(null, "serial-number", mSerialNumber);
}
+ if (mInterfaceName != null) {
+ serializer.attribute(null, "interface-name", mInterfaceName);
+ }
serializer.endTag(null, "usb-device");
}
- private boolean matches(int clasz, int subclass, int protocol) {
- return ((mClass == -1 || clasz == mClass) &&
- (mSubclass == -1 || subclass == mSubclass) &&
- (mProtocol == -1 || protocol == mProtocol));
+ private boolean matches(int usbClass, int subclass, int protocol) {
+ return ((mClass == -1 || usbClass == mClass)
+ && (mSubclass == -1 || subclass == mSubclass)
+ && (mProtocol == -1 || protocol == mProtocol));
+ }
+
+ private boolean matches(int usbClass, int subclass, int protocol, String interfaceName) {
+ if (Flags.enableInterfaceNameDeviceFilter()) {
+ return matches(usbClass, subclass, protocol)
+ && (mInterfaceName == null || mInterfaceName.equals(interfaceName));
+ } else {
+ return matches(usbClass, subclass, protocol);
+ }
}
public boolean matches(UsbDevice device) {
@@ -204,7 +225,7 @@
for (int i = 0; i < count; i++) {
UsbInterface intf = device.getInterface(i);
if (matches(intf.getInterfaceClass(), intf.getInterfaceSubclass(),
- intf.getInterfaceProtocol())) return true;
+ intf.getInterfaceProtocol(), intf.getName())) return true;
}
return false;
@@ -320,11 +341,12 @@
@Override
public String toString() {
- return "DeviceFilter[mVendorId=" + mVendorId + ",mProductId=" + mProductId +
- ",mClass=" + mClass + ",mSubclass=" + mSubclass +
- ",mProtocol=" + mProtocol + ",mManufacturerName=" + mManufacturerName +
- ",mProductName=" + mProductName + ",mSerialNumber=" + mSerialNumber +
- "]";
+ return "DeviceFilter[mVendorId=" + mVendorId + ",mProductId=" + mProductId
+ + ",mClass=" + mClass + ",mSubclass=" + mSubclass
+ + ",mProtocol=" + mProtocol + ",mManufacturerName=" + mManufacturerName
+ + ",mProductName=" + mProductName + ",mSerialNumber=" + mSerialNumber
+ + ",mInterfaceName=" + mInterfaceName
+ + "]";
}
/**
diff --git a/core/java/android/hardware/usb/flags/usb_framework_flags.aconfig b/core/java/android/hardware/usb/flags/usb_framework_flags.aconfig
index 94df160..40e5ffb 100644
--- a/core/java/android/hardware/usb/flags/usb_framework_flags.aconfig
+++ b/core/java/android/hardware/usb/flags/usb_framework_flags.aconfig
@@ -16,3 +16,11 @@
description: "Feature flag for the api to check if a port supports mode change"
bug: "323470419"
}
+
+flag {
+ name: "enable_interface_name_device_filter"
+ is_exported: true
+ namespace: "usb"
+ description: "Feature flag to enable interface name as a parameter for device filter"
+ bug: "312828160"
+}
diff --git a/core/java/android/net/NetworkPolicyManager.java b/core/java/android/net/NetworkPolicyManager.java
index 594ec18..334b231 100644
--- a/core/java/android/net/NetworkPolicyManager.java
+++ b/core/java/android/net/NetworkPolicyManager.java
@@ -173,6 +173,12 @@
public static final String FIREWALL_CHAIN_NAME_LOW_POWER_STANDBY = "low_power_standby";
/** @hide */
public static final String FIREWALL_CHAIN_NAME_BACKGROUND = "background";
+ /** @hide */
+ public static final String FIREWALL_CHAIN_NAME_METERED_ALLOW = "metered_allow";
+ /** @hide */
+ public static final String FIREWALL_CHAIN_NAME_METERED_DENY_USER = "metered_deny_user";
+ /** @hide */
+ public static final String FIREWALL_CHAIN_NAME_METERED_DENY_ADMIN = "metered_deny_admin";
private static final boolean ALLOW_PLATFORM_APP_POLICY = true;
diff --git a/core/java/android/net/Uri.java b/core/java/android/net/Uri.java
index 05a3e18..fedc97d 100644
--- a/core/java/android/net/Uri.java
+++ b/core/java/android/net/Uri.java
@@ -1387,7 +1387,11 @@
* @param scheme name or {@code null} if this is a relative Uri
*/
public Builder scheme(String scheme) {
- this.scheme = scheme;
+ if (scheme != null) {
+ this.scheme = scheme.replaceAll("://", "");
+ } else {
+ this.scheme = null;
+ }
return this;
}
diff --git a/core/java/android/os/VintfObject.java b/core/java/android/os/VintfObject.java
index 5056557..bb89e07 100644
--- a/core/java/android/os/VintfObject.java
+++ b/core/java/android/os/VintfObject.java
@@ -17,8 +17,11 @@
package android.os;
import android.annotation.NonNull;
+import android.annotation.Nullable;
import android.annotation.TestApi;
+import android.app.ActivityThread;
+import java.io.IOException;
import java.util.Map;
/**
@@ -113,5 +116,20 @@
@TestApi
public static native Long getTargetFrameworkCompatibilityMatrixVersion();
+ /**
+ * Executes a shell command using shell user identity, and return the standard output in string.
+ *
+ * @hide
+ */
+ private static @Nullable String runShellCommand(@NonNull String command) throws IOException {
+ var activityThread = ActivityThread.currentActivityThread();
+ var instrumentation = activityThread.getInstrumentation();
+ var automation = instrumentation.getUiAutomation();
+ var pfd = automation.executeShellCommand(command);
+ try (var is = new ParcelFileDescriptor.AutoCloseInputStream(pfd)) {
+ return new String(is.readAllBytes());
+ }
+ }
+
private VintfObject() {}
}
diff --git a/core/java/android/os/storage/StorageManager.java b/core/java/android/os/storage/StorageManager.java
index d45a17f..91ad22f 100644
--- a/core/java/android/os/storage/StorageManager.java
+++ b/core/java/android/os/storage/StorageManager.java
@@ -120,6 +120,8 @@
import java.util.concurrent.ThreadFactory;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
/**
* StorageManager is the interface to the systems storage service. The storage
@@ -2512,6 +2514,9 @@
return userId * PER_USER_RANGE + projectId;
}
+ private static final Pattern PATTERN_USER_ID = Pattern.compile(
+ "(?i)^/storage/emulated/([0-9]+)");
+
/**
* Let StorageManager know that the quota type for a file on external storage should
* be updated. Android tracks quotas for various media types. Consequently, this should be
@@ -2541,26 +2546,35 @@
@SystemApi
public void updateExternalStorageFileQuotaType(@NonNull File path,
@QuotaType int quotaType) throws IOException {
+ if (!path.exists()) return;
+
long projectId;
final String filePath = path.getCanonicalPath();
- int volFlags = FLAG_REAL_STATE | FLAG_INCLUDE_INVISIBLE;
- // If caller has MANAGE_EXTERNAL_STORAGE permission, results from User Profile(s) are also
- // returned by enabling FLAG_INCLUDE_SHARED_PROFILE.
- if (mContext.checkSelfPermission(MANAGE_EXTERNAL_STORAGE) == PERMISSION_GRANTED) {
- volFlags |= FLAG_INCLUDE_SHARED_PROFILE;
- }
- final StorageVolume[] availableVolumes = getVolumeList(mContext.getUserId(), volFlags);
- final StorageVolume volume = getStorageVolume(availableVolumes, path);
- if (volume == null) {
- Log.w(TAG, "Failed to update quota type for " + filePath);
- return;
- }
- if (!volume.isEmulated()) {
- // We only support quota tracking on emulated filesystems
- return;
+
+ final int userId;
+ final Matcher matcher = PATTERN_USER_ID.matcher(filePath);
+ if (matcher.find()) {
+ userId = Integer.parseInt(matcher.group(1));
+ } else { // fallback
+ int volFlags = FLAG_REAL_STATE | FLAG_INCLUDE_INVISIBLE;
+ // If caller has MANAGE_EXTERNAL_STORAGE permission, results from User Profile(s) are
+ // also returned by enabling FLAG_INCLUDE_SHARED_PROFILE.
+ if (mContext.checkSelfPermission(MANAGE_EXTERNAL_STORAGE) == PERMISSION_GRANTED) {
+ volFlags |= FLAG_INCLUDE_SHARED_PROFILE;
+ }
+ final StorageVolume[] availableVolumes = getVolumeList(mContext.getUserId(), volFlags);
+ final StorageVolume volume = getStorageVolume(availableVolumes, path);
+ if (volume == null) {
+ Log.w(TAG, "Failed to update quota type for " + filePath);
+ return;
+ }
+ if (!volume.isEmulated()) {
+ // We only support quota tracking on emulated filesystems
+ return;
+ }
+ userId = volume.getOwner().getIdentifier();
}
- final int userId = volume.getOwner().getIdentifier();
if (userId < 0) {
throw new IllegalStateException("Failed to update quota type for " + filePath);
}
diff --git a/core/java/android/permission/OWNERS b/core/java/android/permission/OWNERS
index d2f4b50..857bacd 100644
--- a/core/java/android/permission/OWNERS
+++ b/core/java/android/permission/OWNERS
@@ -1,14 +1,13 @@
# Bug component: 137825
-augale@google.com
evanseverson@google.com
fayey@google.com
jaysullivan@google.com
joecastro@google.com
-kvakil@google.com
mrulhania@google.com
ntmyren@google.com
rmacgregor@google.com
theianchen@google.com
yutingfang@google.com
zhanghai@google.com
+kiranmr@google.com
diff --git a/core/java/android/permission/flags.aconfig b/core/java/android/permission/flags.aconfig
index 0e28560..2ca58d1 100644
--- a/core/java/android/permission/flags.aconfig
+++ b/core/java/android/permission/flags.aconfig
@@ -166,6 +166,16 @@
}
flag {
+ name: "finish_running_ops_for_killed_packages"
+ namespace: "permissions"
+ description: "Finish all appops for a dead app process"
+ bug: "234630570"
+ metadata {
+ purpose: PURPOSE_BUGFIX
+ }
+}
+
+flag {
name: "runtime_permission_appops_mapping_enabled"
is_fixed_read_only: true
namespace: "permissions"
diff --git a/core/java/android/provider/CallLog.java b/core/java/android/provider/CallLog.java
index 120846c..708c196 100644
--- a/core/java/android/provider/CallLog.java
+++ b/core/java/android/provider/CallLog.java
@@ -2017,7 +2017,7 @@
return false;
}
final UserInfo userInfo = userManager.getUserInfo(userId);
- return userInfo != null && !userInfo.isManagedProfile();
+ return userInfo != null && !userInfo.isProfile();
}
/**
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index 009713f..4f5b67c 100644
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -11173,6 +11173,35 @@
"visual_query_accessibility_detection_enabled";
/**
+ * Timeout to be used for unbinding to the configured remote
+ * {@link android.service.ondeviceintelligence.OnDeviceIntelligenceService} if there are no
+ * requests in the queue. A value of -1 represents to never unbind.
+ *
+ * @hide
+ */
+ public static final String ON_DEVICE_INTELLIGENCE_UNBIND_TIMEOUT_MS =
+ "on_device_intelligence_unbind_timeout_ms";
+
+
+ /**
+ * Timeout that represents maximum idle time before which a callback should be populated.
+ *
+ * @hide
+ */
+ public static final String ON_DEVICE_INTELLIGENCE_IDLE_TIMEOUT_MS =
+ "on_device_intelligence_idle_timeout_ms";
+
+ /**
+ * Timeout to be used for unbinding to the configured remote
+ * {@link android.service.ondeviceintelligence.OnDeviceSandboxedInferenceService} if there
+ * are no requests in the queue. A value of -1 represents to never unbind.
+ *
+ * @hide
+ */
+ public static final String ON_DEVICE_INFERENCE_UNBIND_TIMEOUT_MS =
+ "on_device_inference_unbind_timeout_ms";
+
+ /**
* Control whether Night display is currently activated.
* @hide
*/
@@ -14909,6 +14938,17 @@
public static final String DROPBOX_TAG_PREFIX = "dropbox:";
/**
+ * Lines of kernel logs to include with system crash/ANR/etc. reports, as a
+ * prefix of the dropbox tag of the report type. For example,
+ * "kernel_logs_for_system_server_anr" controls the lines of kernel logs
+ * captured with system server ANR reports. 0 to disable.
+ *
+ * @hide
+ */
+ @Readable
+ public static final String ERROR_KERNEL_LOG_PREFIX = "kernel_logs_for_";
+
+ /**
* Lines of logcat to include with system crash/ANR/etc. reports, as a
* prefix of the dropbox tag of the report type. For example,
* "logcat_for_system_server_anr" controls the lines of logcat captured
diff --git a/core/java/android/provider/TEST_MAPPING b/core/java/android/provider/TEST_MAPPING
index d5ac7a7..2eb285d 100644
--- a/core/java/android/provider/TEST_MAPPING
+++ b/core/java/android/provider/TEST_MAPPING
@@ -8,6 +8,9 @@
}
]
},
+ {
+ "name": "CtsMediaProviderTestCases"
+ },
{
"name": "CalendarProviderTests"
},
diff --git a/core/java/android/service/dreams/DreamService.java b/core/java/android/service/dreams/DreamService.java
index 5f6bdbf..38ab590 100644
--- a/core/java/android/service/dreams/DreamService.java
+++ b/core/java/android/service/dreams/DreamService.java
@@ -18,7 +18,7 @@
import static android.content.pm.PackageManager.PERMISSION_GRANTED;
import static android.service.dreams.Flags.dreamHandlesConfirmKeys;
-import static android.service.dreams.Flags.dreamTracksFocus;
+import static android.service.dreams.Flags.dreamHandlesBeingObscured;
import android.annotation.FlaggedApi;
import android.annotation.IdRes;
@@ -571,15 +571,6 @@
/** {@inheritDoc} */
@Override
public void onWindowFocusChanged(boolean hasFocus) {
- if (!dreamTracksFocus()) {
- return;
- }
-
- try {
- mDreamManager.onDreamFocusChanged(hasFocus);
- } catch (RemoteException ex) {
- // system server died
- }
}
/** {@inheritDoc} */
@@ -1737,7 +1728,7 @@
@Override
public void comeToFront() {
- if (!dreamTracksFocus()) {
+ if (!dreamHandlesBeingObscured()) {
return;
}
diff --git a/core/java/android/service/dreams/IDreamManager.aidl b/core/java/android/service/dreams/IDreamManager.aidl
index 85f0368..cf98bfe0 100644
--- a/core/java/android/service/dreams/IDreamManager.aidl
+++ b/core/java/android/service/dreams/IDreamManager.aidl
@@ -48,5 +48,6 @@
void setSystemDreamComponent(in ComponentName componentName);
void registerDreamOverlayService(in ComponentName componentName);
void startDreamActivity(in Intent intent);
- void onDreamFocusChanged(in boolean hasFocus);
+ @JavaPassthrough(annotation="@android.annotation.RequiresPermission(android.Manifest.permission.WRITE_DREAM_STATE)")
+ oneway void setDreamIsObscured(in boolean isObscured);
}
diff --git a/core/java/android/service/dreams/flags.aconfig b/core/java/android/service/dreams/flags.aconfig
index a42eaff..54d950c 100644
--- a/core/java/android/service/dreams/flags.aconfig
+++ b/core/java/android/service/dreams/flags.aconfig
@@ -39,8 +39,11 @@
}
flag {
- name: "dream_tracks_focus"
+ name: "dream_handles_being_obscured"
namespace: "communal"
- description: "This flag enables the ability for dreams to track whether or not they have focus"
- bug: "331798001"
+ description: "This flag enables the ability for dreams to handle being obscured"
+ bug: "337302237"
+ metadata {
+ purpose: PURPOSE_BUGFIX
+ }
}
diff --git a/core/java/android/service/notification/NotificationAssistantService.java b/core/java/android/service/notification/NotificationAssistantService.java
index 76889df..88da8eb 100644
--- a/core/java/android/service/notification/NotificationAssistantService.java
+++ b/core/java/android/service/notification/NotificationAssistantService.java
@@ -22,6 +22,7 @@
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.SdkConstant;
+import android.annotation.SuppressLint;
import android.annotation.SystemApi;
import android.app.Notification;
import android.app.NotificationChannel;
@@ -37,9 +38,7 @@
import android.os.Message;
import android.os.RemoteException;
import android.util.Log;
-
import com.android.internal.os.SomeArgs;
-
import java.lang.annotation.Retention;
import java.util.List;
@@ -116,6 +115,7 @@
*/
protected Handler mHandler;
+ @SuppressLint("OnNameExpected")
@Override
protected void attachBaseContext(Context base) {
super.attachBaseContext(base);
diff --git a/core/java/android/service/notification/NotificationStats.java b/core/java/android/service/notification/NotificationStats.java
index 07367df..caa0a9c 100644
--- a/core/java/android/service/notification/NotificationStats.java
+++ b/core/java/android/service/notification/NotificationStats.java
@@ -19,6 +19,7 @@
import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.annotation.SuppressLint;
import android.annotation.SystemApi;
import android.app.Flags;
import android.app.RemoteInput;
@@ -229,6 +230,7 @@
/**
* Records that the user has replied to a notification that has a smart reply at least once.
*/
+ @SuppressLint("GetterSetterNames")
@FlaggedApi(Flags.FLAG_LIFETIME_EXTENSION_REFACTOR)
public void setSmartReplied() {
mSmartReplied = true;
diff --git a/core/java/android/service/notification/ZenPolicy.java b/core/java/android/service/notification/ZenPolicy.java
index 1d7091c..910c462 100644
--- a/core/java/android/service/notification/ZenPolicy.java
+++ b/core/java/android/service/notification/ZenPolicy.java
@@ -1007,6 +1007,7 @@
/**
* Set whether priority channels are permitted to break through DND.
*/
+ @SuppressLint("BuilderSetStyle")
@FlaggedApi(Flags.FLAG_MODES_API)
public @NonNull Builder allowPriorityChannels(boolean allow) {
mZenPolicy.mAllowChannels = allow ? CHANNEL_POLICY_PRIORITY : CHANNEL_POLICY_NONE;
diff --git a/core/java/android/service/ondeviceintelligence/OnDeviceIntelligenceService.java b/core/java/android/service/ondeviceintelligence/OnDeviceIntelligenceService.java
index 793e58a..293015f 100644
--- a/core/java/android/service/ondeviceintelligence/OnDeviceIntelligenceService.java
+++ b/core/java/android/service/ondeviceintelligence/OnDeviceIntelligenceService.java
@@ -18,6 +18,9 @@
import static android.app.ondeviceintelligence.flags.Flags.FLAG_ENABLE_ON_DEVICE_INTELLIGENCE;
+import static com.android.internal.util.function.pooled.PooledLambda.obtainMessage;
+
+import android.annotation.CallSuper;
import android.annotation.CallbackExecutor;
import android.annotation.FlaggedApi;
import android.annotation.NonNull;
@@ -40,13 +43,16 @@
import android.os.Binder;
import android.os.Bundle;
import android.os.CancellationSignal;
+import android.os.Handler;
import android.os.IBinder;
import android.os.ICancellationSignal;
+import android.os.Looper;
import android.os.OutcomeReceiver;
import android.os.ParcelFileDescriptor;
import android.os.PersistableBundle;
import android.os.RemoteCallback;
import android.os.RemoteException;
+import android.util.Log;
import android.util.Slog;
import com.android.internal.infra.AndroidFuture;
@@ -88,6 +94,14 @@
private static final String TAG = OnDeviceIntelligenceService.class.getSimpleName();
private volatile IRemoteProcessingService mRemoteProcessingService;
+ private Handler mHandler;
+
+ @CallSuper
+ @Override
+ public void onCreate() {
+ super.onCreate();
+ mHandler = new Handler(Looper.getMainLooper(), null /* callback */, true /* async */);
+ }
/**
* The {@link Intent} that must be declared as handled by the service. To be supported, the
@@ -107,38 +121,49 @@
@Override
public final IBinder onBind(@NonNull Intent intent) {
if (SERVICE_INTERFACE.equals(intent.getAction())) {
- // TODO(326052028) : Move the remote method calls to an app handler from the binder
- // thread.
return new IOnDeviceIntelligenceService.Stub() {
/** {@inheritDoc} */
@Override
public void ready() {
- OnDeviceIntelligenceService.this.onReady();
+ mHandler.executeOrSendMessage(
+ obtainMessage(OnDeviceIntelligenceService::onReady,
+ OnDeviceIntelligenceService.this));
}
@Override
public void getVersion(RemoteCallback remoteCallback) {
Objects.requireNonNull(remoteCallback);
- OnDeviceIntelligenceService.this.onGetVersion(l -> {
- Bundle b = new Bundle();
- b.putLong(OnDeviceIntelligenceManager.API_VERSION_BUNDLE_KEY, l);
- remoteCallback.sendResult(b);
- });
+ mHandler.executeOrSendMessage(
+ obtainMessage(
+ OnDeviceIntelligenceService::onGetVersion,
+ OnDeviceIntelligenceService.this, l -> {
+ Bundle b = new Bundle();
+ b.putLong(
+ OnDeviceIntelligenceManager.API_VERSION_BUNDLE_KEY,
+ l);
+ remoteCallback.sendResult(b);
+ }));
}
@Override
public void listFeatures(int callerUid,
IListFeaturesCallback listFeaturesCallback) {
Objects.requireNonNull(listFeaturesCallback);
- OnDeviceIntelligenceService.this.onListFeatures(callerUid,
- wrapListFeaturesCallback(listFeaturesCallback));
+ mHandler.executeOrSendMessage(
+ obtainMessage(
+ OnDeviceIntelligenceService::onListFeatures,
+ OnDeviceIntelligenceService.this, callerUid,
+ wrapListFeaturesCallback(listFeaturesCallback)));
}
@Override
public void getFeature(int callerUid, int id, IFeatureCallback featureCallback) {
Objects.requireNonNull(featureCallback);
- OnDeviceIntelligenceService.this.onGetFeature(callerUid,
- id, wrapFeatureCallback(featureCallback));
+ mHandler.executeOrSendMessage(
+ obtainMessage(
+ OnDeviceIntelligenceService::onGetFeature,
+ OnDeviceIntelligenceService.this, callerUid,
+ id, wrapFeatureCallback(featureCallback)));
}
@@ -147,9 +172,11 @@
IFeatureDetailsCallback featureDetailsCallback) {
Objects.requireNonNull(feature);
Objects.requireNonNull(featureDetailsCallback);
-
- OnDeviceIntelligenceService.this.onGetFeatureDetails(callerUid,
- feature, wrapFeatureDetailsCallback(featureDetailsCallback));
+ mHandler.executeOrSendMessage(
+ obtainMessage(
+ OnDeviceIntelligenceService::onGetFeatureDetails,
+ OnDeviceIntelligenceService.this, callerUid,
+ feature, wrapFeatureDetailsCallback(featureDetailsCallback)));
}
@Override
@@ -163,10 +190,13 @@
transport = CancellationSignal.createTransport();
cancellationSignalFuture.complete(transport);
}
- OnDeviceIntelligenceService.this.onDownloadFeature(callerUid,
- feature,
- CancellationSignal.fromTransport(transport),
- wrapDownloadCallback(downloadCallback));
+ mHandler.executeOrSendMessage(
+ obtainMessage(
+ OnDeviceIntelligenceService::onDownloadFeature,
+ OnDeviceIntelligenceService.this, callerUid,
+ feature,
+ CancellationSignal.fromTransport(transport),
+ wrapDownloadCallback(downloadCallback)));
}
@Override
@@ -174,9 +204,11 @@
AndroidFuture<ParcelFileDescriptor> future) {
Objects.requireNonNull(fileName);
Objects.requireNonNull(future);
-
- OnDeviceIntelligenceService.this.onGetReadOnlyFileDescriptor(fileName,
- future);
+ mHandler.executeOrSendMessage(
+ obtainMessage(
+ OnDeviceIntelligenceService::onGetReadOnlyFileDescriptor,
+ OnDeviceIntelligenceService.this, fileName,
+ future));
}
@Override
@@ -184,13 +216,15 @@
Feature feature, RemoteCallback remoteCallback) {
Objects.requireNonNull(feature);
Objects.requireNonNull(remoteCallback);
-
- OnDeviceIntelligenceService.this.onGetReadOnlyFeatureFileDescriptorMap(
- feature, parcelFileDescriptorMap -> {
- Bundle bundle = new Bundle();
- parcelFileDescriptorMap.forEach(bundle::putParcelable);
- remoteCallback.sendResult(bundle);
- });
+ mHandler.executeOrSendMessage(
+ obtainMessage(
+ OnDeviceIntelligenceService::onGetReadOnlyFeatureFileDescriptorMap,
+ OnDeviceIntelligenceService.this, feature,
+ parcelFileDescriptorMap -> {
+ Bundle bundle = new Bundle();
+ parcelFileDescriptorMap.forEach(bundle::putParcelable);
+ remoteCallback.sendResult(bundle);
+ }));
}
@Override
@@ -201,12 +235,18 @@
@Override
public void notifyInferenceServiceConnected() {
- OnDeviceIntelligenceService.this.onInferenceServiceConnected();
+ mHandler.executeOrSendMessage(
+ obtainMessage(
+ OnDeviceIntelligenceService::onInferenceServiceConnected,
+ OnDeviceIntelligenceService.this));
}
@Override
public void notifyInferenceServiceDisconnected() {
- OnDeviceIntelligenceService.this.onInferenceServiceDisconnected();
+ mHandler.executeOrSendMessage(
+ obtainMessage(
+ OnDeviceIntelligenceService::onInferenceServiceDisconnected,
+ OnDeviceIntelligenceService.this));
}
};
}
@@ -222,7 +262,8 @@
* @hide
*/
@TestApi
- public void onReady() {}
+ public void onReady() {
+ }
/**
@@ -410,12 +451,16 @@
Slog.v(TAG,
"onGetReadOnlyFileDescriptor: " + fileName + " under internal app storage.");
File f = new File(getBaseContext().getFilesDir(), fileName);
+ if (!f.exists()) {
+ f = new File(fileName);
+ }
ParcelFileDescriptor pfd = null;
try {
pfd = ParcelFileDescriptor.open(f, ParcelFileDescriptor.MODE_READ_ONLY);
Slog.d(TAG, "Successfully opened a file with ParcelFileDescriptor.");
} catch (FileNotFoundException e) {
Slog.e(TAG, "Cannot open file. No ParcelFileDescriptor returned.");
+ future.completeExceptionally(e);
} finally {
future.complete(pfd);
if (pfd != null) {
diff --git a/core/java/android/service/ondeviceintelligence/OnDeviceSandboxedInferenceService.java b/core/java/android/service/ondeviceintelligence/OnDeviceSandboxedInferenceService.java
index 29a6db6..d00485c 100644
--- a/core/java/android/service/ondeviceintelligence/OnDeviceSandboxedInferenceService.java
+++ b/core/java/android/service/ondeviceintelligence/OnDeviceSandboxedInferenceService.java
@@ -19,7 +19,10 @@
import static android.app.ondeviceintelligence.OnDeviceIntelligenceManager.AUGMENT_REQUEST_CONTENT_BUNDLE_KEY;
import static android.app.ondeviceintelligence.flags.Flags.FLAG_ENABLE_ON_DEVICE_INTELLIGENCE;
+import static com.android.internal.util.function.pooled.PooledLambda.obtainMessage;
+
import android.annotation.CallbackExecutor;
+import android.annotation.CallSuper;
import android.annotation.FlaggedApi;
import android.annotation.NonNull;
import android.annotation.Nullable;
@@ -48,6 +51,7 @@
import android.os.HandlerExecutor;
import android.os.IBinder;
import android.os.ICancellationSignal;
+import android.os.Looper;
import android.os.OutcomeReceiver;
import android.os.ParcelFileDescriptor;
import android.os.PersistableBundle;
@@ -105,7 +109,35 @@
public static final String SERVICE_INTERFACE =
"android.service.ondeviceintelligence.OnDeviceSandboxedInferenceService";
+ // TODO(339594686): make API
+ /**
+ * @hide
+ */
+ public static final String REGISTER_MODEL_UPDATE_CALLBACK_BUNDLE_KEY =
+ "register_model_update_callback";
+ /**
+ * @hide
+ */
+ public static final String MODEL_LOADED_BUNDLE_KEY = "model_loaded";
+ /**
+ * @hide
+ */
+ public static final String MODEL_UNLOADED_BUNDLE_KEY = "model_unloaded";
+
+ /**
+ * @hide
+ */
+ public static final String DEVICE_CONFIG_UPDATE_BUNDLE_KEY = "device_config_update";
+
private IRemoteStorageService mRemoteStorageService;
+ private Handler mHandler;
+
+ @CallSuper
+ @Override
+ public void onCreate() {
+ super.onCreate();
+ mHandler = new Handler(Looper.getMainLooper(), null /* callback */, true /* async */);
+ }
/**
* @hide
@@ -132,11 +164,15 @@
transport = CancellationSignal.createTransport();
cancellationSignalFuture.complete(transport);
}
- OnDeviceSandboxedInferenceService.this.onTokenInfoRequest(callerUid,
- feature,
- request,
- CancellationSignal.fromTransport(transport),
- wrapTokenInfoCallback(tokenInfoCallback));
+
+ mHandler.executeOrSendMessage(
+ obtainMessage(
+ OnDeviceSandboxedInferenceService::onTokenInfoRequest,
+ OnDeviceSandboxedInferenceService.this,
+ callerUid, feature,
+ request,
+ CancellationSignal.fromTransport(transport),
+ wrapTokenInfoCallback(tokenInfoCallback)));
}
@Override
@@ -158,13 +194,18 @@
processingSignalTransport = ProcessingSignal.createTransport();
processingSignalFuture.complete(processingSignalTransport);
}
- OnDeviceSandboxedInferenceService.this.onProcessRequestStreaming(callerUid,
- feature,
- request,
- requestType,
- CancellationSignal.fromTransport(transport),
- ProcessingSignal.fromTransport(processingSignalTransport),
- wrapStreamingResponseCallback(callback));
+
+
+ mHandler.executeOrSendMessage(
+ obtainMessage(
+ OnDeviceSandboxedInferenceService::onProcessRequestStreaming,
+ OnDeviceSandboxedInferenceService.this, callerUid,
+ feature,
+ request,
+ requestType,
+ CancellationSignal.fromTransport(transport),
+ ProcessingSignal.fromTransport(processingSignalTransport),
+ wrapStreamingResponseCallback(callback)));
}
@Override
@@ -185,11 +226,14 @@
processingSignalTransport = ProcessingSignal.createTransport();
processingSignalFuture.complete(processingSignalTransport);
}
- OnDeviceSandboxedInferenceService.this.onProcessRequest(callerUid, feature,
- request, requestType,
- CancellationSignal.fromTransport(transport),
- ProcessingSignal.fromTransport(processingSignalTransport),
- wrapResponseCallback(callback));
+ mHandler.executeOrSendMessage(
+ obtainMessage(
+ OnDeviceSandboxedInferenceService::onProcessRequest,
+ OnDeviceSandboxedInferenceService.this, callerUid, feature,
+ request, requestType,
+ CancellationSignal.fromTransport(transport),
+ ProcessingSignal.fromTransport(processingSignalTransport),
+ wrapResponseCallback(callback)));
}
@Override
@@ -197,10 +241,11 @@
IProcessingUpdateStatusCallback callback) {
Objects.requireNonNull(processingState);
Objects.requireNonNull(callback);
-
- OnDeviceSandboxedInferenceService.this.onUpdateProcessingState(processingState,
- wrapOutcomeReceiver(callback)
- );
+ mHandler.executeOrSendMessage(
+ obtainMessage(
+ OnDeviceSandboxedInferenceService::onUpdateProcessingState,
+ OnDeviceSandboxedInferenceService.this, processingState,
+ wrapOutcomeReceiver(callback)));
}
};
}
diff --git a/core/java/android/service/wallpaper/WallpaperService.java b/core/java/android/service/wallpaper/WallpaperService.java
index d174bef..9589785 100644
--- a/core/java/android/service/wallpaper/WallpaperService.java
+++ b/core/java/android/service/wallpaper/WallpaperService.java
@@ -1025,7 +1025,8 @@
mWallpaperDimAmount = (!mShouldDimByDefault) ? mCustomDimAmount
: Math.max(mDefaultDimAmount, mCustomDimAmount);
- if (!ENABLE_WALLPAPER_DIMMING || mBbqSurfaceControl == null
+ if (!ENABLE_WALLPAPER_DIMMING
+ || mBbqSurfaceControl == null || !mBbqSurfaceControl.isValid()
|| mWallpaperDimAmount == mPreviousWallpaperDimAmount) {
return;
}
diff --git a/core/java/android/text/DynamicLayout.java b/core/java/android/text/DynamicLayout.java
index cce4f7b..a78a417 100644
--- a/core/java/android/text/DynamicLayout.java
+++ b/core/java/android/text/DynamicLayout.java
@@ -310,6 +310,7 @@
* @see Layout#getUseBoundsForWidth()
* @see Layout.Builder#setUseBoundsForWidth(boolean)
*/
+ @SuppressLint("MissingGetterMatchingBuilder") // The base class `Layout` has a getter.
@NonNull
@FlaggedApi(FLAG_USE_BOUNDS_FOR_WIDTH)
public Builder setUseBoundsForWidth(boolean useBoundsForWidth) {
diff --git a/core/java/android/text/StaticLayout.java b/core/java/android/text/StaticLayout.java
index 3dd3a9e..95460a3 100644
--- a/core/java/android/text/StaticLayout.java
+++ b/core/java/android/text/StaticLayout.java
@@ -447,6 +447,7 @@
* @see Layout#getUseBoundsForWidth()
* @see Layout.Builder#setUseBoundsForWidth(boolean)
*/
+ @SuppressLint("MissingGetterMatchingBuilder") // The base class `Layout` has a getter.
@NonNull
@FlaggedApi(FLAG_USE_BOUNDS_FOR_WIDTH)
public Builder setUseBoundsForWidth(boolean useBoundsForWidth) {
diff --git a/core/java/android/tracing/perfetto/DataSource.java b/core/java/android/tracing/perfetto/DataSource.java
index b9ab82c..4de7b62 100644
--- a/core/java/android/tracing/perfetto/DataSource.java
+++ b/core/java/android/tracing/perfetto/DataSource.java
@@ -85,7 +85,7 @@
new TracingContext<>(this, instanceIndex);
fun.trace(ctx);
- ctx.flush();
+ nativeWritePackets(mNativeObj, ctx.getAndClearAllPendingTracePackets());
} while (nativePerfettoDsTraceIterateNext(mNativeObj));
} finally {
nativePerfettoDsTraceIterateBreak(mNativeObj);
@@ -130,7 +130,8 @@
* @param params Params to initialize the datasource with.
*/
public void register(DataSourceParams params) {
- nativeRegisterDataSource(this.mNativeObj, params.bufferExhaustedPolicy);
+ nativeRegisterDataSource(this.mNativeObj, params.bufferExhaustedPolicy,
+ params.willNotifyOnStop, params.noFlush);
}
/**
@@ -163,8 +164,8 @@
return this.createInstance(inputStream, instanceIndex);
}
- private static native void nativeRegisterDataSource(
- long dataSourcePtr, int bufferExhaustedPolicy);
+ private static native void nativeRegisterDataSource(long dataSourcePtr,
+ int bufferExhaustedPolicy, boolean willNotifyOnStop, boolean noFlush);
private static native long nativeCreate(DataSource thiz, String name);
private static native void nativeFlushAll(long nativeDataSourcePointer);
@@ -179,4 +180,6 @@
private static native boolean nativePerfettoDsTraceIterateNext(long dataSourcePtr);
private static native void nativePerfettoDsTraceIterateBreak(long dataSourcePtr);
private static native int nativeGetPerfettoDsInstanceIndex(long dataSourcePtr);
+
+ private static native void nativeWritePackets(long dataSourcePtr, byte[][] packetData);
}
diff --git a/core/java/android/tracing/perfetto/DataSourceParams.java b/core/java/android/tracing/perfetto/DataSourceParams.java
index 6cd04e3..e50f9d7 100644
--- a/core/java/android/tracing/perfetto/DataSourceParams.java
+++ b/core/java/android/tracing/perfetto/DataSourceParams.java
@@ -46,12 +46,67 @@
// after a while.
public static final int PERFETTO_DS_BUFFER_EXHAUSTED_POLICY_STALL_AND_ABORT = 1;
- public static DataSourceParams DEFAULTS =
- new DataSourceParams(PERFETTO_DS_BUFFER_EXHAUSTED_POLICY_DROP);
+ public static DataSourceParams DEFAULTS = new DataSourceParams.Builder().build();
- public DataSourceParams(@PerfettoDsBufferExhausted int bufferExhaustedPolicy) {
+ private DataSourceParams(@PerfettoDsBufferExhausted int bufferExhaustedPolicy,
+ boolean willNotifyOnStop, boolean noFlush) {
this.bufferExhaustedPolicy = bufferExhaustedPolicy;
+ this.willNotifyOnStop = willNotifyOnStop;
+ this.noFlush = noFlush;
}
public final @PerfettoDsBufferExhausted int bufferExhaustedPolicy;
+ public final boolean willNotifyOnStop;
+ public final boolean noFlush;
+
+ /**
+ * DataSource Parameters builder
+ *
+ * @hide
+ */
+ public static final class Builder {
+ /**
+ * Specify behavior when running out of shared memory buffer space.
+ */
+ public Builder setBufferExhaustedPolicy(@PerfettoDsBufferExhausted int value) {
+ this.mBufferExhaustedPolicy = value;
+ return this;
+ }
+
+ /**
+ * If true, the data source is expected to ack the stop request through the
+ * NotifyDataSourceStopped() IPC. If false, the service won't wait for an ack.
+ * Set this parameter to false when dealing with potentially frozen producers
+ * that wouldn't be able to quickly ack the stop request.
+ *
+ * Default value: true
+ */
+ public Builder setWillNotifyOnStop(boolean value) {
+ this.mWillNotifyOnStop = value;
+ return this;
+ }
+
+ /**
+ * If true, the service won't emit flush requests for this data source. This
+ * allows the service to reduce the flush-related IPC traffic and better deal
+ * with frozen producers (see go/perfetto-frozen).
+ */
+ public Builder setNoFlush(boolean value) {
+ this.mNoFlush = value;
+ return this;
+ }
+
+ /**
+ * Build the DataSource parameters.
+ */
+ public DataSourceParams build() {
+ return new DataSourceParams(
+ this.mBufferExhaustedPolicy, this.mWillNotifyOnStop, this.mNoFlush);
+ }
+
+ private @PerfettoDsBufferExhausted int mBufferExhaustedPolicy =
+ PERFETTO_DS_BUFFER_EXHAUSTED_POLICY_DROP;
+ private boolean mWillNotifyOnStop = true;
+ private boolean mNoFlush = false;
+ }
}
diff --git a/core/java/android/tracing/perfetto/TracingContext.java b/core/java/android/tracing/perfetto/TracingContext.java
index 6b7df54..98cb4c8 100644
--- a/core/java/android/tracing/perfetto/TracingContext.java
+++ b/core/java/android/tracing/perfetto/TracingContext.java
@@ -59,19 +59,6 @@
}
/**
- * Forces a commit of the thread-local tracing data written so far to the
- * service. This is almost never required (tracing data is periodically
- * committed as trace pages are filled up) and has a non-negligible
- * performance hit (requires an IPC + refresh of the current thread-local
- * chunk). The only case when this should be used is when handling OnStop()
- * asynchronously, to ensure sure that the data is committed before the
- * Stop timeout expires.
- */
- public void flush() {
- nativeFlush(mDataSource.mNativeObj, getAndClearAllPendingTracePackets());
- }
-
- /**
* Can optionally be used to store custom per-sequence
* session data, which is not reset when incremental state is cleared
* (e.g. configuration options).
@@ -109,7 +96,7 @@
return incrementalState;
}
- private byte[][] getAndClearAllPendingTracePackets() {
+ protected byte[][] getAndClearAllPendingTracePackets() {
byte[][] res = new byte[mTracePackets.size()][];
for (int i = 0; i < mTracePackets.size(); i++) {
ProtoOutputStream tracePacket = mTracePackets.get(i);
@@ -120,8 +107,6 @@
return res;
}
- private static native void nativeFlush(long dataSourcePtr, byte[][] packetData);
-
private static native Object nativeGetCustomTls(long nativeDsPtr);
private static native void nativeSetCustomTls(long nativeDsPtr, Object tlsState);
diff --git a/core/java/android/view/Display.java b/core/java/android/view/Display.java
index 4475418..6464239 100644
--- a/core/java/android/view/Display.java
+++ b/core/java/android/view/Display.java
@@ -916,6 +916,12 @@
* {@code getWindowManager()} or {@code getSystemService(Context.WINDOW_SERVICE)}), the
* size of the current app window is returned. As a result, in multi-window mode, the
* returned size can be smaller than the size of the device screen.
+ * The returned window size can vary depending on API level:
+ * <ul>
+ * <li>API level 35 and above, the window size will be returned.
+ * <li>API level 34 and below, the window size minus system decoration areas and
+ * display cutout is returned.
+ * </ul>
* <li>If size is requested from a non-activity context (for example, the application
* context, where the WindowManager is accessed by
* {@code getApplicationContext().getSystemService(Context.WINDOW_SERVICE)}), the
@@ -924,9 +930,10 @@
* <li>API level 29 and below — The size of the entire display (based on
* current rotation) minus system decoration areas is returned.
* <li>API level 30 and above — The size of the top running activity in the
- * current process is returned. If the current process has no running
- * activities, the size of the device default display, including system
- * decoration areas, is returned.
+ * current process is returned, system decoration areas exclusion follows the
+ * behavior defined above, based on the caller's API level. If the current
+ * process has no running activities, the size of the device default display,
+ * including system decoration areas, is returned.
* </ul>
* </ul>
*
diff --git a/core/java/android/view/FocusFinder.java b/core/java/android/view/FocusFinder.java
index 35b137a..c5b6aa7 100644
--- a/core/java/android/view/FocusFinder.java
+++ b/core/java/android/view/FocusFinder.java
@@ -129,7 +129,7 @@
}
ViewGroup effective = null;
ViewParent nextParent = focused.getParent();
- do {
+ while (nextParent instanceof ViewGroup) {
if (nextParent == root) {
return effective != null ? effective : root;
}
@@ -143,7 +143,7 @@
effective = vg;
}
nextParent = nextParent.getParent();
- } while (nextParent instanceof ViewGroup);
+ }
return root;
}
diff --git a/core/java/android/view/GestureDetector.java b/core/java/android/view/GestureDetector.java
index 9ff29a8..4837ee5 100644
--- a/core/java/android/view/GestureDetector.java
+++ b/core/java/android/view/GestureDetector.java
@@ -26,11 +26,13 @@
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.UiContext;
+import android.app.Activity;
import android.compat.annotation.UnsupportedAppUsage;
import android.content.Context;
import android.os.Build;
import android.os.Bundle;
import android.os.Handler;
+import android.os.Looper;
import android.os.Message;
import android.os.StrictMode;
import android.os.SystemClock;
@@ -299,6 +301,11 @@
private VelocityTracker mVelocityTracker;
/**
+ * Determines strategy for velocity calculation
+ */
+ private @VelocityTracker.VelocityTrackerStrategy int mVelocityTrackerStrategy;
+
+ /**
* Consistency verifier for debugging purposes.
*/
private final InputEventConsistencyVerifier mInputEventConsistencyVerifier =
@@ -347,17 +354,17 @@
/**
* Creates a GestureDetector with the supplied listener.
- * This variant of the constructor should be used from a non-UI thread
+ * This variant of the constructor should be used from a non-UI thread
* (as it allows specifying the Handler).
- *
+ *
* @param listener the listener invoked for all the callbacks, this must
* not be null.
* @param handler the handler to use
*
* @throws NullPointerException if {@code listener} is null.
*
- * @deprecated Use {@link #GestureDetector(android.content.Context,
- * android.view.GestureDetector.OnGestureListener, android.os.Handler)} instead.
+ * @deprecated Use {@link #GestureDetector(Context, GestureDetector.OnGestureListener, Handler)}
+ * instead.
*/
@Deprecated
public GestureDetector(@NonNull OnGestureListener listener, @Nullable Handler handler) {
@@ -367,15 +374,14 @@
/**
* Creates a GestureDetector with the supplied listener.
* You may only use this constructor from a UI thread (this is the usual situation).
- * @see android.os.Handler#Handler()
- *
+ * @see Handler#Handler()
+ *
* @param listener the listener invoked for all the callbacks, this must
* not be null.
- *
+ *
* @throws NullPointerException if {@code listener} is null.
*
- * @deprecated Use {@link #GestureDetector(android.content.Context,
- * android.view.GestureDetector.OnGestureListener)} instead.
+ * @deprecated Use {@link #GestureDetector(Context, GestureDetector.OnGestureListener)} instead.
*/
@Deprecated
public GestureDetector(@NonNull OnGestureListener listener) {
@@ -384,10 +390,10 @@
/**
* Creates a GestureDetector with the supplied listener.
- * You may only use this constructor from a {@link android.os.Looper} thread.
- * @see android.os.Handler#Handler()
+ * You may only use this constructor from a {@link Looper} thread.
+ * @see Handler#Handler()
*
- * @param context An {@link android.app.Activity} or a {@link Context} created from
+ * @param context An {@link Activity} or a {@link Context} created from
* {@link Context#createWindowContext(int, Bundle)}
* @param listener the listener invoked for all the callbacks, this must
* not be null. If the listener implements the {@link OnDoubleTapListener} or
@@ -404,10 +410,10 @@
/**
* Creates a GestureDetector with the supplied listener that runs deferred events on the
- * thread associated with the supplied {@link android.os.Handler}.
- * @see android.os.Handler#Handler()
+ * thread associated with the supplied {@link Handler}.
+ * @see Handler#Handler()
*
- * @param context An {@link android.app.Activity} or a {@link Context} created from
+ * @param context An {@link Activity} or a {@link Context} created from
* {@link Context#createWindowContext(int, Bundle)}
* @param listener the listener invoked for all the callbacks, this must
* not be null. If the listener implements the {@link OnDoubleTapListener} or
@@ -419,6 +425,31 @@
*/
public GestureDetector(@Nullable @UiContext Context context,
@NonNull OnGestureListener listener, @Nullable Handler handler) {
+ this(context, listener, handler, VelocityTracker.VELOCITY_TRACKER_STRATEGY_DEFAULT);
+ }
+
+ /**
+ * Creates a GestureDetector with the supplied listener that runs deferred events on the
+ * thread associated with the supplied {@link Handler}.
+ * @see Handler#Handler()
+ *
+ * @param context An {@link Activity} or a {@link Context} created from
+ * {@link Context#createWindowContext(int, Bundle)}
+ * @param listener the listener invoked for all the callbacks, this must
+ * not be null. If the listener implements the {@link OnDoubleTapListener} or
+ * {@link OnContextClickListener} then it will also be set as the listener for
+ * these callbacks (for example when using the {@link SimpleOnGestureListener}).
+ * @param handler the handler to use for running deferred listener events.
+ * @param velocityTrackerStrategy strategy to use for velocity calculation of scroll/fling
+ * events.
+ *
+ * @throws NullPointerException if {@code listener} is null.
+ *
+ * @hide
+ */
+ public GestureDetector(@Nullable @UiContext Context context,
+ @NonNull OnGestureListener listener, @Nullable Handler handler,
+ @VelocityTracker.VelocityTrackerStrategy int velocityTrackerStrategy) {
if (handler != null) {
mHandler = new GestureHandler(handler);
} else {
@@ -431,15 +462,16 @@
if (listener instanceof OnContextClickListener) {
setContextClickListener((OnContextClickListener) listener);
}
+ mVelocityTrackerStrategy = velocityTrackerStrategy;
init(context);
}
-
+
/**
* Creates a GestureDetector with the supplied listener that runs deferred events on the
- * thread associated with the supplied {@link android.os.Handler}.
- * @see android.os.Handler#Handler()
+ * thread associated with the supplied {@link Handler}.
+ * @see Handler#Handler()
*
- * @param context An {@link android.app.Activity} or a {@link Context} created from
+ * @param context An {@link Activity} or a {@link Context} created from
* {@link Context#createWindowContext(int, Bundle)}
* @param listener the listener invoked for all the callbacks, this must
* not be null.
@@ -547,7 +579,7 @@
mCurrentMotionEvent = MotionEvent.obtain(ev);
if (mVelocityTracker == null) {
- mVelocityTracker = VelocityTracker.obtain();
+ mVelocityTracker = VelocityTracker.obtain(mVelocityTrackerStrategy);
}
mVelocityTracker.addMovement(ev);
diff --git a/core/java/android/view/ImeBackAnimationController.java b/core/java/android/view/ImeBackAnimationController.java
index 9503f49..8c14de6 100644
--- a/core/java/android/view/ImeBackAnimationController.java
+++ b/core/java/android/view/ImeBackAnimationController.java
@@ -210,18 +210,9 @@
mInsetsController.setPredictiveBackImeHideAnimInProgress(true);
notifyHideIme();
}
- if (mStartRootScrollY != 0) {
- // RootView is panned, ensure that it is scrolled back to the intended scroll position
- if (triggerBack) {
- // requesting ime as invisible
- mInsetsController.setRequestedVisibleTypes(0, ime());
- // changes the animation state and notifies RootView of changed insets, which
- // causes it to reset its scrollY to 0f (animated)
- mInsetsController.onAnimationStateChanged(ime(), /*running*/ true);
- } else {
- // This causes RootView to update its scroll back to the panned position
- mInsetsController.getHost().notifyInsetsChanged();
- }
+ if (mStartRootScrollY != 0 && !triggerBack) {
+ // This causes RootView to update its scroll back to the panned position
+ mInsetsController.getHost().notifyInsetsChanged();
}
}
@@ -237,6 +228,12 @@
// the IME away
mInsetsController.getHost().getInputMethodManager()
.notifyImeHidden(mInsetsController.getHost().getWindowToken(), statsToken);
+
+ // requesting IME as invisible during post-commit
+ mInsetsController.setRequestedVisibleTypes(0, ime());
+ // Changes the animation state. This also notifies RootView of changed insets, which causes
+ // it to reset its scrollY to 0f (animated) if it was panned
+ mInsetsController.onAnimationStateChanged(ime(), /*running*/ true);
}
private void reset() {
diff --git a/core/java/android/view/InputDevice.java b/core/java/android/view/InputDevice.java
index d22d2a5..609ad5b 100644
--- a/core/java/android/view/InputDevice.java
+++ b/core/java/android/view/InputDevice.java
@@ -78,6 +78,7 @@
private final InputDeviceIdentifier mIdentifier;
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P)
private final boolean mIsExternal;
+ @Source
private final int mSources;
private final int mKeyboardType;
private final KeyCharacterMap mKeyCharacterMap;
@@ -92,6 +93,7 @@
private final boolean mHasBattery;
private final HostUsiVersion mHostUsiVersion;
private final int mAssociatedDisplayId;
+ private final boolean mEnabled;
private final ArrayList<MotionRange> mMotionRanges = new ArrayList<MotionRange>();
private final ViewBehavior mViewBehavior = new ViewBehavior(this);
@@ -359,6 +361,28 @@
*/
public static final int SOURCE_ANY = 0xffffff00;
+ /** @hide */
+ @IntDef(flag = true, prefix = { "SOURCE_" }, value = {
+ SOURCE_UNKNOWN,
+ SOURCE_KEYBOARD,
+ SOURCE_DPAD,
+ SOURCE_GAMEPAD,
+ SOURCE_TOUCHSCREEN,
+ SOURCE_MOUSE,
+ SOURCE_STYLUS,
+ SOURCE_BLUETOOTH_STYLUS,
+ SOURCE_TRACKBALL,
+ SOURCE_MOUSE_RELATIVE,
+ SOURCE_TOUCHPAD,
+ SOURCE_TOUCH_NAVIGATION,
+ SOURCE_ROTARY_ENCODER,
+ SOURCE_JOYSTICK,
+ SOURCE_HDMI,
+ SOURCE_SENSOR,
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ @interface Source {}
+
/**
* Constant for retrieving the range of values for {@link MotionEvent#AXIS_X}.
*
@@ -479,7 +503,7 @@
int keyboardType, KeyCharacterMap keyCharacterMap, @Nullable String keyboardLanguageTag,
@Nullable String keyboardLayoutType, boolean hasVibrator, boolean hasMicrophone,
boolean hasButtonUnderPad, boolean hasSensor, boolean hasBattery, int usiVersionMajor,
- int usiVersionMinor, int associatedDisplayId) {
+ int usiVersionMinor, int associatedDisplayId, boolean enabled) {
mId = id;
mGeneration = generation;
mControllerNumber = controllerNumber;
@@ -510,6 +534,7 @@
mIdentifier = new InputDeviceIdentifier(descriptor, vendorId, productId);
mHostUsiVersion = new HostUsiVersion(usiVersionMajor, usiVersionMinor);
mAssociatedDisplayId = associatedDisplayId;
+ mEnabled = enabled;
}
private InputDevice(Parcel in) {
@@ -534,6 +559,7 @@
mHasBattery = in.readInt() != 0;
mHostUsiVersion = HostUsiVersion.CREATOR.createFromParcel(in);
mAssociatedDisplayId = in.readInt();
+ mEnabled = in.readInt() != 0;
mIdentifier = new InputDeviceIdentifier(mDescriptor, mVendorId, mProductId);
int numRanges = in.readInt();
@@ -578,6 +604,8 @@
private int mUsiVersionMajor = -1;
private int mUsiVersionMinor = -1;
private int mAssociatedDisplayId = Display.INVALID_DISPLAY;
+ // The default is true, the same as the native default state.
+ private boolean mEnabled = true;
private List<MotionRange> mMotionRanges = new ArrayList<>();
private boolean mShouldSmoothScroll;
@@ -708,6 +736,12 @@
return this;
}
+ /** @see InputDevice#isEnabled() */
+ public Builder setEnabled(boolean enabled) {
+ mEnabled = enabled;
+ return this;
+ }
+
/** @see InputDevice#getMotionRanges() */
public Builder addMotionRange(int axis, int source,
float min, float max, float flat, float fuzz, float resolution) {
@@ -749,7 +783,8 @@
mHasBattery,
mUsiVersionMajor,
mUsiVersionMinor,
- mAssociatedDisplayId);
+ mAssociatedDisplayId,
+ mEnabled);
final int numRanges = mMotionRanges.size();
for (int i = 0; i < numRanges; i++) {
@@ -1298,7 +1333,7 @@
* @return Whether the input device is enabled.
*/
public boolean isEnabled() {
- return InputManagerGlobal.getInstance().isInputDeviceEnabled(mId);
+ return mEnabled;
}
/**
@@ -1588,6 +1623,7 @@
out.writeInt(mHasBattery ? 1 : 0);
mHostUsiVersion.writeToParcel(out, flags);
out.writeInt(mAssociatedDisplayId);
+ out.writeInt(mEnabled ? 1 : 0);
int numRanges = mMotionRanges.size();
numRanges = numRanges > MAX_RANGES ? MAX_RANGES : numRanges;
@@ -1619,6 +1655,7 @@
description.append(" Generation: ").append(mGeneration).append("\n");
description.append(" Location: ").append(mIsExternal ? "external" : "built-in").append(
"\n");
+ description.append(" Enabled: ").append(isEnabled()).append("\n");
description.append(" Keyboard Type: ");
switch (mKeyboardType) {
diff --git a/core/java/android/view/InsetsController.java b/core/java/android/view/InsetsController.java
index f1cb410..d7f2b01 100644
--- a/core/java/android/view/InsetsController.java
+++ b/core/java/android/view/InsetsController.java
@@ -1262,10 +1262,13 @@
mHost.getInputMethodManager(), null /* icProto */);
}
+ final var statsToken = ImeTracker.forLogging().onStart(ImeTracker.TYPE_USER,
+ ImeTracker.ORIGIN_CLIENT, SoftInputShowHideReason.CONTROL_WINDOW_INSETS_ANIMATION,
+ mHost.isHandlingPointerEvent() /* fromUser */);
controlAnimationUnchecked(types, cancellationSignal, listener, mFrame, fromIme, durationMs,
interpolator, animationType,
getLayoutInsetsDuringAnimationMode(types, fromPredictiveBack),
- false /* useInsetsAnimationThread */, null /* statsToken */);
+ false /* useInsetsAnimationThread */, statsToken);
}
private void controlAnimationUnchecked(@InsetsType int types,
@@ -1567,7 +1570,9 @@
return;
}
final ImeTracker.Token statsToken = runner.getStatsToken();
- if (shown) {
+ if (runner.getAnimationType() == ANIMATION_TYPE_USER) {
+ ImeTracker.forLogging().onUserFinished(statsToken, shown);
+ } else if (shown) {
ImeTracker.forLogging().onProgress(statsToken,
ImeTracker.PHASE_CLIENT_ANIMATION_FINISHED_SHOW);
ImeTracker.forLogging().onShown(statsToken);
@@ -1838,6 +1843,9 @@
Trace.asyncTraceEnd(TRACE_TAG_VIEW, "IC.pendingAnim", 0);
mHost.dispatchWindowInsetsAnimationStart(animation, bounds);
mStartingAnimation = true;
+ if (runner.getAnimationType() == ANIMATION_TYPE_USER) {
+ ImeTracker.forLogging().onDispatched(runner.getStatsToken());
+ }
runner.setReadyDispatched(true);
listener.onReady(runner, types);
mStartingAnimation = false;
diff --git a/core/java/android/view/KeyEvent.java b/core/java/android/view/KeyEvent.java
index 154f1fe..01015ea 100644
--- a/core/java/android/view/KeyEvent.java
+++ b/core/java/android/view/KeyEvent.java
@@ -20,6 +20,7 @@
import static android.view.Display.INVALID_DISPLAY;
import android.annotation.FlaggedApi;
+import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.TestApi;
@@ -35,6 +36,8 @@
import com.android.hardware.input.Flags;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
import java.util.concurrent.TimeUnit;
/**
@@ -944,7 +947,7 @@
@FlaggedApi(Flags.FLAG_EMOJI_AND_SCREENSHOT_KEYCODES_AVAILABLE)
public static final int KEYCODE_SCREENSHOT = 318;
- /**
+ /**
* Integer value of the last KEYCODE. Increases as new keycodes are added to KeyEvent.
* @hide
*/
@@ -1034,6 +1037,15 @@
@Deprecated
public static final int ACTION_MULTIPLE = 2;
+ /** @hide */
+ @IntDef(prefix = {"ACTION_"}, value = {
+ ACTION_DOWN,
+ ACTION_UP,
+ ACTION_MULTIPLE,
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ @interface Action {}
+
/**
* SHIFT key locked in CAPS mode.
* Reserved for use by {@link MetaKeyKeyListener} for a published constant in its API.
@@ -1222,6 +1234,33 @@
*/
public static final int META_SCROLL_LOCK_ON = 0x400000;
+ /** @hide */
+ @IntDef(flag = true, prefix = {"META_"}, value = {
+ META_CAP_LOCKED,
+ META_ALT_LOCKED,
+ META_SYM_LOCKED,
+ META_SELECTING,
+ META_ALT_ON,
+ META_ALT_LEFT_ON,
+ META_ALT_RIGHT_ON,
+ META_SHIFT_ON,
+ META_SHIFT_LEFT_ON,
+ META_SHIFT_RIGHT_ON,
+ META_SYM_ON,
+ META_FUNCTION_ON,
+ META_CTRL_ON,
+ META_CTRL_LEFT_ON,
+ META_CTRL_RIGHT_ON,
+ META_META_ON,
+ META_META_LEFT_ON,
+ META_META_RIGHT_ON,
+ META_CAPS_LOCK_ON,
+ META_NUM_LOCK_ON,
+ META_SCROLL_LOCK_ON,
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ @interface MetaState {}
+
/**
* This mask is a combination of {@link #META_SHIFT_ON}, {@link #META_SHIFT_LEFT_ON}
* and {@link #META_SHIFT_RIGHT_ON}.
@@ -1366,6 +1405,27 @@
*/
public static final int FLAG_TAINTED = IInputConstants.INPUT_EVENT_FLAG_TAINTED;
+ /** @hide */
+ @IntDef(flag = true, prefix = { "FLAG_" }, value = {
+ FLAG_WOKE_HERE,
+ FLAG_SOFT_KEYBOARD,
+ FLAG_KEEP_TOUCH_MODE,
+ FLAG_FROM_SYSTEM,
+ FLAG_EDITOR_ACTION,
+ FLAG_CANCELED,
+ FLAG_VIRTUAL_HARD_KEY,
+ FLAG_LONG_PRESS,
+ FLAG_CANCELED_LONG_PRESS,
+ FLAG_TRACKING,
+ FLAG_FALLBACK,
+ FLAG_IS_ACCESSIBILITY_EVENT,
+ FLAG_PREDISPATCH,
+ FLAG_START_TRACKING,
+ FLAG_TAINTED,
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ @interface Flag {}
+
/**
* Returns the maximum keycode.
*/
@@ -1401,8 +1461,10 @@
// NOTE: mHmac is private and not used in this class, but it's used on native side / parcel.
private @Nullable byte[] mHmac;
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
+ @MetaState
private int mMetaState;
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
+ @Action
private int mAction;
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
private int mKeyCode;
@@ -1411,6 +1473,7 @@
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
private int mRepeatCount;
@UnsupportedAppUsage
+ @Flag
private int mFlags;
/**
* The time when the key initially was pressed, in nanoseconds. Only millisecond precision is
diff --git a/core/java/android/view/MotionEvent.java b/core/java/android/view/MotionEvent.java
index 38f9a91..6db40bf 100644
--- a/core/java/android/view/MotionEvent.java
+++ b/core/java/android/view/MotionEvent.java
@@ -50,6 +50,7 @@
import dalvik.annotation.optimization.FastNative;
import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
import java.util.Objects;
/**
@@ -445,6 +446,25 @@
@Deprecated
public static final int ACTION_POINTER_ID_SHIFT = 8;
+ /** @hide */
+ @IntDef(prefix = { "ACTION_" }, value = {
+ ACTION_DOWN,
+ ACTION_UP,
+ ACTION_MOVE,
+ ACTION_CANCEL,
+ ACTION_OUTSIDE,
+ ACTION_POINTER_DOWN,
+ ACTION_POINTER_UP,
+ ACTION_HOVER_MOVE,
+ ACTION_SCROLL,
+ ACTION_HOVER_ENTER,
+ ACTION_HOVER_EXIT,
+ ACTION_BUTTON_PRESS,
+ ACTION_BUTTON_RELEASE,
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ @interface ActionMasked {}
+
/**
* This flag indicates that the window that received this motion event is partly
* or wholly obscured by another visible window above it and the event directly passed through
@@ -548,6 +568,21 @@
public static final int FLAG_TARGET_ACCESSIBILITY_FOCUS =
MOTION_EVENT_FLAG_TARGET_ACCESSIBILITY_FOCUS;
+ /** @hide */
+ @IntDef(flag = true, prefix = { "FLAG_" }, value = {
+ FLAG_WINDOW_IS_OBSCURED,
+ FLAG_WINDOW_IS_PARTIALLY_OBSCURED,
+ FLAG_HOVER_EXIT_PENDING,
+ FLAG_IS_GENERATED_GESTURE,
+ FLAG_CANCELED,
+ FLAG_NO_FOCUS_CHANGE,
+ FLAG_IS_ACCESSIBILITY_EVENT,
+ FLAG_TAINTED,
+ FLAG_TARGET_ACCESSIBILITY_FOCUS,
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ @interface Flag {}
+
/**
* Flag indicating the motion event intersected the top edge of the screen.
*/
@@ -1422,6 +1457,63 @@
names.append(AXIS_GESTURE_SWIPE_FINGER_COUNT, "AXIS_GESTURE_SWIPE_FINGER_COUNT");
}
+ /** @hide */
+ @IntDef(prefix = { "AXIS_" }, value = {
+ AXIS_X,
+ AXIS_Y,
+ AXIS_PRESSURE,
+ AXIS_SIZE,
+ AXIS_TOUCH_MAJOR,
+ AXIS_TOUCH_MINOR,
+ AXIS_TOOL_MAJOR,
+ AXIS_TOOL_MINOR,
+ AXIS_ORIENTATION,
+ AXIS_VSCROLL,
+ AXIS_HSCROLL,
+ AXIS_Z,
+ AXIS_RX,
+ AXIS_RY,
+ AXIS_RZ,
+ AXIS_HAT_X,
+ AXIS_HAT_Y,
+ AXIS_LTRIGGER,
+ AXIS_RTRIGGER,
+ AXIS_THROTTLE,
+ AXIS_RUDDER,
+ AXIS_WHEEL,
+ AXIS_GAS,
+ AXIS_BRAKE,
+ AXIS_DISTANCE,
+ AXIS_TILT,
+ AXIS_SCROLL,
+ AXIS_RELATIVE_X,
+ AXIS_RELATIVE_Y,
+ AXIS_GENERIC_1,
+ AXIS_GENERIC_2,
+ AXIS_GENERIC_3,
+ AXIS_GENERIC_4,
+ AXIS_GENERIC_5,
+ AXIS_GENERIC_6,
+ AXIS_GENERIC_7,
+ AXIS_GENERIC_8,
+ AXIS_GENERIC_9,
+ AXIS_GENERIC_10,
+ AXIS_GENERIC_11,
+ AXIS_GENERIC_12,
+ AXIS_GENERIC_13,
+ AXIS_GENERIC_14,
+ AXIS_GENERIC_15,
+ AXIS_GENERIC_16,
+ AXIS_GESTURE_X_OFFSET,
+ AXIS_GESTURE_Y_OFFSET,
+ AXIS_GESTURE_SCROLL_X_DISTANCE,
+ AXIS_GESTURE_SCROLL_Y_DISTANCE,
+ AXIS_GESTURE_PINCH_SCALE_FACTOR,
+ AXIS_GESTURE_SWIPE_FINGER_COUNT,
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ @interface Axis {}
+
/**
* Button constant: Primary button (left mouse button).
*
@@ -1522,6 +1614,19 @@
"0x80000000",
};
+ /** @hide */
+ @IntDef(flag = true, prefix = { "BUTTON_" }, value = {
+ BUTTON_PRIMARY,
+ BUTTON_SECONDARY,
+ BUTTON_TERTIARY,
+ BUTTON_BACK,
+ BUTTON_FORWARD,
+ BUTTON_STYLUS_PRIMARY,
+ BUTTON_STYLUS_SECONDARY,
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ @interface Button {}
+
/**
* Classification constant: None.
*
diff --git a/core/java/android/view/VelocityTracker.java b/core/java/android/view/VelocityTracker.java
index d31f823..27176a4 100644
--- a/core/java/android/view/VelocityTracker.java
+++ b/core/java/android/view/VelocityTracker.java
@@ -264,7 +264,6 @@
/**
* Obtains a velocity tracker with the specified strategy.
- * For testing and comparison purposes only.
*
* @param strategy The strategy Id, VELOCITY_TRACKER_STRATEGY_DEFAULT to use the default.
* @return The velocity tracker.
@@ -272,6 +271,9 @@
* @hide
*/
public static VelocityTracker obtain(int strategy) {
+ if (strategy == VELOCITY_TRACKER_STRATEGY_DEFAULT) {
+ return obtain();
+ }
return new VelocityTracker(strategy);
}
diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java
index 155c053..0715474 100644
--- a/core/java/android/view/ViewRootImpl.java
+++ b/core/java/android/view/ViewRootImpl.java
@@ -427,12 +427,6 @@
private static final long NANOS_PER_SEC = 1000000000;
- // If the ViewRootImpl has been idle for more than 200ms, clear the preferred
- // frame rate category and frame rate.
- private static final int IDLE_TIME_MILLIS = 250;
-
- private static final long NANOS_PER_MILLI = 1_000_000;
-
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
static final ThreadLocal<HandlerActionQueue> sRunQueues = new ThreadLocal<HandlerActionQueue>();
@@ -665,8 +659,6 @@
private int mMinusOneFrameIntervalMillis = 0;
// VRR interval between the previous and the frame before
private int mMinusTwoFrameIntervalMillis = 0;
- // VRR has the invalidation idle message been posted?
- private boolean mInvalidationIdleMessagePosted = false;
/**
* Update the Choreographer's FrameInfo object with the timing information for the current
@@ -929,15 +921,6 @@
private String mFrameRateCategoryView;
/**
- * The resolved pointer icon type requested by this window.
- * A null value indicates the resolved pointer icon has not yet been calculated.
- */
- // TODO(b/293587049): Remove pointer icon tracking by type when refactor is complete.
- @Nullable
- private Integer mPointerIconType = null;
- private PointerIcon mCustomPointerIcon = null;
-
- /**
* The resolved pointer icon requested by this window.
* A null value indicates the resolved pointer icon has not yet been calculated.
*/
@@ -4278,10 +4261,6 @@
// when the values are applicable.
if (mDrawnThisFrame) {
mDrawnThisFrame = false;
- if (!mInvalidationIdleMessagePosted) {
- mInvalidationIdleMessagePosted = true;
- mHandler.sendEmptyMessageDelayed(MSG_CHECK_INVALIDATION_IDLE, IDLE_TIME_MILLIS);
- }
setCategoryFromCategoryCounts();
updateInfrequentCount();
setPreferredFrameRate(mPreferredFrameRate);
@@ -6442,7 +6421,6 @@
private static final int MSG_SYNTHESIZE_INPUT_EVENT = 24;
private static final int MSG_DISPATCH_WINDOW_SHOWN = 25;
private static final int MSG_REQUEST_KEYBOARD_SHORTCUTS = 26;
- private static final int MSG_UPDATE_POINTER_ICON = 27;
private static final int MSG_POINTER_CAPTURE_CHANGED = 28;
private static final int MSG_INSETS_CONTROL_CHANGED = 29;
private static final int MSG_SYSTEM_GESTURE_EXCLUSION_CHANGED = 30;
@@ -6507,8 +6485,6 @@
return "MSG_SYNTHESIZE_INPUT_EVENT";
case MSG_DISPATCH_WINDOW_SHOWN:
return "MSG_DISPATCH_WINDOW_SHOWN";
- case MSG_UPDATE_POINTER_ICON:
- return "MSG_UPDATE_POINTER_ICON";
case MSG_POINTER_CAPTURE_CHANGED:
return "MSG_POINTER_CAPTURE_CHANGED";
case MSG_INSETS_CONTROL_CHANGED:
@@ -6523,8 +6499,6 @@
return "MSG_WINDOW_TOUCH_MODE_CHANGED";
case MSG_KEEP_CLEAR_RECTS_CHANGED:
return "MSG_KEEP_CLEAR_RECTS_CHANGED";
- case MSG_CHECK_INVALIDATION_IDLE:
- return "MSG_CHECK_INVALIDATION_IDLE";
case MSG_REFRESH_POINTER_ICON:
return "MSG_REFRESH_POINTER_ICON";
case MSG_TOUCH_BOOST_TIMEOUT:
@@ -6761,10 +6735,6 @@
final int deviceId = msg.arg1;
handleRequestKeyboardShortcuts(receiver, deviceId);
} break;
- case MSG_UPDATE_POINTER_ICON: {
- MotionEvent event = (MotionEvent) msg.obj;
- resetPointerIcon(event);
- } break;
case MSG_POINTER_CAPTURE_CHANGED: {
final boolean hasCapture = msg.arg1 != 0;
handlePointerCaptureChanged(hasCapture);
@@ -6789,30 +6759,6 @@
mNumPausedForSync = 0;
scheduleTraversals();
break;
- case MSG_CHECK_INVALIDATION_IDLE: {
- long delta;
- if (mIsTouchBoosting || mIsFrameRateBoosting || mInsetsAnimationRunning) {
- delta = 0;
- } else {
- delta = System.nanoTime() / NANOS_PER_MILLI - mLastUpdateTimeMillis;
- }
- if (delta >= IDLE_TIME_MILLIS) {
- mFrameRateCategoryHighCount = 0;
- mFrameRateCategoryHighHintCount = 0;
- mFrameRateCategoryNormalCount = 0;
- mFrameRateCategoryLowCount = 0;
- mPreferredFrameRate = 0;
- mPreferredFrameRateCategory = FRAME_RATE_CATEGORY_NO_PREFERENCE;
- setPreferredFrameRateCategory(FRAME_RATE_CATEGORY_NO_PREFERENCE);
- setPreferredFrameRate(0f);
- mInvalidationIdleMessagePosted = false;
- } else {
- mInvalidationIdleMessagePosted = true;
- mHandler.sendEmptyMessageDelayed(MSG_CHECK_INVALIDATION_IDLE,
- IDLE_TIME_MILLIS - delta);
- }
- break;
- }
case MSG_TOUCH_BOOST_TIMEOUT:
/**
* Lower the frame rate after the boosting period (FRAME_RATE_TOUCH_BOOST_TIME).
@@ -7874,14 +7820,12 @@
|| action == MotionEvent.ACTION_HOVER_EXIT) {
// Other apps or the window manager may change the icon type outside of
// this app, therefore the icon type has to be reset on enter/exit event.
- mPointerIconType = null;
mResolvedPointerIcon = null;
}
if (action != MotionEvent.ACTION_HOVER_EXIT) {
// Resolve the pointer icon
if (!updatePointerIcon(event) && action == MotionEvent.ACTION_HOVER_MOVE) {
- mPointerIconType = null;
mResolvedPointerIcon = null;
}
}
@@ -7944,12 +7888,6 @@
return mAttachInfo.mHandlingPointerEvent;
}
- private void resetPointerIcon(MotionEvent event) {
- mPointerIconType = null;
- mResolvedPointerIcon = null;
- updatePointerIcon(event);
- }
-
/**
* If there is pointer that is showing a PointerIcon in this window, refresh the icon for that
@@ -8609,48 +8547,55 @@
private int mPendingKeyMetaState;
- private final GestureDetector mGestureDetector = new GestureDetector(mContext,
- new GestureDetector.OnGestureListener() {
- @Override
- public boolean onDown(@NonNull MotionEvent e) {
- // This can be ignored since it's not clear what KeyEvent this will
- // belong to.
- return true;
- }
-
- @Override
- public void onShowPress(@NonNull MotionEvent e) {
-
- }
-
- @Override
- public boolean onSingleTapUp(@NonNull MotionEvent e) {
- dispatchTap(e.getEventTime());
- return true;
- }
-
- @Override
- public boolean onScroll(@Nullable MotionEvent e1, @NonNull MotionEvent e2,
- float distanceX, float distanceY) {
- // Scroll doesn't translate to DPAD events so should be ignored.
- return true;
- }
-
- @Override
- public void onLongPress(@NonNull MotionEvent e) {
- // Long presses don't translate to DPAD events so should be ignored.
- }
-
- @Override
- public boolean onFling(@Nullable MotionEvent e1, @NonNull MotionEvent e2,
- float velocityX, float velocityY) {
- dispatchFling(velocityX, velocityY, e2.getEventTime());
- return true;
- }
- });
+ private final GestureDetector mGestureDetector;
SyntheticTouchNavigationHandler() {
super(true);
+ int gestureDetectorVelocityStrategy =
+ android.companion.virtual.flags.Flags
+ .impulseVelocityStrategyForTouchNavigation()
+ ? VelocityTracker.VELOCITY_TRACKER_STRATEGY_IMPULSE
+ : VelocityTracker.VELOCITY_TRACKER_STRATEGY_DEFAULT;
+ mGestureDetector = new GestureDetector(mContext,
+ new GestureDetector.OnGestureListener() {
+ @Override
+ public boolean onDown(@NonNull MotionEvent e) {
+ // This can be ignored since it's not clear what KeyEvent this will
+ // belong to.
+ return true;
+ }
+
+ @Override
+ public void onShowPress(@NonNull MotionEvent e) {
+ }
+
+ @Override
+ public boolean onSingleTapUp(@NonNull MotionEvent e) {
+ dispatchTap(e.getEventTime());
+ return true;
+ }
+
+ @Override
+ public boolean onScroll(@Nullable MotionEvent e1, @NonNull MotionEvent e2,
+ float distanceX, float distanceY) {
+ // Scroll doesn't translate to DPAD events so should be ignored.
+ return true;
+ }
+
+ @Override
+ public void onLongPress(@NonNull MotionEvent e) {
+ // Long presses don't translate to DPAD events so should be ignored.
+ }
+
+ @Override
+ public boolean onFling(@Nullable MotionEvent e1, @NonNull MotionEvent e2,
+ float velocityX, float velocityY) {
+ dispatchFling(velocityX, velocityY, e2.getEventTime());
+ return true;
+ }
+ },
+ /* handler= */ null,
+ gestureDetectorVelocityStrategy);
}
public void process(MotionEvent event) {
@@ -13034,10 +12979,6 @@
private void removeVrrMessages() {
mHandler.removeMessages(MSG_TOUCH_BOOST_TIMEOUT);
mHandler.removeMessages(MSG_FRAME_RATE_SETTING);
- if (mInvalidationIdleMessagePosted) {
- mInvalidationIdleMessagePosted = false;
- mHandler.removeMessages(MSG_CHECK_INVALIDATION_IDLE);
- }
}
/**
diff --git a/core/java/android/view/WindowManager.java b/core/java/android/view/WindowManager.java
index f22e8f5..0f54940b 100644
--- a/core/java/android/view/WindowManager.java
+++ b/core/java/android/view/WindowManager.java
@@ -781,7 +781,7 @@
* <p>
* The metrics describe the size of the area the window would occupy with
* {@link LayoutParams#MATCH_PARENT MATCH_PARENT} width and height, and the {@link WindowInsets}
- * such a window would have.
+ * such a window would have. The {@link WindowInsets} are not deducted from the bounds.
* <p>
* The value of this is based on the <b>current</b> windowing state of the system.
*
@@ -811,7 +811,7 @@
* <p>
* The metrics describe the size of the largest potential area the window might occupy with
* {@link LayoutParams#MATCH_PARENT MATCH_PARENT} width and height, and the {@link WindowInsets}
- * such a window would have.
+ * such a window would have. The {@link WindowInsets} are not deducted from the bounds.
* <p>
* Note that this might still be smaller than the size of the physical display if certain areas
* of the display are not available to windows created in this {@link Context}.
@@ -4264,11 +4264,9 @@
* no letterbox is applied."/>
*
* <p>
- * A cutout in the corner is considered to be on the short edge: <br/>
- * <img src="{@docRoot}reference/android/images/display_cutout/short_edge/fullscreen_corner_no_letterbox.png"
- * height="720"
- * alt="Screenshot of a fullscreen activity on a display with a cutout in the corner in
- * portrait, no letterbox is applied."/>
+ * A cutout in the corner can be considered to be on different edge in different device
+ * rotations. This behavior may vary from device to device. Use this flag is possible to
+ * letterbox your app if the display cutout is at corner.
*
* <p>
* On the other hand, should the cutout be on the long edge of the display, a letterbox will
diff --git a/core/java/android/view/WindowMetrics.java b/core/java/android/view/WindowMetrics.java
index 26298bc..8bcc9de 100644
--- a/core/java/android/view/WindowMetrics.java
+++ b/core/java/android/view/WindowMetrics.java
@@ -101,9 +101,13 @@
* Returns the bounds of the area associated with this window or {@code UiContext}.
* <p>
* <b>Note that the size of the reported bounds can have different size than
- * {@link Display#getSize(Point)}.</b> This method reports the window size including all system
- * bar areas, while {@link Display#getSize(Point)} reports the area excluding navigation bars
- * and display cutout areas. The value reported by {@link Display#getSize(Point)} can be
+ * {@link Display#getSize(Point)} based on your target API level and calling context.</b>
+ * This method reports the window size including all system
+ * bar areas, while {@link Display#getSize(Point)} can report the area excluding navigation bars
+ * and display cutout areas depending on the calling context and target SDK level. Please refer
+ * to {@link Display#getSize(Point)} for details.
+ * <p>
+ * The value reported by {@link Display#getSize(Point)} excluding system decoration areas can be
* obtained by using:
* <pre class="prettyprint">
* final WindowMetrics metrics = windowManager.getCurrentWindowMetrics();
diff --git a/core/java/android/view/inputmethod/IInputMethodManagerGlobalInvoker.java b/core/java/android/view/inputmethod/IInputMethodManagerGlobalInvoker.java
index f454a6a..3091bf4 100644
--- a/core/java/android/view/inputmethod/IInputMethodManagerGlobalInvoker.java
+++ b/core/java/android/view/inputmethod/IInputMethodManagerGlobalInvoker.java
@@ -754,6 +754,19 @@
}
}
+ /** @see com.android.server.inputmethod.ImeTrackerService#onDispatched */
+ static void onDispatched(@NonNull ImeTracker.Token statsToken) {
+ final IImeTracker service = getImeTrackerService();
+ if (service == null) {
+ return;
+ }
+ try {
+ service.onDispatched(statsToken);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
/** @see com.android.server.inputmethod.ImeTrackerService#hasPendingImeVisibilityRequests */
@AnyThread
@RequiresPermission(Manifest.permission.TEST_INPUT_METHOD)
diff --git a/core/java/android/view/inputmethod/ImeTracker.java b/core/java/android/view/inputmethod/ImeTracker.java
index d992feb..edc9921 100644
--- a/core/java/android/view/inputmethod/ImeTracker.java
+++ b/core/java/android/view/inputmethod/ImeTracker.java
@@ -71,24 +71,40 @@
/** The type of the IME request. */
@IntDef(prefix = { "TYPE_" }, value = {
TYPE_SHOW,
- TYPE_HIDE
+ TYPE_HIDE,
+ TYPE_USER,
})
@Retention(RetentionPolicy.SOURCE)
@interface Type {}
- /** IME show request type. */
+ /**
+ * IME show request type.
+ *
+ * @see android.view.InsetsController#ANIMATION_TYPE_SHOW
+ */
int TYPE_SHOW = ImeProtoEnums.TYPE_SHOW;
- /** IME hide request type. */
+ /**
+ * IME hide request type.
+ *
+ * @see android.view.InsetsController#ANIMATION_TYPE_HIDE
+ */
int TYPE_HIDE = ImeProtoEnums.TYPE_HIDE;
+ /**
+ * IME user-controlled animation request type.
+ *
+ * @see android.view.InsetsController#ANIMATION_TYPE_USER
+ */
+ int TYPE_USER = ImeProtoEnums.TYPE_USER;
+
/** The status of the IME request. */
@IntDef(prefix = { "STATUS_" }, value = {
STATUS_RUN,
STATUS_CANCEL,
STATUS_FAIL,
STATUS_SUCCESS,
- STATUS_TIMEOUT
+ STATUS_TIMEOUT,
})
@Retention(RetentionPolicy.SOURCE)
@interface Status {}
@@ -117,7 +133,7 @@
@IntDef(prefix = { "ORIGIN_" }, value = {
ORIGIN_CLIENT,
ORIGIN_SERVER,
- ORIGIN_IME
+ ORIGIN_IME,
})
@Retention(RetentionPolicy.SOURCE)
@interface Origin {}
@@ -400,20 +416,36 @@
void onCancelled(@Nullable Token token, @Phase int phase);
/**
- * Called when the IME show request is successful.
+ * Called when the show IME request is successful.
*
* @param token the token tracking the current IME request or {@code null} otherwise.
*/
void onShown(@Nullable Token token);
/**
- * Called when the IME hide request is successful.
+ * Called when the hide IME request is successful.
*
* @param token the token tracking the current IME request or {@code null} otherwise.
*/
void onHidden(@Nullable Token token);
/**
+ * Called when the user-controlled IME request was dispatched to the requesting app. The
+ * user animation can take an undetermined amount of time, so it shouldn't be tracked.
+ *
+ * @param token the token tracking the current IME request or {@code null} otherwise.
+ */
+ void onDispatched(@Nullable Token token);
+
+ /**
+ * Called when the animation of the user-controlled IME request finished.
+ *
+ * @param token the token tracking the current IME request or {@code null} otherwise.
+ * @param shown whether the end state of the animation was shown or hidden.
+ */
+ void onUserFinished(@Nullable Token token, boolean shown);
+
+ /**
* Returns whether the current IME request was created due to a user interaction. This can
* only be {@code true} when running on the view's UI thread.
*
@@ -482,13 +514,6 @@
/** Whether the stack trace at the request call site should be logged. */
private boolean mLogStackTrace;
- private void reloadSystemProperties() {
- mLogProgress = SystemProperties.getBoolean(
- "persist.debug.imetracker", false);
- mLogStackTrace = SystemProperties.getBoolean(
- "persist.debug.imerequest.logstacktrace", false);
- }
-
@NonNull
@Override
public Token onStart(@NonNull String component, int uid, @Type int type, @Origin int origin,
@@ -497,7 +522,7 @@
final var token = IInputMethodManagerGlobalInvoker.onStart(tag, uid, type,
origin, reason, fromUser);
- Log.i(TAG, token.mTag + ": onRequest" + (type == TYPE_SHOW ? "Show" : "Hide")
+ Log.i(TAG, token.mTag + ": " + getOnStartPrefix(type)
+ " at " + Debug.originToString(origin)
+ " reason " + InputMethodDebug.softInputDisplayReasonToString(reason)
+ " fromUser " + fromUser,
@@ -552,6 +577,45 @@
Log.i(TAG, token.mTag + ": onHidden");
}
+
+ @Override
+ public void onDispatched(@Nullable Token token) {
+ if (token == null) return;
+ IInputMethodManagerGlobalInvoker.onDispatched(token);
+
+ Log.i(TAG, token.mTag + ": onDispatched");
+ }
+
+ @Override
+ public void onUserFinished(@Nullable Token token, boolean shown) {
+ if (token == null) return;
+ // This is already sent to ImeTrackerService to mark it finished during onDispatched.
+
+ Log.i(TAG, token.mTag + ": onUserFinished " + (shown ? "shown" : "hidden"));
+ }
+
+ /**
+ * Gets the prefix string for {@link #onStart} based on the given request type.
+ *
+ * @param type request type for which to create the prefix string with.
+ */
+ @NonNull
+ private static String getOnStartPrefix(@Type int type) {
+ return switch (type) {
+ case TYPE_SHOW -> "onRequestShow";
+ case TYPE_HIDE -> "onRequestHide";
+ case TYPE_USER -> "onRequestUser";
+ default -> "onRequestUnknown";
+ };
+ }
+
+ /** Reloads the system properties related to this class. */
+ private void reloadSystemProperties() {
+ mLogProgress = SystemProperties.getBoolean(
+ "persist.debug.imetracker", false);
+ mLogStackTrace = SystemProperties.getBoolean(
+ "persist.debug.imerequest.logstacktrace", false);
+ }
};
/** The singleton IME tracker instance for instrumenting jank metrics. */
diff --git a/core/java/android/view/inputmethod/InputMethodInfo.java b/core/java/android/view/inputmethod/InputMethodInfo.java
index 7885cd9..11ee286 100644
--- a/core/java/android/view/inputmethod/InputMethodInfo.java
+++ b/core/java/android/view/inputmethod/InputMethodInfo.java
@@ -54,6 +54,7 @@
import java.io.IOException;
import java.util.ArrayList;
+import java.util.Collections;
import java.util.List;
/**
@@ -431,6 +432,14 @@
* @hide
*/
public InputMethodInfo(InputMethodInfo source) {
+ this(source, Collections.emptyList());
+ }
+
+ /**
+ * @hide
+ */
+ public InputMethodInfo(@NonNull InputMethodInfo source,
+ @NonNull List<InputMethodSubtype> additionalSubtypes) {
mId = source.mId;
mSettingsActivityName = source.mSettingsActivityName;
mLanguageSettingsActivityName = source.mLanguageSettingsActivityName;
@@ -445,7 +454,19 @@
mIsVrOnly = source.mIsVrOnly;
mIsVirtualDeviceOnly = source.mIsVirtualDeviceOnly;
mService = source.mService;
- mSubtypes = source.mSubtypes;
+ if (additionalSubtypes.isEmpty()) {
+ mSubtypes = source.mSubtypes;
+ } else {
+ final ArrayList<InputMethodSubtype> subtypes = source.mSubtypes.toList();
+ final int additionalSubtypeCount = additionalSubtypes.size();
+ for (int i = 0; i < additionalSubtypeCount; ++i) {
+ final InputMethodSubtype additionalSubtype = additionalSubtypes.get(i);
+ if (!subtypes.contains(additionalSubtype)) {
+ subtypes.add(additionalSubtype);
+ }
+ }
+ mSubtypes = new InputMethodSubtypeArray(subtypes);
+ }
mHandledConfigChanges = source.mHandledConfigChanges;
mSupportsStylusHandwriting = source.mSupportsStylusHandwriting;
mSupportsConnectionlessStylusHandwriting = source.mSupportsConnectionlessStylusHandwriting;
diff --git a/core/java/android/view/inputmethod/InputMethodSubtypeArray.java b/core/java/android/view/inputmethod/InputMethodSubtypeArray.java
index c243a22..e2d343f 100644
--- a/core/java/android/view/inputmethod/InputMethodSubtypeArray.java
+++ b/core/java/android/view/inputmethod/InputMethodSubtypeArray.java
@@ -25,6 +25,7 @@
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
+import java.util.ArrayList;
import java.util.List;
import java.util.zip.GZIPInputStream;
import java.util.zip.GZIPOutputStream;
@@ -163,6 +164,18 @@
}
/**
+ * @return A list of {@link InputMethodInfo} copied from this array.
+ */
+ @NonNull
+ public ArrayList<InputMethodSubtype> toList() {
+ final ArrayList<InputMethodSubtype> list = new ArrayList<>(mCount);
+ for (int i = 0; i < mCount; ++i) {
+ list.add(get(i));
+ }
+ return list;
+ }
+
+ /**
* Return the number of {@link InputMethodSubtype} objects.
*/
public int getCount() {
diff --git a/core/java/android/widget/flags/notification_widget_flags.aconfig b/core/java/android/widget/flags/notification_widget_flags.aconfig
index 3a39631..503e542 100644
--- a/core/java/android/widget/flags/notification_widget_flags.aconfig
+++ b/core/java/android/widget/flags/notification_widget_flags.aconfig
@@ -47,3 +47,13 @@
purpose: PURPOSE_BUGFIX
}
}
+
+flag {
+ name: "messaging_child_request_layout"
+ namespace: "systemui"
+ description: "MessagingChild always needs to be measured during MessagingLinearLayout onMeasure."
+ bug: "324537506"
+ metadata {
+ purpose: PURPOSE_BUGFIX
+ }
+}
diff --git a/core/java/android/window/BackProgressAnimator.java b/core/java/android/window/BackProgressAnimator.java
index 163e43a..6fe0784 100644
--- a/core/java/android/window/BackProgressAnimator.java
+++ b/core/java/android/window/BackProgressAnimator.java
@@ -114,7 +114,6 @@
* dispatches as the progress animation updates.
*/
public void onBackStarted(BackMotionEvent event, ProgressCallback callback) {
- reset();
mLastBackEvent = event;
mCallback = callback;
mBackAnimationInProgress = true;
diff --git a/core/java/android/window/SplashScreenView.java b/core/java/android/window/SplashScreenView.java
index 31e3a34..af3d7eb 100644
--- a/core/java/android/window/SplashScreenView.java
+++ b/core/java/android/window/SplashScreenView.java
@@ -56,6 +56,8 @@
import com.android.internal.jank.InteractionJankMonitor;
import com.android.internal.policy.DecorView;
+import java.io.Closeable;
+import java.io.IOException;
import java.time.Duration;
import java.time.Instant;
import java.util.function.Consumer;
@@ -568,6 +570,12 @@
protected void onDetachedFromWindow() {
super.onDetachedFromWindow();
releaseAnimationSurfaceHost();
+ if (mIconView instanceof ImageView imageView
+ && imageView.getDrawable() instanceof Closeable closeableDrawable) {
+ try {
+ closeableDrawable.close();
+ } catch (IOException ignore) { }
+ }
}
@Override
diff --git a/core/java/android/window/TaskFragmentOperation.java b/core/java/android/window/TaskFragmentOperation.java
index d0f3c3e..6f7660a 100644
--- a/core/java/android/window/TaskFragmentOperation.java
+++ b/core/java/android/window/TaskFragmentOperation.java
@@ -84,8 +84,8 @@
/**
* Sets the activity navigation to be isolated, where the activity navigation on the
* TaskFragment is separated from the rest activities in the Task. Activities cannot be
- * started on an isolated TaskFragment unless the activities are launched from the same
- * TaskFragment or explicitly requested to.
+ * started on an isolated TaskFragment unless explicitly requested to. That said, new launched
+ * activities should be positioned as a sibling to the TaskFragment with higher z-ordering.
*/
public static final int OP_TYPE_SET_ISOLATED_NAVIGATION = 11;
@@ -149,6 +149,18 @@
*/
public static final int OP_TYPE_SET_DECOR_SURFACE_BOOSTED = 18;
+ /**
+ * Sets the TaskFragment to be pinned.
+ * <p>
+ * If a TaskFragment is pinned, the TaskFragment should be the top-most TaskFragment among other
+ * sibling TaskFragments. Any newly launched and embeddable activity should not be placed in the
+ * pinned TaskFragment, unless the activity is launched from the pinned TaskFragment or
+ * explicitly requested to. Non-embeddable activities are not restricted to.
+ * <p>
+ * See {@link #OP_TYPE_REORDER_TO_FRONT} on how to reorder a pinned TaskFragment to the top.
+ */
+ public static final int OP_TYPE_SET_PINNED = 19;
+
@IntDef(prefix = { "OP_TYPE_" }, value = {
OP_TYPE_UNKNOWN,
OP_TYPE_CREATE_TASK_FRAGMENT,
@@ -170,6 +182,7 @@
OP_TYPE_SET_DIM_ON_TASK,
OP_TYPE_SET_MOVE_TO_BOTTOM_IF_CLEAR_WHEN_LAUNCH,
OP_TYPE_SET_DECOR_SURFACE_BOOSTED,
+ OP_TYPE_SET_PINNED,
})
@Retention(RetentionPolicy.SOURCE)
public @interface OperationType {}
diff --git a/core/java/android/window/TaskFragmentParentInfo.java b/core/java/android/window/TaskFragmentParentInfo.java
index a77c234..1555416 100644
--- a/core/java/android/window/TaskFragmentParentInfo.java
+++ b/core/java/android/window/TaskFragmentParentInfo.java
@@ -18,6 +18,8 @@
import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.annotation.SuppressLint;
+import android.annotation.TestApi;
import android.app.WindowConfiguration;
import android.content.res.Configuration;
import android.os.Parcel;
@@ -27,10 +29,13 @@
import java.util.Objects;
/**
- * The information about the parent Task of a particular TaskFragment
+ * The information about the parent Task of a particular TaskFragment.
+ *
* @hide
*/
-public class TaskFragmentParentInfo implements Parcelable {
+@SuppressLint("UnflaggedApi") // @TestApi to replace legacy usages.
+@TestApi
+public final class TaskFragmentParentInfo implements Parcelable {
@NonNull
private final Configuration mConfiguration = new Configuration();
@@ -42,6 +47,7 @@
@Nullable private final SurfaceControl mDecorSurface;
+ /** @hide */
public TaskFragmentParentInfo(@NonNull Configuration configuration, int displayId,
boolean visible, boolean hasDirectActivity, @Nullable SurfaceControl decorSurface) {
mConfiguration.setTo(configuration);
@@ -51,6 +57,7 @@
mDecorSurface = decorSurface;
}
+ /** @hide */
public TaskFragmentParentInfo(@NonNull TaskFragmentParentInfo info) {
mConfiguration.setTo(info.getConfiguration());
mDisplayId = info.mDisplayId;
@@ -59,7 +66,11 @@
mDecorSurface = info.mDecorSurface;
}
- /** The {@link Configuration} of the parent Task */
+ /**
+ * The {@link Configuration} of the parent Task
+ *
+ * @hide
+ */
@NonNull
public Configuration getConfiguration() {
return mConfiguration;
@@ -68,19 +79,27 @@
/**
* The display ID of the parent Task. {@link android.view.Display#INVALID_DISPLAY} means the
* Task is detached from previously associated display.
+ *
+ * @hide
*/
public int getDisplayId() {
return mDisplayId;
}
- /** Whether the parent Task is visible or not */
+ /**
+ * Whether the parent Task is visible or not
+ *
+ * @hide
+ */
public boolean isVisible() {
return mVisible;
}
/**
* Whether the parent Task has any direct child activity, which is not embedded in any
- * TaskFragment, or not
+ * TaskFragment, or not.
+ *
+ * @hide
*/
public boolean hasDirectActivity() {
return mHasDirectActivity;
@@ -93,6 +112,8 @@
* {@link com.android.server.wm.WindowOrganizerController#configurationsAreEqualForOrganizer(
* Configuration, Configuration)} to determine if this {@link TaskFragmentParentInfo} should
* be dispatched to the client.
+ *
+ * @hide
*/
public boolean equalsForTaskFragmentOrganizer(@Nullable TaskFragmentParentInfo that) {
if (that == null) {
@@ -103,6 +124,7 @@
&& mDecorSurface == that.mDecorSurface;
}
+ /** @hide */
@Nullable
public SurfaceControl getDecorSurface() {
return mDecorSurface;
@@ -156,6 +178,7 @@
return result;
}
+ @SuppressLint("UnflaggedApi") // @TestApi to replace legacy usages.
@Override
public void writeToParcel(@NonNull Parcel dest, int flags) {
mConfiguration.writeToParcel(dest, flags);
@@ -173,6 +196,8 @@
mDecorSurface = in.readTypedObject(SurfaceControl.CREATOR);
}
+ @SuppressLint("UnflaggedApi") // @TestApi to replace legacy usages.
+ @NonNull
public static final Creator<TaskFragmentParentInfo> CREATOR =
new Creator<TaskFragmentParentInfo>() {
@Override
@@ -186,6 +211,7 @@
}
};
+ @SuppressLint("UnflaggedApi") // @TestApi to replace legacy usages.
@Override
public int describeContents() {
return 0;
diff --git a/core/java/android/window/TaskFragmentTransaction.java b/core/java/android/window/TaskFragmentTransaction.java
index 4dada10..1e5b097 100644
--- a/core/java/android/window/TaskFragmentTransaction.java
+++ b/core/java/android/window/TaskFragmentTransaction.java
@@ -24,7 +24,6 @@
import android.annotation.SuppressLint;
import android.annotation.TestApi;
import android.content.Intent;
-import android.content.res.Configuration;
import android.os.Binder;
import android.os.Bundle;
import android.os.IBinder;
@@ -190,6 +189,10 @@
@Nullable
private IBinder mActivityToken;
+ /** @see #setOtherActivityToken(IBinder) */
+ @Nullable
+ private IBinder mOtherActivityToken;
+
@Nullable
private TaskFragmentParentInfo mTaskFragmentParentInfo;
@@ -211,6 +214,7 @@
mActivityToken = in.readStrongBinder();
mTaskFragmentParentInfo = in.readTypedObject(TaskFragmentParentInfo.CREATOR);
mSurfaceControl = in.readTypedObject(SurfaceControl.CREATOR);
+ mOtherActivityToken = in.readStrongBinder();
}
@Override
@@ -225,6 +229,7 @@
dest.writeStrongBinder(mActivityToken);
dest.writeTypedObject(mTaskFragmentParentInfo, flags);
dest.writeTypedObject(mSurfaceControl, flags);
+ dest.writeStrongBinder(mOtherActivityToken);
}
/** The change is related to the TaskFragment created with this unique token. */
@@ -248,13 +253,6 @@
return this;
}
- // TODO(b/241043377): Keep this API to prevent @TestApi changes. Remove in the next release.
- /** Configuration of the parent Task. */
- @NonNull
- public Change setTaskConfiguration(@NonNull Configuration configuration) {
- return this;
- }
-
/**
* If the {@link #TYPE_TASK_FRAGMENT_ERROR} is from a {@link WindowContainerTransaction}
* from the {@link TaskFragmentOrganizer}, it may come with an error callback token to
@@ -299,12 +297,26 @@
return this;
}
- // TODO(b/241043377): Hide this API to prevent @TestApi changes. Remove in the next release.
+ /**
+ * Token of another activity.
+ * <p>For {@link #TYPE_ACTIVITY_REPARENTED_TO_TASK}, it is the next activity (behind the
+ * reparented one) that fills the Task and occludes other activities. It will be the
+ * actual activity token if the activity belongs to the same process as the organizer.
+ * Otherwise, it is {@code null}.
+ *
+ * @hide
+ */
+ @NonNull
+ public Change setOtherActivityToken(@NonNull IBinder activityToken) {
+ mOtherActivityToken = requireNonNull(activityToken);
+ return this;
+ }
+
/**
* Sets info of the parent Task of the embedded TaskFragment.
* @see TaskFragmentParentInfo
*
- * @hide pending unhide
+ * @hide
*/
@NonNull
public Change setTaskFragmentParentInfo(@NonNull TaskFragmentParentInfo info) {
@@ -338,12 +350,6 @@
return mTaskId;
}
- // TODO(b/241043377): Keep this API to prevent @TestApi changes. Remove in the next release.
- @Nullable
- public Configuration getTaskConfiguration() {
- return mTaskFragmentParentInfo.getConfiguration();
- }
-
@Nullable
public IBinder getErrorCallbackToken() {
return mErrorCallbackToken;
@@ -365,8 +371,16 @@
return mActivityToken;
}
- // TODO(b/241043377): Hide this API to prevent @TestApi changes. Remove in the next release.
- /** @hide pending unhide */
+ /** @hide */
+ @Nullable
+ public IBinder getOtherActivityToken() {
+ return mOtherActivityToken;
+ }
+
+ /**
+ * Obtains the {@link TaskFragmentParentInfo} for this transaction.
+ */
+ @SuppressLint("UnflaggedApi") // @TestApi to replace legacy usages.
@Nullable
public TaskFragmentParentInfo getTaskFragmentParentInfo() {
return mTaskFragmentParentInfo;
diff --git a/core/java/android/window/TaskSnapshot.java b/core/java/android/window/TaskSnapshot.java
index 41b6d31..a2e3d40 100644
--- a/core/java/android/window/TaskSnapshot.java
+++ b/core/java/android/window/TaskSnapshot.java
@@ -16,6 +16,7 @@
package android.window;
+import android.annotation.IntDef;
import android.annotation.NonNull;
import android.compat.annotation.UnsupportedAppUsage;
import android.content.ComponentName;
@@ -32,6 +33,9 @@
import android.view.Surface;
import android.view.WindowInsetsController;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
/**
* Represents a task snapshot.
* @hide
@@ -68,6 +72,21 @@
private final boolean mHasImeSurface;
// Must be one of the named color spaces, otherwise, always use SRGB color space.
private final ColorSpace mColorSpace;
+ private int mInternalReferences;
+
+ /** This snapshot object is being broadcast. */
+ public static final int REFERENCE_BROADCAST = 1;
+ /** This snapshot object is in the cache. */
+ public static final int REFERENCE_CACHE = 1 << 1;
+ /** This snapshot object is being persistent. */
+ public static final int REFERENCE_PERSIST = 1 << 2;
+ @IntDef(flag = true, prefix = { "REFERENCE_" }, value = {
+ REFERENCE_BROADCAST,
+ REFERENCE_CACHE,
+ REFERENCE_PERSIST
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ @interface ReferenceFlags {}
public TaskSnapshot(long id, long captureTime,
@NonNull ComponentName topActivityComponent, HardwareBuffer snapshot,
@@ -296,7 +315,28 @@
+ " mWindowingMode=" + mWindowingMode
+ " mAppearance=" + mAppearance
+ " mIsTranslucent=" + mIsTranslucent
- + " mHasImeSurface=" + mHasImeSurface;
+ + " mHasImeSurface=" + mHasImeSurface
+ + " mInternalReferences=" + mInternalReferences;
+ }
+
+ /**
+ * Adds a reference when the object is held somewhere.
+ * Only used in core.
+ */
+ public synchronized void addReference(@ReferenceFlags int usage) {
+ mInternalReferences |= usage;
+ }
+
+ /**
+ * Removes a reference when the object is not held from somewhere. The snapshot will be closed
+ * once the reference becomes zero.
+ * Only used in core.
+ */
+ public synchronized void removeReference(@ReferenceFlags int usage) {
+ mInternalReferences &= ~usage;
+ if (mInternalReferences == 0 && mSnapshot != null && !mSnapshot.isClosed()) {
+ mSnapshot.close();
+ }
}
public static final @NonNull Creator<TaskSnapshot> CREATOR = new Creator<TaskSnapshot>() {
diff --git a/core/java/android/window/TransitionFilter.java b/core/java/android/window/TransitionFilter.java
index 64fe66e..ec4e3e9 100644
--- a/core/java/android/window/TransitionFilter.java
+++ b/core/java/android/window/TransitionFilter.java
@@ -25,6 +25,7 @@
import android.app.ActivityManager;
import android.app.WindowConfiguration;
import android.content.ComponentName;
+import android.os.IBinder;
import android.os.Parcel;
import android.os.Parcelable;
import android.view.WindowManager;
@@ -180,6 +181,7 @@
public @ContainerOrder int mOrder = CONTAINER_ORDER_ANY;
public ComponentName mTopActivity;
+ public IBinder mLaunchCookie;
public Requirement() {
}
@@ -193,6 +195,7 @@
mMustBeTask = in.readBoolean();
mOrder = in.readInt();
mTopActivity = in.readTypedObject(ComponentName.CREATOR);
+ mLaunchCookie = in.readStrongBinder();
}
/** Go through changes and find if at-least one change matches this filter */
@@ -231,6 +234,9 @@
if (mMustBeTask && change.getTaskInfo() == null) {
continue;
}
+ if (!matchesCookie(change.getTaskInfo())) {
+ continue;
+ }
return true;
}
return false;
@@ -247,13 +253,25 @@
return false;
}
+ private boolean matchesCookie(ActivityManager.RunningTaskInfo info) {
+ if (mLaunchCookie == null) return true;
+ if (info == null) return false;
+ for (IBinder cookie : info.launchCookies) {
+ if (mLaunchCookie.equals(cookie)) {
+ return true;
+ }
+ }
+ return false;
+ }
+
/** Check if the request matches this filter. It may generate false positives */
boolean matches(@NonNull TransitionRequestInfo request) {
// Can't check modes/order since the transition hasn't been built at this point.
if (mActivityType == ACTIVITY_TYPE_UNDEFINED) return true;
return request.getTriggerTask() != null
&& request.getTriggerTask().getActivityType() == mActivityType
- && matchesTopActivity(request.getTriggerTask(), null /* activityCmp */);
+ && matchesTopActivity(request.getTriggerTask(), null /* activityCmp */)
+ && matchesCookie(request.getTriggerTask());
}
@Override
@@ -267,6 +285,7 @@
dest.writeBoolean(mMustBeTask);
dest.writeInt(mOrder);
dest.writeTypedObject(mTopActivity, flags);
+ dest.writeStrongBinder(mLaunchCookie);
}
@NonNull
@@ -307,6 +326,7 @@
out.append(" mustBeTask=" + mMustBeTask);
out.append(" order=" + containerOrderToString(mOrder));
out.append(" topActivity=").append(mTopActivity);
+ out.append(" launchCookie=").append(mLaunchCookie);
out.append("}");
return out.toString();
}
diff --git a/core/java/android/window/flags/large_screen_experiences_app_compat.aconfig b/core/java/android/window/flags/large_screen_experiences_app_compat.aconfig
index cd13c4a..4b2beb9 100644
--- a/core/java/android/window/flags/large_screen_experiences_app_compat.aconfig
+++ b/core/java/android/window/flags/large_screen_experiences_app_compat.aconfig
@@ -9,6 +9,16 @@
}
flag {
+ name: "disable_thin_letterboxing_policy"
+ namespace: "large_screen_experiences_app_compat"
+ description: "Whether reachability is disabled in case of thin letterboxing"
+ bug: "341027847"
+ metadata {
+ purpose: PURPOSE_BUGFIX
+ }
+}
+
+flag {
name: "allows_screen_size_decoupled_from_status_bar_and_cutout"
namespace: "large_screen_experiences_app_compat"
description: "When necessary, configuration decoupled from status bar and display cutout"
diff --git a/core/java/android/window/flags/lse_desktop_experience.aconfig b/core/java/android/window/flags/lse_desktop_experience.aconfig
index 760c916..f94766e 100644
--- a/core/java/android/window/flags/lse_desktop_experience.aconfig
+++ b/core/java/android/window/flags/lse_desktop_experience.aconfig
@@ -78,3 +78,10 @@
description: "Allows for additional windows tied to WindowDecoration to be layered between status bar and notification shade."
bug: "316186265"
}
+
+flag {
+ name: "enable_app_header_with_task_density"
+ namespace: "lse_desktop_experience"
+ description: "Matches the App Header density to that of the app window, instead of SysUI's"
+ bug: "332414819"
+}
diff --git a/core/java/android/window/flags/windowing_sdk.aconfig b/core/java/android/window/flags/windowing_sdk.aconfig
index 4d1b87a..b4678f6 100644
--- a/core/java/android/window/flags/windowing_sdk.aconfig
+++ b/core/java/android/window/flags/windowing_sdk.aconfig
@@ -124,7 +124,10 @@
flag {
namespace: "windowing_sdk"
- name: "pip_restore_to_overlay"
+ name: "fix_pip_restore_to_overlay"
description: "Restore exit-pip activity back to ActivityEmbedding overlay"
bug: "297887697"
+ metadata {
+ purpose: PURPOSE_BUGFIX
+ }
}
\ No newline at end of file
diff --git a/core/java/com/android/internal/accessibility/util/AccessibilityStatsLogUtils.java b/core/java/com/android/internal/accessibility/util/AccessibilityStatsLogUtils.java
index 6864bf7..8e18f84 100644
--- a/core/java/com/android/internal/accessibility/util/AccessibilityStatsLogUtils.java
+++ b/core/java/com/android/internal/accessibility/util/AccessibilityStatsLogUtils.java
@@ -24,6 +24,7 @@
import static com.android.internal.accessibility.AccessibilityShortcutController.MAGNIFICATION_COMPONENT_NAME;
import static com.android.internal.accessibility.common.ShortcutConstants.UserShortcutType.HARDWARE;
+import static com.android.internal.accessibility.common.ShortcutConstants.UserShortcutType.QUICK_SETTINGS;
import static com.android.internal.accessibility.common.ShortcutConstants.UserShortcutType.SOFTWARE;
import static com.android.internal.util.FrameworkStatsLog.ACCESSIBILITY_SHORTCUT_REPORTED__SERVICE_STATUS__DISABLED;
import static com.android.internal.util.FrameworkStatsLog.ACCESSIBILITY_SHORTCUT_REPORTED__SERVICE_STATUS__ENABLED;
@@ -32,6 +33,7 @@
import static com.android.internal.util.FrameworkStatsLog.ACCESSIBILITY_SHORTCUT_REPORTED__SHORTCUT_TYPE__A11Y_BUTTON_LONG_PRESS;
import static com.android.internal.util.FrameworkStatsLog.ACCESSIBILITY_SHORTCUT_REPORTED__SHORTCUT_TYPE__A11Y_FLOATING_MENU;
import static com.android.internal.util.FrameworkStatsLog.ACCESSIBILITY_SHORTCUT_REPORTED__SHORTCUT_TYPE__A11Y_GESTURE;
+import static com.android.internal.util.FrameworkStatsLog.ACCESSIBILITY_SHORTCUT_REPORTED__SHORTCUT_TYPE__QUICK_SETTINGS;
import static com.android.internal.util.FrameworkStatsLog.ACCESSIBILITY_SHORTCUT_REPORTED__SHORTCUT_TYPE__TRIPLE_TAP;
import static com.android.internal.util.FrameworkStatsLog.ACCESSIBILITY_SHORTCUT_REPORTED__SHORTCUT_TYPE__TWO_FINGER_TRIPLE_TAP;
import static com.android.internal.util.FrameworkStatsLog.ACCESSIBILITY_SHORTCUT_REPORTED__SHORTCUT_TYPE__UNKNOWN_TYPE;
@@ -48,7 +50,6 @@
import android.content.Context;
import android.provider.Settings;
-import com.android.internal.accessibility.common.ShortcutConstants;
import com.android.internal.accessibility.common.ShortcutConstants.UserShortcutType;
import com.android.internal.util.FrameworkStatsLog;
@@ -248,6 +249,8 @@
}
case HARDWARE:
return ACCESSIBILITY_SHORTCUT_REPORTED__SHORTCUT_TYPE__VOLUME_KEY;
+ case QUICK_SETTINGS:
+ return ACCESSIBILITY_SHORTCUT_REPORTED__SHORTCUT_TYPE__QUICK_SETTINGS;
}
return ACCESSIBILITY_SHORTCUT_REPORTED__SHORTCUT_TYPE__UNKNOWN_TYPE;
}
diff --git a/core/java/com/android/internal/content/om/OverlayConfigParser.java b/core/java/com/android/internal/content/om/OverlayConfigParser.java
index faaf7d5..8132652 100644
--- a/core/java/com/android/internal/content/om/OverlayConfigParser.java
+++ b/core/java/com/android/internal/content/om/OverlayConfigParser.java
@@ -24,6 +24,8 @@
import android.content.pm.PackagePartitions.SystemPartition;
import android.os.Build;
import android.os.FileUtils;
+import android.os.SystemProperties;
+import android.text.TextUtils;
import android.util.ArraySet;
import android.util.Log;
import android.util.Xml;
@@ -241,6 +243,18 @@
}
}
+ @FunctionalInterface
+ public interface SysPropWrapper{
+ /**
+ * Get system property
+ *
+ * @param property the key to look up.
+ *
+ * @return The property value if found, empty string otherwise.
+ */
+ String get(String property);
+ }
+
/**
* Retrieves overlays configured within the partition in increasing priority order.
*
@@ -320,6 +334,76 @@
}
/**
+ * Expand the property inside a rro configuration path.
+ *
+ * A RRO configuration can contain a property, this method expands
+ * the property to its value.
+ *
+ * Only read only properties allowed, prefixed with ro. Other
+ * properties will raise exception.
+ *
+ * Only a single property in the path is allowed.
+ *
+ * Example "${ro.boot.hardware.sku}/config.xml" would expand to
+ * "G020N/config.xml"
+ *
+ * @param configPath path to expand
+ * @param sysPropWrapper method used for reading properties
+ *
+ * @return The expanded path. Returns null if configPath is null.
+ */
+ @VisibleForTesting
+ public static String expandProperty(String configPath,
+ SysPropWrapper sysPropWrapper) {
+ if (configPath == null) {
+ return null;
+ }
+
+ int propStartPos = configPath.indexOf("${");
+ if (propStartPos == -1) {
+ // No properties inside the string, return as is
+ return configPath;
+ }
+
+ final StringBuilder sb = new StringBuilder();
+ sb.append(configPath.substring(0, propStartPos));
+
+ // Read out the end position
+ int propEndPos = configPath.indexOf("}", propStartPos);
+ if (propEndPos == -1) {
+ throw new IllegalStateException("Malformed property, unmatched braces, in: "
+ + configPath);
+ }
+
+ // Confirm that there is only one property inside the string
+ if (configPath.indexOf("${", propStartPos + 2) != -1) {
+ throw new IllegalStateException("Only a single property supported in path: "
+ + configPath);
+ }
+
+ final String propertyName = configPath.substring(propStartPos + 2, propEndPos);
+ if (!propertyName.startsWith("ro.")) {
+ throw new IllegalStateException("Only read only properties can be used when "
+ + "merging RRO config files: " + propertyName);
+ }
+ final String propertyValue = sysPropWrapper.get(propertyName);
+ if (TextUtils.isEmpty(propertyValue)) {
+ throw new IllegalStateException("Property is empty or doesn't exist: " + propertyName);
+ }
+ Log.d(TAG, String.format("Using property in overlay config path: \"%s\"", propertyName));
+ sb.append(propertyValue);
+
+ // propEndPos points to '}', need to step to next character, might be outside of string
+ propEndPos = propEndPos + 1;
+ // Append the remainder, if exists
+ if (propEndPos < configPath.length()) {
+ sb.append(configPath.substring(propEndPos));
+ }
+
+ return sb.toString();
+ }
+
+ /**
* Parses a <merge> tag within an overlay configuration file.
*
* Merge tags allow for other configuration files to be "merged" at the current parsing
@@ -331,10 +415,21 @@
@Nullable OverlayScanner scanner,
@Nullable Map<String, ParsedOverlayInfo> packageManagerOverlayInfos,
@NonNull ParsingContext parsingContext) {
- final String path = parser.getAttributeValue(null, "path");
+ final String path;
+
+ try {
+ SysPropWrapper sysPropWrapper = p -> {
+ return SystemProperties.get(p, "");
+ };
+ path = expandProperty(parser.getAttributeValue(null, "path"), sysPropWrapper);
+ } catch (IllegalStateException e) {
+ throw new IllegalStateException(String.format("<merge> path expand error in %s at %s",
+ configFile, parser.getPositionDescription()), e);
+ }
+
if (path == null) {
- throw new IllegalStateException(String.format("<merge> without path in %s at %s"
- + configFile, parser.getPositionDescription()));
+ throw new IllegalStateException(String.format("<merge> without path in %s at %s",
+ configFile, parser.getPositionDescription()));
}
if (path.startsWith("/")) {
diff --git a/core/java/com/android/internal/inputmethod/IImeTracker.aidl b/core/java/com/android/internal/inputmethod/IImeTracker.aidl
index ab4edb6..ebae39e 100644
--- a/core/java/com/android/internal/inputmethod/IImeTracker.aidl
+++ b/core/java/com/android/internal/inputmethod/IImeTracker.aidl
@@ -64,20 +64,28 @@
oneway void onCancelled(in ImeTracker.Token statsToken, int phase);
/**
- * Called when the IME show request is successful.
+ * Called when the show IME request is successful.
*
* @param statsToken the token tracking the current IME request.
*/
oneway void onShown(in ImeTracker.Token statsToken);
/**
- * Called when the IME hide request is successful.
+ * Called when the hide IME request is successful.
*
* @param statsToken the token tracking the current IME request.
*/
oneway void onHidden(in ImeTracker.Token statsToken);
/**
+ * Called when the user-controlled IME request was dispatched to the requesting app. The
+ * user animation can take an undetermined amount of time, so it shouldn't be tracked.
+ *
+ * @param statsToken the token tracking the current IME request.
+ */
+ oneway void onDispatched(in ImeTracker.Token statsToken);
+
+ /**
* Checks whether there are any pending IME visibility requests.
*
* @return {@code true} iff there are pending IME visibility requests.
diff --git a/core/java/com/android/internal/inputmethod/ImeTracingPerfettoImpl.java b/core/java/com/android/internal/inputmethod/ImeTracingPerfettoImpl.java
index 91b80dd..24cd1c9 100644
--- a/core/java/com/android/internal/inputmethod/ImeTracingPerfettoImpl.java
+++ b/core/java/com/android/internal/inputmethod/ImeTracingPerfettoImpl.java
@@ -52,8 +52,14 @@
ImeTracingPerfettoImpl() {
Producer.init(InitArguments.DEFAULTS);
- mDataSource.register(
- new DataSourceParams(PERFETTO_DS_BUFFER_EXHAUSTED_POLICY_STALL_AND_ABORT));
+ DataSourceParams params =
+ new DataSourceParams.Builder()
+ .setBufferExhaustedPolicy(
+ PERFETTO_DS_BUFFER_EXHAUSTED_POLICY_STALL_AND_ABORT)
+ .setNoFlush(true)
+ .setWillNotifyOnStop(false)
+ .build();
+ mDataSource.register(params);
}
diff --git a/core/java/com/android/internal/inputmethod/InlineSuggestionsRequestCallback.java b/core/java/com/android/internal/inputmethod/InlineSuggestionsRequestCallback.java
new file mode 100644
index 0000000..92d453b
--- /dev/null
+++ b/core/java/com/android/internal/inputmethod/InlineSuggestionsRequestCallback.java
@@ -0,0 +1,81 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.internal.inputmethod;
+
+import android.annotation.BinderThread;
+import android.view.autofill.AutofillId;
+import android.view.inputmethod.InlineSuggestionsRequest;
+
+/**
+ * An internal interface that mirrors {@link IInlineSuggestionsRequestCallback}.
+ *
+ * <p>This interface is used to forward incoming IPCs from
+ * {@link com.android.server.inputmethod.AutofillSuggestionsController} to
+ * {@link com.android.server.autofill.AutofillInlineSuggestionsRequestSession}.</p>
+ */
+public interface InlineSuggestionsRequestCallback {
+
+ /**
+ * Forwards {@link IInlineSuggestionsRequestCallback#onInlineSuggestionsUnsupported()}.
+ */
+ @BinderThread
+ void onInlineSuggestionsUnsupported();
+
+ /**
+ * Forwards {@link IInlineSuggestionsRequestCallback#onInlineSuggestionsRequest(
+ * InlineSuggestionsRequest, IInlineSuggestionsResponseCallback)}.
+ */
+ @BinderThread
+ void onInlineSuggestionsRequest(InlineSuggestionsRequest request,
+ IInlineSuggestionsResponseCallback callback);
+
+ /**
+ * Forwards {@link IInlineSuggestionsRequestCallback#onInputMethodStartInput(AutofillId)}.
+ */
+ @BinderThread
+ void onInputMethodStartInput(AutofillId imeFieldId);
+
+ /**
+ * Forwards {@link IInlineSuggestionsRequestCallback#onInputMethodShowInputRequested(boolean)}.
+ */
+ @BinderThread
+ void onInputMethodShowInputRequested(boolean requestResult);
+
+ /**
+ * Forwards {@link IInlineSuggestionsRequestCallback#onInputMethodStartInputView()}.
+ */
+ @BinderThread
+ void onInputMethodStartInputView();
+
+ /**
+ * Forwards {@link IInlineSuggestionsRequestCallback#onInputMethodFinishInputView()}.
+ */
+ @BinderThread
+ void onInputMethodFinishInputView();
+
+ /**
+ * Forwards {@link IInlineSuggestionsRequestCallback#onInputMethodFinishInput()}.
+ */
+ @BinderThread
+ void onInputMethodFinishInput();
+
+ /**
+ * Forwards {@link IInlineSuggestionsRequestCallback#onInlineSuggestionsSessionInvalidated()}.
+ */
+ @BinderThread
+ void onInlineSuggestionsSessionInvalidated();
+}
diff --git a/core/java/com/android/internal/inputmethod/InputMethodDebug.java b/core/java/com/android/internal/inputmethod/InputMethodDebug.java
index a0aad31..2a5593f 100644
--- a/core/java/com/android/internal/inputmethod/InputMethodDebug.java
+++ b/core/java/com/android/internal/inputmethod/InputMethodDebug.java
@@ -297,6 +297,8 @@
return "SHOW_SOFT_INPUT_IME_TOGGLE_SOFT_INPUT";
case SoftInputShowHideReason.SHOW_SOFT_INPUT_IMM_DEPRECATION:
return "SHOW_SOFT_INPUT_IMM_DEPRECATION";
+ case SoftInputShowHideReason.CONTROL_WINDOW_INSETS_ANIMATION:
+ return "CONTROL_WINDOW_INSETS_ANIMATION";
default:
return "Unknown=" + reason;
}
diff --git a/core/java/com/android/internal/inputmethod/SoftInputShowHideReason.java b/core/java/com/android/internal/inputmethod/SoftInputShowHideReason.java
index da738a0..eb6a810 100644
--- a/core/java/com/android/internal/inputmethod/SoftInputShowHideReason.java
+++ b/core/java/com/android/internal/inputmethod/SoftInputShowHideReason.java
@@ -88,6 +88,7 @@
SoftInputShowHideReason.HIDE_SOFT_INPUT_REQUEST_HIDE_WITH_CONTROL,
SoftInputShowHideReason.SHOW_SOFT_INPUT_IME_TOGGLE_SOFT_INPUT,
SoftInputShowHideReason.SHOW_SOFT_INPUT_IMM_DEPRECATION,
+ SoftInputShowHideReason.CONTROL_WINDOW_INSETS_ANIMATION,
})
public @interface SoftInputShowHideReason {
/** Default, undefined reason. */
@@ -397,4 +398,10 @@
* {@link InputMethodManager#showSoftInputFromInputMethod(IBinder, int)}.
*/
int SHOW_SOFT_INPUT_IMM_DEPRECATION = ImeProtoEnums.REASON_SHOW_SOFT_INPUT_IMM_DEPRECATION;
+
+ /**
+ * Show / Hide soft input by application-controlled animation in
+ * {@link android.view.InsetsController#controlWindowInsetsAnimation}.
+ */
+ int CONTROL_WINDOW_INSETS_ANIMATION = ImeProtoEnums.REASON_CONTROL_WINDOW_INSETS_ANIMATION;
}
diff --git a/core/java/com/android/internal/os/PowerStats.java b/core/java/com/android/internal/os/PowerStats.java
index 9f9aae5..24971f5 100644
--- a/core/java/com/android/internal/os/PowerStats.java
+++ b/core/java/com/android/internal/os/PowerStats.java
@@ -19,6 +19,7 @@
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.os.BatteryConsumer;
+import android.os.BatteryStats;
import android.os.Bundle;
import android.os.Parcel;
import android.os.PersistableBundle;
@@ -34,8 +35,12 @@
import org.xmlpull.v1.XmlPullParserException;
import java.io.IOException;
+import java.util.ArrayList;
import java.util.Arrays;
+import java.util.List;
import java.util.Objects;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
/**
* Container for power stats, acquired by various PowerStatsCollector classes. See subclasses for
@@ -75,6 +80,10 @@
*/
@android.ravenwood.annotation.RavenwoodKeepWholeClass
public static class Descriptor {
+ public static final String EXTRA_DEVICE_STATS_FORMAT = "format-device";
+ public static final String EXTRA_STATE_STATS_FORMAT = "format-state";
+ public static final String EXTRA_UID_STATS_FORMAT = "format-uid";
+
public static final String XML_TAG_DESCRIPTOR = "descriptor";
private static final String XML_ATTR_ID = "id";
private static final String XML_ATTR_NAME = "name";
@@ -120,6 +129,10 @@
*/
public final PersistableBundle extras;
+ private PowerStatsFormatter mDeviceStatsFormatter;
+ private PowerStatsFormatter mStateStatsFormatter;
+ private PowerStatsFormatter mUidStatsFormatter;
+
public Descriptor(@BatteryConsumer.PowerComponent int powerComponentId,
int statsArrayLength, @Nullable SparseArray<String> stateLabels,
int stateStatsArrayLength, int uidStatsArrayLength,
@@ -131,7 +144,7 @@
public Descriptor(int customPowerComponentId, String name, int statsArrayLength,
@Nullable SparseArray<String> stateLabels, int stateStatsArrayLength,
- int uidStatsArrayLength, PersistableBundle extras) {
+ int uidStatsArrayLength, @NonNull PersistableBundle extras) {
if (statsArrayLength > MAX_STATS_ARRAY_LENGTH) {
throw new IllegalArgumentException(
"statsArrayLength is too high. Max = " + MAX_STATS_ARRAY_LENGTH);
@@ -154,6 +167,39 @@
}
/**
+ * Returns a custom formatter for this type of power stats.
+ */
+ public PowerStatsFormatter getDeviceStatsFormatter() {
+ if (mDeviceStatsFormatter == null) {
+ mDeviceStatsFormatter = new PowerStatsFormatter(
+ extras.getString(EXTRA_DEVICE_STATS_FORMAT));
+ }
+ return mDeviceStatsFormatter;
+ }
+
+ /**
+ * Returns a custom formatter for this type of power stats, specifically per-state stats.
+ */
+ public PowerStatsFormatter getStateStatsFormatter() {
+ if (mStateStatsFormatter == null) {
+ mStateStatsFormatter = new PowerStatsFormatter(
+ extras.getString(EXTRA_STATE_STATS_FORMAT));
+ }
+ return mStateStatsFormatter;
+ }
+
+ /**
+ * Returns a custom formatter for this type of power stats, specifically per-UID stats.
+ */
+ public PowerStatsFormatter getUidStatsFormatter() {
+ if (mUidStatsFormatter == null) {
+ mUidStatsFormatter = new PowerStatsFormatter(
+ extras.getString(EXTRA_UID_STATS_FORMAT));
+ }
+ return mUidStatsFormatter;
+ }
+
+ /**
* Returns the label associated with the give state key, e.g. "5G-high" for the
* state of Mobile Radio representing the 5G mode and high signal power.
*/
@@ -491,20 +537,22 @@
StringBuilder sb = new StringBuilder();
sb.append("duration=").append(durationMs).append(" ").append(descriptor.name);
if (stats.length > 0) {
- sb.append("=").append(Arrays.toString(stats));
+ sb.append("=").append(descriptor.getDeviceStatsFormatter().format(stats));
}
if (descriptor.stateStatsArrayLength != 0) {
+ PowerStatsFormatter formatter = descriptor.getStateStatsFormatter();
for (int i = 0; i < stateStats.size(); i++) {
- sb.append(" [");
+ sb.append(" (");
sb.append(descriptor.getStateLabel(stateStats.keyAt(i)));
- sb.append("]=");
- sb.append(Arrays.toString(stateStats.valueAt(i)));
+ sb.append(") ");
+ sb.append(formatter.format(stateStats.valueAt(i)));
}
}
+ PowerStatsFormatter uidStatsFormatter = descriptor.getUidStatsFormatter();
for (int i = 0; i < uidStats.size(); i++) {
sb.append(uidPrefix)
.append(UserHandle.formatUid(uidStats.keyAt(i)))
- .append(": ").append(Arrays.toString(uidStats.valueAt(i)));
+ .append(": ").append(uidStatsFormatter.format(uidStats.valueAt(i)));
}
return sb.toString();
}
@@ -513,26 +561,29 @@
* Prints the contents of the stats snapshot.
*/
public void dump(IndentingPrintWriter pw) {
- pw.println("PowerStats: " + descriptor.name + " (" + descriptor.powerComponentId + ')');
+ pw.println(descriptor.name + " (" + descriptor.powerComponentId + ')');
pw.increaseIndent();
pw.print("duration", durationMs).println();
+
if (descriptor.statsArrayLength != 0) {
- pw.print("stats", Arrays.toString(stats)).println();
+ pw.println(descriptor.getDeviceStatsFormatter().format(stats));
}
if (descriptor.stateStatsArrayLength != 0) {
+ PowerStatsFormatter formatter = descriptor.getStateStatsFormatter();
for (int i = 0; i < stateStats.size(); i++) {
- pw.print("state ");
+ pw.print(" (");
pw.print(descriptor.getStateLabel(stateStats.keyAt(i)));
- pw.print(": ");
- pw.print(Arrays.toString(stateStats.valueAt(i)));
+ pw.print(") ");
+ pw.print(formatter.format(stateStats.valueAt(i)));
pw.println();
}
}
+ PowerStatsFormatter uidStatsFormatter = descriptor.getUidStatsFormatter();
for (int i = 0; i < uidStats.size(); i++) {
pw.print("UID ");
- pw.print(uidStats.keyAt(i));
+ pw.print(UserHandle.formatUid(uidStats.keyAt(i)));
pw.print(": ");
- pw.print(Arrays.toString(uidStats.valueAt(i)));
+ pw.print(uidStatsFormatter.format(uidStats.valueAt(i)));
pw.println();
}
pw.decreaseIndent();
@@ -542,4 +593,126 @@
public String toString() {
return "PowerStats: " + formatForBatteryHistory(" UID ");
}
+
+ public static class PowerStatsFormatter {
+ private static class Section {
+ public String label;
+ public int position;
+ public int length;
+ public boolean optional;
+ public boolean typePower;
+ }
+
+ private static final double NANO_TO_MILLI_MULTIPLIER = 1.0 / 1000000.0;
+ private static final Pattern SECTION_PATTERN =
+ Pattern.compile("([^:]+):(\\d+)(\\[(?<L>\\d+)])?(?<F>\\S*)\\s*");
+ private final List<Section> mSections;
+
+ public PowerStatsFormatter(String format) {
+ mSections = parseFormat(format);
+ }
+
+ /**
+ * Produces a formatted string representing the supplied array, with labels
+ * and other adornments specific to the power stats layout.
+ */
+ public String format(long[] stats) {
+ return format(mSections, stats);
+ }
+
+ private List<Section> parseFormat(String format) {
+ if (format == null || format.isBlank()) {
+ return null;
+ }
+
+ ArrayList<Section> sections = new ArrayList<>();
+ Matcher matcher = SECTION_PATTERN.matcher(format);
+ for (int position = 0; position < format.length(); position = matcher.end()) {
+ if (!matcher.find() || matcher.start() != position) {
+ Slog.wtf(TAG, "Bad power stats format '" + format + "'");
+ return null;
+ }
+ Section section = new Section();
+ section.label = matcher.group(1);
+ section.position = Integer.parseUnsignedInt(matcher.group(2));
+ String length = matcher.group("L");
+ if (length != null) {
+ section.length = Integer.parseUnsignedInt(length);
+ } else {
+ section.length = 1;
+ }
+ String flags = matcher.group("F");
+ if (flags != null) {
+ for (int i = 0; i < flags.length(); i++) {
+ char flag = flags.charAt(i);
+ switch (flag) {
+ case '?':
+ section.optional = true;
+ break;
+ case 'p':
+ section.typePower = true;
+ break;
+ default:
+ Slog.e(TAG,
+ "Unsupported format option '" + flag + "' in " + format);
+ break;
+ }
+ }
+ }
+ sections.add(section);
+ }
+
+ return sections;
+ }
+
+ private String format(List<Section> sections, long[] stats) {
+ if (sections == null) {
+ return Arrays.toString(stats);
+ }
+
+ StringBuilder sb = new StringBuilder();
+ for (int i = 0, count = sections.size(); i < count; i++) {
+ Section section = sections.get(i);
+ if (section.length == 0) {
+ continue;
+ }
+
+ if (section.optional) {
+ boolean nonZero = false;
+ for (int offset = 0; offset < section.length; offset++) {
+ if (stats[section.position + offset] != 0) {
+ nonZero = true;
+ break;
+ }
+ }
+ if (!nonZero) {
+ continue;
+ }
+ }
+
+ if (!sb.isEmpty()) {
+ sb.append(' ');
+ }
+ sb.append(section.label).append(": ");
+ if (section.length != 1) {
+ sb.append('[');
+ }
+ for (int offset = 0; offset < section.length; offset++) {
+ if (offset != 0) {
+ sb.append(", ");
+ }
+ if (section.typePower) {
+ sb.append(BatteryStats.formatCharge(
+ stats[section.position + offset] * NANO_TO_MILLI_MULTIPLIER));
+ } else {
+ sb.append(stats[section.position + offset]);
+ }
+ }
+ if (section.length != 1) {
+ sb.append(']');
+ }
+ }
+ return sb.toString();
+ }
+ }
}
diff --git a/core/java/com/android/internal/pm/pkg/parsing/ParsingPackageUtils.java b/core/java/com/android/internal/pm/pkg/parsing/ParsingPackageUtils.java
index 97ce96e..1dcd893 100644
--- a/core/java/com/android/internal/pm/pkg/parsing/ParsingPackageUtils.java
+++ b/core/java/com/android/internal/pm/pkg/parsing/ParsingPackageUtils.java
@@ -1908,12 +1908,16 @@
} else if (parser.getName().equals("package")) {
final TypedArray sa = res.obtainAttributes(parser,
R.styleable.AndroidManifestQueriesPackage);
- final String packageName = sa.getNonConfigurationString(
- R.styleable.AndroidManifestQueriesPackage_name, 0);
- if (TextUtils.isEmpty(packageName)) {
- return input.error("Package name is missing from package tag.");
+ try {
+ final String packageName = sa.getNonConfigurationString(
+ R.styleable.AndroidManifestQueriesPackage_name, 0);
+ if (TextUtils.isEmpty(packageName)) {
+ return input.error("Package name is missing from package tag.");
+ }
+ pkg.addQueriesPackage(packageName.intern());
+ } finally {
+ sa.recycle();
}
- pkg.addQueriesPackage(packageName.intern());
} else if (parser.getName().equals("provider")) {
final TypedArray sa = res.obtainAttributes(parser,
R.styleable.AndroidManifestQueriesProvider);
diff --git a/core/java/com/android/internal/protolog/PerfettoProtoLogImpl.java b/core/java/com/android/internal/protolog/PerfettoProtoLogImpl.java
index ee33eb4..37b7288 100644
--- a/core/java/com/android/internal/protolog/PerfettoProtoLogImpl.java
+++ b/core/java/com/android/internal/protolog/PerfettoProtoLogImpl.java
@@ -131,8 +131,13 @@
Runnable cacheUpdater
) {
Producer.init(InitArguments.DEFAULTS);
- mDataSource.register(new DataSourceParams(
- DataSourceParams.PERFETTO_DS_BUFFER_EXHAUSTED_POLICY_STALL_AND_ABORT));
+ DataSourceParams params =
+ new DataSourceParams.Builder()
+ .setBufferExhaustedPolicy(
+ DataSourceParams
+ .PERFETTO_DS_BUFFER_EXHAUSTED_POLICY_STALL_AND_ABORT)
+ .build();
+ mDataSource.register(params);
this.mViewerConfigInputStreamProvider = viewerConfigInputStreamProvider;
this.mViewerConfigReader = viewerConfigReader;
this.mLogGroups = logGroups;
@@ -186,8 +191,6 @@
}
os.end(outProtologViewerConfigToken);
-
- ctx.flush();
} catch (IOException e) {
Log.e(LOG_TAG, "Failed to read ProtoLog viewer config to dump on tracing end", e);
}
diff --git a/core/java/com/android/internal/ravenwood/RavenwoodEnvironment.java b/core/java/com/android/internal/ravenwood/RavenwoodEnvironment.java
new file mode 100644
index 0000000..1340156
--- /dev/null
+++ b/core/java/com/android/internal/ravenwood/RavenwoodEnvironment.java
@@ -0,0 +1,55 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.internal.ravenwood;
+
+/**
+ * Class to interact with the Ravenwood environment.
+ */
+@android.ravenwood.annotation.RavenwoodKeepWholeClass
+public class RavenwoodEnvironment {
+ private static RavenwoodEnvironment sInstance = new RavenwoodEnvironment();
+
+ private RavenwoodEnvironment() {
+ }
+
+ /**
+ * @return the singleton instance.
+ */
+ public static RavenwoodEnvironment getInstance() {
+ return sInstance;
+ }
+
+ /**
+ * USE IT SPARINGLY! Returns true if it's running on Ravenwood, hostside test environment.
+ *
+ * <p>Using this allows code to behave differently on a real device and on Ravenwood, but
+ * generally speaking, that's a bad idea because we want the test target code to behave
+ * differently.
+ *
+ * <p>This should be only used when different behavior is absolutely needed.
+ *
+ * <p>If someone needs it without having access to the SDK, the following hack would work too.
+ * <code>System.getProperty("java.class.path").contains("ravenwood")</code>
+ */
+ @android.ravenwood.annotation.RavenwoodReplace
+ public boolean isRunningOnRavenwood() {
+ return false;
+ }
+
+ public boolean isRunningOnRavenwood$ravenwood() {
+ return true;
+ }
+}
diff --git a/core/java/com/android/internal/widget/CompactMessagingLayout.java b/core/java/com/android/internal/widget/CompactMessagingLayout.java
new file mode 100644
index 0000000..1e2c01a
--- /dev/null
+++ b/core/java/com/android/internal/widget/CompactMessagingLayout.java
@@ -0,0 +1,263 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.internal.widget;
+
+import android.app.Notification;
+import android.app.Notification.MessagingStyle;
+import android.app.Person;
+import android.content.Context;
+import android.content.res.ColorStateList;
+import android.graphics.drawable.Drawable;
+import android.graphics.drawable.Icon;
+import android.os.Bundle;
+import android.os.Parcelable;
+import android.text.TextUtils;
+import android.util.AttributeSet;
+import android.view.RemotableViewMethod;
+import android.view.View;
+import android.view.ViewStub;
+import android.widget.FrameLayout;
+import android.widget.ImageView;
+import android.widget.RemoteViews;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+
+import com.android.internal.R;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * A custom-built layout for the compact Heads Up of Notification.MessagingStyle .
+ */
+@RemoteViews.RemoteView
+public class CompactMessagingLayout extends FrameLayout {
+
+ private final PeopleHelper mPeopleHelper = new PeopleHelper();
+
+ private ViewStub mConversationFacePileViewStub;
+
+ private int mNotificationBackgroundColor;
+ private int mFacePileSize;
+ private int mFacePileAvatarSize;
+ private int mFacePileProtectionWidth;
+ private int mLayoutColor;
+
+ public CompactMessagingLayout(@NonNull Context context) {
+ super(context);
+ }
+
+ public CompactMessagingLayout(@NonNull Context context,
+ @Nullable AttributeSet attrs) {
+ super(context, attrs);
+ }
+
+ public CompactMessagingLayout(@NonNull Context context, @Nullable AttributeSet attrs,
+ int defStyleAttr) {
+ super(context, attrs, defStyleAttr);
+ }
+
+ public CompactMessagingLayout(@NonNull Context context, @Nullable AttributeSet attrs,
+ int defStyleAttr, int defStyleRes) {
+ super(context, attrs, defStyleAttr, defStyleRes);
+ }
+
+ @Override
+ protected void onFinishInflate() {
+ super.onFinishInflate();
+ mPeopleHelper.init(getContext());
+ mConversationFacePileViewStub = requireViewById(R.id.conversation_face_pile);
+ mFacePileSize = getResources()
+ .getDimensionPixelSize(R.dimen.conversation_compact_face_pile_size);
+ mFacePileAvatarSize = getResources()
+ .getDimensionPixelSize(R.dimen.conversation_compact_face_pile_avatar_size);
+ mFacePileProtectionWidth = getResources().getDimensionPixelSize(
+ R.dimen.conversation_compact_face_pile_protection_width);
+ }
+
+ /**
+ * Set conversation data
+ *
+ * @param extras Bundle contains conversation data
+ */
+ @RemotableViewMethod(asyncImpl = "setGroupFacePileAsync")
+ public void setGroupFacePile(Bundle extras) {
+ // NO-OP
+ }
+
+ /**
+ * async version of {@link ConversationLayout#setLayoutColor}
+ */
+ @RemotableViewMethod
+ public Runnable setLayoutColorAsync(int color) {
+ mLayoutColor = color;
+ return NotificationRunnables.NOOP;
+ }
+
+ @RemotableViewMethod(asyncImpl = "setLayoutColorAsync")
+ public void setLayoutColor(int color) {
+ mLayoutColor = color;
+ }
+
+ /**
+ * @param color the color of the notification background
+ */
+ @RemotableViewMethod
+ public void setNotificationBackgroundColor(int color) {
+ mNotificationBackgroundColor = color;
+ }
+
+ /**
+ * async version of {@link CompactMessagingLayout#setGroupFacePile}
+ * setGroupFacePile!
+ */
+ public Runnable setGroupFacePileAsync(Bundle extras) {
+ final Parcelable[] messages = extras.getParcelableArray(Notification.EXTRA_MESSAGES);
+ final List<Notification.MessagingStyle.Message> newMessages =
+ Notification.MessagingStyle.Message.getMessagesFromBundleArray(messages);
+ final Parcelable[] histMessages =
+ extras.getParcelableArray(Notification.EXTRA_HISTORIC_MESSAGES);
+ final List<Notification.MessagingStyle.Message> newHistoricMessages =
+ Notification.MessagingStyle.Message.getMessagesFromBundleArray(histMessages);
+ final Person user = extras.getParcelable(Notification.EXTRA_MESSAGING_PERSON, Person.class);
+
+ final List<List<MessagingStyle.Message>> groups = groupMessages(newMessages,
+ newHistoricMessages);
+ final PeopleHelper.NameToPrefixMap nameToPrefixMap =
+ mPeopleHelper.mapUniqueNamesToPrefixWithGroupList(groups);
+ final int layoutColor = mLayoutColor;
+ // Find last two person's icon to show them in the face pile.
+ Icon secondLastIcon = null;
+ Icon lastIcon = null;
+ CharSequence lastKey = null;
+ final CharSequence userKey = getPersonKey(user);
+ for (int i = groups.size() - 1; i >= 0; i--) {
+ final MessagingStyle.Message message = groups.get(i).get(0);
+ final Person sender =
+ message.getSenderPerson() != null ? message.getSenderPerson() : user;
+ final CharSequence senderKey = getPersonKey(sender);
+ final boolean notUser = senderKey != userKey;
+ final boolean notIncluded = senderKey != lastKey;
+
+ if ((notUser && notIncluded) || (i == 0 && lastKey == null)) {
+ final Icon icon = getSenderIcon(sender, nameToPrefixMap, layoutColor);
+ if (lastIcon == null) {
+ lastIcon = icon;
+ lastKey = senderKey;
+ } else {
+ secondLastIcon = icon;
+ break;
+ }
+ }
+ }
+
+ if (lastIcon == null) {
+ lastIcon = getSenderIcon(null, null, layoutColor);
+ }
+
+ if (secondLastIcon == null) {
+ secondLastIcon = getSenderIcon(null, null, layoutColor);
+ }
+ final Drawable secondLastIconDrawable = secondLastIcon.loadDrawable(getContext());
+ final Drawable lastIconDrawable = lastIcon.loadDrawable(getContext());
+ return () -> {
+ final View conversationFacePile = mConversationFacePileViewStub.inflate();
+ conversationFacePile.setVisibility(VISIBLE);
+
+ final ImageView facePileBottomBg = conversationFacePile.requireViewById(
+ com.android.internal.R.id.conversation_face_pile_bottom_background);
+ final ImageView facePileTop = conversationFacePile.requireViewById(
+ com.android.internal.R.id.conversation_face_pile_top);
+ final ImageView facePileBottom = conversationFacePile.requireViewById(
+ com.android.internal.R.id.conversation_face_pile_bottom);
+
+ facePileTop.setImageDrawable(secondLastIconDrawable);
+ facePileBottom.setImageDrawable(lastIconDrawable);
+ facePileBottomBg.setImageTintList(ColorStateList.valueOf(mNotificationBackgroundColor));
+ setSize(conversationFacePile, mFacePileSize);
+ setSize(facePileBottom, mFacePileAvatarSize);
+ setSize(facePileTop, mFacePileAvatarSize);
+ setSize(facePileBottomBg, mFacePileAvatarSize + 2 * mFacePileProtectionWidth);
+ };
+ }
+
+ @NonNull
+ private Icon getSenderIcon(@Nullable Person sender,
+ @Nullable PeopleHelper.NameToPrefixMap uniqueNames,
+ int layoutColor) {
+ if (sender == null) {
+ return mPeopleHelper.createAvatarSymbol(/* name = */ "", /* symbol = */ "",
+ layoutColor);
+ }
+
+ if (sender.getIcon() != null) {
+ return sender.getIcon();
+ }
+
+ final CharSequence senderName = sender.getName();
+ if (!TextUtils.isEmpty(senderName)) {
+ final String symbol = uniqueNames != null ? uniqueNames.getPrefix(senderName) : "";
+ return mPeopleHelper.createAvatarSymbol(senderName, symbol, layoutColor);
+ }
+
+ return mPeopleHelper.createAvatarSymbol(/* name = */ "", /* symbol = */ "", layoutColor);
+ }
+
+
+ /**
+ * Groups the given messages by their sender.
+ */
+ private static List<List<MessagingStyle.Message>> groupMessages(
+ List<MessagingStyle.Message> messages,
+ List<MessagingStyle.Message> historicMessages
+ ) {
+ if (messages.isEmpty() && historicMessages.isEmpty()) return List.of();
+
+ ArrayList<MessagingStyle.Message> currentGroup = null;
+ CharSequence currentSenderKey = null;
+ final ArrayList<List<MessagingStyle.Message>> groups = new ArrayList<>();
+ final int histSize = historicMessages.size();
+
+ for (int i = 0; i < histSize + messages.size(); i++) {
+ final MessagingStyle.Message message = i < histSize ? historicMessages.get(i)
+ : messages.get(i - histSize);
+ if (message == null) continue;
+
+ final CharSequence senderKey = getPersonKey(message.getSenderPerson());
+ final boolean isNewGroup = currentGroup == null || senderKey != currentSenderKey;
+ if (isNewGroup) {
+ currentGroup = new ArrayList<>();
+ groups.add(currentGroup);
+ currentSenderKey = senderKey;
+ }
+ currentGroup.add(message);
+ }
+ return groups;
+ }
+
+ private static CharSequence getPersonKey(@Nullable Person person) {
+ return person == null ? null : person.getKey() == null ? person.getName() : person.getKey();
+ }
+
+ private static void setSize(View view, int size) {
+ final FrameLayout.LayoutParams lp = (FrameLayout.LayoutParams) view.getLayoutParams();
+ lp.width = size;
+ lp.height = size;
+ view.setLayoutParams(lp);
+ }
+}
diff --git a/core/java/com/android/internal/widget/MessagingLinearLayout.java b/core/java/com/android/internal/widget/MessagingLinearLayout.java
index e07acac..3d8237e 100644
--- a/core/java/com/android/internal/widget/MessagingLinearLayout.java
+++ b/core/java/com/android/internal/widget/MessagingLinearLayout.java
@@ -16,6 +16,8 @@
package com.android.internal.widget;
+import static android.widget.flags.Flags.messagingChildRequestLayout;
+
import android.annotation.Nullable;
import android.annotation.Px;
import android.content.Context;
@@ -92,6 +94,10 @@
final View child = getChildAt(i);
final LayoutParams lp = (LayoutParams) child.getLayoutParams();
lp.hide = true;
+ // Child always needs to be measured to calculate hide property correctly in onMeasure.
+ if (messagingChildRequestLayout()) {
+ child.requestLayout();
+ }
if (child instanceof MessagingChild) {
MessagingChild messagingChild = (MessagingChild) child;
// Whenever we encounter the message first, it's always first in the layout
diff --git a/core/java/com/android/internal/widget/NotificationRowIconView.java b/core/java/com/android/internal/widget/NotificationRowIconView.java
index 4031b2f..0f4615a 100644
--- a/core/java/com/android/internal/widget/NotificationRowIconView.java
+++ b/core/java/com/android/internal/widget/NotificationRowIconView.java
@@ -16,18 +16,27 @@
package com.android.internal.widget;
+import android.annotation.Nullable;
import android.app.Flags;
import android.content.Context;
+import android.graphics.Bitmap;
+import android.graphics.BitmapShader;
+import android.graphics.Canvas;
+import android.graphics.Paint;
+import android.graphics.drawable.BitmapDrawable;
+import android.graphics.drawable.Drawable;
+import android.graphics.drawable.Icon;
import android.util.AttributeSet;
+import android.view.RemotableViewMethod;
import android.widget.RemoteViews;
-import androidx.annotation.Nullable;
-
/**
* An image view that holds the icon displayed on the left side of a notification row.
*/
@RemoteViews.RemoteView
public class NotificationRowIconView extends CachingIconView {
+ private boolean mApplyCircularCrop = false;
+
public NotificationRowIconView(Context context) {
super(context);
}
@@ -57,4 +66,82 @@
super.onFinishInflate();
}
+
+ @Nullable
+ @Override
+ Drawable loadSizeRestrictedIcon(@Nullable Icon icon) {
+ final Drawable original = super.loadSizeRestrictedIcon(icon);
+ final Drawable result;
+ if (mApplyCircularCrop) {
+ result = makeCircularDrawable(original);
+ } else {
+ result = original;
+ }
+
+ return result;
+ }
+
+ /**
+ * Enables circle crop that makes given image circular
+ */
+ @RemotableViewMethod(asyncImpl = "setApplyCircularCropAsync")
+ public void setApplyCircularCrop(boolean applyCircularCrop) {
+ mApplyCircularCrop = applyCircularCrop;
+ }
+
+ /**
+ * Async version of {@link NotificationRowIconView#setApplyCircularCrop}
+ */
+ public Runnable setApplyCircularCropAsync(boolean applyCircularCrop) {
+ mApplyCircularCrop = applyCircularCrop;
+ return () -> {
+ };
+ }
+
+ @Nullable
+ private Drawable makeCircularDrawable(@Nullable Drawable original) {
+ if (original == null) {
+ return original;
+ }
+
+ final Bitmap source = drawableToBitmap(original);
+
+ int size = Math.min(source.getWidth(), source.getHeight());
+
+ Bitmap squared = Bitmap.createScaledBitmap(source, size, size, /* filter= */ false);
+ Bitmap result = Bitmap.createBitmap(size, size, Bitmap.Config.ARGB_8888);
+
+ final Canvas canvas = new Canvas(result);
+ final Paint paint = new Paint();
+ paint.setShader(
+ new BitmapShader(squared, BitmapShader.TileMode.CLAMP,
+ BitmapShader.TileMode.CLAMP));
+ paint.setAntiAlias(true);
+ float radius = size / 2f;
+ canvas.drawCircle(radius, radius, radius, paint);
+ return new BitmapDrawable(getResources(), result);
+ }
+
+ private static Bitmap drawableToBitmap(Drawable drawable) {
+ if (drawable instanceof BitmapDrawable bitmapDrawable) {
+ final Bitmap bitmap = bitmapDrawable.getBitmap();
+ if (bitmap.getConfig() == Bitmap.Config.HARDWARE) {
+ return bitmap.copy(Bitmap.Config.ARGB_8888, false);
+ } else {
+ return bitmap;
+ }
+ }
+
+ int width = drawable.getIntrinsicWidth();
+ width = width > 0 ? width : 1;
+ int height = drawable.getIntrinsicHeight();
+ height = height > 0 ? height : 1;
+
+ Bitmap bitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
+ Canvas canvas = new Canvas(bitmap);
+ drawable.setBounds(0, 0, canvas.getWidth(), canvas.getHeight());
+ drawable.draw(canvas);
+
+ return bitmap;
+ }
}
diff --git a/core/java/com/android/internal/widget/TEST_MAPPING b/core/java/com/android/internal/widget/TEST_MAPPING
new file mode 100644
index 0000000..91cecfd
--- /dev/null
+++ b/core/java/com/android/internal/widget/TEST_MAPPING
@@ -0,0 +1,19 @@
+{
+ // v2/sysui/suite/test-mapping-sysui-screenshot-test
+ "sysui-screenshot-test": [
+ {
+ "name": "SystemUIGoogleScreenshotTests",
+ "options": [
+ {
+ "exclude-annotation": "org.junit.Ignore"
+ },
+ {
+ "exclude-annotation": "androidx.test.filters.FlakyTest"
+ },
+ {
+ "exclude-annotation": "android.platform.test.annotations.Postsubmit"
+ }
+ ]
+ }
+ ]
+}
\ No newline at end of file
diff --git a/core/jni/Android.bp b/core/jni/Android.bp
index 80a7599..d9c40c3 100644
--- a/core/jni/Android.bp
+++ b/core/jni/Android.bp
@@ -483,4 +483,8 @@
"libnativehelper",
"libvintf",
],
+
+ required: [
+ "vintf",
+ ],
}
diff --git a/core/jni/android_os_VintfObject.cpp b/core/jni/android_os_VintfObject.cpp
index 8dc9d0a..7a4854b 100644
--- a/core/jni/android_os_VintfObject.cpp
+++ b/core/jni/android_os_VintfObject.cpp
@@ -32,6 +32,8 @@
static jmethodID gHashMapPut;
static jclass gLongClazz;
static jmethodID gLongValueOf;
+static jclass gVintfObjectClazz;
+static jmethodID gRunCommand;
namespace android {
@@ -47,6 +49,56 @@
using vintf::Vndk;
using vintf::CheckFlags::ENABLE_ALL_CHECKS;
+// Instead of VintfObject::GetXxx(), we construct
+// HalManifest/CompatibilityMatrix objects by calling `vintf` through
+// UiAutomation.executeShellCommand() so that the commands are executed
+// using shell identity. Otherwise, we would need to allow "apps" to access
+// files like apex-info-list.xml which we don't want to open to apps.
+// This is okay because VintfObject is @TestApi and only used in CTS tests.
+
+static std::string runCmd(JNIEnv* env, const char* cmd) {
+ jstring jstr = (jstring)env->CallStaticObjectMethod(gVintfObjectClazz, gRunCommand,
+ env->NewStringUTF(cmd));
+ std::string output;
+ if (jstr) {
+ auto cstr = env->GetStringUTFChars(jstr, nullptr);
+ output = std::string(cstr);
+ env->ReleaseStringUTFChars(jstr, cstr);
+ } else {
+ LOG(WARNING) << "Failed to run " << cmd;
+ env->ExceptionDescribe();
+ env->ExceptionClear();
+ }
+ return output;
+}
+
+template <typename T>
+static std::shared_ptr<const T> fromXml(const std::string& content) {
+ std::shared_ptr<T> object = std::make_unique<T>();
+ std::string error;
+ if (fromXml(object.get(), content, &error)) {
+ return object;
+ }
+ LOG(WARNING) << "Unabled to parse: " << error;
+ return nullptr;
+}
+
+static std::shared_ptr<const HalManifest> getDeviceHalManifest(JNIEnv* env) {
+ return fromXml<HalManifest>(runCmd(env, "vintf dm"));
+}
+
+static std::shared_ptr<const HalManifest> getFrameworkHalManifest(JNIEnv* env) {
+ return fromXml<HalManifest>(runCmd(env, "vintf fm"));
+}
+
+static std::shared_ptr<const CompatibilityMatrix> getDeviceCompatibilityMatrix(JNIEnv* env) {
+ return fromXml<CompatibilityMatrix>(runCmd(env, "vintf dcm"));
+}
+
+static std::shared_ptr<const CompatibilityMatrix> getFrameworkCompatibilityMatrix(JNIEnv* env) {
+ return fromXml<CompatibilityMatrix>(runCmd(env, "vintf fcm"));
+}
+
template<typename V>
static inline jobjectArray toJavaStringArray(JNIEnv* env, const V& v) {
size_t i;
@@ -83,12 +135,10 @@
{
std::vector<std::string> cStrings;
- tryAddSchema(VintfObject::GetDeviceHalManifest(), "device manifest", &cStrings);
- tryAddSchema(VintfObject::GetFrameworkHalManifest(), "framework manifest", &cStrings);
- tryAddSchema(VintfObject::GetDeviceCompatibilityMatrix(), "device compatibility matrix",
- &cStrings);
- tryAddSchema(VintfObject::GetFrameworkCompatibilityMatrix(), "framework compatibility matrix",
- &cStrings);
+ tryAddSchema(getDeviceHalManifest(env), "device manifest", &cStrings);
+ tryAddSchema(getFrameworkHalManifest(env), "framework manifest", &cStrings);
+ tryAddSchema(getDeviceCompatibilityMatrix(env), "device compatibility matrix", &cStrings);
+ tryAddSchema(getFrameworkCompatibilityMatrix(env), "framework compatibility matrix", &cStrings);
return toJavaStringArray(env, cStrings);
}
@@ -108,15 +158,13 @@
static jobjectArray android_os_VintfObject_getHalNamesAndVersions(JNIEnv* env, jclass) {
std::set<std::string> halNames;
- tryAddHalNamesAndVersions(VintfObject::GetDeviceHalManifest(),
- "device manifest", &halNames);
- tryAddHalNamesAndVersions(VintfObject::GetFrameworkHalManifest(),
- "framework manifest", &halNames);
+ tryAddHalNamesAndVersions(getDeviceHalManifest(env), "device manifest", &halNames);
+ tryAddHalNamesAndVersions(getFrameworkHalManifest(env), "framework manifest", &halNames);
return toJavaStringArray(env, halNames);
}
static jstring android_os_VintfObject_getSepolicyVersion(JNIEnv* env, jclass) {
- std::shared_ptr<const HalManifest> manifest = VintfObject::GetDeviceHalManifest();
+ std::shared_ptr<const HalManifest> manifest = getDeviceHalManifest(env);
if (manifest == nullptr || manifest->type() != SchemaType::DEVICE) {
LOG(WARNING) << __FUNCTION__ << "Cannot get device manifest";
return nullptr;
@@ -126,8 +174,7 @@
}
static jstring android_os_VintfObject_getPlatformSepolicyVersion(JNIEnv* env, jclass) {
- std::shared_ptr<const CompatibilityMatrix> matrix =
- VintfObject::GetFrameworkCompatibilityMatrix();
+ std::shared_ptr<const CompatibilityMatrix> matrix = getFrameworkCompatibilityMatrix(env);
if (matrix == nullptr || matrix->type() != SchemaType::FRAMEWORK) {
jniThrowRuntimeException(env, "Cannot get framework compatibility matrix");
return nullptr;
@@ -148,7 +195,7 @@
}
static jobject android_os_VintfObject_getVndkSnapshots(JNIEnv* env, jclass) {
- std::shared_ptr<const HalManifest> manifest = VintfObject::GetFrameworkHalManifest();
+ std::shared_ptr<const HalManifest> manifest = getFrameworkHalManifest(env);
if (manifest == nullptr || manifest->type() != SchemaType::FRAMEWORK) {
LOG(WARNING) << __FUNCTION__ << "Cannot get framework manifest";
return nullptr;
@@ -163,7 +210,7 @@
}
static jobject android_os_VintfObject_getTargetFrameworkCompatibilityMatrixVersion(JNIEnv* env, jclass) {
- std::shared_ptr<const HalManifest> manifest = VintfObject::GetDeviceHalManifest();
+ std::shared_ptr<const HalManifest> manifest = getDeviceHalManifest(env);
if (manifest == nullptr || manifest->level() == Level::UNSPECIFIED) {
return nullptr;
}
@@ -188,19 +235,20 @@
const char* const kVintfObjectPathName = "android/os/VintfObject";
-int register_android_os_VintfObject(JNIEnv* env)
-{
-
+int register_android_os_VintfObject(JNIEnv* env) {
gString = MakeGlobalRefOrDie(env, FindClassOrDie(env, "java/lang/String"));
gHashMapClazz = MakeGlobalRefOrDie(env, FindClassOrDie(env, "java/util/HashMap"));
gHashMapInit = GetMethodIDOrDie(env, gHashMapClazz, "<init>", "()V");
- gHashMapPut = GetMethodIDOrDie(env, gHashMapClazz,
- "put", "(Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;");
+ gHashMapPut = GetMethodIDOrDie(env, gHashMapClazz, "put",
+ "(Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;");
gLongClazz = MakeGlobalRefOrDie(env, FindClassOrDie(env, "java/lang/Long"));
gLongValueOf = GetStaticMethodIDOrDie(env, gLongClazz, "valueOf", "(J)Ljava/lang/Long;");
+ gVintfObjectClazz = MakeGlobalRefOrDie(env, FindClassOrDie(env, kVintfObjectPathName));
+ gRunCommand = GetStaticMethodIDOrDie(env, gVintfObjectClazz, "runShellCommand",
+ "(Ljava/lang/String;)Ljava/lang/String;");
return RegisterMethodsOrDie(env, kVintfObjectPathName, gVintfObjectMethods,
- NELEM(gVintfObjectMethods));
+ NELEM(gVintfObjectMethods));
}
extern int register_android_os_VintfRuntimeInfo(JNIEnv* env);
diff --git a/core/jni/android_tracing_PerfettoDataSource.cpp b/core/jni/android_tracing_PerfettoDataSource.cpp
index f82ebfe..17129d8 100644
--- a/core/jni/android_tracing_PerfettoDataSource.cpp
+++ b/core/jni/android_tracing_PerfettoDataSource.cpp
@@ -244,8 +244,8 @@
return static_cast<jlong>(reinterpret_cast<uintptr_t>(&nativeDestroy));
}
-void nativeFlush(JNIEnv* env, jclass clazz, jlong ds_ptr, jobjectArray packets) {
- ALOG(LOG_DEBUG, LOG_TAG, "nativeFlush(%p)", (void*)ds_ptr);
+void nativeWritePackets(JNIEnv* env, jclass clazz, jlong ds_ptr, jobjectArray packets) {
+ ALOG(LOG_DEBUG, LOG_TAG, "nativeWritePackets(%p)", (void*)ds_ptr);
sp<PerfettoDataSource> datasource = reinterpret_cast<PerfettoDataSource*>(ds_ptr);
datasource->WritePackets(env, packets);
}
@@ -256,10 +256,12 @@
}
void nativeRegisterDataSource(JNIEnv* env, jclass clazz, jlong datasource_ptr,
- jint buffer_exhausted_policy) {
+ jint buffer_exhausted_policy, jboolean will_notify_on_stop,
+ jboolean no_flush) {
sp<PerfettoDataSource> datasource = reinterpret_cast<PerfettoDataSource*>(datasource_ptr);
struct PerfettoDsParams params = PerfettoDsParamsDefault();
+ params.will_notify_on_stop = will_notify_on_stop;
params.buffer_exhausted_policy = (PerfettoDsBufferExhaustedPolicy)buffer_exhausted_policy;
params.user_arg = reinterpret_cast<void*>(datasource.get());
@@ -325,13 +327,15 @@
datasource_instance->onStart(env);
};
- params.on_flush_cb = [](struct PerfettoDsImpl*, PerfettoDsInstanceIndex, void*, void* inst_ctx,
- struct PerfettoDsOnFlushArgs*) {
- JNIEnv* env = GetOrAttachJNIEnvironment(gVm, JNI_VERSION_1_6);
+ if (!no_flush) {
+ params.on_flush_cb = [](struct PerfettoDsImpl*, PerfettoDsInstanceIndex, void*,
+ void* inst_ctx, struct PerfettoDsOnFlushArgs*) {
+ JNIEnv* env = GetOrAttachJNIEnvironment(gVm, JNI_VERSION_1_6);
- auto* datasource_instance = static_cast<PerfettoDataSourceInstance*>(inst_ctx);
- datasource_instance->onFlush(env);
- };
+ auto* datasource_instance = static_cast<PerfettoDataSourceInstance*>(inst_ctx);
+ datasource_instance->onFlush(env);
+ };
+ }
params.on_stop_cb = [](struct PerfettoDsImpl*, PerfettoDsInstanceIndex inst_id, void* user_arg,
void* inst_ctx, struct PerfettoDsOnStopArgs*) {
@@ -422,7 +426,7 @@
(void*)nativeCreate},
{"nativeFlushAll", "(J)V", (void*)nativeFlushAll},
{"nativeGetFinalizer", "()J", (void*)nativeGetFinalizer},
- {"nativeRegisterDataSource", "(JI)V", (void*)nativeRegisterDataSource},
+ {"nativeRegisterDataSource", "(JIZZ)V", (void*)nativeRegisterDataSource},
{"nativeGetPerfettoInstanceLocked", "(JI)Landroid/tracing/perfetto/DataSourceInstance;",
(void*)nativeGetPerfettoInstanceLocked},
{"nativeReleasePerfettoInstanceLocked", "(JI)V",
@@ -431,11 +435,12 @@
{"nativePerfettoDsTraceIterateBegin", "(J)Z", (void*)nativePerfettoDsTraceIterateBegin},
{"nativePerfettoDsTraceIterateNext", "(J)Z", (void*)nativePerfettoDsTraceIterateNext},
{"nativePerfettoDsTraceIterateBreak", "(J)V", (void*)nativePerfettoDsTraceIterateBreak},
- {"nativeGetPerfettoDsInstanceIndex", "(J)I", (void*)nativeGetPerfettoDsInstanceIndex}};
+ {"nativeGetPerfettoDsInstanceIndex", "(J)I", (void*)nativeGetPerfettoDsInstanceIndex},
+
+ {"nativeWritePackets", "(J[[B)V", (void*)nativeWritePackets}};
const JNINativeMethod gMethodsTracingContext[] = {
/* name, signature, funcPtr */
- {"nativeFlush", "(J[[B)V", (void*)nativeFlush},
{"nativeGetCustomTls", "(J)Ljava/lang/Object;", (void*)nativeGetCustomTls},
{"nativeGetIncrementalState", "(J)Ljava/lang/Object;", (void*)nativeGetIncrementalState},
{"nativeSetCustomTls", "(JLjava/lang/Object;)V", (void*)nativeSetCustomTls},
diff --git a/core/jni/android_view_InputDevice.cpp b/core/jni/android_view_InputDevice.cpp
index aae0da9..f5992d9 100644
--- a/core/jni/android_view_InputDevice.cpp
+++ b/core/jni/android_view_InputDevice.cpp
@@ -90,7 +90,8 @@
deviceInfo.hasButtonUnderPad(), deviceInfo.hasSensor(),
deviceInfo.hasBattery(), usiVersion.majorVersion,
usiVersion.minorVersion,
- deviceInfo.getAssociatedDisplayId()));
+ deviceInfo.getAssociatedDisplayId(),
+ deviceInfo.isEnabled()));
// Note: We do not populate the Bluetooth address into the InputDevice object to avoid leaking
// it to apps that do not have the Bluetooth permission.
@@ -126,7 +127,7 @@
gInputDeviceClassInfo.ctor = GetMethodIDOrDie(env, gInputDeviceClassInfo.clazz, "<init>",
"(IIILjava/lang/String;IIILjava/lang/"
"String;ZIILandroid/view/KeyCharacterMap;Ljava/"
- "lang/String;Ljava/lang/String;ZZZZZIII)V");
+ "lang/String;Ljava/lang/String;ZZZZZIIIZ)V");
gInputDeviceClassInfo.addMotionRange =
GetMethodIDOrDie(env, gInputDeviceClassInfo.clazz, "addMotionRange", "(IIFFFFF)V");
diff --git a/core/jni/platform/host/HostRuntime.cpp b/core/jni/platform/host/HostRuntime.cpp
index bf2fdda..acef609 100644
--- a/core/jni/platform/host/HostRuntime.cpp
+++ b/core/jni/platform/host/HostRuntime.cpp
@@ -330,7 +330,7 @@
InputDeviceInfo info = InputDeviceInfo();
info.initialize(keyboardId, 0, 0, InputDeviceIdentifier(),
"keyboard " + std::to_string(keyboardId), true, false,
- ui::ADISPLAY_ID_DEFAULT);
+ ui::LogicalDisplayId::DEFAULT);
info.setKeyboardType(AINPUT_KEYBOARD_TYPE_ALPHABETIC);
info.setKeyCharacterMap(*charMap);
diff --git a/core/proto/android/server/windowmanagerservice.proto b/core/proto/android/server/windowmanagerservice.proto
index c92435f..654d83c 100644
--- a/core/proto/android/server/windowmanagerservice.proto
+++ b/core/proto/android/server/windowmanagerservice.proto
@@ -607,7 +607,7 @@
optional InsetsSourceProviderProto insets_source_provider = 1;
optional WindowStateProto ime_target_from_ime = 2;
- optional bool is_ime_layout_drawn = 3;
+ optional bool is_ime_layout_drawn = 3 [deprecated=true];
}
message BackNavigationProto {
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index 70d923b..8541704 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -299,6 +299,9 @@
<protected-broadcast android:name="android.hardware.display.action.WIFI_DISPLAY_STATUS_CHANGED" />
+ <protected-broadcast android:name="android.hardware.hdmi.action.OSD_MESSAGE" />
+ <protected-broadcast android:name="android.hardware.hdmi.action.ON_ACTIVE_SOURCE_RECOVERED_DISMISS_UI" />
+
<protected-broadcast android:name="android.hardware.usb.action.USB_STATE" />
<protected-broadcast android:name="android.hardware.usb.action.USB_PORT_CHANGED" />
<protected-broadcast android:name="android.hardware.usb.action.USB_PORT_COMPLIANCE_CHANGED" />
diff --git a/core/res/res/drawable/ic_signal_cellular_1_4_bar.xml b/core/res/res/drawable/ic_signal_cellular_1_4_bar.xml
index 7c45c20..c692967 100644
--- a/core/res/res/drawable/ic_signal_cellular_1_4_bar.xml
+++ b/core/res/res/drawable/ic_signal_cellular_1_4_bar.xml
@@ -22,11 +22,11 @@
<path
android:fillColor="@android:color/white"
android:pathData="M20,7v13H7L20,7 M22,2L2,22h20V2L22,2z" />
- <clip-path android:name="triangle" android:pathData="M20,7v13H7L20,7z">
+ <clip-path android:name="triangle" android:pathData="M21,5 V21 H5 z">
<!-- 1 bar. move to higher ground. -->
<path
android:name="ic_signal_cellular_1_4_bar"
android:fillColor="@android:color/white"
- android:pathData="M6,0 H11 V20 H6 z" />
+ android:pathData="M0,0 H11 V24 H0 z" />
</clip-path>
</vector>
\ No newline at end of file
diff --git a/core/res/res/drawable/ic_signal_cellular_1_5_bar.xml b/core/res/res/drawable/ic_signal_cellular_1_5_bar.xml
index 02b646d..b01c269 100644
--- a/core/res/res/drawable/ic_signal_cellular_1_5_bar.xml
+++ b/core/res/res/drawable/ic_signal_cellular_1_5_bar.xml
@@ -22,11 +22,11 @@
<path
android:fillColor="@android:color/white"
android:pathData="M20,7V20H7L20,7m2-5L2,22H22V2Z" />
- <clip-path android:name="triangle" android:pathData="M20,7v13H7L20,7z">
+ <clip-path android:name="triangle" android:pathData="M21,5 V21 H5 z">
<!-- 1 bar. might have to call you back. -->
<path
android:name="ic_signal_cellular_1_5_bar"
android:fillColor="@android:color/white"
- android:pathData="M6,0 H12 V20 H6 z" />
+ android:pathData="M0,0 H12 V24 H0 z" />
</clip-path>
</vector>
\ No newline at end of file
diff --git a/core/res/res/drawable/ic_signal_cellular_2_4_bar.xml b/core/res/res/drawable/ic_signal_cellular_2_4_bar.xml
index 514d169..982623d 100644
--- a/core/res/res/drawable/ic_signal_cellular_2_4_bar.xml
+++ b/core/res/res/drawable/ic_signal_cellular_2_4_bar.xml
@@ -22,11 +22,11 @@
<path
android:fillColor="@android:color/white"
android:pathData="M20,7v13H7L20,7 M22,2L2,22h20V2L22,2z" />
- <clip-path android:name="triangle" android:pathData="M20,7v13H7L20,7z">
+ <clip-path android:name="triangle" android:pathData="M21,5 V21 H5 z">
<!-- 2 bars. 2 out of 4 ain't bad. -->
<path
android:name="ic_signal_cellular_2_4_bar"
android:fillColor="@android:color/white"
- android:pathData="M6,0 H14 V20 H6 z" />
+ android:pathData="M0,0 H14 V24 H0 z" />
</clip-path>
</vector>
\ No newline at end of file
diff --git a/core/res/res/drawable/ic_signal_cellular_2_5_bar.xml b/core/res/res/drawable/ic_signal_cellular_2_5_bar.xml
index a97f771..75daadd 100644
--- a/core/res/res/drawable/ic_signal_cellular_2_5_bar.xml
+++ b/core/res/res/drawable/ic_signal_cellular_2_5_bar.xml
@@ -23,11 +23,11 @@
<path
android:fillColor="@android:color/white"
android:pathData="M20,7V20H7L20,7m2-5L2,22H22V2Z" />
- <clip-path android:name="triangle" android:pathData="M20,7v13H7L20,7z">
+ <clip-path android:name="triangle" android:pathData="M21,5 V21 H5 z">
<!-- 2 bars. hanging in there. -->
<path
android:name="ic_signal_cellular_2_5_bar"
android:fillColor="@android:color/white"
- android:pathData="M6,0 H14 V20 H6 z" />
+ android:pathData="M0,0 H14 V24 H0 z" />
</clip-path>
</vector>
\ No newline at end of file
diff --git a/core/res/res/drawable/ic_signal_cellular_3_4_bar.xml b/core/res/res/drawable/ic_signal_cellular_3_4_bar.xml
index 1bacf4a..4e4bea3 100644
--- a/core/res/res/drawable/ic_signal_cellular_3_4_bar.xml
+++ b/core/res/res/drawable/ic_signal_cellular_3_4_bar.xml
@@ -22,11 +22,11 @@
<path
android:fillColor="@android:color/white"
android:pathData="M20,7v13H7L20,7 M22,2L2,22h20V2L22,2z" />
- <clip-path android:name="triangle" android:pathData="M20,7v13H7L20,7z">
+ <clip-path android:name="triangle" android:pathData="M21,5 V21 H5 z">
<!-- 3 bars. quite nice. -->
<path
android:name="ic_signal_cellular_3_4_bar"
android:fillColor="@android:color/white"
- android:pathData="M6,0 H17 V20 H6 z" />
+ android:pathData="M0,0 H17 V24 H0 z" />
</clip-path>
</vector>
\ No newline at end of file
diff --git a/core/res/res/drawable/ic_signal_cellular_3_5_bar.xml b/core/res/res/drawable/ic_signal_cellular_3_5_bar.xml
index 2789d3e..9a98c29 100644
--- a/core/res/res/drawable/ic_signal_cellular_3_5_bar.xml
+++ b/core/res/res/drawable/ic_signal_cellular_3_5_bar.xml
@@ -22,11 +22,11 @@
<path
android:fillColor="@android:color/white"
android:pathData="M20,7V20H7L20,7m2-5L2,22H22V2Z" />
- <clip-path android:name="triangle" android:pathData="M20,7v13H7L20,7z">
+ <clip-path android:name="triangle" android:pathData="M21,5 V21 H5 z">
<!-- 3 bars. not great, not terrible. -->
<path
android:name="ic_signal_cellular_3_5_bar"
android:fillColor="@android:color/white"
- android:pathData="M6,0 H16 V20 H6 z" />
+ android:pathData="M0,0 H16 V24 H0 z" />
</clip-path>
</vector>
\ No newline at end of file
diff --git a/core/res/res/drawable/ic_signal_cellular_4_5_bar.xml b/core/res/res/drawable/ic_signal_cellular_4_5_bar.xml
index 8286dbb..2a37d01 100644
--- a/core/res/res/drawable/ic_signal_cellular_4_5_bar.xml
+++ b/core/res/res/drawable/ic_signal_cellular_4_5_bar.xml
@@ -22,11 +22,11 @@
<path
android:fillColor="@android:color/white"
android:pathData="M20,7V20H7L20,7m2-5L2,22H22V2Z" />
- <clip-path android:name="triangle" android:pathData="M20,7v13H7L20,7z">
+ <clip-path android:name="triangle" android:pathData="M21,5 V21 H5 z">
<!-- 4 bars. extremely respectable. -->
<path
android:name="ic_signal_cellular_4_5_bar"
android:fillColor="@android:color/white"
- android:pathData="M6,0 H18 V20 H6 z" />
+ android:pathData="M0,0 H18 V24 H0 z" />
</clip-path>
</vector>
\ No newline at end of file
diff --git a/core/res/res/layout/notification_template_material_messaging_compact_heads_up.xml b/core/res/res/layout/notification_template_material_messaging_compact_heads_up.xml
new file mode 100644
index 0000000..82920ba
--- /dev/null
+++ b/core/res/res/layout/notification_template_material_messaging_compact_heads_up.xml
@@ -0,0 +1,112 @@
+<?xml version="1.0" encoding="utf-8"?><!--
+ ~ Copyright (C) 2024 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License
+ -->
+<com.android.internal.widget.CompactMessagingLayout
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:id="@+id/status_bar_latest_event_content"
+ android:layout_width="match_parent"
+ android:layout_height="@dimen/notification_header_height"
+ android:clipChildren="false"
+ android:tag="compactMessagingHUN"
+ android:gravity="center_vertical"
+ android:theme="@style/Theme.DeviceDefault.Notification"
+ android:importantForAccessibility="no">
+ <com.android.internal.widget.NotificationRowIconView
+ android:id="@+id/icon"
+ android:layout_width="@dimen/notification_icon_circle_size"
+ android:layout_height="@dimen/notification_icon_circle_size"
+ android:layout_gravity="center_vertical|start"
+ android:layout_marginStart="@dimen/notification_icon_circle_start"
+ android:background="@drawable/notification_icon_circle"
+ android:padding="@dimen/notification_icon_circle_padding"
+ android:maxDrawableWidth="@dimen/notification_icon_circle_size"
+ android:maxDrawableHeight="@dimen/notification_icon_circle_size"
+ />
+ <com.android.internal.widget.NotificationRowIconView
+ android:id="@+id/conversation_icon"
+ android:layout_width="@dimen/notification_icon_circle_size"
+ android:layout_height="@dimen/notification_icon_circle_size"
+ android:layout_gravity="center_vertical|start"
+ android:layout_marginStart="@dimen/notification_icon_circle_start"
+ android:maxDrawableWidth="@dimen/notification_icon_circle_size"
+ android:maxDrawableHeight="@dimen/notification_icon_circle_size"
+ android:scaleType="centerCrop"
+ android:importantForAccessibility="no"
+ />
+ <ViewStub
+ android:layout="@layout/conversation_face_pile_layout"
+ android:layout_gravity="center_vertical|start"
+ android:layout_width="@dimen/conversation_compact_face_pile_size"
+ android:layout_height="@dimen/conversation_compact_face_pile_size"
+ android:layout_marginStart="@dimen/notification_icon_circle_start"
+ android:id="@+id/conversation_face_pile"
+ />
+ <FrameLayout
+ android:id="@+id/alternate_expand_target"
+ android:layout_width="@dimen/notification_content_margin_start"
+ android:layout_height="match_parent"
+ android:layout_gravity="start"
+ android:importantForAccessibility="no"
+ android:focusable="false"
+ />
+ <LinearLayout
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:layout_marginStart="@dimen/notification_content_margin_start"
+ android:orientation="horizontal"
+ >
+ <NotificationTopLineView
+ android:id="@+id/notification_top_line"
+ android:layout_width="0dp"
+ android:layout_height="match_parent"
+ android:layout_centerVertical="true"
+ android:layout_weight="1"
+ android:clipChildren="false"
+ android:gravity="center_vertical"
+ android:theme="@style/Theme.DeviceDefault.Notification"
+ >
+ <TextView
+ android:id="@+id/title"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_marginEnd="@dimen/notification_header_separating_margin"
+ android:ellipsize="end"
+ android:fadingEdge="horizontal"
+ android:singleLine="true"
+ android:textAlignment="viewStart"
+ android:textAppearance="@style/TextAppearance.DeviceDefault.Notification.Title"
+ />
+ <include layout="@layout/notification_top_line_views" />
+ </NotificationTopLineView>
+ <FrameLayout
+ android:id="@+id/reply_action_container"
+ android:layout_width="wrap_content"
+ android:layout_height="@dimen/notification_action_list_height"
+ android:gravity="center_vertical"
+ android:orientation="horizontal" />
+ <FrameLayout
+ android:id="@+id/expand_button_touch_container"
+ android:layout_width="wrap_content"
+ android:layout_height="match_parent"
+ android:minWidth="@dimen/notification_content_margin_end"
+ >
+ <include layout="@layout/notification_expand_button"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_gravity="center_vertical|end"
+ />
+ </FrameLayout>
+ </LinearLayout>
+</com.android.internal.widget.CompactMessagingLayout>
diff --git a/core/res/res/layout/side_fps_toast.xml b/core/res/res/layout/side_fps_toast.xml
index 2c35c9b..78299ab 100644
--- a/core/res/res/layout/side_fps_toast.xml
+++ b/core/res/res/layout/side_fps_toast.xml
@@ -25,6 +25,7 @@
android:layout_height="wrap_content"
android:layout_width="0dp"
android:layout_weight="6"
+ android:paddingBottom="10dp"
android:text="@string/fp_power_button_enrollment_title"
android:textColor="@color/side_fps_text_color"
android:paddingLeft="20dp"/>
@@ -36,6 +37,7 @@
android:layout_height="wrap_content"
android:layout_width="0dp"
android:layout_weight="3"
+ android:paddingBottom="10dp"
android:text="@string/fp_power_button_enrollment_button_text"
style="?android:attr/buttonBarNegativeButtonStyle"
android:textColor="@color/side_fps_button_color"
diff --git a/core/res/res/values-af/strings.xml b/core/res/res/values-af/strings.xml
index 2a3c691..7fe8641 100644
--- a/core/res/res/values-af/strings.xml
+++ b/core/res/res/values-af/strings.xml
@@ -283,6 +283,8 @@
<string name="global_action_voice_assist" msgid="6655788068555086695">"Stembystand"</string>
<string name="global_action_lockdown" msgid="2475471405907902963">"Snelsluit"</string>
<string name="status_bar_notification_info_overflow" msgid="3330152558746563475">"999+"</string>
+ <!-- no translation found for notification_compact_heads_up_reply (2425293958371284340) -->
+ <skip />
<string name="notification_hidden_text" msgid="2835519769868187223">"Nuwe kennisgewing"</string>
<string name="notification_channel_physical_keyboard" msgid="5417306456125988096">"Fisieke sleutelbord"</string>
<string name="notification_channel_security" msgid="8516754650348238057">"Sekuriteit"</string>
@@ -2413,22 +2415,15 @@
<string name="satellite_notification_open_message" msgid="4149234979688273729">"Maak Boodskappe oop"</string>
<string name="satellite_notification_how_it_works" msgid="3132069321977520519">"Hoe dit werk"</string>
<string name="unarchival_session_app_label" msgid="6811856981546348205">"Hangend …"</string>
- <!-- no translation found for fingerprint_dangling_notification_title (7362075195588639989) -->
+ <string name="fingerprint_dangling_notification_title" msgid="7362075195588639989">"Stel Vingerafdrukslot weer op"</string>
+ <!-- no translation found for fingerprint_dangling_notification_msg_1 (8517140433796229725) -->
<skip />
- <!-- no translation found for fingerprint_dangling_notification_msg_1 (6261149111900787302) -->
+ <!-- no translation found for fingerprint_dangling_notification_msg_2 (7578829498452127613) -->
<skip />
- <!-- no translation found for fingerprint_dangling_notification_msg_2 (7688302770424064884) -->
- <skip />
- <!-- no translation found for fingerprint_dangling_notification_msg_all_deleted_1 (2927018569542316055) -->
- <skip />
- <!-- no translation found for fingerprint_dangling_notification_msg_all_deleted_2 (6897989352716156176) -->
- <skip />
- <!-- no translation found for face_dangling_notification_title (947852541060975473) -->
- <skip />
- <!-- no translation found for face_dangling_notification_msg (8806849376915541655) -->
- <skip />
- <!-- no translation found for biometric_dangling_notification_action_set_up (8246885009807817961) -->
- <skip />
- <!-- no translation found for biometric_dangling_notification_action_not_now (8095249216864443491) -->
- <skip />
+ <string name="fingerprint_dangling_notification_msg_all_deleted_1" msgid="2927018569542316055">"<xliff:g id="FINGERPRINT">%s</xliff:g> het nie goed gewerk nie en is uitgevee. Stel dit weer op om jou foon met vingerafdruk te ontsluit."</string>
+ <string name="fingerprint_dangling_notification_msg_all_deleted_2" msgid="6897989352716156176">"<xliff:g id="FINGERPRINT_0">%1$s</xliff:g> en <xliff:g id="FINGERPRINT_1">%2$s</xliff:g> het nie goed gewerk nie en is uitgevee. Stel dit weer op om jou foon met jou vingerafdruk te ontsluit."</string>
+ <string name="face_dangling_notification_title" msgid="947852541060975473">"Stel Gesigslot weer op"</string>
+ <string name="face_dangling_notification_msg" msgid="8806849376915541655">"Jou gesigmodel het nie goed gewerk nie en is uitgevee. Stel dit weer op om jou foon met gesig te ontsluit."</string>
+ <string name="biometric_dangling_notification_action_set_up" msgid="8246885009807817961">"Stel op"</string>
+ <string name="biometric_dangling_notification_action_not_now" msgid="8095249216864443491">"Nie nou nie"</string>
</resources>
diff --git a/core/res/res/values-am/strings.xml b/core/res/res/values-am/strings.xml
index 08a290c..1d1ded6 100644
--- a/core/res/res/values-am/strings.xml
+++ b/core/res/res/values-am/strings.xml
@@ -283,6 +283,8 @@
<string name="global_action_voice_assist" msgid="6655788068555086695">"የድምጽ እርዳታ"</string>
<string name="global_action_lockdown" msgid="2475471405907902963">"መቆለፊያ"</string>
<string name="status_bar_notification_info_overflow" msgid="3330152558746563475">"999+"</string>
+ <!-- no translation found for notification_compact_heads_up_reply (2425293958371284340) -->
+ <skip />
<string name="notification_hidden_text" msgid="2835519769868187223">"አዲስ ማሳወቂያ"</string>
<string name="notification_channel_physical_keyboard" msgid="5417306456125988096">"አካላዊ ቁልፍ ሰሌዳ"</string>
<string name="notification_channel_security" msgid="8516754650348238057">"ደህንነት"</string>
@@ -2413,22 +2415,15 @@
<string name="satellite_notification_open_message" msgid="4149234979688273729">"መልዕክቶች ይክፈቱ"</string>
<string name="satellite_notification_how_it_works" msgid="3132069321977520519">"እንዴት እንደሚሠራ"</string>
<string name="unarchival_session_app_label" msgid="6811856981546348205">"በመጠባበቅ ላይ..."</string>
- <!-- no translation found for fingerprint_dangling_notification_title (7362075195588639989) -->
+ <string name="fingerprint_dangling_notification_title" msgid="7362075195588639989">"በጣት አሻራ መክፈቻን እንደገና ያዋቅሩ"</string>
+ <!-- no translation found for fingerprint_dangling_notification_msg_1 (8517140433796229725) -->
<skip />
- <!-- no translation found for fingerprint_dangling_notification_msg_1 (6261149111900787302) -->
+ <!-- no translation found for fingerprint_dangling_notification_msg_2 (7578829498452127613) -->
<skip />
- <!-- no translation found for fingerprint_dangling_notification_msg_2 (7688302770424064884) -->
- <skip />
- <!-- no translation found for fingerprint_dangling_notification_msg_all_deleted_1 (2927018569542316055) -->
- <skip />
- <!-- no translation found for fingerprint_dangling_notification_msg_all_deleted_2 (6897989352716156176) -->
- <skip />
- <!-- no translation found for face_dangling_notification_title (947852541060975473) -->
- <skip />
- <!-- no translation found for face_dangling_notification_msg (8806849376915541655) -->
- <skip />
- <!-- no translation found for biometric_dangling_notification_action_set_up (8246885009807817961) -->
- <skip />
- <!-- no translation found for biometric_dangling_notification_action_not_now (8095249216864443491) -->
- <skip />
+ <string name="fingerprint_dangling_notification_msg_all_deleted_1" msgid="2927018569542316055">"<xliff:g id="FINGERPRINT">%s</xliff:g> በደንብ እየሠራ አልነበረም እና ተሰርዟል። ስልክዎን በጣት አሻራ ለመክፈት እንደገና ያዋቅሩት።"</string>
+ <string name="fingerprint_dangling_notification_msg_all_deleted_2" msgid="6897989352716156176">"<xliff:g id="FINGERPRINT_0">%1$s</xliff:g> እና <xliff:g id="FINGERPRINT_1">%2$s</xliff:g> በደንብ እየሠሩ አልነበረም እና ተሰርዘዋል። ስልክዎን በጣት አሻራ ለመክፈት እንደገና ያዋቅሯቸው።"</string>
+ <string name="face_dangling_notification_title" msgid="947852541060975473">"በመልክ መክፈትን እንደገና ያዋቅሩ"</string>
+ <string name="face_dangling_notification_msg" msgid="8806849376915541655">"የእርስዎ የመልክ ሞዴል በደንብ እየሠራ አልነበረም እና ተሰርዟል። ስልክዎን በመልክ ለመክፈት እንደገና ያዋቅሩት።"</string>
+ <string name="biometric_dangling_notification_action_set_up" msgid="8246885009807817961">"ያዋቅሩ"</string>
+ <string name="biometric_dangling_notification_action_not_now" msgid="8095249216864443491">"አሁን አይደለም"</string>
</resources>
diff --git a/core/res/res/values-ar/strings.xml b/core/res/res/values-ar/strings.xml
index 20d491f..6ee9d5d 100644
--- a/core/res/res/values-ar/strings.xml
+++ b/core/res/res/values-ar/strings.xml
@@ -287,6 +287,8 @@
<string name="global_action_voice_assist" msgid="6655788068555086695">"المساعد الصوتي"</string>
<string name="global_action_lockdown" msgid="2475471405907902963">"إلغاء التأمين"</string>
<string name="status_bar_notification_info_overflow" msgid="3330152558746563475">"999+"</string>
+ <!-- no translation found for notification_compact_heads_up_reply (2425293958371284340) -->
+ <skip />
<string name="notification_hidden_text" msgid="2835519769868187223">"إشعار جديد"</string>
<string name="notification_channel_physical_keyboard" msgid="5417306456125988096">"لوحة المفاتيح الخارجية"</string>
<string name="notification_channel_security" msgid="8516754650348238057">"الأمان"</string>
@@ -2398,9 +2400,9 @@
<string name="keyboard_layout_notification_more_than_three_selected_message" msgid="1581834181578206937">"تم ضبط تنسيق لوحة المفاتيح على <xliff:g id="LAYOUT_1">%1$s</xliff:g> و<xliff:g id="LAYOUT_2">%2$s</xliff:g> و<xliff:g id="LAYOUT_3">%3$s</xliff:g>… انقر لتغييره."</string>
<string name="keyboard_layout_notification_multiple_selected_title" msgid="5242444914367024499">"تم إعداد لوحات المفاتيح الخارجية"</string>
<string name="keyboard_layout_notification_multiple_selected_message" msgid="6576533454124419202">"انقر لعرض لوحات المفاتيح."</string>
- <string name="profile_label_private" msgid="6463418670715290696">"ملف شخصي خاص"</string>
+ <string name="profile_label_private" msgid="6463418670715290696">"المساحة الخاصة"</string>
<string name="profile_label_clone" msgid="769106052210954285">"نسخة طبق الأصل"</string>
- <string name="profile_label_work" msgid="3495359133038584618">"ملف العمل"</string>
+ <string name="profile_label_work" msgid="3495359133038584618">"مساحة العمل"</string>
<string name="profile_label_work_2" msgid="4691533661598632135">"ملف العمل 2"</string>
<string name="profile_label_work_3" msgid="4834572253956798917">"ملف العمل 3"</string>
<string name="profile_label_test" msgid="9168641926186071947">"ملف شخصي تجريبي"</string>
@@ -2417,22 +2419,15 @@
<string name="satellite_notification_open_message" msgid="4149234979688273729">"فتح تطبيق \"الرسائل\""</string>
<string name="satellite_notification_how_it_works" msgid="3132069321977520519">"طريقة العمل"</string>
<string name="unarchival_session_app_label" msgid="6811856981546348205">"بانتظار الإزالة من الأرشيف…"</string>
- <!-- no translation found for fingerprint_dangling_notification_title (7362075195588639989) -->
+ <string name="fingerprint_dangling_notification_title" msgid="7362075195588639989">"إعادة إعداد ميزة \"فتح الجهاز ببصمة الإصبع\""</string>
+ <!-- no translation found for fingerprint_dangling_notification_msg_1 (8517140433796229725) -->
<skip />
- <!-- no translation found for fingerprint_dangling_notification_msg_1 (6261149111900787302) -->
+ <!-- no translation found for fingerprint_dangling_notification_msg_2 (7578829498452127613) -->
<skip />
- <!-- no translation found for fingerprint_dangling_notification_msg_2 (7688302770424064884) -->
- <skip />
- <!-- no translation found for fingerprint_dangling_notification_msg_all_deleted_1 (2927018569542316055) -->
- <skip />
- <!-- no translation found for fingerprint_dangling_notification_msg_all_deleted_2 (6897989352716156176) -->
- <skip />
- <!-- no translation found for face_dangling_notification_title (947852541060975473) -->
- <skip />
- <!-- no translation found for face_dangling_notification_msg (8806849376915541655) -->
- <skip />
- <!-- no translation found for biometric_dangling_notification_action_set_up (8246885009807817961) -->
- <skip />
- <!-- no translation found for biometric_dangling_notification_action_not_now (8095249216864443491) -->
- <skip />
+ <string name="fingerprint_dangling_notification_msg_all_deleted_1" msgid="2927018569542316055">"هناك مشكلة في <xliff:g id="FINGERPRINT">%s</xliff:g> وتم حذفها. يُرجى إعدادها مرة أخرى لفتح قفل هاتفك باستخدام بصمة الإصبع."</string>
+ <string name="fingerprint_dangling_notification_msg_all_deleted_2" msgid="6897989352716156176">"هناك مشكلة في <xliff:g id="FINGERPRINT_0">%1$s</xliff:g> و<xliff:g id="FINGERPRINT_1">%2$s</xliff:g> وتم حذفهما. يُرجى إعادة إعدادهما لفتح قفل هاتفك باستخدام بصمة الإصبع."</string>
+ <string name="face_dangling_notification_title" msgid="947852541060975473">"إعادة إعداد ميزة \"فتح الجهاز بالتعرّف على الوجه\""</string>
+ <string name="face_dangling_notification_msg" msgid="8806849376915541655">"هناك مشكلة في نموذج الوجه الخاص بك وتم حذفه. يُرجى إعداده مرة أخرى لفتح قفل هاتفك باستخدام وجهك."</string>
+ <string name="biometric_dangling_notification_action_set_up" msgid="8246885009807817961">"إعداد"</string>
+ <string name="biometric_dangling_notification_action_not_now" msgid="8095249216864443491">"لاحقًا"</string>
</resources>
diff --git a/core/res/res/values-as/strings.xml b/core/res/res/values-as/strings.xml
index 30ced90..9064df1 100644
--- a/core/res/res/values-as/strings.xml
+++ b/core/res/res/values-as/strings.xml
@@ -283,6 +283,8 @@
<string name="global_action_voice_assist" msgid="6655788068555086695">"কণ্ঠধ্বনিৰে সহায়"</string>
<string name="global_action_lockdown" msgid="2475471405907902963">"লকডাউন"</string>
<string name="status_bar_notification_info_overflow" msgid="3330152558746563475">"৯৯৯+"</string>
+ <!-- no translation found for notification_compact_heads_up_reply (2425293958371284340) -->
+ <skip />
<string name="notification_hidden_text" msgid="2835519769868187223">"নতুন জাননী"</string>
<string name="notification_channel_physical_keyboard" msgid="5417306456125988096">"কায়িক কীব’ৰ্ড"</string>
<string name="notification_channel_security" msgid="8516754650348238057">"সুৰক্ষা"</string>
@@ -2413,22 +2415,15 @@
<string name="satellite_notification_open_message" msgid="4149234979688273729">"Messages খোলক"</string>
<string name="satellite_notification_how_it_works" msgid="3132069321977520519">"ই কেনেকৈ কাম কৰে"</string>
<string name="unarchival_session_app_label" msgid="6811856981546348205">"বিবেচনাধীন হৈ আছে..."</string>
- <!-- no translation found for fingerprint_dangling_notification_title (7362075195588639989) -->
+ <string name="fingerprint_dangling_notification_title" msgid="7362075195588639989">"ফিংগাৰপ্ৰিণ্ট আনলক পুনৰ ছেট আপ কৰক"</string>
+ <!-- no translation found for fingerprint_dangling_notification_msg_1 (8517140433796229725) -->
<skip />
- <!-- no translation found for fingerprint_dangling_notification_msg_1 (6261149111900787302) -->
+ <!-- no translation found for fingerprint_dangling_notification_msg_2 (7578829498452127613) -->
<skip />
- <!-- no translation found for fingerprint_dangling_notification_msg_2 (7688302770424064884) -->
- <skip />
- <!-- no translation found for fingerprint_dangling_notification_msg_all_deleted_1 (2927018569542316055) -->
- <skip />
- <!-- no translation found for fingerprint_dangling_notification_msg_all_deleted_2 (6897989352716156176) -->
- <skip />
- <!-- no translation found for face_dangling_notification_title (947852541060975473) -->
- <skip />
- <!-- no translation found for face_dangling_notification_msg (8806849376915541655) -->
- <skip />
- <!-- no translation found for biometric_dangling_notification_action_set_up (8246885009807817961) -->
- <skip />
- <!-- no translation found for biometric_dangling_notification_action_not_now (8095249216864443491) -->
- <skip />
+ <string name="fingerprint_dangling_notification_msg_all_deleted_1" msgid="2927018569542316055">"<xliff:g id="FINGERPRINT">%s</xliff:g>এ ভালদৰে কাম কৰা নাছিল আৰু সেইটো মচি পেলোৱা হৈছে। ফিংগাৰপ্ৰিণ্টৰ জৰিয়তে আপোনাৰ ফ’নটো আনলক কৰিবলৈ এইটো পুনৰ ছেট আপ কৰক।"</string>
+ <string name="fingerprint_dangling_notification_msg_all_deleted_2" msgid="6897989352716156176">"<xliff:g id="FINGERPRINT_0">%1$s</xliff:g> আৰু <xliff:g id="FINGERPRINT_1">%2$s</xliff:g>এ ভালদৰে কাম কৰা নাছিল আৰু সেয়া মচি পেলোৱা হৈছে। ফিংগাৰপ্ৰিণ্টৰ জৰিয়তে আপোনাৰ ফ’নটো আনলক কৰিবলৈ সেয়া পুনৰ ছেট আপ কৰক।"</string>
+ <string name="face_dangling_notification_title" msgid="947852541060975473">"ফে’চ আনলক পুনৰ ছেট আপ কৰক"</string>
+ <string name="face_dangling_notification_msg" msgid="8806849376915541655">"আপোনাৰ মুখাৱয়বৰ মডেলটোৱে ভালদৰে কাম কৰা নাছিল আৰু সেইটো মচি পেলোৱা হৈছে। মুখাৱয়বৰ জৰিয়তে আপোনাৰ ফ’নটো আনলক কৰিবলৈ এইটো পুনৰ ছেট আপ কৰক।"</string>
+ <string name="biometric_dangling_notification_action_set_up" msgid="8246885009807817961">"ছেট আপ কৰক"</string>
+ <string name="biometric_dangling_notification_action_not_now" msgid="8095249216864443491">"এতিয়া নহয়"</string>
</resources>
diff --git a/core/res/res/values-az/strings.xml b/core/res/res/values-az/strings.xml
index 49073f1..23a9668 100644
--- a/core/res/res/values-az/strings.xml
+++ b/core/res/res/values-az/strings.xml
@@ -283,6 +283,8 @@
<string name="global_action_voice_assist" msgid="6655788068555086695">"Səs Yardımçısı"</string>
<string name="global_action_lockdown" msgid="2475471405907902963">"Kilidləyin"</string>
<string name="status_bar_notification_info_overflow" msgid="3330152558746563475">"999+"</string>
+ <!-- no translation found for notification_compact_heads_up_reply (2425293958371284340) -->
+ <skip />
<string name="notification_hidden_text" msgid="2835519769868187223">"Yeni bildiriş"</string>
<string name="notification_channel_physical_keyboard" msgid="5417306456125988096">"Fiziki klaviatura"</string>
<string name="notification_channel_security" msgid="8516754650348238057">"Güvənlik"</string>
@@ -2413,22 +2415,15 @@
<string name="satellite_notification_open_message" msgid="4149234979688273729">"Mesajı açın"</string>
<string name="satellite_notification_how_it_works" msgid="3132069321977520519">"Haqqında"</string>
<string name="unarchival_session_app_label" msgid="6811856981546348205">"Gözləmədə..."</string>
- <!-- no translation found for fingerprint_dangling_notification_title (7362075195588639989) -->
+ <string name="fingerprint_dangling_notification_title" msgid="7362075195588639989">"Barmaqla Kilidaçmanı yenidən ayarlayın"</string>
+ <!-- no translation found for fingerprint_dangling_notification_msg_1 (8517140433796229725) -->
<skip />
- <!-- no translation found for fingerprint_dangling_notification_msg_1 (6261149111900787302) -->
+ <!-- no translation found for fingerprint_dangling_notification_msg_2 (7578829498452127613) -->
<skip />
- <!-- no translation found for fingerprint_dangling_notification_msg_2 (7688302770424064884) -->
- <skip />
- <!-- no translation found for fingerprint_dangling_notification_msg_all_deleted_1 (2927018569542316055) -->
- <skip />
- <!-- no translation found for fingerprint_dangling_notification_msg_all_deleted_2 (6897989352716156176) -->
- <skip />
- <!-- no translation found for face_dangling_notification_title (947852541060975473) -->
- <skip />
- <!-- no translation found for face_dangling_notification_msg (8806849376915541655) -->
- <skip />
- <!-- no translation found for biometric_dangling_notification_action_set_up (8246885009807817961) -->
- <skip />
- <!-- no translation found for biometric_dangling_notification_action_not_now (8095249216864443491) -->
- <skip />
+ <string name="fingerprint_dangling_notification_msg_all_deleted_1" msgid="2927018569542316055">"<xliff:g id="FINGERPRINT">%s</xliff:g> yaxşı işləmirdi və silindi. Telefonu barmaq izi ilə kiliddən çıxarmaq üçün onu yenidən ayarlayın."</string>
+ <string name="fingerprint_dangling_notification_msg_all_deleted_2" msgid="6897989352716156176">"<xliff:g id="FINGERPRINT_0">%1$s</xliff:g> və <xliff:g id="FINGERPRINT_1">%2$s</xliff:g> yaxşı işləmirdi və silindi. Telefonu barmaq izi ilə kiliddən çıxarmaq üçün onları yenidən ayarlayın."</string>
+ <string name="face_dangling_notification_title" msgid="947852541060975473">"Üzlə Kilidaçmanı yenidən ayarlayın"</string>
+ <string name="face_dangling_notification_msg" msgid="8806849376915541655">"Üz modeliniz yaxşı işləmirdi və silindi. Telefonu üzlə kiliddən çıxarmaq üçün onu yenidən ayarlayın."</string>
+ <string name="biometric_dangling_notification_action_set_up" msgid="8246885009807817961">"Ayarlayın"</string>
+ <string name="biometric_dangling_notification_action_not_now" msgid="8095249216864443491">"İndi yox"</string>
</resources>
diff --git a/core/res/res/values-b+sr+Latn/strings.xml b/core/res/res/values-b+sr+Latn/strings.xml
index ca66ef4..db9a93f 100644
--- a/core/res/res/values-b+sr+Latn/strings.xml
+++ b/core/res/res/values-b+sr+Latn/strings.xml
@@ -284,6 +284,8 @@
<string name="global_action_voice_assist" msgid="6655788068555086695">"Glasovna pomoć"</string>
<string name="global_action_lockdown" msgid="2475471405907902963">"Zaključavanje"</string>
<string name="status_bar_notification_info_overflow" msgid="3330152558746563475">"999+"</string>
+ <!-- no translation found for notification_compact_heads_up_reply (2425293958371284340) -->
+ <skip />
<string name="notification_hidden_text" msgid="2835519769868187223">"Novo obaveštenje"</string>
<string name="notification_channel_physical_keyboard" msgid="5417306456125988096">"Fizička tastatura"</string>
<string name="notification_channel_security" msgid="8516754650348238057">"Bezbednost"</string>
@@ -835,11 +837,11 @@
<string name="policylab_watchLogin" msgid="7599669460083719504">"Nadzor pokušaja otključavanja ekrana"</string>
<string name="policydesc_watchLogin" product="tablet" msgid="2388436408621909298">"Prati broj netačno unetih lozinki prilikom otključavanja ekrana i zaključava tablet ili briše podatke sa tableta ako je netačna lozinka uneta previše puta."</string>
<string name="policydesc_watchLogin" product="tv" msgid="2140588224468517507">"Nadgleda broj netačnih lozinki unetih pri otključavanju ekrana i zaključava Android TV uređaj ili briše sve podatke sa Android TV uređaja ako se unese previše netačnih lozinki."</string>
- <string name="policydesc_watchLogin" product="automotive" msgid="7011438994051251521">"Prati broj netačno unetih lozinki pri otključavanju ekrana i zaključava sistem za info-zabavu ili briše sve podatke sa sistema za info-zabavu ako je netačna lozinka uneta previše puta."</string>
+ <string name="policydesc_watchLogin" product="automotive" msgid="7011438994051251521">"Prati broj netačno unetih lozinki pri otključavanju ekrana i zaključava sistem za informacije i zabavu ili briše sve podatke sa sistema za informacije i zabavu ako je netačna lozinka uneta previše puta."</string>
<string name="policydesc_watchLogin" product="default" msgid="4885030206253600299">"Prati broj netačno unetih lozinki pri otključavanju ekrana i zaključava telefon ili briše sve podatke sa telefona ako je netačna lozinka uneta previše puta."</string>
<string name="policydesc_watchLogin_secondaryUser" product="tablet" msgid="2049038943004297474">"Nadgleda broj netačnih lozinki unetih pri otključavanju ekrana i zaključava tablet ili briše sve podatke ovog korisnika ako se unese previše netačnih lozinki."</string>
<string name="policydesc_watchLogin_secondaryUser" product="tv" msgid="8965224107449407052">"Nadgleda broj netačnih lozinki unetih pri otključavanju ekrana i zaključava Android TV uređaj ili briše sve podatke ovog korisnika ako se unese previše netačnih lozinki."</string>
- <string name="policydesc_watchLogin_secondaryUser" product="automotive" msgid="7180857406058327941">"Nadgleda broj netačnih lozinki unetih pri otključavanju ekrana i zaključava sistem za info-zabavu ili briše sve podatke ovog profila ako se unese previše netačnih lozinki."</string>
+ <string name="policydesc_watchLogin_secondaryUser" product="automotive" msgid="7180857406058327941">"Nadgleda broj netačnih lozinki unetih pri otključavanju ekrana i zaključava sistem za informacije i zabavu ili briše sve podatke ovog profila ako se unese previše netačnih lozinki."</string>
<string name="policydesc_watchLogin_secondaryUser" product="default" msgid="9177645136475155924">"Nadgleda broj netačnih lozinki unetih pri otključavanju ekrana i zaključava telefon ili briše sve podatke ovog korisnika ako se unese previše netačnih lozinki."</string>
<string name="policylab_resetPassword" msgid="214556238645096520">"Promena zaključavanja ekrana"</string>
<string name="policydesc_resetPassword" msgid="4626419138439341851">"Menja otključavanje ekrana."</string>
@@ -848,13 +850,13 @@
<string name="policylab_wipeData" msgid="1359485247727537311">"Brisanje svih podataka"</string>
<string name="policydesc_wipeData" product="tablet" msgid="7245372676261947507">"Brisanje podataka na tabletu bez upozorenja resetovanjem na fabrička podešavanja."</string>
<string name="policydesc_wipeData" product="tv" msgid="513862488950801261">"Briše podatke Android TV uređaja bez upozorenja pomoću resetovanja na fabrička podešavanja."</string>
- <string name="policydesc_wipeData" product="automotive" msgid="660804547737323300">"Briše podatke na sistemu za info-zabavu bez upozorenja resetovanjem na fabrička podešavanja."</string>
+ <string name="policydesc_wipeData" product="automotive" msgid="660804547737323300">"Briše podatke na sistemu za informacije i zabavu bez upozorenja resetovanjem na fabrička podešavanja."</string>
<string name="policydesc_wipeData" product="default" msgid="8036084184768379022">"Brisanje podataka na telefonu bez upozorenja resetovanjem na fabrička podešavanja."</string>
<string name="policylab_wipeData_secondaryUser" product="automotive" msgid="115034358520328373">"Brisanje podataka profila"</string>
<string name="policylab_wipeData_secondaryUser" product="default" msgid="413813645323433166">"Obriši podatke korisnika"</string>
<string name="policydesc_wipeData_secondaryUser" product="tablet" msgid="2336676480090926470">"Briše podatke ovog korisnika na ovom tabletu bez upozorenja."</string>
<string name="policydesc_wipeData_secondaryUser" product="tv" msgid="2293713284515865200">"Briše podatke ovog korisnika na ovom Android TV uređaju bez upozorenja."</string>
- <string name="policydesc_wipeData_secondaryUser" product="automotive" msgid="4658832487305780879">"Briše podatke ovog profila na ovom sistemu za info-zabavu bez upozorenja."</string>
+ <string name="policydesc_wipeData_secondaryUser" product="automotive" msgid="4658832487305780879">"Briše podatke ovog profila na ovom sistemu za informacije i zabavu bez upozorenja."</string>
<string name="policydesc_wipeData_secondaryUser" product="default" msgid="2788325512167208654">"Briše podatke ovog korisnika na ovom telefonu bez upozorenja."</string>
<string name="policylab_setGlobalProxy" msgid="215332221188670221">"Podesite globalni proksi server uređaja"</string>
<string name="policydesc_setGlobalProxy" msgid="7149665222705519604">"Podešava globalni proksi uređaja koji će se koristiti dok su smernice omogućene. Samo vlasnik uređaja može da podesi globalni proksi."</string>
@@ -2414,22 +2416,15 @@
<string name="satellite_notification_open_message" msgid="4149234979688273729">"Otvori Messages"</string>
<string name="satellite_notification_how_it_works" msgid="3132069321977520519">"Princip rada"</string>
<string name="unarchival_session_app_label" msgid="6811856981546348205">"Na čekanju..."</string>
- <!-- no translation found for fingerprint_dangling_notification_title (7362075195588639989) -->
+ <string name="fingerprint_dangling_notification_title" msgid="7362075195588639989">"Ponovo podesite otključavanje otiskom prsta"</string>
+ <!-- no translation found for fingerprint_dangling_notification_msg_1 (8517140433796229725) -->
<skip />
- <!-- no translation found for fingerprint_dangling_notification_msg_1 (6261149111900787302) -->
+ <!-- no translation found for fingerprint_dangling_notification_msg_2 (7578829498452127613) -->
<skip />
- <!-- no translation found for fingerprint_dangling_notification_msg_2 (7688302770424064884) -->
- <skip />
- <!-- no translation found for fingerprint_dangling_notification_msg_all_deleted_1 (2927018569542316055) -->
- <skip />
- <!-- no translation found for fingerprint_dangling_notification_msg_all_deleted_2 (6897989352716156176) -->
- <skip />
- <!-- no translation found for face_dangling_notification_title (947852541060975473) -->
- <skip />
- <!-- no translation found for face_dangling_notification_msg (8806849376915541655) -->
- <skip />
- <!-- no translation found for biometric_dangling_notification_action_set_up (8246885009807817961) -->
- <skip />
- <!-- no translation found for biometric_dangling_notification_action_not_now (8095249216864443491) -->
- <skip />
+ <string name="fingerprint_dangling_notification_msg_all_deleted_1" msgid="2927018569542316055">"<xliff:g id="FINGERPRINT">%s</xliff:g> nije funkcionisao i izbrisali smo ga. Ponovo ga podesite da biste telefon otključavali otiskom prsta."</string>
+ <string name="fingerprint_dangling_notification_msg_all_deleted_2" msgid="6897989352716156176">"<xliff:g id="FINGERPRINT_0">%1$s</xliff:g> i <xliff:g id="FINGERPRINT_1">%2$s</xliff:g> nisu funkcionisali i izbrisali smo ih. Ponovo ih podesite da biste telefon otključavali otiskom prsta."</string>
+ <string name="face_dangling_notification_title" msgid="947852541060975473">"Ponovo podesite otključavanje licem"</string>
+ <string name="face_dangling_notification_msg" msgid="8806849376915541655">"Vaš model lica nije funkcionisao i izbrisali smo ga. Ponovo ga podesite da biste telefon otključavali licem."</string>
+ <string name="biometric_dangling_notification_action_set_up" msgid="8246885009807817961">"Podesi"</string>
+ <string name="biometric_dangling_notification_action_not_now" msgid="8095249216864443491">"Ne sada"</string>
</resources>
diff --git a/core/res/res/values-be/strings.xml b/core/res/res/values-be/strings.xml
index 4cd150a..ec19c2d 100644
--- a/core/res/res/values-be/strings.xml
+++ b/core/res/res/values-be/strings.xml
@@ -285,6 +285,8 @@
<string name="global_action_voice_assist" msgid="6655788068555086695">"Галас. дапамога"</string>
<string name="global_action_lockdown" msgid="2475471405907902963">"Блакіроўка"</string>
<string name="status_bar_notification_info_overflow" msgid="3330152558746563475">"999+"</string>
+ <!-- no translation found for notification_compact_heads_up_reply (2425293958371284340) -->
+ <skip />
<string name="notification_hidden_text" msgid="2835519769868187223">"Новае апавяшчэнне"</string>
<string name="notification_channel_physical_keyboard" msgid="5417306456125988096">"Фізічная клавіятура"</string>
<string name="notification_channel_security" msgid="8516754650348238057">"Бяспека"</string>
@@ -2415,22 +2417,15 @@
<string name="satellite_notification_open_message" msgid="4149234979688273729">"Адкрыць Паведамленні"</string>
<string name="satellite_notification_how_it_works" msgid="3132069321977520519">"Як гэта працуе"</string>
<string name="unarchival_session_app_label" msgid="6811856981546348205">"У чаканні..."</string>
- <!-- no translation found for fingerprint_dangling_notification_title (7362075195588639989) -->
+ <string name="fingerprint_dangling_notification_title" msgid="7362075195588639989">"Наладзіць разблакіроўку адбіткам пальца паўторна"</string>
+ <!-- no translation found for fingerprint_dangling_notification_msg_1 (8517140433796229725) -->
<skip />
- <!-- no translation found for fingerprint_dangling_notification_msg_1 (6261149111900787302) -->
+ <!-- no translation found for fingerprint_dangling_notification_msg_2 (7578829498452127613) -->
<skip />
- <!-- no translation found for fingerprint_dangling_notification_msg_2 (7688302770424064884) -->
- <skip />
- <!-- no translation found for fingerprint_dangling_notification_msg_all_deleted_1 (2927018569542316055) -->
- <skip />
- <!-- no translation found for fingerprint_dangling_notification_msg_all_deleted_2 (6897989352716156176) -->
- <skip />
- <!-- no translation found for face_dangling_notification_title (947852541060975473) -->
- <skip />
- <!-- no translation found for face_dangling_notification_msg (8806849376915541655) -->
- <skip />
- <!-- no translation found for biometric_dangling_notification_action_set_up (8246885009807817961) -->
- <skip />
- <!-- no translation found for biometric_dangling_notification_action_not_now (8095249216864443491) -->
- <skip />
+ <string name="fingerprint_dangling_notification_msg_all_deleted_1" msgid="2927018569542316055">"Адбітак пальца \"<xliff:g id="FINGERPRINT">%s</xliff:g>\" не працаваў належным чынам і быў выдалены. Каб мець магчымасць разблакіраваць тэлефон з дапамогай адбітка пальца, наладзьце яго яшчэ раз."</string>
+ <string name="fingerprint_dangling_notification_msg_all_deleted_2" msgid="6897989352716156176">"Адбіткі пальцаў \"<xliff:g id="FINGERPRINT_0">%1$s</xliff:g>\" і \"<xliff:g id="FINGERPRINT_1">%2$s</xliff:g>\" не працавалі належным чынам і былі выдалены. Каб мець магчымасць разблакіраваць тэлефон з дапамогай адбітка пальца, наладзьце іх яшчэ раз."</string>
+ <string name="face_dangling_notification_title" msgid="947852541060975473">"Паўторна наладзьце распазнаванне твару"</string>
+ <string name="face_dangling_notification_msg" msgid="8806849376915541655">"Мадэль твару не працавала належным чынам і была выдалена. Каб мець магчымасць разблакіраваць тэлефон з дапамогай распазнавання твару, наладзьце яго яшчэ раз."</string>
+ <string name="biometric_dangling_notification_action_set_up" msgid="8246885009807817961">"Наладзіць"</string>
+ <string name="biometric_dangling_notification_action_not_now" msgid="8095249216864443491">"Не зараз"</string>
</resources>
diff --git a/core/res/res/values-bg/strings.xml b/core/res/res/values-bg/strings.xml
index eb2e920..1b73710 100644
--- a/core/res/res/values-bg/strings.xml
+++ b/core/res/res/values-bg/strings.xml
@@ -283,6 +283,8 @@
<string name="global_action_voice_assist" msgid="6655788068555086695">"Гласова помощ"</string>
<string name="global_action_lockdown" msgid="2475471405907902963">"Заключване"</string>
<string name="status_bar_notification_info_overflow" msgid="3330152558746563475">"999+"</string>
+ <!-- no translation found for notification_compact_heads_up_reply (2425293958371284340) -->
+ <skip />
<string name="notification_hidden_text" msgid="2835519769868187223">"Ново известие"</string>
<string name="notification_channel_physical_keyboard" msgid="5417306456125988096">"Физическа клавиатура"</string>
<string name="notification_channel_security" msgid="8516754650348238057">"Сигурност"</string>
@@ -1726,7 +1728,7 @@
<string name="accessibility_shortcut_off" msgid="3651336255403648739">"Без включване"</string>
<string name="accessibility_shortcut_menu_item_status_on" msgid="6608392117189732543">"ВКЛ."</string>
<string name="accessibility_shortcut_menu_item_status_off" msgid="5531598275559472393">"ИЗКЛ."</string>
- <string name="accessibility_enable_service_title" msgid="3931558336268541484">"Искате ли да разрешите на <xliff:g id="SERVICE">%1$s</xliff:g> да има пълен контрол над устройството ви?"</string>
+ <string name="accessibility_enable_service_title" msgid="3931558336268541484">"Искате ли да разрешите на „<xliff:g id="SERVICE">%1$s</xliff:g>“ да има пълен контрол над устройството ви?"</string>
<string name="accessibility_service_warning_description" msgid="291674995220940133">"Пълният контрол е подходящ за приложенията, които помагат на потребителите със специални нужди, но не и за повечето приложения."</string>
<string name="accessibility_service_screen_control_title" msgid="190017412626919776">"Преглед и управление на екрана"</string>
<string name="accessibility_service_screen_control_description" msgid="6946315917771791525">"Услугата може да чете цялото съдържание на екрана и да показва такова върху други приложения."</string>
@@ -2413,22 +2415,15 @@
<string name="satellite_notification_open_message" msgid="4149234979688273729">"Отваряне на Messages"</string>
<string name="satellite_notification_how_it_works" msgid="3132069321977520519">"Начин на работа"</string>
<string name="unarchival_session_app_label" msgid="6811856981546348205">"Изчаква..."</string>
- <!-- no translation found for fingerprint_dangling_notification_title (7362075195588639989) -->
+ <string name="fingerprint_dangling_notification_title" msgid="7362075195588639989">"Повторно настройване на „Отключване с отпечатък“"</string>
+ <!-- no translation found for fingerprint_dangling_notification_msg_1 (8517140433796229725) -->
<skip />
- <!-- no translation found for fingerprint_dangling_notification_msg_1 (6261149111900787302) -->
+ <!-- no translation found for fingerprint_dangling_notification_msg_2 (7578829498452127613) -->
<skip />
- <!-- no translation found for fingerprint_dangling_notification_msg_2 (7688302770424064884) -->
- <skip />
- <!-- no translation found for fingerprint_dangling_notification_msg_all_deleted_1 (2927018569542316055) -->
- <skip />
- <!-- no translation found for fingerprint_dangling_notification_msg_all_deleted_2 (6897989352716156176) -->
- <skip />
- <!-- no translation found for face_dangling_notification_title (947852541060975473) -->
- <skip />
- <!-- no translation found for face_dangling_notification_msg (8806849376915541655) -->
- <skip />
- <!-- no translation found for biometric_dangling_notification_action_set_up (8246885009807817961) -->
- <skip />
- <!-- no translation found for biometric_dangling_notification_action_not_now (8095249216864443491) -->
- <skip />
+ <string name="fingerprint_dangling_notification_msg_all_deleted_1" msgid="2927018569542316055">"Отпечатъкът „<xliff:g id="FINGERPRINT">%s</xliff:g>“ бе изтрит, защото не работеше добре. Настройте го отново, за да отключвате телефона си с отпечатък."</string>
+ <string name="fingerprint_dangling_notification_msg_all_deleted_2" msgid="6897989352716156176">"Отпечатъците „<xliff:g id="FINGERPRINT_0">%1$s</xliff:g>“ и „<xliff:g id="FINGERPRINT_1">%2$s</xliff:g>“ бяха изтрити, защото не работеха добре. Настройте ги отново, за да отключвате телефона си с отпечатък."</string>
+ <string name="face_dangling_notification_title" msgid="947852541060975473">"Повторно настройване на „Отключване с лице“"</string>
+ <string name="face_dangling_notification_msg" msgid="8806849376915541655">"Моделът на лицето ви бе изтрит, защото не работеше добре. Настройте го отново, за да отключвате телефона си с лице."</string>
+ <string name="biometric_dangling_notification_action_set_up" msgid="8246885009807817961">"Настройване"</string>
+ <string name="biometric_dangling_notification_action_not_now" msgid="8095249216864443491">"Не сега"</string>
</resources>
diff --git a/core/res/res/values-bn/strings.xml b/core/res/res/values-bn/strings.xml
index 6618127..0e32bbe6 100644
--- a/core/res/res/values-bn/strings.xml
+++ b/core/res/res/values-bn/strings.xml
@@ -283,6 +283,8 @@
<string name="global_action_voice_assist" msgid="6655788068555086695">"ভয়েস সহায়তা"</string>
<string name="global_action_lockdown" msgid="2475471405907902963">"লকডাউন"</string>
<string name="status_bar_notification_info_overflow" msgid="3330152558746563475">"৯৯৯+"</string>
+ <!-- no translation found for notification_compact_heads_up_reply (2425293958371284340) -->
+ <skip />
<string name="notification_hidden_text" msgid="2835519769868187223">"নতুন বিজ্ঞপ্তি"</string>
<string name="notification_channel_physical_keyboard" msgid="5417306456125988096">"ফিজিক্যাল কীবোর্ড"</string>
<string name="notification_channel_security" msgid="8516754650348238057">"নিরাপত্তা"</string>
@@ -644,7 +646,7 @@
<string name="biometric_or_screen_lock_dialog_default_subtitle" msgid="159539678371552009">"চালিয়ে যেতে আপনার বায়োমেট্রিক্স বা স্ক্রিন লক ব্যবহার করুন"</string>
<string name="biometric_error_hw_unavailable" msgid="2494077380540615216">"বায়োমেট্রিক হার্ডওয়্যার পাওয়া যাবে না"</string>
<string name="biometric_error_user_canceled" msgid="6732303949695293730">"যাচাইকরণ বাতিল হয়েছে"</string>
- <string name="biometric_not_recognized" msgid="5106687642694635888">"স্বীকৃত নয়"</string>
+ <string name="biometric_not_recognized" msgid="5106687642694635888">"শনাক্ত করা যায়নি"</string>
<string name="biometric_face_not_recognized" msgid="5535599455744525200">"ফেস চেনা যায়নি"</string>
<string name="biometric_error_canceled" msgid="8266582404844179778">"যাচাইকরণ বাতিল হয়েছে"</string>
<string name="biometric_error_device_not_secured" msgid="3129845065043995924">"পিন, প্যাটার্ন অথবা পাসওয়ার্ড সেট করা নেই"</string>
@@ -2413,22 +2415,15 @@
<string name="satellite_notification_open_message" msgid="4149234979688273729">"Messages খুলুন"</string>
<string name="satellite_notification_how_it_works" msgid="3132069321977520519">"এটি কীভাবে কাজ করে"</string>
<string name="unarchival_session_app_label" msgid="6811856981546348205">"বাকি আছে…"</string>
- <!-- no translation found for fingerprint_dangling_notification_title (7362075195588639989) -->
+ <string name="fingerprint_dangling_notification_title" msgid="7362075195588639989">"\'ফিঙ্গারপ্রিন্ট আনলক\' আবার সেট-আপ করুন"</string>
+ <!-- no translation found for fingerprint_dangling_notification_msg_1 (8517140433796229725) -->
<skip />
- <!-- no translation found for fingerprint_dangling_notification_msg_1 (6261149111900787302) -->
+ <!-- no translation found for fingerprint_dangling_notification_msg_2 (7578829498452127613) -->
<skip />
- <!-- no translation found for fingerprint_dangling_notification_msg_2 (7688302770424064884) -->
- <skip />
- <!-- no translation found for fingerprint_dangling_notification_msg_all_deleted_1 (2927018569542316055) -->
- <skip />
- <!-- no translation found for fingerprint_dangling_notification_msg_all_deleted_2 (6897989352716156176) -->
- <skip />
- <!-- no translation found for face_dangling_notification_title (947852541060975473) -->
- <skip />
- <!-- no translation found for face_dangling_notification_msg (8806849376915541655) -->
- <skip />
- <!-- no translation found for biometric_dangling_notification_action_set_up (8246885009807817961) -->
- <skip />
- <!-- no translation found for biometric_dangling_notification_action_not_now (8095249216864443491) -->
- <skip />
+ <string name="fingerprint_dangling_notification_msg_all_deleted_1" msgid="2927018569542316055">"<xliff:g id="FINGERPRINT">%s</xliff:g> ভালোভাবে কাজ করছিল না বলে সেটি মুছে ফেলা হয়েছে। ফিঙ্গারপ্রিন্ট ব্যবহার করে আপনার ফোন আনলক করতে হলে এটি আবার সেট-আপ করুন।"</string>
+ <string name="fingerprint_dangling_notification_msg_all_deleted_2" msgid="6897989352716156176">"<xliff:g id="FINGERPRINT_0">%1$s</xliff:g> ও <xliff:g id="FINGERPRINT_1">%2$s</xliff:g> ভালোভাবে কাজ করছিল না বলে মুছে ফেলা হয়েছে। ফিঙ্গারপ্রিন্ট ব্যবহার করে আপনার ফোন আনলক করতে হলে সেগুলি আবার সেট-আপ করুন।"</string>
+ <string name="face_dangling_notification_title" msgid="947852541060975473">"\'ফেস আনলক\' আবার সেট-আপ করুন"</string>
+ <string name="face_dangling_notification_msg" msgid="8806849376915541655">"আপনার ফেস মডেল ভালোভাবে কাজ করছিল না বলে সেটি মুছে ফেলা হয়েছে। ফেস ব্যবহার করে আপনার ফোন আনলক করতে হলে এটি আবার সেট-আপ করুন।"</string>
+ <string name="biometric_dangling_notification_action_set_up" msgid="8246885009807817961">"সেট-আপ করুন"</string>
+ <string name="biometric_dangling_notification_action_not_now" msgid="8095249216864443491">"এখন নয়"</string>
</resources>
diff --git a/core/res/res/values-bs/strings.xml b/core/res/res/values-bs/strings.xml
index 4f058bf..ddc5153 100644
--- a/core/res/res/values-bs/strings.xml
+++ b/core/res/res/values-bs/strings.xml
@@ -284,6 +284,8 @@
<string name="global_action_voice_assist" msgid="6655788068555086695">"Glasovna pomoć"</string>
<string name="global_action_lockdown" msgid="2475471405907902963">"Zaključaj"</string>
<string name="status_bar_notification_info_overflow" msgid="3330152558746563475">"999+"</string>
+ <!-- no translation found for notification_compact_heads_up_reply (2425293958371284340) -->
+ <skip />
<string name="notification_hidden_text" msgid="2835519769868187223">"Novo obavještenje"</string>
<string name="notification_channel_physical_keyboard" msgid="5417306456125988096">"Fizička tastatura"</string>
<string name="notification_channel_security" msgid="8516754650348238057">"Sigurnost"</string>
@@ -2414,22 +2416,15 @@
<string name="satellite_notification_open_message" msgid="4149234979688273729">"Otvorite Messages"</string>
<string name="satellite_notification_how_it_works" msgid="3132069321977520519">"Kako ovo funkcionira"</string>
<string name="unarchival_session_app_label" msgid="6811856981546348205">"Na čekanju…"</string>
- <!-- no translation found for fingerprint_dangling_notification_title (7362075195588639989) -->
+ <string name="fingerprint_dangling_notification_title" msgid="7362075195588639989">"Ponovo postavite otključavanje otiskom prsta"</string>
+ <!-- no translation found for fingerprint_dangling_notification_msg_1 (8517140433796229725) -->
<skip />
- <!-- no translation found for fingerprint_dangling_notification_msg_1 (6261149111900787302) -->
+ <!-- no translation found for fingerprint_dangling_notification_msg_2 (7578829498452127613) -->
<skip />
- <!-- no translation found for fingerprint_dangling_notification_msg_2 (7688302770424064884) -->
- <skip />
- <!-- no translation found for fingerprint_dangling_notification_msg_all_deleted_1 (2927018569542316055) -->
- <skip />
- <!-- no translation found for fingerprint_dangling_notification_msg_all_deleted_2 (6897989352716156176) -->
- <skip />
- <!-- no translation found for face_dangling_notification_title (947852541060975473) -->
- <skip />
- <!-- no translation found for face_dangling_notification_msg (8806849376915541655) -->
- <skip />
- <!-- no translation found for biometric_dangling_notification_action_set_up (8246885009807817961) -->
- <skip />
- <!-- no translation found for biometric_dangling_notification_action_not_now (8095249216864443491) -->
- <skip />
+ <string name="fingerprint_dangling_notification_msg_all_deleted_1" msgid="2927018569542316055">"Otisak prsta <xliff:g id="FINGERPRINT">%s</xliff:g> nije dobro funkcionirao, pa je izbrisan. Postavite ga ponovo da otključavate telefon otiskom prsta."</string>
+ <string name="fingerprint_dangling_notification_msg_all_deleted_2" msgid="6897989352716156176">"Otisci prstiju <xliff:g id="FINGERPRINT_0">%1$s</xliff:g> i <xliff:g id="FINGERPRINT_1">%2$s</xliff:g> nisu dobro funkcionirali, pa su izbrisani. Postavite ih ponovo da otključavate telefon otiskom prsta."</string>
+ <string name="face_dangling_notification_title" msgid="947852541060975473">"Ponovo postavite otključavanje licem"</string>
+ <string name="face_dangling_notification_msg" msgid="8806849376915541655">"Model lica nije dobro funkcionirao, pa je izbrisan. Postavite ga ponovo da otključavate telefon licem."</string>
+ <string name="biometric_dangling_notification_action_set_up" msgid="8246885009807817961">"Postavite"</string>
+ <string name="biometric_dangling_notification_action_not_now" msgid="8095249216864443491">"Ne sada"</string>
</resources>
diff --git a/core/res/res/values-ca/strings.xml b/core/res/res/values-ca/strings.xml
index 77fd6b8..1fd814d 100644
--- a/core/res/res/values-ca/strings.xml
+++ b/core/res/res/values-ca/strings.xml
@@ -85,7 +85,7 @@
<string name="NetworkPreferenceSwitchTitle" msgid="1008329951315753038">"No es pot accedir a la xarxa mòbil"</string>
<string name="NetworkPreferenceSwitchSummary" msgid="2086506181486324860">"Prova de canviar de xarxa preferent. Toca per canviar-la."</string>
<string name="EmergencyCallWarningTitle" msgid="1615688002899152860">"Les trucades d\'emergència no estan disponibles"</string>
- <string name="EmergencyCallWarningSummary" msgid="9102799172089265268">"Per poder fer trucades d\'emergència, cal tenir connexió a una xarxa mòbil"</string>
+ <string name="EmergencyCallWarningSummary" msgid="9102799172089265268">"Per poder fer trucades d\'emergència, cal tenir connexió de xarxa mòbil"</string>
<string name="notification_channel_network_alert" msgid="4788053066033851841">"Alertes"</string>
<string name="notification_channel_call_forward" msgid="8230490317314272406">"Desviació de trucades"</string>
<string name="notification_channel_emergency_callback" msgid="54074839059123159">"Mode de devolució de trucada d\'emergència"</string>
@@ -284,6 +284,8 @@
<string name="global_action_voice_assist" msgid="6655788068555086695">"Assist. per veu"</string>
<string name="global_action_lockdown" msgid="2475471405907902963">"Bloqueig de seguretat"</string>
<string name="status_bar_notification_info_overflow" msgid="3330152558746563475">"+999"</string>
+ <!-- no translation found for notification_compact_heads_up_reply (2425293958371284340) -->
+ <skip />
<string name="notification_hidden_text" msgid="2835519769868187223">"Notificació nova"</string>
<string name="notification_channel_physical_keyboard" msgid="5417306456125988096">"Teclat físic"</string>
<string name="notification_channel_security" msgid="8516754650348238057">"Seguretat"</string>
@@ -1905,8 +1907,8 @@
<string name="package_updated_device_owner" msgid="7560272363805506941">"Actualitzat per l\'administrador"</string>
<string name="package_deleted_device_owner" msgid="2292335928930293023">"Suprimit per l\'administrador"</string>
<string name="confirm_battery_saver" msgid="5247976246208245754">"D\'acord"</string>
- <string name="battery_saver_description_with_learn_more" msgid="5444908404021316250">"Estalvi de bateria activa el tema fosc i limita o desactiva l\'activitat en segon pla, alguns efectes visuals, determinades funcions i algunes connexions a la xarxa."</string>
- <string name="battery_saver_description" msgid="8518809702138617167">"Estalvi de bateria activa el tema fosc i limita o desactiva l\'activitat en segon pla, alguns efectes visuals, determinades funcions i algunes connexions a la xarxa."</string>
+ <string name="battery_saver_description_with_learn_more" msgid="5444908404021316250">"Estalvi de bateria activa el tema fosc i limita o desactiva l\'activitat en segon pla, alguns efectes visuals, determinades funcions i algunes connexions de xarxa."</string>
+ <string name="battery_saver_description" msgid="8518809702138617167">"Estalvi de bateria activa el tema fosc i limita o desactiva l\'activitat en segon pla, alguns efectes visuals, determinades funcions i algunes connexions de xarxa."</string>
<string name="data_saver_description" msgid="4995164271550590517">"Per reduir l\'ús de dades, la funció Estalvi de dades evita que determinades aplicacions enviïn o rebin dades en segon pla. L\'aplicació que estiguis fent servir podrà accedir a les dades, però menys sovint. Això vol dir, per exemple, que les imatges no es mostraran fins que no les toquis."</string>
<string name="data_saver_enable_title" msgid="7080620065745260137">"Vols activar l\'Estalvi de dades?"</string>
<string name="data_saver_enable_button" msgid="4399405762586419726">"Activa"</string>
@@ -2414,22 +2416,15 @@
<string name="satellite_notification_open_message" msgid="4149234979688273729">"Obre Missatges"</string>
<string name="satellite_notification_how_it_works" msgid="3132069321977520519">"Com funciona"</string>
<string name="unarchival_session_app_label" msgid="6811856981546348205">"Pendent..."</string>
- <!-- no translation found for fingerprint_dangling_notification_title (7362075195588639989) -->
+ <string name="fingerprint_dangling_notification_title" msgid="7362075195588639989">"Torna a configurar Desbloqueig amb empremta digital"</string>
+ <!-- no translation found for fingerprint_dangling_notification_msg_1 (8517140433796229725) -->
<skip />
- <!-- no translation found for fingerprint_dangling_notification_msg_1 (6261149111900787302) -->
+ <!-- no translation found for fingerprint_dangling_notification_msg_2 (7578829498452127613) -->
<skip />
- <!-- no translation found for fingerprint_dangling_notification_msg_2 (7688302770424064884) -->
- <skip />
- <!-- no translation found for fingerprint_dangling_notification_msg_all_deleted_1 (2927018569542316055) -->
- <skip />
- <!-- no translation found for fingerprint_dangling_notification_msg_all_deleted_2 (6897989352716156176) -->
- <skip />
- <!-- no translation found for face_dangling_notification_title (947852541060975473) -->
- <skip />
- <!-- no translation found for face_dangling_notification_msg (8806849376915541655) -->
- <skip />
- <!-- no translation found for biometric_dangling_notification_action_set_up (8246885009807817961) -->
- <skip />
- <!-- no translation found for biometric_dangling_notification_action_not_now (8095249216864443491) -->
- <skip />
+ <string name="fingerprint_dangling_notification_msg_all_deleted_1" msgid="2927018569542316055">"<xliff:g id="FINGERPRINT">%s</xliff:g> no funcionava correctament i s\'ha suprimit. Torna a configurar-la per desbloquejar el telèfon amb l\'empremta digital."</string>
+ <string name="fingerprint_dangling_notification_msg_all_deleted_2" msgid="6897989352716156176">"<xliff:g id="FINGERPRINT_0">%1$s</xliff:g> i <xliff:g id="FINGERPRINT_1">%2$s</xliff:g> no funcionaven correctament i s\'han suprimit. Torna a configurar-les per desbloquejar el telèfon amb l\'empremta digital."</string>
+ <string name="face_dangling_notification_title" msgid="947852541060975473">"Torna a configurar Desbloqueig facial"</string>
+ <string name="face_dangling_notification_msg" msgid="8806849376915541655">"El teu model facial no funcionava correctament i s\'ha suprimit. Torna a configurar-lo per desbloquejar el telèfon amb la cara."</string>
+ <string name="biometric_dangling_notification_action_set_up" msgid="8246885009807817961">"Configura"</string>
+ <string name="biometric_dangling_notification_action_not_now" msgid="8095249216864443491">"Ara no"</string>
</resources>
diff --git a/core/res/res/values-cs/strings.xml b/core/res/res/values-cs/strings.xml
index 5615f79..ec7bb29 100644
--- a/core/res/res/values-cs/strings.xml
+++ b/core/res/res/values-cs/strings.xml
@@ -285,6 +285,8 @@
<string name="global_action_voice_assist" msgid="6655788068555086695">"Hlas. asistence"</string>
<string name="global_action_lockdown" msgid="2475471405907902963">"Zamknout"</string>
<string name="status_bar_notification_info_overflow" msgid="3330152558746563475">"999+"</string>
+ <!-- no translation found for notification_compact_heads_up_reply (2425293958371284340) -->
+ <skip />
<string name="notification_hidden_text" msgid="2835519769868187223">"Nové oznámení"</string>
<string name="notification_channel_physical_keyboard" msgid="5417306456125988096">"Fyzická klávesnice"</string>
<string name="notification_channel_security" msgid="8516754650348238057">"Zabezpečení"</string>
@@ -2415,22 +2417,15 @@
<string name="satellite_notification_open_message" msgid="4149234979688273729">"Otevřít Zprávy"</string>
<string name="satellite_notification_how_it_works" msgid="3132069321977520519">"Jak to funguje"</string>
<string name="unarchival_session_app_label" msgid="6811856981546348205">"Čeká na vyřízení…"</string>
- <!-- no translation found for fingerprint_dangling_notification_title (7362075195588639989) -->
+ <string name="fingerprint_dangling_notification_title" msgid="7362075195588639989">"Opětovné nastavení odemknutí otiskem prstu"</string>
+ <!-- no translation found for fingerprint_dangling_notification_msg_1 (8517140433796229725) -->
<skip />
- <!-- no translation found for fingerprint_dangling_notification_msg_1 (6261149111900787302) -->
+ <!-- no translation found for fingerprint_dangling_notification_msg_2 (7578829498452127613) -->
<skip />
- <!-- no translation found for fingerprint_dangling_notification_msg_2 (7688302770424064884) -->
- <skip />
- <!-- no translation found for fingerprint_dangling_notification_msg_all_deleted_1 (2927018569542316055) -->
- <skip />
- <!-- no translation found for fingerprint_dangling_notification_msg_all_deleted_2 (6897989352716156176) -->
- <skip />
- <!-- no translation found for face_dangling_notification_title (947852541060975473) -->
- <skip />
- <!-- no translation found for face_dangling_notification_msg (8806849376915541655) -->
- <skip />
- <!-- no translation found for biometric_dangling_notification_action_set_up (8246885009807817961) -->
- <skip />
- <!-- no translation found for biometric_dangling_notification_action_not_now (8095249216864443491) -->
- <skip />
+ <string name="fingerprint_dangling_notification_msg_all_deleted_1" msgid="2927018569542316055">"<xliff:g id="FINGERPRINT">%s</xliff:g> nefungoval správně a byl vymazán. Pokud chcete telefon odemykat otiskem prstu, nastavte jej znovu."</string>
+ <string name="fingerprint_dangling_notification_msg_all_deleted_2" msgid="6897989352716156176">"<xliff:g id="FINGERPRINT_0">%1$s</xliff:g> a <xliff:g id="FINGERPRINT_1">%2$s</xliff:g> nefungovaly správně a byly vymazány. Pokud chcete telefon odemykat otiskem prstu, nastavte je znovu."</string>
+ <string name="face_dangling_notification_title" msgid="947852541060975473">"Nastavte odemknutí obličejem znovu"</string>
+ <string name="face_dangling_notification_msg" msgid="8806849376915541655">"Váš model obličeje nefungoval správně a byl vymazán. Pokud chcete telefon odemykat obličejem, nastavte jej znovu."</string>
+ <string name="biometric_dangling_notification_action_set_up" msgid="8246885009807817961">"Nastavit"</string>
+ <string name="biometric_dangling_notification_action_not_now" msgid="8095249216864443491">"Teď ne"</string>
</resources>
diff --git a/core/res/res/values-da/strings.xml b/core/res/res/values-da/strings.xml
index fd04e42..ef412628 100644
--- a/core/res/res/values-da/strings.xml
+++ b/core/res/res/values-da/strings.xml
@@ -283,6 +283,8 @@
<string name="global_action_voice_assist" msgid="6655788068555086695">"Taleassistent"</string>
<string name="global_action_lockdown" msgid="2475471405907902963">"Låsning"</string>
<string name="status_bar_notification_info_overflow" msgid="3330152558746563475">"999+"</string>
+ <!-- no translation found for notification_compact_heads_up_reply (2425293958371284340) -->
+ <skip />
<string name="notification_hidden_text" msgid="2835519769868187223">"Ny notifikation"</string>
<string name="notification_channel_physical_keyboard" msgid="5417306456125988096">"Fysisk tastatur"</string>
<string name="notification_channel_security" msgid="8516754650348238057">"Sikkerhed"</string>
@@ -2413,22 +2415,15 @@
<string name="satellite_notification_open_message" msgid="4149234979688273729">"Åbn Beskeder"</string>
<string name="satellite_notification_how_it_works" msgid="3132069321977520519">"Sådan fungerer det"</string>
<string name="unarchival_session_app_label" msgid="6811856981546348205">"Afventer…"</string>
- <!-- no translation found for fingerprint_dangling_notification_title (7362075195588639989) -->
+ <string name="fingerprint_dangling_notification_title" msgid="7362075195588639989">"Konfigurer fingeroplåsning igen"</string>
+ <!-- no translation found for fingerprint_dangling_notification_msg_1 (8517140433796229725) -->
<skip />
- <!-- no translation found for fingerprint_dangling_notification_msg_1 (6261149111900787302) -->
+ <!-- no translation found for fingerprint_dangling_notification_msg_2 (7578829498452127613) -->
<skip />
- <!-- no translation found for fingerprint_dangling_notification_msg_2 (7688302770424064884) -->
- <skip />
- <!-- no translation found for fingerprint_dangling_notification_msg_all_deleted_1 (2927018569542316055) -->
- <skip />
- <!-- no translation found for fingerprint_dangling_notification_msg_all_deleted_2 (6897989352716156176) -->
- <skip />
- <!-- no translation found for face_dangling_notification_title (947852541060975473) -->
- <skip />
- <!-- no translation found for face_dangling_notification_msg (8806849376915541655) -->
- <skip />
- <!-- no translation found for biometric_dangling_notification_action_set_up (8246885009807817961) -->
- <skip />
- <!-- no translation found for biometric_dangling_notification_action_not_now (8095249216864443491) -->
- <skip />
+ <string name="fingerprint_dangling_notification_msg_all_deleted_1" msgid="2927018569542316055">"<xliff:g id="FINGERPRINT">%s</xliff:g> virkede ikke optimalt og er derfor slettet. Konfigurer den igen for at bruge fingeroplåsning på din telefon."</string>
+ <string name="fingerprint_dangling_notification_msg_all_deleted_2" msgid="6897989352716156176">"<xliff:g id="FINGERPRINT_0">%1$s</xliff:g> og <xliff:g id="FINGERPRINT_1">%2$s</xliff:g> virkede ikke optimalt og er derfor slettet. Konfigurer dem igen for at bruge dit fingeraftryk til at låse din telefon op."</string>
+ <string name="face_dangling_notification_title" msgid="947852541060975473">"Konfigurer ansigtsoplåsning igen"</string>
+ <string name="face_dangling_notification_msg" msgid="8806849376915541655">"Din ansigtsmodel virkede ikke optimalt og er derfor slettet. Konfigurer den igen for at bruge ansigtsoplåsning på din telefon."</string>
+ <string name="biometric_dangling_notification_action_set_up" msgid="8246885009807817961">"Konfigurer"</string>
+ <string name="biometric_dangling_notification_action_not_now" msgid="8095249216864443491">"Ikke nu"</string>
</resources>
diff --git a/core/res/res/values-de/strings.xml b/core/res/res/values-de/strings.xml
index 630ec75..1df8954 100644
--- a/core/res/res/values-de/strings.xml
+++ b/core/res/res/values-de/strings.xml
@@ -283,6 +283,8 @@
<string name="global_action_voice_assist" msgid="6655788068555086695">"Sprachassistent"</string>
<string name="global_action_lockdown" msgid="2475471405907902963">"Sperren"</string>
<string name="status_bar_notification_info_overflow" msgid="3330152558746563475">"999+"</string>
+ <!-- no translation found for notification_compact_heads_up_reply (2425293958371284340) -->
+ <skip />
<string name="notification_hidden_text" msgid="2835519769868187223">"Neue Benachrichtigung"</string>
<string name="notification_channel_physical_keyboard" msgid="5417306456125988096">"Physische Tastatur"</string>
<string name="notification_channel_security" msgid="8516754650348238057">"Sicherheit"</string>
@@ -1997,7 +1999,7 @@
<string name="work_mode_emergency_call_button" msgid="6818855962881612322">"Notruf"</string>
<string name="set_up_screen_lock_title" msgid="8346083801616474030">"Displaysperre einrichten"</string>
<string name="set_up_screen_lock_action_label" msgid="2687634803649209367">"Displaysperre einrichten"</string>
- <string name="private_space_set_up_screen_lock_message" msgid="1109956797005149814">"Richte zur Nutzung des privaten Bereichs auf dem Gerät die Displaysperre ein"</string>
+ <string name="private_space_set_up_screen_lock_message" msgid="1109956797005149814">"Richte zur Nutzung des vertraulichen Profils auf dem Gerät die Displaysperre ein"</string>
<string name="app_blocked_title" msgid="7353262160455028160">"App ist nicht verfügbar"</string>
<string name="app_blocked_message" msgid="542972921087873023">"<xliff:g id="APP_NAME">%1$s</xliff:g> ist derzeit nicht verfügbar."</string>
<string name="app_streaming_blocked_title" msgid="6090945835898766139">"<xliff:g id="ACTIVITY">%1$s</xliff:g> nicht verfügbar"</string>
@@ -2413,22 +2415,15 @@
<string name="satellite_notification_open_message" msgid="4149234979688273729">"Messages öffnen"</string>
<string name="satellite_notification_how_it_works" msgid="3132069321977520519">"So funktionierts"</string>
<string name="unarchival_session_app_label" msgid="6811856981546348205">"Ausstehend…"</string>
- <!-- no translation found for fingerprint_dangling_notification_title (7362075195588639989) -->
+ <string name="fingerprint_dangling_notification_title" msgid="7362075195588639989">"Entsperrung per Fingerabdruck neu einrichten"</string>
+ <!-- no translation found for fingerprint_dangling_notification_msg_1 (8517140433796229725) -->
<skip />
- <!-- no translation found for fingerprint_dangling_notification_msg_1 (6261149111900787302) -->
+ <!-- no translation found for fingerprint_dangling_notification_msg_2 (7578829498452127613) -->
<skip />
- <!-- no translation found for fingerprint_dangling_notification_msg_2 (7688302770424064884) -->
- <skip />
- <!-- no translation found for fingerprint_dangling_notification_msg_all_deleted_1 (2927018569542316055) -->
- <skip />
- <!-- no translation found for fingerprint_dangling_notification_msg_all_deleted_2 (6897989352716156176) -->
- <skip />
- <!-- no translation found for face_dangling_notification_title (947852541060975473) -->
- <skip />
- <!-- no translation found for face_dangling_notification_msg (8806849376915541655) -->
- <skip />
- <!-- no translation found for biometric_dangling_notification_action_set_up (8246885009807817961) -->
- <skip />
- <!-- no translation found for biometric_dangling_notification_action_not_now (8095249216864443491) -->
- <skip />
+ <string name="fingerprint_dangling_notification_msg_all_deleted_1" msgid="2927018569542316055">"<xliff:g id="FINGERPRINT">%s</xliff:g> hat nicht einwandfrei funktioniert und wurde gelöscht. Richte ihn noch einmal ein, um dein Smartphone per Fingerabdruck zu entsperren."</string>
+ <string name="fingerprint_dangling_notification_msg_all_deleted_2" msgid="6897989352716156176">"<xliff:g id="FINGERPRINT_0">%1$s</xliff:g> und <xliff:g id="FINGERPRINT_1">%2$s</xliff:g> haben nicht einwandfrei funktioniert und wurden gelöscht. Richte sie noch einmal ein, um dein Smartphone per Fingerabdruck zu entsperren."</string>
+ <string name="face_dangling_notification_title" msgid="947852541060975473">"Entsperrung per Gesichtserkennung neu einrichten"</string>
+ <string name="face_dangling_notification_msg" msgid="8806849376915541655">"Dein Gesichtsmodell hat nicht einwandfrei funktioniert und wurde gelöscht. Richte es noch einmal ein, um dein Smartphone per Gesichtserkennung zu entsperren."</string>
+ <string name="biometric_dangling_notification_action_set_up" msgid="8246885009807817961">"Einrichten"</string>
+ <string name="biometric_dangling_notification_action_not_now" msgid="8095249216864443491">"Nicht jetzt"</string>
</resources>
diff --git a/core/res/res/values-el/strings.xml b/core/res/res/values-el/strings.xml
index 5490131..3c7d167 100644
--- a/core/res/res/values-el/strings.xml
+++ b/core/res/res/values-el/strings.xml
@@ -283,6 +283,8 @@
<string name="global_action_voice_assist" msgid="6655788068555086695">"Φων.υποβοηθ."</string>
<string name="global_action_lockdown" msgid="2475471405907902963">"Κλείδωμα"</string>
<string name="status_bar_notification_info_overflow" msgid="3330152558746563475">"999+"</string>
+ <!-- no translation found for notification_compact_heads_up_reply (2425293958371284340) -->
+ <skip />
<string name="notification_hidden_text" msgid="2835519769868187223">"Νέα ειδοποίηση"</string>
<string name="notification_channel_physical_keyboard" msgid="5417306456125988096">"Κανονικό πληκτρολόγιο"</string>
<string name="notification_channel_security" msgid="8516754650348238057">"Ασφάλεια"</string>
@@ -2402,7 +2404,7 @@
<string name="profile_label_test" msgid="9168641926186071947">"Δοκιμή"</string>
<string name="profile_label_communal" msgid="8743921499944800427">"Κοινόχρηστο"</string>
<string name="accessibility_label_managed_profile" msgid="3366526886209832641">"Προφίλ εργασίας"</string>
- <string name="accessibility_label_private_profile" msgid="1436459319135548969">"Απόρρητος χώρος"</string>
+ <string name="accessibility_label_private_profile" msgid="1436459319135548969">"Ιδιωτικός χώρος"</string>
<string name="accessibility_label_clone_profile" msgid="7579118375042398784">"Κλώνος"</string>
<string name="accessibility_label_communal_profile" msgid="1437173163111334791">"Κοινόχρηστο"</string>
<string name="redacted_notification_message" msgid="1520587845842228816">"Έγινε απόκρυψη της ειδοποίησης ευαίσθητου περιεχομένου"</string>
@@ -2413,22 +2415,15 @@
<string name="satellite_notification_open_message" msgid="4149234979688273729">"Άνοιγμα Messages"</string>
<string name="satellite_notification_how_it_works" msgid="3132069321977520519">"Πώς λειτουργεί"</string>
<string name="unarchival_session_app_label" msgid="6811856981546348205">"Σε εκκρεμότητα…"</string>
- <!-- no translation found for fingerprint_dangling_notification_title (7362075195588639989) -->
+ <string name="fingerprint_dangling_notification_title" msgid="7362075195588639989">"Επαναρρύθμιση λειτουργίας Ξεκλείδωμα με δακτυλικό αποτύπωμα"</string>
+ <!-- no translation found for fingerprint_dangling_notification_msg_1 (8517140433796229725) -->
<skip />
- <!-- no translation found for fingerprint_dangling_notification_msg_1 (6261149111900787302) -->
+ <!-- no translation found for fingerprint_dangling_notification_msg_2 (7578829498452127613) -->
<skip />
- <!-- no translation found for fingerprint_dangling_notification_msg_2 (7688302770424064884) -->
- <skip />
- <!-- no translation found for fingerprint_dangling_notification_msg_all_deleted_1 (2927018569542316055) -->
- <skip />
- <!-- no translation found for fingerprint_dangling_notification_msg_all_deleted_2 (6897989352716156176) -->
- <skip />
- <!-- no translation found for face_dangling_notification_title (947852541060975473) -->
- <skip />
- <!-- no translation found for face_dangling_notification_msg (8806849376915541655) -->
- <skip />
- <!-- no translation found for biometric_dangling_notification_action_set_up (8246885009807817961) -->
- <skip />
- <!-- no translation found for biometric_dangling_notification_action_not_now (8095249216864443491) -->
- <skip />
+ <string name="fingerprint_dangling_notification_msg_all_deleted_1" msgid="2927018569542316055">"Το δακτυλικό αποτύπωμα <xliff:g id="FINGERPRINT">%s</xliff:g> δεν λειτουργούσε καλά και διαγράφηκε. Ρυθμίστε το ξανά για να ξεκλειδώνετε το τηλέφωνο με το δακτυλικό αποτύπωμά σας."</string>
+ <string name="fingerprint_dangling_notification_msg_all_deleted_2" msgid="6897989352716156176">"Τα δακτυλικά αποτυπώματα <xliff:g id="FINGERPRINT_0">%1$s</xliff:g> και <xliff:g id="FINGERPRINT_1">%2$s</xliff:g> δεν λειτουργούσαν καλά και διαγράφηκαν. Ρυθμίστε τα ξανά για να ξεκλειδώνετε το τηλέφωνο με το δακτυλικό αποτύπωμά σας."</string>
+ <string name="face_dangling_notification_title" msgid="947852541060975473">"Επαναρρύθμιση λειτουργίας Ξεκλείδωμα με το πρόσωπο"</string>
+ <string name="face_dangling_notification_msg" msgid="8806849376915541655">"Το μοντέλο προσώπου δεν λειτουργούσε καλά και διαγράφηκε. Ρυθμίστε το ξανά για να ξεκλειδώνετε το τηλέφωνο με το πρόσωπό σας."</string>
+ <string name="biometric_dangling_notification_action_set_up" msgid="8246885009807817961">"Ρύθμιση"</string>
+ <string name="biometric_dangling_notification_action_not_now" msgid="8095249216864443491">"Όχι τώρα"</string>
</resources>
diff --git a/core/res/res/values-en-rAU/strings.xml b/core/res/res/values-en-rAU/strings.xml
index a32fcca..5f7dd65 100644
--- a/core/res/res/values-en-rAU/strings.xml
+++ b/core/res/res/values-en-rAU/strings.xml
@@ -283,6 +283,8 @@
<string name="global_action_voice_assist" msgid="6655788068555086695">"Voice Assist"</string>
<string name="global_action_lockdown" msgid="2475471405907902963">"Lockdown"</string>
<string name="status_bar_notification_info_overflow" msgid="3330152558746563475">"999+"</string>
+ <!-- no translation found for notification_compact_heads_up_reply (2425293958371284340) -->
+ <skip />
<string name="notification_hidden_text" msgid="2835519769868187223">"New notification"</string>
<string name="notification_channel_physical_keyboard" msgid="5417306456125988096">"Physical keyboard"</string>
<string name="notification_channel_security" msgid="8516754650348238057">"Security"</string>
@@ -2413,22 +2415,15 @@
<string name="satellite_notification_open_message" msgid="4149234979688273729">"Open Messages"</string>
<string name="satellite_notification_how_it_works" msgid="3132069321977520519">"How it works"</string>
<string name="unarchival_session_app_label" msgid="6811856981546348205">"Pending…"</string>
- <!-- no translation found for fingerprint_dangling_notification_title (7362075195588639989) -->
+ <string name="fingerprint_dangling_notification_title" msgid="7362075195588639989">"Set up Fingerprint Unlock again"</string>
+ <!-- no translation found for fingerprint_dangling_notification_msg_1 (8517140433796229725) -->
<skip />
- <!-- no translation found for fingerprint_dangling_notification_msg_1 (6261149111900787302) -->
+ <!-- no translation found for fingerprint_dangling_notification_msg_2 (7578829498452127613) -->
<skip />
- <!-- no translation found for fingerprint_dangling_notification_msg_2 (7688302770424064884) -->
- <skip />
- <!-- no translation found for fingerprint_dangling_notification_msg_all_deleted_1 (2927018569542316055) -->
- <skip />
- <!-- no translation found for fingerprint_dangling_notification_msg_all_deleted_2 (6897989352716156176) -->
- <skip />
- <!-- no translation found for face_dangling_notification_title (947852541060975473) -->
- <skip />
- <!-- no translation found for face_dangling_notification_msg (8806849376915541655) -->
- <skip />
- <!-- no translation found for biometric_dangling_notification_action_set_up (8246885009807817961) -->
- <skip />
- <!-- no translation found for biometric_dangling_notification_action_not_now (8095249216864443491) -->
- <skip />
+ <string name="fingerprint_dangling_notification_msg_all_deleted_1" msgid="2927018569542316055">"<xliff:g id="FINGERPRINT">%s</xliff:g> wasn\'t working well and was deleted. Set it up again to unlock your phone with your fingerprint."</string>
+ <string name="fingerprint_dangling_notification_msg_all_deleted_2" msgid="6897989352716156176">"<xliff:g id="FINGERPRINT_0">%1$s</xliff:g> and <xliff:g id="FINGERPRINT_1">%2$s</xliff:g> weren\'t working well and were deleted. Set them up again to unlock your phone with your fingerprint."</string>
+ <string name="face_dangling_notification_title" msgid="947852541060975473">"Set up Face Unlock again"</string>
+ <string name="face_dangling_notification_msg" msgid="8806849376915541655">"Your face model wasn\'t working well and was deleted. Set it up again to unlock your phone with your face."</string>
+ <string name="biometric_dangling_notification_action_set_up" msgid="8246885009807817961">"Set up"</string>
+ <string name="biometric_dangling_notification_action_not_now" msgid="8095249216864443491">"Not now"</string>
</resources>
diff --git a/core/res/res/values-en-rCA/strings.xml b/core/res/res/values-en-rCA/strings.xml
index 9f06f71..d1894b5 100644
--- a/core/res/res/values-en-rCA/strings.xml
+++ b/core/res/res/values-en-rCA/strings.xml
@@ -283,6 +283,7 @@
<string name="global_action_voice_assist" msgid="6655788068555086695">"Voice Assist"</string>
<string name="global_action_lockdown" msgid="2475471405907902963">"Lockdown"</string>
<string name="status_bar_notification_info_overflow" msgid="3330152558746563475">"999+"</string>
+ <string name="notification_compact_heads_up_reply" msgid="2425293958371284340">"Reply"</string>
<string name="notification_hidden_text" msgid="2835519769868187223">"New notification"</string>
<string name="notification_channel_physical_keyboard" msgid="5417306456125988096">"Physical keyboard"</string>
<string name="notification_channel_security" msgid="8516754650348238057">"Security"</string>
@@ -2414,8 +2415,10 @@
<string name="satellite_notification_how_it_works" msgid="3132069321977520519">"How it works"</string>
<string name="unarchival_session_app_label" msgid="6811856981546348205">"Pending..."</string>
<string name="fingerprint_dangling_notification_title" msgid="7362075195588639989">"Set up Fingerprint Unlock again"</string>
- <string name="fingerprint_dangling_notification_msg_1" msgid="6261149111900787302">"<xliff:g id="FINGERPRINT">%s</xliff:g> wasn\'t working well and was deleted to improve performance"</string>
- <string name="fingerprint_dangling_notification_msg_2" msgid="7688302770424064884">"<xliff:g id="FINGERPRINT_0">%1$s</xliff:g> and <xliff:g id="FINGERPRINT_1">%2$s</xliff:g> weren\'t working well and were deleted to improve performance"</string>
+ <!-- no translation found for fingerprint_dangling_notification_msg_1 (8517140433796229725) -->
+ <skip />
+ <!-- no translation found for fingerprint_dangling_notification_msg_2 (7578829498452127613) -->
+ <skip />
<string name="fingerprint_dangling_notification_msg_all_deleted_1" msgid="2927018569542316055">"<xliff:g id="FINGERPRINT">%s</xliff:g> wasn\'t working well and was deleted. Set it up again to unlock your phone with fingerprint."</string>
<string name="fingerprint_dangling_notification_msg_all_deleted_2" msgid="6897989352716156176">"<xliff:g id="FINGERPRINT_0">%1$s</xliff:g> and <xliff:g id="FINGERPRINT_1">%2$s</xliff:g> weren\'t working well and were deleted. Set them up again to unlock your phone with your fingerprint."</string>
<string name="face_dangling_notification_title" msgid="947852541060975473">"Set up Face Unlock again"</string>
diff --git a/core/res/res/values-en-rGB/strings.xml b/core/res/res/values-en-rGB/strings.xml
index bfcc4be..12fd027 100644
--- a/core/res/res/values-en-rGB/strings.xml
+++ b/core/res/res/values-en-rGB/strings.xml
@@ -283,6 +283,8 @@
<string name="global_action_voice_assist" msgid="6655788068555086695">"Voice Assist"</string>
<string name="global_action_lockdown" msgid="2475471405907902963">"Lockdown"</string>
<string name="status_bar_notification_info_overflow" msgid="3330152558746563475">"999+"</string>
+ <!-- no translation found for notification_compact_heads_up_reply (2425293958371284340) -->
+ <skip />
<string name="notification_hidden_text" msgid="2835519769868187223">"New notification"</string>
<string name="notification_channel_physical_keyboard" msgid="5417306456125988096">"Physical keyboard"</string>
<string name="notification_channel_security" msgid="8516754650348238057">"Security"</string>
@@ -2413,22 +2415,15 @@
<string name="satellite_notification_open_message" msgid="4149234979688273729">"Open Messages"</string>
<string name="satellite_notification_how_it_works" msgid="3132069321977520519">"How it works"</string>
<string name="unarchival_session_app_label" msgid="6811856981546348205">"Pending…"</string>
- <!-- no translation found for fingerprint_dangling_notification_title (7362075195588639989) -->
+ <string name="fingerprint_dangling_notification_title" msgid="7362075195588639989">"Set up Fingerprint Unlock again"</string>
+ <!-- no translation found for fingerprint_dangling_notification_msg_1 (8517140433796229725) -->
<skip />
- <!-- no translation found for fingerprint_dangling_notification_msg_1 (6261149111900787302) -->
+ <!-- no translation found for fingerprint_dangling_notification_msg_2 (7578829498452127613) -->
<skip />
- <!-- no translation found for fingerprint_dangling_notification_msg_2 (7688302770424064884) -->
- <skip />
- <!-- no translation found for fingerprint_dangling_notification_msg_all_deleted_1 (2927018569542316055) -->
- <skip />
- <!-- no translation found for fingerprint_dangling_notification_msg_all_deleted_2 (6897989352716156176) -->
- <skip />
- <!-- no translation found for face_dangling_notification_title (947852541060975473) -->
- <skip />
- <!-- no translation found for face_dangling_notification_msg (8806849376915541655) -->
- <skip />
- <!-- no translation found for biometric_dangling_notification_action_set_up (8246885009807817961) -->
- <skip />
- <!-- no translation found for biometric_dangling_notification_action_not_now (8095249216864443491) -->
- <skip />
+ <string name="fingerprint_dangling_notification_msg_all_deleted_1" msgid="2927018569542316055">"<xliff:g id="FINGERPRINT">%s</xliff:g> wasn\'t working well and was deleted. Set it up again to unlock your phone with your fingerprint."</string>
+ <string name="fingerprint_dangling_notification_msg_all_deleted_2" msgid="6897989352716156176">"<xliff:g id="FINGERPRINT_0">%1$s</xliff:g> and <xliff:g id="FINGERPRINT_1">%2$s</xliff:g> weren\'t working well and were deleted. Set them up again to unlock your phone with your fingerprint."</string>
+ <string name="face_dangling_notification_title" msgid="947852541060975473">"Set up Face Unlock again"</string>
+ <string name="face_dangling_notification_msg" msgid="8806849376915541655">"Your face model wasn\'t working well and was deleted. Set it up again to unlock your phone with your face."</string>
+ <string name="biometric_dangling_notification_action_set_up" msgid="8246885009807817961">"Set up"</string>
+ <string name="biometric_dangling_notification_action_not_now" msgid="8095249216864443491">"Not now"</string>
</resources>
diff --git a/core/res/res/values-en-rIN/strings.xml b/core/res/res/values-en-rIN/strings.xml
index 8000732..129310e 100644
--- a/core/res/res/values-en-rIN/strings.xml
+++ b/core/res/res/values-en-rIN/strings.xml
@@ -283,6 +283,8 @@
<string name="global_action_voice_assist" msgid="6655788068555086695">"Voice Assist"</string>
<string name="global_action_lockdown" msgid="2475471405907902963">"Lockdown"</string>
<string name="status_bar_notification_info_overflow" msgid="3330152558746563475">"999+"</string>
+ <!-- no translation found for notification_compact_heads_up_reply (2425293958371284340) -->
+ <skip />
<string name="notification_hidden_text" msgid="2835519769868187223">"New notification"</string>
<string name="notification_channel_physical_keyboard" msgid="5417306456125988096">"Physical keyboard"</string>
<string name="notification_channel_security" msgid="8516754650348238057">"Security"</string>
@@ -2413,22 +2415,15 @@
<string name="satellite_notification_open_message" msgid="4149234979688273729">"Open Messages"</string>
<string name="satellite_notification_how_it_works" msgid="3132069321977520519">"How it works"</string>
<string name="unarchival_session_app_label" msgid="6811856981546348205">"Pending…"</string>
- <!-- no translation found for fingerprint_dangling_notification_title (7362075195588639989) -->
+ <string name="fingerprint_dangling_notification_title" msgid="7362075195588639989">"Set up Fingerprint Unlock again"</string>
+ <!-- no translation found for fingerprint_dangling_notification_msg_1 (8517140433796229725) -->
<skip />
- <!-- no translation found for fingerprint_dangling_notification_msg_1 (6261149111900787302) -->
+ <!-- no translation found for fingerprint_dangling_notification_msg_2 (7578829498452127613) -->
<skip />
- <!-- no translation found for fingerprint_dangling_notification_msg_2 (7688302770424064884) -->
- <skip />
- <!-- no translation found for fingerprint_dangling_notification_msg_all_deleted_1 (2927018569542316055) -->
- <skip />
- <!-- no translation found for fingerprint_dangling_notification_msg_all_deleted_2 (6897989352716156176) -->
- <skip />
- <!-- no translation found for face_dangling_notification_title (947852541060975473) -->
- <skip />
- <!-- no translation found for face_dangling_notification_msg (8806849376915541655) -->
- <skip />
- <!-- no translation found for biometric_dangling_notification_action_set_up (8246885009807817961) -->
- <skip />
- <!-- no translation found for biometric_dangling_notification_action_not_now (8095249216864443491) -->
- <skip />
+ <string name="fingerprint_dangling_notification_msg_all_deleted_1" msgid="2927018569542316055">"<xliff:g id="FINGERPRINT">%s</xliff:g> wasn\'t working well and was deleted. Set it up again to unlock your phone with your fingerprint."</string>
+ <string name="fingerprint_dangling_notification_msg_all_deleted_2" msgid="6897989352716156176">"<xliff:g id="FINGERPRINT_0">%1$s</xliff:g> and <xliff:g id="FINGERPRINT_1">%2$s</xliff:g> weren\'t working well and were deleted. Set them up again to unlock your phone with your fingerprint."</string>
+ <string name="face_dangling_notification_title" msgid="947852541060975473">"Set up Face Unlock again"</string>
+ <string name="face_dangling_notification_msg" msgid="8806849376915541655">"Your face model wasn\'t working well and was deleted. Set it up again to unlock your phone with your face."</string>
+ <string name="biometric_dangling_notification_action_set_up" msgid="8246885009807817961">"Set up"</string>
+ <string name="biometric_dangling_notification_action_not_now" msgid="8095249216864443491">"Not now"</string>
</resources>
diff --git a/core/res/res/values-en-rXC/strings.xml b/core/res/res/values-en-rXC/strings.xml
index 0fe2ccc..e08f934 100644
--- a/core/res/res/values-en-rXC/strings.xml
+++ b/core/res/res/values-en-rXC/strings.xml
@@ -283,6 +283,7 @@
<string name="global_action_voice_assist" msgid="6655788068555086695">"Voice Assist"</string>
<string name="global_action_lockdown" msgid="2475471405907902963">"Lockdown"</string>
<string name="status_bar_notification_info_overflow" msgid="3330152558746563475">"999+"</string>
+ <string name="notification_compact_heads_up_reply" msgid="2425293958371284340">"Reply"</string>
<string name="notification_hidden_text" msgid="2835519769868187223">"New notification"</string>
<string name="notification_channel_physical_keyboard" msgid="5417306456125988096">"Physical keyboard"</string>
<string name="notification_channel_security" msgid="8516754650348238057">"Security"</string>
@@ -2414,8 +2415,10 @@
<string name="satellite_notification_how_it_works" msgid="3132069321977520519">"How it works"</string>
<string name="unarchival_session_app_label" msgid="6811856981546348205">"Pending..."</string>
<string name="fingerprint_dangling_notification_title" msgid="7362075195588639989">"Set up Fingerprint Unlock again"</string>
- <string name="fingerprint_dangling_notification_msg_1" msgid="6261149111900787302">"<xliff:g id="FINGERPRINT">%s</xliff:g> wasn\'t working well and was deleted to improve performance"</string>
- <string name="fingerprint_dangling_notification_msg_2" msgid="7688302770424064884">"<xliff:g id="FINGERPRINT_0">%1$s</xliff:g> and <xliff:g id="FINGERPRINT_1">%2$s</xliff:g> weren\'t working well and were deleted to improve performance"</string>
+ <!-- no translation found for fingerprint_dangling_notification_msg_1 (8517140433796229725) -->
+ <skip />
+ <!-- no translation found for fingerprint_dangling_notification_msg_2 (7578829498452127613) -->
+ <skip />
<string name="fingerprint_dangling_notification_msg_all_deleted_1" msgid="2927018569542316055">"<xliff:g id="FINGERPRINT">%s</xliff:g> wasn\'t working well and was deleted. Set it up again to unlock your phone with fingerprint."</string>
<string name="fingerprint_dangling_notification_msg_all_deleted_2" msgid="6897989352716156176">"<xliff:g id="FINGERPRINT_0">%1$s</xliff:g> and <xliff:g id="FINGERPRINT_1">%2$s</xliff:g> weren\'t working well and were deleted. Set them up again to unlock your phone with your fingerprint."</string>
<string name="face_dangling_notification_title" msgid="947852541060975473">"Set up Face Unlock again"</string>
diff --git a/core/res/res/values-es-rUS/strings.xml b/core/res/res/values-es-rUS/strings.xml
index 8717640..30a0457 100644
--- a/core/res/res/values-es-rUS/strings.xml
+++ b/core/res/res/values-es-rUS/strings.xml
@@ -284,6 +284,8 @@
<string name="global_action_voice_assist" msgid="6655788068555086695">"Asistente voz"</string>
<string name="global_action_lockdown" msgid="2475471405907902963">"Bloqueo"</string>
<string name="status_bar_notification_info_overflow" msgid="3330152558746563475">"999+"</string>
+ <!-- no translation found for notification_compact_heads_up_reply (2425293958371284340) -->
+ <skip />
<string name="notification_hidden_text" msgid="2835519769868187223">"Notificación nueva"</string>
<string name="notification_channel_physical_keyboard" msgid="5417306456125988096">"Teclado físico"</string>
<string name="notification_channel_security" msgid="8516754650348238057">"Seguridad"</string>
@@ -2414,22 +2416,15 @@
<string name="satellite_notification_open_message" msgid="4149234979688273729">"Abrir Mensajes"</string>
<string name="satellite_notification_how_it_works" msgid="3132069321977520519">"Cómo funciona"</string>
<string name="unarchival_session_app_label" msgid="6811856981546348205">"Pendiente…"</string>
- <!-- no translation found for fingerprint_dangling_notification_title (7362075195588639989) -->
+ <string name="fingerprint_dangling_notification_title" msgid="7362075195588639989">"Vuelve a configurar el Desbloqueo con huellas dactilares"</string>
+ <!-- no translation found for fingerprint_dangling_notification_msg_1 (8517140433796229725) -->
<skip />
- <!-- no translation found for fingerprint_dangling_notification_msg_1 (6261149111900787302) -->
+ <!-- no translation found for fingerprint_dangling_notification_msg_2 (7578829498452127613) -->
<skip />
- <!-- no translation found for fingerprint_dangling_notification_msg_2 (7688302770424064884) -->
- <skip />
- <!-- no translation found for fingerprint_dangling_notification_msg_all_deleted_1 (2927018569542316055) -->
- <skip />
- <!-- no translation found for fingerprint_dangling_notification_msg_all_deleted_2 (6897989352716156176) -->
- <skip />
- <!-- no translation found for face_dangling_notification_title (947852541060975473) -->
- <skip />
- <!-- no translation found for face_dangling_notification_msg (8806849376915541655) -->
- <skip />
- <!-- no translation found for biometric_dangling_notification_action_set_up (8246885009807817961) -->
- <skip />
- <!-- no translation found for biometric_dangling_notification_action_not_now (8095249216864443491) -->
- <skip />
+ <string name="fingerprint_dangling_notification_msg_all_deleted_1" msgid="2927018569542316055">"Se borró <xliff:g id="FINGERPRINT">%s</xliff:g> porque no funcionaba correctamente. Vuelve a configurarla para desbloquear el teléfono con la huella dactilar."</string>
+ <string name="fingerprint_dangling_notification_msg_all_deleted_2" msgid="6897989352716156176">"Se borraron <xliff:g id="FINGERPRINT_0">%1$s</xliff:g> y <xliff:g id="FINGERPRINT_1">%2$s</xliff:g> porque no funcionaban correctamente. Vuelve a configurarlas para desbloquear el teléfono con la huella dactilar."</string>
+ <string name="face_dangling_notification_title" msgid="947852541060975473">"Vuelve a configurar el Desbloqueo facial"</string>
+ <string name="face_dangling_notification_msg" msgid="8806849376915541655">"Se borró tu modelo de rostro porque no funcionaba correctamente. Vuelve a configurarlo para desbloquear el teléfono con el rostro."</string>
+ <string name="biometric_dangling_notification_action_set_up" msgid="8246885009807817961">"Configurar"</string>
+ <string name="biometric_dangling_notification_action_not_now" msgid="8095249216864443491">"Ahora no"</string>
</resources>
diff --git a/core/res/res/values-es/strings.xml b/core/res/res/values-es/strings.xml
index 6549da2..5079fdf 100644
--- a/core/res/res/values-es/strings.xml
+++ b/core/res/res/values-es/strings.xml
@@ -284,6 +284,8 @@
<string name="global_action_voice_assist" msgid="6655788068555086695">"Asistente voz"</string>
<string name="global_action_lockdown" msgid="2475471405907902963">"Bloqueo de seguridad"</string>
<string name="status_bar_notification_info_overflow" msgid="3330152558746563475">"> 999"</string>
+ <!-- no translation found for notification_compact_heads_up_reply (2425293958371284340) -->
+ <skip />
<string name="notification_hidden_text" msgid="2835519769868187223">"Notificación nueva"</string>
<string name="notification_channel_physical_keyboard" msgid="5417306456125988096">"Teclado físico"</string>
<string name="notification_channel_security" msgid="8516754650348238057">"Seguridad"</string>
@@ -1402,7 +1404,7 @@
<string name="usb_unsupported_audio_accessory_title" msgid="2335775548086533065">"Se ha detectado un accesorio de audio analógico"</string>
<string name="usb_unsupported_audio_accessory_message" msgid="1300168007129796621">"El dispositivo adjunto no es compatible con este teléfono. Toca para obtener más información."</string>
<string name="adb_active_notification_title" msgid="408390247354560331">"Depuración por USB activa"</string>
- <string name="adb_active_notification_message" msgid="5617264033476778211">"Toca para desactivar la depuración USB"</string>
+ <string name="adb_active_notification_message" msgid="5617264033476778211">"Toca para desactivar la depuración por USB"</string>
<string name="adb_active_notification_message" product="tv" msgid="6624498401272780855">"Seleccionar para inhabilitar la depuración por USB"</string>
<string name="adbwifi_active_notification_title" msgid="6147343659168302473">"Depuración inalámbrica conectada"</string>
<string name="adbwifi_active_notification_message" msgid="930987922852867972">"Toca para desactivar la depuración inalámbrica"</string>
@@ -2414,22 +2416,15 @@
<string name="satellite_notification_open_message" msgid="4149234979688273729">"Abre Mensajes"</string>
<string name="satellite_notification_how_it_works" msgid="3132069321977520519">"Cómo funciona"</string>
<string name="unarchival_session_app_label" msgid="6811856981546348205">"Pendiente..."</string>
- <!-- no translation found for fingerprint_dangling_notification_title (7362075195588639989) -->
+ <string name="fingerprint_dangling_notification_title" msgid="7362075195588639989">"Configura Desbloqueo con huella digital de nuevo"</string>
+ <!-- no translation found for fingerprint_dangling_notification_msg_1 (8517140433796229725) -->
<skip />
- <!-- no translation found for fingerprint_dangling_notification_msg_1 (6261149111900787302) -->
+ <!-- no translation found for fingerprint_dangling_notification_msg_2 (7578829498452127613) -->
<skip />
- <!-- no translation found for fingerprint_dangling_notification_msg_2 (7688302770424064884) -->
- <skip />
- <!-- no translation found for fingerprint_dangling_notification_msg_all_deleted_1 (2927018569542316055) -->
- <skip />
- <!-- no translation found for fingerprint_dangling_notification_msg_all_deleted_2 (6897989352716156176) -->
- <skip />
- <!-- no translation found for face_dangling_notification_title (947852541060975473) -->
- <skip />
- <!-- no translation found for face_dangling_notification_msg (8806849376915541655) -->
- <skip />
- <!-- no translation found for biometric_dangling_notification_action_set_up (8246885009807817961) -->
- <skip />
- <!-- no translation found for biometric_dangling_notification_action_not_now (8095249216864443491) -->
- <skip />
+ <string name="fingerprint_dangling_notification_msg_all_deleted_1" msgid="2927018569542316055">"<xliff:g id="FINGERPRINT">%s</xliff:g> no funcionaba correctamente y se ha eliminado. Configúrala de nuevo para desbloquear el teléfono con la huella digital."</string>
+ <string name="fingerprint_dangling_notification_msg_all_deleted_2" msgid="6897989352716156176">"<xliff:g id="FINGERPRINT_0">%1$s</xliff:g> y <xliff:g id="FINGERPRINT_1">%2$s</xliff:g> no funcionaban correctamente y se han eliminado. Configúralas de nuevo para desbloquear el teléfono con la huella digital."</string>
+ <string name="face_dangling_notification_title" msgid="947852541060975473">"Configura Desbloqueo facial de nuevo"</string>
+ <string name="face_dangling_notification_msg" msgid="8806849376915541655">"Tu modelo facial no funcionaba correctamente y se ha eliminado. Configúralo de nuevo para desbloquear el teléfono con la cara."</string>
+ <string name="biometric_dangling_notification_action_set_up" msgid="8246885009807817961">"Configurar"</string>
+ <string name="biometric_dangling_notification_action_not_now" msgid="8095249216864443491">"Ahora no"</string>
</resources>
diff --git a/core/res/res/values-et/strings.xml b/core/res/res/values-et/strings.xml
index 92f89e3..16b0ea2 100644
--- a/core/res/res/values-et/strings.xml
+++ b/core/res/res/values-et/strings.xml
@@ -283,6 +283,8 @@
<string name="global_action_voice_assist" msgid="6655788068555086695">"Häälabi"</string>
<string name="global_action_lockdown" msgid="2475471405907902963">"Lukusta"</string>
<string name="status_bar_notification_info_overflow" msgid="3330152558746563475">"999+"</string>
+ <!-- no translation found for notification_compact_heads_up_reply (2425293958371284340) -->
+ <skip />
<string name="notification_hidden_text" msgid="2835519769868187223">"Uus märguanne"</string>
<string name="notification_channel_physical_keyboard" msgid="5417306456125988096">"Füüsiline klaviatuur"</string>
<string name="notification_channel_security" msgid="8516754650348238057">"Turvalisus"</string>
@@ -2413,22 +2415,15 @@
<string name="satellite_notification_open_message" msgid="4149234979688273729">"Ava rakendus Messages"</string>
<string name="satellite_notification_how_it_works" msgid="3132069321977520519">"Tööpõhimõtted"</string>
<string name="unarchival_session_app_label" msgid="6811856981546348205">"Ootel …"</string>
- <!-- no translation found for fingerprint_dangling_notification_title (7362075195588639989) -->
+ <string name="fingerprint_dangling_notification_title" msgid="7362075195588639989">"Seadistage sõrmejäljega avamine uuesti"</string>
+ <!-- no translation found for fingerprint_dangling_notification_msg_1 (8517140433796229725) -->
<skip />
- <!-- no translation found for fingerprint_dangling_notification_msg_1 (6261149111900787302) -->
+ <!-- no translation found for fingerprint_dangling_notification_msg_2 (7578829498452127613) -->
<skip />
- <!-- no translation found for fingerprint_dangling_notification_msg_2 (7688302770424064884) -->
- <skip />
- <!-- no translation found for fingerprint_dangling_notification_msg_all_deleted_1 (2927018569542316055) -->
- <skip />
- <!-- no translation found for fingerprint_dangling_notification_msg_all_deleted_2 (6897989352716156176) -->
- <skip />
- <!-- no translation found for face_dangling_notification_title (947852541060975473) -->
- <skip />
- <!-- no translation found for face_dangling_notification_msg (8806849376915541655) -->
- <skip />
- <!-- no translation found for biometric_dangling_notification_action_set_up (8246885009807817961) -->
- <skip />
- <!-- no translation found for biometric_dangling_notification_action_not_now (8095249216864443491) -->
- <skip />
+ <string name="fingerprint_dangling_notification_msg_all_deleted_1" msgid="2927018569542316055">"<xliff:g id="FINGERPRINT">%s</xliff:g> ei töötanud hästi ja kustutati. Telefoni sõrmejäljega avamiseks seadistage see uuesti."</string>
+ <string name="fingerprint_dangling_notification_msg_all_deleted_2" msgid="6897989352716156176">"<xliff:g id="FINGERPRINT_0">%1$s</xliff:g> ja <xliff:g id="FINGERPRINT_1">%2$s</xliff:g> ei töötanud hästi ning kustutati. Telefoni sõrmejäljega avamiseks seadistage need uuesti."</string>
+ <string name="face_dangling_notification_title" msgid="947852541060975473">"Seadistage näoga avamine uuesti"</string>
+ <string name="face_dangling_notification_msg" msgid="8806849376915541655">"Teie näomudel ei töötanud hästi ja kustutati. Telefoni näoga avamiseks seadistage see uuesti."</string>
+ <string name="biometric_dangling_notification_action_set_up" msgid="8246885009807817961">"Seadista"</string>
+ <string name="biometric_dangling_notification_action_not_now" msgid="8095249216864443491">"Mitte praegu"</string>
</resources>
diff --git a/core/res/res/values-eu/strings.xml b/core/res/res/values-eu/strings.xml
index 2d4130e..4fb4726 100644
--- a/core/res/res/values-eu/strings.xml
+++ b/core/res/res/values-eu/strings.xml
@@ -283,6 +283,8 @@
<string name="global_action_voice_assist" msgid="6655788068555086695">"Ahots-laguntza"</string>
<string name="global_action_lockdown" msgid="2475471405907902963">"Blokeatu"</string>
<string name="status_bar_notification_info_overflow" msgid="3330152558746563475">"999+"</string>
+ <!-- no translation found for notification_compact_heads_up_reply (2425293958371284340) -->
+ <skip />
<string name="notification_hidden_text" msgid="2835519769868187223">"Jakinarazpen berria"</string>
<string name="notification_channel_physical_keyboard" msgid="5417306456125988096">"Teklatu fisikoa"</string>
<string name="notification_channel_security" msgid="8516754650348238057">"Segurtasuna"</string>
@@ -2413,22 +2415,15 @@
<string name="satellite_notification_open_message" msgid="4149234979688273729">"Ireki Mezuak"</string>
<string name="satellite_notification_how_it_works" msgid="3132069321977520519">"Nola funtzionatzen du?"</string>
<string name="unarchival_session_app_label" msgid="6811856981546348205">"Zain…"</string>
- <!-- no translation found for fingerprint_dangling_notification_title (7362075195588639989) -->
+ <string name="fingerprint_dangling_notification_title" msgid="7362075195588639989">"Konfiguratu berriro hatz-marka bidez desblokeatzeko eginbidea"</string>
+ <!-- no translation found for fingerprint_dangling_notification_msg_1 (8517140433796229725) -->
<skip />
- <!-- no translation found for fingerprint_dangling_notification_msg_1 (6261149111900787302) -->
+ <!-- no translation found for fingerprint_dangling_notification_msg_2 (7578829498452127613) -->
<skip />
- <!-- no translation found for fingerprint_dangling_notification_msg_2 (7688302770424064884) -->
- <skip />
- <!-- no translation found for fingerprint_dangling_notification_msg_all_deleted_1 (2927018569542316055) -->
- <skip />
- <!-- no translation found for fingerprint_dangling_notification_msg_all_deleted_2 (6897989352716156176) -->
- <skip />
- <!-- no translation found for face_dangling_notification_title (947852541060975473) -->
- <skip />
- <!-- no translation found for face_dangling_notification_msg (8806849376915541655) -->
- <skip />
- <!-- no translation found for biometric_dangling_notification_action_set_up (8246885009807817961) -->
- <skip />
- <!-- no translation found for biometric_dangling_notification_action_not_now (8095249216864443491) -->
- <skip />
+ <string name="fingerprint_dangling_notification_msg_all_deleted_1" msgid="2927018569542316055">"<xliff:g id="FINGERPRINT">%s</xliff:g> ezabatu egin da, ez zuelako ondo funtzionatzen. Telefonoa hatz-markarekin desblokeatzeko, konfigura ezazu berriro."</string>
+ <string name="fingerprint_dangling_notification_msg_all_deleted_2" msgid="6897989352716156176">"<xliff:g id="FINGERPRINT_0">%1$s</xliff:g> eta <xliff:g id="FINGERPRINT_1">%2$s</xliff:g> ezabatu egin dira, ez zutelako ondo funtzionatzen. Telefonoa hatz-markarekin desblokeatzeko, konfigura itzazu berriro."</string>
+ <string name="face_dangling_notification_title" msgid="947852541060975473">"Konfiguratu berriro aurpegi bidez desblokeatzeko eginbidea"</string>
+ <string name="face_dangling_notification_msg" msgid="8806849376915541655">"Aurpegi-eredua ezabatu egin da, ez zuelako ondo funtzionatzen. Telefonoa aurpegiarekin desblokeatzeko, konfigura ezazu berriro."</string>
+ <string name="biometric_dangling_notification_action_set_up" msgid="8246885009807817961">"Konfiguratu"</string>
+ <string name="biometric_dangling_notification_action_not_now" msgid="8095249216864443491">"Orain ez"</string>
</resources>
diff --git a/core/res/res/values-fa/strings.xml b/core/res/res/values-fa/strings.xml
index 07b2674..7272bcfc 100644
--- a/core/res/res/values-fa/strings.xml
+++ b/core/res/res/values-fa/strings.xml
@@ -283,6 +283,8 @@
<string name="global_action_voice_assist" msgid="6655788068555086695">"دستیار صوتی"</string>
<string name="global_action_lockdown" msgid="2475471405907902963">"قفل همه"</string>
<string name="status_bar_notification_info_overflow" msgid="3330152558746563475">"۹۹۹+"</string>
+ <!-- no translation found for notification_compact_heads_up_reply (2425293958371284340) -->
+ <skip />
<string name="notification_hidden_text" msgid="2835519769868187223">"اعلان جدید"</string>
<string name="notification_channel_physical_keyboard" msgid="5417306456125988096">"صفحهکلید فیزیکی"</string>
<string name="notification_channel_security" msgid="8516754650348238057">"امنیت"</string>
@@ -666,7 +668,7 @@
</string-array>
<string name="fingerprint_error_not_match" msgid="4599441812893438961">"اثر انگشت تشخیص داده نشد"</string>
<string name="fingerprint_udfps_error_not_match" msgid="8236930793223158856">"اثر انگشت تشخیص داده نشد"</string>
- <string name="fingerprint_dialog_use_fingerprint_instead" msgid="5590293588784953188">"چهره شناسایی نشد. درعوض از اثر انگشت استفاده کنید."</string>
+ <string name="fingerprint_dialog_use_fingerprint_instead" msgid="5590293588784953188">"چهره شناسایی نشد، از اثر انگشت استفاده کنید."</string>
<string name="fingerprint_authenticated" msgid="2024862866860283100">"اثر انگشت اصالتسنجی شد"</string>
<string name="face_authenticated_no_confirmation_required" msgid="8867889115112348167">"چهره اصالتسنجی شد"</string>
<string name="face_authenticated_confirmation_required" msgid="6872632732508013755">"چهره اصالتسنجی شد، لطفاً تأیید را فشار دهید"</string>
@@ -2413,22 +2415,15 @@
<string name="satellite_notification_open_message" msgid="4149234979688273729">"باز کردن «پیامها»"</string>
<string name="satellite_notification_how_it_works" msgid="3132069321977520519">"روش کار"</string>
<string name="unarchival_session_app_label" msgid="6811856981546348205">"درحال تعلیق…"</string>
- <!-- no translation found for fingerprint_dangling_notification_title (7362075195588639989) -->
+ <string name="fingerprint_dangling_notification_title" msgid="7362075195588639989">"راهاندازی مجدد «قفلگشایی با اثر انگشت»"</string>
+ <!-- no translation found for fingerprint_dangling_notification_msg_1 (8517140433796229725) -->
<skip />
- <!-- no translation found for fingerprint_dangling_notification_msg_1 (6261149111900787302) -->
+ <!-- no translation found for fingerprint_dangling_notification_msg_2 (7578829498452127613) -->
<skip />
- <!-- no translation found for fingerprint_dangling_notification_msg_2 (7688302770424064884) -->
- <skip />
- <!-- no translation found for fingerprint_dangling_notification_msg_all_deleted_1 (2927018569542316055) -->
- <skip />
- <!-- no translation found for fingerprint_dangling_notification_msg_all_deleted_2 (6897989352716156176) -->
- <skip />
- <!-- no translation found for face_dangling_notification_title (947852541060975473) -->
- <skip />
- <!-- no translation found for face_dangling_notification_msg (8806849376915541655) -->
- <skip />
- <!-- no translation found for biometric_dangling_notification_action_set_up (8246885009807817961) -->
- <skip />
- <!-- no translation found for biometric_dangling_notification_action_not_now (8095249216864443491) -->
- <skip />
+ <string name="fingerprint_dangling_notification_msg_all_deleted_1" msgid="2927018569542316055">"<xliff:g id="FINGERPRINT">%s</xliff:g> خوب کار نمیکرد و حذف شد. برای باز کردن قفل تلفن با اثر انگشت، آن را دوباره راهاندازی کنید."</string>
+ <string name="fingerprint_dangling_notification_msg_all_deleted_2" msgid="6897989352716156176">"<xliff:g id="FINGERPRINT_0">%1$s</xliff:g> و <xliff:g id="FINGERPRINT_1">%2$s</xliff:g> خوب کار نمیکرد و حذف شد. برای باز کردن قفل تلفن با اثر انگشت، آنها را دوباره راهاندازی کنید."</string>
+ <string name="face_dangling_notification_title" msgid="947852541060975473">"راهاندازی مجدد «قفلگشایی با چهره»"</string>
+ <string name="face_dangling_notification_msg" msgid="8806849376915541655">"مدل چهره شما خوب کار نمیکرد و حذف شد. برای باز کردن قفل تلفن با چهره، دوباره آن را راهاندازی کنید."</string>
+ <string name="biometric_dangling_notification_action_set_up" msgid="8246885009807817961">"راهاندازی"</string>
+ <string name="biometric_dangling_notification_action_not_now" msgid="8095249216864443491">"حالا نه"</string>
</resources>
diff --git a/core/res/res/values-fi/strings.xml b/core/res/res/values-fi/strings.xml
index e802443..7062a89 100644
--- a/core/res/res/values-fi/strings.xml
+++ b/core/res/res/values-fi/strings.xml
@@ -283,6 +283,8 @@
<string name="global_action_voice_assist" msgid="6655788068555086695">"Ääniapuri"</string>
<string name="global_action_lockdown" msgid="2475471405907902963">"Lukitse"</string>
<string name="status_bar_notification_info_overflow" msgid="3330152558746563475">"999+"</string>
+ <!-- no translation found for notification_compact_heads_up_reply (2425293958371284340) -->
+ <skip />
<string name="notification_hidden_text" msgid="2835519769868187223">"Uusi ilmoitus"</string>
<string name="notification_channel_physical_keyboard" msgid="5417306456125988096">"Fyysinen näppäimistö"</string>
<string name="notification_channel_security" msgid="8516754650348238057">"Turvallisuus"</string>
@@ -2413,22 +2415,15 @@
<string name="satellite_notification_open_message" msgid="4149234979688273729">"Avaa Messages"</string>
<string name="satellite_notification_how_it_works" msgid="3132069321977520519">"Näin se toimii"</string>
<string name="unarchival_session_app_label" msgid="6811856981546348205">"Odottaa…"</string>
- <!-- no translation found for fingerprint_dangling_notification_title (7362075195588639989) -->
+ <string name="fingerprint_dangling_notification_title" msgid="7362075195588639989">"Ota sormenjälkiavaus uudelleen käyttöön"</string>
+ <!-- no translation found for fingerprint_dangling_notification_msg_1 (8517140433796229725) -->
<skip />
- <!-- no translation found for fingerprint_dangling_notification_msg_1 (6261149111900787302) -->
+ <!-- no translation found for fingerprint_dangling_notification_msg_2 (7578829498452127613) -->
<skip />
- <!-- no translation found for fingerprint_dangling_notification_msg_2 (7688302770424064884) -->
- <skip />
- <!-- no translation found for fingerprint_dangling_notification_msg_all_deleted_1 (2927018569542316055) -->
- <skip />
- <!-- no translation found for fingerprint_dangling_notification_msg_all_deleted_2 (6897989352716156176) -->
- <skip />
- <!-- no translation found for face_dangling_notification_title (947852541060975473) -->
- <skip />
- <!-- no translation found for face_dangling_notification_msg (8806849376915541655) -->
- <skip />
- <!-- no translation found for biometric_dangling_notification_action_set_up (8246885009807817961) -->
- <skip />
- <!-- no translation found for biometric_dangling_notification_action_not_now (8095249216864443491) -->
- <skip />
+ <string name="fingerprint_dangling_notification_msg_all_deleted_1" msgid="2927018569542316055">"<xliff:g id="FINGERPRINT">%s</xliff:g> ei toiminut kunnolla, ja se poistettiin. Ota se uudelleen käyttöön, jotta voit avata puhelimen lukituksen sormenjäljellä."</string>
+ <string name="fingerprint_dangling_notification_msg_all_deleted_2" msgid="6897989352716156176">"<xliff:g id="FINGERPRINT_0">%1$s</xliff:g> ja <xliff:g id="FINGERPRINT_1">%2$s</xliff:g> eivät toimineet kunnolla, ja ne poistettiin. Ota ne uudelleen käyttöön, jotta voit avata puhelimen lukituksen sormenjäljellä."</string>
+ <string name="face_dangling_notification_title" msgid="947852541060975473">"Ota kasvojentunnistusavaus uudelleen käyttöön"</string>
+ <string name="face_dangling_notification_msg" msgid="8806849376915541655">"Kasvomallisi ei toiminut kunnolla, ja se poistettiin. Ota se uudelleen käyttöön, jotta voit avata puhelimen lukituksen kasvoilla."</string>
+ <string name="biometric_dangling_notification_action_set_up" msgid="8246885009807817961">"Ota käyttöön"</string>
+ <string name="biometric_dangling_notification_action_not_now" msgid="8095249216864443491">"Ei nyt"</string>
</resources>
diff --git a/core/res/res/values-fr-rCA/strings.xml b/core/res/res/values-fr-rCA/strings.xml
index bf70c3c..bc61dea 100644
--- a/core/res/res/values-fr-rCA/strings.xml
+++ b/core/res/res/values-fr-rCA/strings.xml
@@ -284,6 +284,8 @@
<string name="global_action_voice_assist" msgid="6655788068555086695">"Assist. vocale"</string>
<string name="global_action_lockdown" msgid="2475471405907902963">"Verrouillage"</string>
<string name="status_bar_notification_info_overflow" msgid="3330152558746563475">">999"</string>
+ <!-- no translation found for notification_compact_heads_up_reply (2425293958371284340) -->
+ <skip />
<string name="notification_hidden_text" msgid="2835519769868187223">"Nouvelle notification"</string>
<string name="notification_channel_physical_keyboard" msgid="5417306456125988096">"Clavier physique"</string>
<string name="notification_channel_security" msgid="8516754650348238057">"Sécurité"</string>
@@ -2414,22 +2416,15 @@
<string name="satellite_notification_open_message" msgid="4149234979688273729">"Ouvrir Messages"</string>
<string name="satellite_notification_how_it_works" msgid="3132069321977520519">"Fonctionnement"</string>
<string name="unarchival_session_app_label" msgid="6811856981546348205">"En attente…"</string>
- <!-- no translation found for fingerprint_dangling_notification_title (7362075195588639989) -->
+ <string name="fingerprint_dangling_notification_title" msgid="7362075195588639989">"Configurer le Déverrouillage par empreinte digitale à nouveau"</string>
+ <!-- no translation found for fingerprint_dangling_notification_msg_1 (8517140433796229725) -->
<skip />
- <!-- no translation found for fingerprint_dangling_notification_msg_1 (6261149111900787302) -->
+ <!-- no translation found for fingerprint_dangling_notification_msg_2 (7578829498452127613) -->
<skip />
- <!-- no translation found for fingerprint_dangling_notification_msg_2 (7688302770424064884) -->
- <skip />
- <!-- no translation found for fingerprint_dangling_notification_msg_all_deleted_1 (2927018569542316055) -->
- <skip />
- <!-- no translation found for fingerprint_dangling_notification_msg_all_deleted_2 (6897989352716156176) -->
- <skip />
- <!-- no translation found for face_dangling_notification_title (947852541060975473) -->
- <skip />
- <!-- no translation found for face_dangling_notification_msg (8806849376915541655) -->
- <skip />
- <!-- no translation found for biometric_dangling_notification_action_set_up (8246885009807817961) -->
- <skip />
- <!-- no translation found for biometric_dangling_notification_action_not_now (8095249216864443491) -->
- <skip />
+ <string name="fingerprint_dangling_notification_msg_all_deleted_1" msgid="2927018569542316055">"<xliff:g id="FINGERPRINT">%s</xliff:g> ne fonctionnait pas bien et a été supprimée. Configurez-le à nouveau pour déverrouiller votre téléphone avec l\'empreinte digitale."</string>
+ <string name="fingerprint_dangling_notification_msg_all_deleted_2" msgid="6897989352716156176">"<xliff:g id="FINGERPRINT_0">%1$s</xliff:g> et <xliff:g id="FINGERPRINT_1">%2$s</xliff:g> ne fonctionnaient pas bien et ont été supprimées. Configurez-les à nouveau pour déverrouiller votre téléphone avec votre empreinte digitale."</string>
+ <string name="face_dangling_notification_title" msgid="947852541060975473">"Configurer le Déverrouillage par reconnaissance faciale à nouveau"</string>
+ <string name="face_dangling_notification_msg" msgid="8806849376915541655">"Votre modèle facial ne fonctionnait pas bien et a été supprimé. Configurez-le à nouveau pour déverrouiller votre téléphone avec votre visage."</string>
+ <string name="biometric_dangling_notification_action_set_up" msgid="8246885009807817961">"Configurer"</string>
+ <string name="biometric_dangling_notification_action_not_now" msgid="8095249216864443491">"Plus tard"</string>
</resources>
diff --git a/core/res/res/values-fr/strings.xml b/core/res/res/values-fr/strings.xml
index 5727224..69d4219 100644
--- a/core/res/res/values-fr/strings.xml
+++ b/core/res/res/values-fr/strings.xml
@@ -284,6 +284,8 @@
<string name="global_action_voice_assist" msgid="6655788068555086695">"Assistance vocale"</string>
<string name="global_action_lockdown" msgid="2475471405907902963">"Verrouiller"</string>
<string name="status_bar_notification_info_overflow" msgid="3330152558746563475">">999"</string>
+ <!-- no translation found for notification_compact_heads_up_reply (2425293958371284340) -->
+ <skip />
<string name="notification_hidden_text" msgid="2835519769868187223">"Nouvelle notification"</string>
<string name="notification_channel_physical_keyboard" msgid="5417306456125988096">"Clavier physique"</string>
<string name="notification_channel_security" msgid="8516754650348238057">"Sécurité"</string>
@@ -2414,22 +2416,15 @@
<string name="satellite_notification_open_message" msgid="4149234979688273729">"Ouvrir Messages"</string>
<string name="satellite_notification_how_it_works" msgid="3132069321977520519">"Fonctionnement"</string>
<string name="unarchival_session_app_label" msgid="6811856981546348205">"En attente…"</string>
- <!-- no translation found for fingerprint_dangling_notification_title (7362075195588639989) -->
+ <string name="fingerprint_dangling_notification_title" msgid="7362075195588639989">"Reconfigurer le déverrouillage par empreinte digitale"</string>
+ <!-- no translation found for fingerprint_dangling_notification_msg_1 (8517140433796229725) -->
<skip />
- <!-- no translation found for fingerprint_dangling_notification_msg_1 (6261149111900787302) -->
+ <!-- no translation found for fingerprint_dangling_notification_msg_2 (7578829498452127613) -->
<skip />
- <!-- no translation found for fingerprint_dangling_notification_msg_2 (7688302770424064884) -->
- <skip />
- <!-- no translation found for fingerprint_dangling_notification_msg_all_deleted_1 (2927018569542316055) -->
- <skip />
- <!-- no translation found for fingerprint_dangling_notification_msg_all_deleted_2 (6897989352716156176) -->
- <skip />
- <!-- no translation found for face_dangling_notification_title (947852541060975473) -->
- <skip />
- <!-- no translation found for face_dangling_notification_msg (8806849376915541655) -->
- <skip />
- <!-- no translation found for biometric_dangling_notification_action_set_up (8246885009807817961) -->
- <skip />
- <!-- no translation found for biometric_dangling_notification_action_not_now (8095249216864443491) -->
- <skip />
+ <string name="fingerprint_dangling_notification_msg_all_deleted_1" msgid="2927018569542316055">"<xliff:g id="FINGERPRINT">%s</xliff:g> ne fonctionnait pas correctement et a été supprimée. Configurez-la à nouveau pour déverrouiller votre téléphone à l\'aide votre empreinte digitale."</string>
+ <string name="fingerprint_dangling_notification_msg_all_deleted_2" msgid="6897989352716156176">"<xliff:g id="FINGERPRINT_0">%1$s</xliff:g> et <xliff:g id="FINGERPRINT_1">%2$s</xliff:g> ne fonctionnaient pas correctement et ont été supprimées. Configurez-les à nouveau pour déverrouiller votre téléphone à l\'aide de votre empreinte digitale."</string>
+ <string name="face_dangling_notification_title" msgid="947852541060975473">"Reconfigurer le déverrouillage par reconnaissance faciale"</string>
+ <string name="face_dangling_notification_msg" msgid="8806849376915541655">"Votre empreinte faciale ne fonctionnait pas correctement et a été supprimée. Configurez-la à nouveau pour déverrouiller votre téléphone à l\'aide votre visage."</string>
+ <string name="biometric_dangling_notification_action_set_up" msgid="8246885009807817961">"Configuration"</string>
+ <string name="biometric_dangling_notification_action_not_now" msgid="8095249216864443491">"Pas maintenant"</string>
</resources>
diff --git a/core/res/res/values-gl/strings.xml b/core/res/res/values-gl/strings.xml
index 663ef9a..dfa4fe3 100644
--- a/core/res/res/values-gl/strings.xml
+++ b/core/res/res/values-gl/strings.xml
@@ -283,6 +283,8 @@
<string name="global_action_voice_assist" msgid="6655788068555086695">"Asistente voz"</string>
<string name="global_action_lockdown" msgid="2475471405907902963">"Bloquear"</string>
<string name="status_bar_notification_info_overflow" msgid="3330152558746563475">">999"</string>
+ <!-- no translation found for notification_compact_heads_up_reply (2425293958371284340) -->
+ <skip />
<string name="notification_hidden_text" msgid="2835519769868187223">"Notificación nova"</string>
<string name="notification_channel_physical_keyboard" msgid="5417306456125988096">"Teclado físico"</string>
<string name="notification_channel_security" msgid="8516754650348238057">"Seguranza"</string>
@@ -2413,22 +2415,15 @@
<string name="satellite_notification_open_message" msgid="4149234979688273729">"Abrir Mensaxes"</string>
<string name="satellite_notification_how_it_works" msgid="3132069321977520519">"Como funciona?"</string>
<string name="unarchival_session_app_label" msgid="6811856981546348205">"Pendente..."</string>
- <!-- no translation found for fingerprint_dangling_notification_title (7362075195588639989) -->
+ <string name="fingerprint_dangling_notification_title" msgid="7362075195588639989">"Configura de novo o desbloqueo dactilar"</string>
+ <!-- no translation found for fingerprint_dangling_notification_msg_1 (8517140433796229725) -->
<skip />
- <!-- no translation found for fingerprint_dangling_notification_msg_1 (6261149111900787302) -->
+ <!-- no translation found for fingerprint_dangling_notification_msg_2 (7578829498452127613) -->
<skip />
- <!-- no translation found for fingerprint_dangling_notification_msg_2 (7688302770424064884) -->
- <skip />
- <!-- no translation found for fingerprint_dangling_notification_msg_all_deleted_1 (2927018569542316055) -->
- <skip />
- <!-- no translation found for fingerprint_dangling_notification_msg_all_deleted_2 (6897989352716156176) -->
- <skip />
- <!-- no translation found for face_dangling_notification_title (947852541060975473) -->
- <skip />
- <!-- no translation found for face_dangling_notification_msg (8806849376915541655) -->
- <skip />
- <!-- no translation found for biometric_dangling_notification_action_set_up (8246885009807817961) -->
- <skip />
- <!-- no translation found for biometric_dangling_notification_action_not_now (8095249216864443491) -->
- <skip />
+ <string name="fingerprint_dangling_notification_msg_all_deleted_1" msgid="2927018569542316055">"A <xliff:g id="FINGERPRINT">%s</xliff:g> non funcionaba correctamente, polo que se eliminou. Configúraa de novo para desbloquear o teléfono usando a impresión dixital."</string>
+ <string name="fingerprint_dangling_notification_msg_all_deleted_2" msgid="6897989352716156176">"As impresións dixitais <xliff:g id="FINGERPRINT_0">%1$s</xliff:g> e <xliff:g id="FINGERPRINT_1">%2$s</xliff:g> non funcionaban correctamente, polo que se eliminaron. Configúraas de novo para desbloquear o teléfono usando a impresión dixital."</string>
+ <string name="face_dangling_notification_title" msgid="947852541060975473">"Configura de novo o desbloqueo facial"</string>
+ <string name="face_dangling_notification_msg" msgid="8806849376915541655">"O teu modelo facial non funcionaba correctamente, polo que se eliminou. Configúrao de novo para desbloquear o teléfono usando a cara."</string>
+ <string name="biometric_dangling_notification_action_set_up" msgid="8246885009807817961">"Configurar"</string>
+ <string name="biometric_dangling_notification_action_not_now" msgid="8095249216864443491">"Agora non"</string>
</resources>
diff --git a/core/res/res/values-gu/strings.xml b/core/res/res/values-gu/strings.xml
index dc42537..8a84d25 100644
--- a/core/res/res/values-gu/strings.xml
+++ b/core/res/res/values-gu/strings.xml
@@ -283,6 +283,8 @@
<string name="global_action_voice_assist" msgid="6655788068555086695">"વૉઇસ સહાય"</string>
<string name="global_action_lockdown" msgid="2475471405907902963">"લૉકડાઉન"</string>
<string name="status_bar_notification_info_overflow" msgid="3330152558746563475">"999+"</string>
+ <!-- no translation found for notification_compact_heads_up_reply (2425293958371284340) -->
+ <skip />
<string name="notification_hidden_text" msgid="2835519769868187223">"નવું નોટિફિકેશન"</string>
<string name="notification_channel_physical_keyboard" msgid="5417306456125988096">"ભૌતિક કીબોર્ડ"</string>
<string name="notification_channel_security" msgid="8516754650348238057">"સુરક્ષા"</string>
@@ -2413,22 +2415,15 @@
<string name="satellite_notification_open_message" msgid="4149234979688273729">"Messages ખોલો"</string>
<string name="satellite_notification_how_it_works" msgid="3132069321977520519">"તેની કામ કરવાની રીત"</string>
<string name="unarchival_session_app_label" msgid="6811856981546348205">"બાકી..."</string>
- <!-- no translation found for fingerprint_dangling_notification_title (7362075195588639989) -->
+ <string name="fingerprint_dangling_notification_title" msgid="7362075195588639989">"ફિંગરપ્રિન્ટ અનલૉક સુવિધાનું ફરી સેટઅપ કરો"</string>
+ <!-- no translation found for fingerprint_dangling_notification_msg_1 (8517140433796229725) -->
<skip />
- <!-- no translation found for fingerprint_dangling_notification_msg_1 (6261149111900787302) -->
+ <!-- no translation found for fingerprint_dangling_notification_msg_2 (7578829498452127613) -->
<skip />
- <!-- no translation found for fingerprint_dangling_notification_msg_2 (7688302770424064884) -->
- <skip />
- <!-- no translation found for fingerprint_dangling_notification_msg_all_deleted_1 (2927018569542316055) -->
- <skip />
- <!-- no translation found for fingerprint_dangling_notification_msg_all_deleted_2 (6897989352716156176) -->
- <skip />
- <!-- no translation found for face_dangling_notification_title (947852541060975473) -->
- <skip />
- <!-- no translation found for face_dangling_notification_msg (8806849376915541655) -->
- <skip />
- <!-- no translation found for biometric_dangling_notification_action_set_up (8246885009807817961) -->
- <skip />
- <!-- no translation found for biometric_dangling_notification_action_not_now (8095249216864443491) -->
- <skip />
+ <string name="fingerprint_dangling_notification_msg_all_deleted_1" msgid="2927018569542316055">"<xliff:g id="FINGERPRINT">%s</xliff:g> બરાબર કામ કરતી ન હતી અને તેને ડિલીટ કરવામાં આવી હતી. તમારા ફોનને ફિંગરપ્રિન્ટ વડે અનલૉક કરવા માટે, તેનું ફરીથી સેટઅપ કરો."</string>
+ <string name="fingerprint_dangling_notification_msg_all_deleted_2" msgid="6897989352716156176">"<xliff:g id="FINGERPRINT_0">%1$s</xliff:g> અને <xliff:g id="FINGERPRINT_1">%2$s</xliff:g> બરાબર કામ કરતી ન હતી અને તેને ડિલીટ કરવામાં આવી હતી. તમારા ફોનને તમારી ફિંગરપ્રિન્ટ વડે અનલૉક કરવા માટે, તેનું ફરીથી સેટઅપ કરો."</string>
+ <string name="face_dangling_notification_title" msgid="947852541060975473">"ફેસ અનલૉક સુવિધાનું ફરી સેટઅપ કરો"</string>
+ <string name="face_dangling_notification_msg" msgid="8806849376915541655">"તમારા ચહેરાનું મૉડલ બરાબર કામ કરતું ન હતું અને તેને ડિલીટ કરવામાં આવ્યું હતું. તમારા ફોનને ચહેરા વડે અનલૉક કરવા માટે, તેનું ફરીથી સેટઅપ કરો."</string>
+ <string name="biometric_dangling_notification_action_set_up" msgid="8246885009807817961">"સેટઅપ કરો"</string>
+ <string name="biometric_dangling_notification_action_not_now" msgid="8095249216864443491">"હમણાં નહીં"</string>
</resources>
diff --git a/core/res/res/values-hi/strings.xml b/core/res/res/values-hi/strings.xml
index 25f6ca1..e434912 100644
--- a/core/res/res/values-hi/strings.xml
+++ b/core/res/res/values-hi/strings.xml
@@ -283,6 +283,8 @@
<string name="global_action_voice_assist" msgid="6655788068555086695">"आवाज़ से डिवाइस का इस्तेमाल"</string>
<string name="global_action_lockdown" msgid="2475471405907902963">"लॉकडाउन"</string>
<string name="status_bar_notification_info_overflow" msgid="3330152558746563475">"999+"</string>
+ <!-- no translation found for notification_compact_heads_up_reply (2425293958371284340) -->
+ <skip />
<string name="notification_hidden_text" msgid="2835519769868187223">"नई सूचना"</string>
<string name="notification_channel_physical_keyboard" msgid="5417306456125988096">"सामान्य कीबोर्ड"</string>
<string name="notification_channel_security" msgid="8516754650348238057">"सुरक्षा"</string>
@@ -666,7 +668,7 @@
</string-array>
<string name="fingerprint_error_not_match" msgid="4599441812893438961">"फ़िंगरप्रिंट की पहचान नहीं हो पाई"</string>
<string name="fingerprint_udfps_error_not_match" msgid="8236930793223158856">"फ़िंगरप्रिंट की पहचान नहीं हो पाई"</string>
- <string name="fingerprint_dialog_use_fingerprint_instead" msgid="5590293588784953188">"चेहरा नहीं पहचाना गया. फ़िंगरप्रिंट इस्तेमाल करें."</string>
+ <string name="fingerprint_dialog_use_fingerprint_instead" msgid="5590293588784953188">"चेहरा की पहचान नहीं हो पाई. फ़िंगरप्रिंट का इस्तेमाल करें."</string>
<string name="fingerprint_authenticated" msgid="2024862866860283100">"फ़िंगरप्रिंट की पुष्टि हो गई"</string>
<string name="face_authenticated_no_confirmation_required" msgid="8867889115112348167">"चेहरे की पहचान की गई"</string>
<string name="face_authenticated_confirmation_required" msgid="6872632732508013755">"चेहरे की पहचान की गई, कृपया पुष्टि बटन दबाएं"</string>
@@ -2394,7 +2396,7 @@
<string name="keyboard_layout_notification_more_than_three_selected_message" msgid="1581834181578206937">"कीबोर्ड का लेआउट <xliff:g id="LAYOUT_1">%1$s</xliff:g>, <xliff:g id="LAYOUT_2">%2$s</xliff:g>, <xliff:g id="LAYOUT_3">%3$s</xliff:g>… पर सेट कर दिया गया है. इसे बदलने के लिए टैप करें."</string>
<string name="keyboard_layout_notification_multiple_selected_title" msgid="5242444914367024499">"फ़िज़िकल कीबोर्ड कॉन्फ़िगर किए गए"</string>
<string name="keyboard_layout_notification_multiple_selected_message" msgid="6576533454124419202">"कीबोर्ड देखने के लिए टैप करें"</string>
- <string name="profile_label_private" msgid="6463418670715290696">"निजी"</string>
+ <string name="profile_label_private" msgid="6463418670715290696">"प्राइवेट"</string>
<string name="profile_label_clone" msgid="769106052210954285">"क्लोन"</string>
<string name="profile_label_work" msgid="3495359133038584618">"ऑफ़िस"</string>
<string name="profile_label_work_2" msgid="4691533661598632135">"ऑफ़िस 2"</string>
@@ -2413,22 +2415,15 @@
<string name="satellite_notification_open_message" msgid="4149234979688273729">"Messages ऐप्लिकेशन खोलें"</string>
<string name="satellite_notification_how_it_works" msgid="3132069321977520519">"यह सेटिंग कैसे काम करती है"</string>
<string name="unarchival_session_app_label" msgid="6811856981546348205">"प्रोसेस जारी है..."</string>
- <!-- no translation found for fingerprint_dangling_notification_title (7362075195588639989) -->
+ <string name="fingerprint_dangling_notification_title" msgid="7362075195588639989">"फ़िंगरप्रिंट अनलॉक की सुविधा दोबारा सेट अप करें"</string>
+ <!-- no translation found for fingerprint_dangling_notification_msg_1 (8517140433796229725) -->
<skip />
- <!-- no translation found for fingerprint_dangling_notification_msg_1 (6261149111900787302) -->
+ <!-- no translation found for fingerprint_dangling_notification_msg_2 (7578829498452127613) -->
<skip />
- <!-- no translation found for fingerprint_dangling_notification_msg_2 (7688302770424064884) -->
- <skip />
- <!-- no translation found for fingerprint_dangling_notification_msg_all_deleted_1 (2927018569542316055) -->
- <skip />
- <!-- no translation found for fingerprint_dangling_notification_msg_all_deleted_2 (6897989352716156176) -->
- <skip />
- <!-- no translation found for face_dangling_notification_title (947852541060975473) -->
- <skip />
- <!-- no translation found for face_dangling_notification_msg (8806849376915541655) -->
- <skip />
- <!-- no translation found for biometric_dangling_notification_action_set_up (8246885009807817961) -->
- <skip />
- <!-- no translation found for biometric_dangling_notification_action_not_now (8095249216864443491) -->
- <skip />
+ <string name="fingerprint_dangling_notification_msg_all_deleted_1" msgid="2927018569542316055">"अच्छे से काम न करने की वजह से <xliff:g id="FINGERPRINT">%s</xliff:g> को मिटा दिया गया. फ़िंगरप्रिंट की मदद से फ़ोन अनलॉक करने के लिए, फ़िंगरप्रिंट अनलॉक की सुविधा को दोबारा सेट अप करें."</string>
+ <string name="fingerprint_dangling_notification_msg_all_deleted_2" msgid="6897989352716156176">"अच्छे से काम न करने की वजह से, <xliff:g id="FINGERPRINT_0">%1$s</xliff:g> और <xliff:g id="FINGERPRINT_1">%2$s</xliff:g> को मिटा दिया गया. फ़िंगरप्रिंट की मदद से फ़ोन अनलॉक करने के लिए, फ़िंगरप्रिंट अनलॉक की सुविधा दोबारा सेट अप करें."</string>
+ <string name="face_dangling_notification_title" msgid="947852541060975473">"फ़ेस अनलॉक की सुविधा को दोबारा सेट अप करें"</string>
+ <string name="face_dangling_notification_msg" msgid="8806849376915541655">"अच्छे से काम न करने की वजह से, चेहरे का मॉडल मिटा दिया गया. फ़ेस अनलॉक की सुविधा की मदद से फ़ोन अनलॉक करने के लिए, इस सुविधा को दोबारा सेट अप करें."</string>
+ <string name="biometric_dangling_notification_action_set_up" msgid="8246885009807817961">"सेट अप करें"</string>
+ <string name="biometric_dangling_notification_action_not_now" msgid="8095249216864443491">"अभी नहीं"</string>
</resources>
diff --git a/core/res/res/values-hr/strings.xml b/core/res/res/values-hr/strings.xml
index 117d4e5..0bd8be3 100644
--- a/core/res/res/values-hr/strings.xml
+++ b/core/res/res/values-hr/strings.xml
@@ -284,6 +284,8 @@
<string name="global_action_voice_assist" msgid="6655788068555086695">"Glasovna pomoć"</string>
<string name="global_action_lockdown" msgid="2475471405907902963">"Zaključaj"</string>
<string name="status_bar_notification_info_overflow" msgid="3330152558746563475">"999+"</string>
+ <!-- no translation found for notification_compact_heads_up_reply (2425293958371284340) -->
+ <skip />
<string name="notification_hidden_text" msgid="2835519769868187223">"Nova obavijest"</string>
<string name="notification_channel_physical_keyboard" msgid="5417306456125988096">"Fizička tipkovnica"</string>
<string name="notification_channel_security" msgid="8516754650348238057">"Sigurnost"</string>
@@ -2414,22 +2416,15 @@
<string name="satellite_notification_open_message" msgid="4149234979688273729">"Otvori Poruke"</string>
<string name="satellite_notification_how_it_works" msgid="3132069321977520519">"Kako to funkcionira"</string>
<string name="unarchival_session_app_label" msgid="6811856981546348205">"Na čekanju..."</string>
- <!-- no translation found for fingerprint_dangling_notification_title (7362075195588639989) -->
+ <string name="fingerprint_dangling_notification_title" msgid="7362075195588639989">"Ponovno postavite otključavanje otiskom prsta"</string>
+ <!-- no translation found for fingerprint_dangling_notification_msg_1 (8517140433796229725) -->
<skip />
- <!-- no translation found for fingerprint_dangling_notification_msg_1 (6261149111900787302) -->
+ <!-- no translation found for fingerprint_dangling_notification_msg_2 (7578829498452127613) -->
<skip />
- <!-- no translation found for fingerprint_dangling_notification_msg_2 (7688302770424064884) -->
- <skip />
- <!-- no translation found for fingerprint_dangling_notification_msg_all_deleted_1 (2927018569542316055) -->
- <skip />
- <!-- no translation found for fingerprint_dangling_notification_msg_all_deleted_2 (6897989352716156176) -->
- <skip />
- <!-- no translation found for face_dangling_notification_title (947852541060975473) -->
- <skip />
- <!-- no translation found for face_dangling_notification_msg (8806849376915541655) -->
- <skip />
- <!-- no translation found for biometric_dangling_notification_action_set_up (8246885009807817961) -->
- <skip />
- <!-- no translation found for biometric_dangling_notification_action_not_now (8095249216864443491) -->
- <skip />
+ <string name="fingerprint_dangling_notification_msg_all_deleted_1" msgid="2927018569542316055">"Otisak prsta <xliff:g id="FINGERPRINT">%s</xliff:g> nije dobro funkcionirao i izbrisan je. Ponovno ga postavite da biste otključali telefon otiskom prsta."</string>
+ <string name="fingerprint_dangling_notification_msg_all_deleted_2" msgid="6897989352716156176">"Otisci prstiju <xliff:g id="FINGERPRINT_0">%1$s</xliff:g> i <xliff:g id="FINGERPRINT_1">%2$s</xliff:g> nisu dobro funkcionirali i izbrisani su. Ponovno ih postavite da biste otključali telefon otiskom prsta."</string>
+ <string name="face_dangling_notification_title" msgid="947852541060975473">"Ponovno postavite otključavanje licem"</string>
+ <string name="face_dangling_notification_msg" msgid="8806849376915541655">"Model vašeg lica nije dobro funkcionirao i izbrisan je. Ponovno ga postavite da biste otključali telefon licem."</string>
+ <string name="biometric_dangling_notification_action_set_up" msgid="8246885009807817961">"Postavi"</string>
+ <string name="biometric_dangling_notification_action_not_now" msgid="8095249216864443491">"Ne sad"</string>
</resources>
diff --git a/core/res/res/values-hu/strings.xml b/core/res/res/values-hu/strings.xml
index de8fa84..aeebbdc 100644
--- a/core/res/res/values-hu/strings.xml
+++ b/core/res/res/values-hu/strings.xml
@@ -283,6 +283,8 @@
<string name="global_action_voice_assist" msgid="6655788068555086695">"Hangsegéd"</string>
<string name="global_action_lockdown" msgid="2475471405907902963">"Zárolás"</string>
<string name="status_bar_notification_info_overflow" msgid="3330152558746563475">"999+"</string>
+ <!-- no translation found for notification_compact_heads_up_reply (2425293958371284340) -->
+ <skip />
<string name="notification_hidden_text" msgid="2835519769868187223">"Új értesítés"</string>
<string name="notification_channel_physical_keyboard" msgid="5417306456125988096">"Fizikai billentyűzet"</string>
<string name="notification_channel_security" msgid="8516754650348238057">"Biztonság"</string>
@@ -1997,7 +1999,7 @@
<string name="work_mode_emergency_call_button" msgid="6818855962881612322">"Vészhelyzet"</string>
<string name="set_up_screen_lock_title" msgid="8346083801616474030">"Állítson be képernyőzárat"</string>
<string name="set_up_screen_lock_action_label" msgid="2687634803649209367">"Képernyőzár beállítása"</string>
- <string name="private_space_set_up_screen_lock_message" msgid="1109956797005149814">"A magánterület használatához állítson be képernyőzárat"</string>
+ <string name="private_space_set_up_screen_lock_message" msgid="1109956797005149814">"A privát terület használatához állítson be képernyőzárat"</string>
<string name="app_blocked_title" msgid="7353262160455028160">"Az alkalmazás nem hozzáférhető"</string>
<string name="app_blocked_message" msgid="542972921087873023">"A(z) <xliff:g id="APP_NAME">%1$s</xliff:g> jelenleg nem hozzáférhető."</string>
<string name="app_streaming_blocked_title" msgid="6090945835898766139">"A(z) <xliff:g id="ACTIVITY">%1$s</xliff:g> nem áll rendelkezése"</string>
@@ -2413,22 +2415,15 @@
<string name="satellite_notification_open_message" msgid="4149234979688273729">"A Messages megnyitása"</string>
<string name="satellite_notification_how_it_works" msgid="3132069321977520519">"Hogyan működik?"</string>
<string name="unarchival_session_app_label" msgid="6811856981546348205">"Függőben…"</string>
- <!-- no translation found for fingerprint_dangling_notification_title (7362075195588639989) -->
+ <string name="fingerprint_dangling_notification_title" msgid="7362075195588639989">"A Feloldás ujjlenyomattal funkció újbóli beállítása"</string>
+ <!-- no translation found for fingerprint_dangling_notification_msg_1 (8517140433796229725) -->
<skip />
- <!-- no translation found for fingerprint_dangling_notification_msg_1 (6261149111900787302) -->
+ <!-- no translation found for fingerprint_dangling_notification_msg_2 (7578829498452127613) -->
<skip />
- <!-- no translation found for fingerprint_dangling_notification_msg_2 (7688302770424064884) -->
- <skip />
- <!-- no translation found for fingerprint_dangling_notification_msg_all_deleted_1 (2927018569542316055) -->
- <skip />
- <!-- no translation found for fingerprint_dangling_notification_msg_all_deleted_2 (6897989352716156176) -->
- <skip />
- <!-- no translation found for face_dangling_notification_title (947852541060975473) -->
- <skip />
- <!-- no translation found for face_dangling_notification_msg (8806849376915541655) -->
- <skip />
- <!-- no translation found for biometric_dangling_notification_action_set_up (8246885009807817961) -->
- <skip />
- <!-- no translation found for biometric_dangling_notification_action_not_now (8095249216864443491) -->
- <skip />
+ <string name="fingerprint_dangling_notification_msg_all_deleted_1" msgid="2927018569542316055">"A(z) <xliff:g id="FINGERPRINT">%s</xliff:g> nem működött megfelelően, ezért törölve lett. Állítsa be újra, hogy feloldhassa a telefonját az ujjlenyomata segítségével."</string>
+ <string name="fingerprint_dangling_notification_msg_all_deleted_2" msgid="6897989352716156176">"A(z) <xliff:g id="FINGERPRINT_0">%1$s</xliff:g> és a(z) <xliff:g id="FINGERPRINT_1">%2$s</xliff:g> nem működtek megfelelően, ezért törölve lettek. Állítsa be őket újra, hogy feloldhassa a telefonját az ujjlenyomata segítségével."</string>
+ <string name="face_dangling_notification_title" msgid="947852541060975473">"Állítsa be újra az Arcalapú feloldást"</string>
+ <string name="face_dangling_notification_msg" msgid="8806849376915541655">"Az arcmodellje nem működött megfelelően, ezért törölve lett. Állítsa be újra, hogy feloldhassa a telefonját az arca segítségével."</string>
+ <string name="biometric_dangling_notification_action_set_up" msgid="8246885009807817961">"Beállítás"</string>
+ <string name="biometric_dangling_notification_action_not_now" msgid="8095249216864443491">"Most nem"</string>
</resources>
diff --git a/core/res/res/values-hy/strings.xml b/core/res/res/values-hy/strings.xml
index d379934..a0e0bd2 100644
--- a/core/res/res/values-hy/strings.xml
+++ b/core/res/res/values-hy/strings.xml
@@ -283,6 +283,8 @@
<string name="global_action_voice_assist" msgid="6655788068555086695">"Ձայնային օգնութ"</string>
<string name="global_action_lockdown" msgid="2475471405907902963">"Արգելափակում"</string>
<string name="status_bar_notification_info_overflow" msgid="3330152558746563475">"999+"</string>
+ <!-- no translation found for notification_compact_heads_up_reply (2425293958371284340) -->
+ <skip />
<string name="notification_hidden_text" msgid="2835519769868187223">"Նոր ծանուցում"</string>
<string name="notification_channel_physical_keyboard" msgid="5417306456125988096">"Ֆիզիկական ստեղնաշար"</string>
<string name="notification_channel_security" msgid="8516754650348238057">"Անվտանգություն"</string>
@@ -2394,7 +2396,7 @@
<string name="keyboard_layout_notification_more_than_three_selected_message" msgid="1581834181578206937">"Ստեղնաշարի համար կարգավորված են <xliff:g id="LAYOUT_1">%1$s</xliff:g>, <xliff:g id="LAYOUT_2">%2$s</xliff:g>, <xliff:g id="LAYOUT_3">%3$s</xliff:g> դասավորությունները։ Հպեք փոխելու համար։"</string>
<string name="keyboard_layout_notification_multiple_selected_title" msgid="5242444914367024499">"Ֆիզիկական ստեղնաշարերը կարգավորված են"</string>
<string name="keyboard_layout_notification_multiple_selected_message" msgid="6576533454124419202">"Հպեք՝ ստեղնաշարերը դիտելու համար"</string>
- <string name="profile_label_private" msgid="6463418670715290696">"Անձնական"</string>
+ <string name="profile_label_private" msgid="6463418670715290696">"Մասնավոր"</string>
<string name="profile_label_clone" msgid="769106052210954285">"Կլոն"</string>
<string name="profile_label_work" msgid="3495359133038584618">"Աշխատանքային"</string>
<string name="profile_label_work_2" msgid="4691533661598632135">"Աշխատանքային 2"</string>
@@ -2413,22 +2415,15 @@
<string name="satellite_notification_open_message" msgid="4149234979688273729">"Բացել Messages-ը"</string>
<string name="satellite_notification_how_it_works" msgid="3132069321977520519">"Ինչպես է դա աշխատում"</string>
<string name="unarchival_session_app_label" msgid="6811856981546348205">"Առկախ է…"</string>
- <!-- no translation found for fingerprint_dangling_notification_title (7362075195588639989) -->
+ <string name="fingerprint_dangling_notification_title" msgid="7362075195588639989">"Նորից կարգավորեք մատնահետքով ապակողպումը"</string>
+ <!-- no translation found for fingerprint_dangling_notification_msg_1 (8517140433796229725) -->
<skip />
- <!-- no translation found for fingerprint_dangling_notification_msg_1 (6261149111900787302) -->
+ <!-- no translation found for fingerprint_dangling_notification_msg_2 (7578829498452127613) -->
<skip />
- <!-- no translation found for fingerprint_dangling_notification_msg_2 (7688302770424064884) -->
- <skip />
- <!-- no translation found for fingerprint_dangling_notification_msg_all_deleted_1 (2927018569542316055) -->
- <skip />
- <!-- no translation found for fingerprint_dangling_notification_msg_all_deleted_2 (6897989352716156176) -->
- <skip />
- <!-- no translation found for face_dangling_notification_title (947852541060975473) -->
- <skip />
- <!-- no translation found for face_dangling_notification_msg (8806849376915541655) -->
- <skip />
- <!-- no translation found for biometric_dangling_notification_action_set_up (8246885009807817961) -->
- <skip />
- <!-- no translation found for biometric_dangling_notification_action_not_now (8095249216864443491) -->
- <skip />
+ <string name="fingerprint_dangling_notification_msg_all_deleted_1" msgid="2927018569542316055">"«<xliff:g id="FINGERPRINT">%s</xliff:g>» մատնահետքը հեռացվել է, քանի որ լավ չէր աշխատում։ Նորից կարգավորեք այն՝ ձեր հեռախոսը մատնահետքով ապակողպելու համար։"</string>
+ <string name="fingerprint_dangling_notification_msg_all_deleted_2" msgid="6897989352716156176">"«<xliff:g id="FINGERPRINT_0">%1$s</xliff:g>» և «<xliff:g id="FINGERPRINT_1">%2$s</xliff:g>» մատնահետքերը հեռացվել են, քանի որ լավ չէին աշխատում։ Նորից կարգավորեք դրանք՝ ձեր հեռախոսը մատնահետքով ապակողպելու համար։"</string>
+ <string name="face_dangling_notification_title" msgid="947852541060975473">"Նորից կարգավորեք դեմքով ապակողպումը"</string>
+ <string name="face_dangling_notification_msg" msgid="8806849376915541655">"Ձեր դեմքի նմուշը հեռացվել է, քանի որ լավ չէր աշխատում։ Նորից կարգավորեք այն՝ ձեր հեռախոսը դեմքով ապակողպելու համար։"</string>
+ <string name="biometric_dangling_notification_action_set_up" msgid="8246885009807817961">"Կարգավորել"</string>
+ <string name="biometric_dangling_notification_action_not_now" msgid="8095249216864443491">"Ոչ հիմա"</string>
</resources>
diff --git a/core/res/res/values-in/strings.xml b/core/res/res/values-in/strings.xml
index e6634d2..fb6180b 100644
--- a/core/res/res/values-in/strings.xml
+++ b/core/res/res/values-in/strings.xml
@@ -283,6 +283,8 @@
<string name="global_action_voice_assist" msgid="6655788068555086695">"Bantuan Suara"</string>
<string name="global_action_lockdown" msgid="2475471405907902963">"Kunci total"</string>
<string name="status_bar_notification_info_overflow" msgid="3330152558746563475">"999+"</string>
+ <!-- no translation found for notification_compact_heads_up_reply (2425293958371284340) -->
+ <skip />
<string name="notification_hidden_text" msgid="2835519769868187223">"Notifikasi baru"</string>
<string name="notification_channel_physical_keyboard" msgid="5417306456125988096">"Keyboard fisik"</string>
<string name="notification_channel_security" msgid="8516754650348238057">"Keamanan"</string>
@@ -2413,22 +2415,15 @@
<string name="satellite_notification_open_message" msgid="4149234979688273729">"Buka Message"</string>
<string name="satellite_notification_how_it_works" msgid="3132069321977520519">"Cara kerjanya"</string>
<string name="unarchival_session_app_label" msgid="6811856981546348205">"Tertunda..."</string>
- <!-- no translation found for fingerprint_dangling_notification_title (7362075195588639989) -->
+ <string name="fingerprint_dangling_notification_title" msgid="7362075195588639989">"Siapkan Buka dengan Sidik Jari lagi"</string>
+ <!-- no translation found for fingerprint_dangling_notification_msg_1 (8517140433796229725) -->
<skip />
- <!-- no translation found for fingerprint_dangling_notification_msg_1 (6261149111900787302) -->
+ <!-- no translation found for fingerprint_dangling_notification_msg_2 (7578829498452127613) -->
<skip />
- <!-- no translation found for fingerprint_dangling_notification_msg_2 (7688302770424064884) -->
- <skip />
- <!-- no translation found for fingerprint_dangling_notification_msg_all_deleted_1 (2927018569542316055) -->
- <skip />
- <!-- no translation found for fingerprint_dangling_notification_msg_all_deleted_2 (6897989352716156176) -->
- <skip />
- <!-- no translation found for face_dangling_notification_title (947852541060975473) -->
- <skip />
- <!-- no translation found for face_dangling_notification_msg (8806849376915541655) -->
- <skip />
- <!-- no translation found for biometric_dangling_notification_action_set_up (8246885009807817961) -->
- <skip />
- <!-- no translation found for biometric_dangling_notification_action_not_now (8095249216864443491) -->
- <skip />
+ <string name="fingerprint_dangling_notification_msg_all_deleted_1" msgid="2927018569542316055">"<xliff:g id="FINGERPRINT">%s</xliff:g> tidak berfungsi dengan baik dan telah dihapus. Siapkan lagi untuk membuka kunci ponsel Anda dengan sidik jari."</string>
+ <string name="fingerprint_dangling_notification_msg_all_deleted_2" msgid="6897989352716156176">"<xliff:g id="FINGERPRINT_0">%1$s</xliff:g> dan <xliff:g id="FINGERPRINT_1">%2$s</xliff:g> tidak berfungsi dengan baik dan telah dihapus. Siapkan lagi untuk membuka kunci ponsel Anda dengan sidik jari."</string>
+ <string name="face_dangling_notification_title" msgid="947852541060975473">"Siapkan Buka dengan Wajah lagi"</string>
+ <string name="face_dangling_notification_msg" msgid="8806849376915541655">"Model wajah Anda tidak berfungsi dengan baik dan telah dihapus. Siapkan lagi untuk membuka kunci ponsel Anda dengan wajah."</string>
+ <string name="biometric_dangling_notification_action_set_up" msgid="8246885009807817961">"Penyiapan"</string>
+ <string name="biometric_dangling_notification_action_not_now" msgid="8095249216864443491">"Lain kali"</string>
</resources>
diff --git a/core/res/res/values-is/strings.xml b/core/res/res/values-is/strings.xml
index f77a18a..3f96dae 100644
--- a/core/res/res/values-is/strings.xml
+++ b/core/res/res/values-is/strings.xml
@@ -283,6 +283,8 @@
<string name="global_action_voice_assist" msgid="6655788068555086695">"Raddaðstoð"</string>
<string name="global_action_lockdown" msgid="2475471405907902963">"Læsing"</string>
<string name="status_bar_notification_info_overflow" msgid="3330152558746563475">"999+"</string>
+ <!-- no translation found for notification_compact_heads_up_reply (2425293958371284340) -->
+ <skip />
<string name="notification_hidden_text" msgid="2835519769868187223">"Ný tilkynning"</string>
<string name="notification_channel_physical_keyboard" msgid="5417306456125988096">"Vélbúnaðarlyklaborð"</string>
<string name="notification_channel_security" msgid="8516754650348238057">"Öryggi"</string>
@@ -2413,22 +2415,15 @@
<string name="satellite_notification_open_message" msgid="4149234979688273729">"Opna Messages"</string>
<string name="satellite_notification_how_it_works" msgid="3132069321977520519">"Svona virkar þetta"</string>
<string name="unarchival_session_app_label" msgid="6811856981546348205">"Í bið…"</string>
- <!-- no translation found for fingerprint_dangling_notification_title (7362075195588639989) -->
+ <string name="fingerprint_dangling_notification_title" msgid="7362075195588639989">"Setja upp fingrafarskenni aftur"</string>
+ <!-- no translation found for fingerprint_dangling_notification_msg_1 (8517140433796229725) -->
<skip />
- <!-- no translation found for fingerprint_dangling_notification_msg_1 (6261149111900787302) -->
+ <!-- no translation found for fingerprint_dangling_notification_msg_2 (7578829498452127613) -->
<skip />
- <!-- no translation found for fingerprint_dangling_notification_msg_2 (7688302770424064884) -->
- <skip />
- <!-- no translation found for fingerprint_dangling_notification_msg_all_deleted_1 (2927018569542316055) -->
- <skip />
- <!-- no translation found for fingerprint_dangling_notification_msg_all_deleted_2 (6897989352716156176) -->
- <skip />
- <!-- no translation found for face_dangling_notification_title (947852541060975473) -->
- <skip />
- <!-- no translation found for face_dangling_notification_msg (8806849376915541655) -->
- <skip />
- <!-- no translation found for biometric_dangling_notification_action_set_up (8246885009807817961) -->
- <skip />
- <!-- no translation found for biometric_dangling_notification_action_not_now (8095249216864443491) -->
- <skip />
+ <string name="fingerprint_dangling_notification_msg_all_deleted_1" msgid="2927018569542316055">"<xliff:g id="FINGERPRINT">%s</xliff:g> virkaði illa og var eytt. Settu það upp aftur til að taka símann úr lás með fingrafari."</string>
+ <string name="fingerprint_dangling_notification_msg_all_deleted_2" msgid="6897989352716156176">"<xliff:g id="FINGERPRINT_0">%1$s</xliff:g> og <xliff:g id="FINGERPRINT_1">%2$s</xliff:g> virkuðu illa og var eytt. Settu þau upp aftur til að taka símann úr lás með fingrafarinu þínu."</string>
+ <string name="face_dangling_notification_title" msgid="947852541060975473">"Setja upp andlitskenni aftur"</string>
+ <string name="face_dangling_notification_msg" msgid="8806849376915541655">"Andlitslíkanið þitt virkaði illa og var eytt. Settu það upp aftur til að taka símann úr lás með andlitinu."</string>
+ <string name="biometric_dangling_notification_action_set_up" msgid="8246885009807817961">"Setja upp"</string>
+ <string name="biometric_dangling_notification_action_not_now" msgid="8095249216864443491">"Ekki núna"</string>
</resources>
diff --git a/core/res/res/values-it/strings.xml b/core/res/res/values-it/strings.xml
index 95ccad5..f22b653 100644
--- a/core/res/res/values-it/strings.xml
+++ b/core/res/res/values-it/strings.xml
@@ -284,6 +284,8 @@
<string name="global_action_voice_assist" msgid="6655788068555086695">"Voice Assist"</string>
<string name="global_action_lockdown" msgid="2475471405907902963">"Blocco"</string>
<string name="status_bar_notification_info_overflow" msgid="3330152558746563475">"999+"</string>
+ <!-- no translation found for notification_compact_heads_up_reply (2425293958371284340) -->
+ <skip />
<string name="notification_hidden_text" msgid="2835519769868187223">"Nuova notifica"</string>
<string name="notification_channel_physical_keyboard" msgid="5417306456125988096">"Tastiera fisica"</string>
<string name="notification_channel_security" msgid="8516754650348238057">"Sicurezza"</string>
@@ -2414,22 +2416,15 @@
<string name="satellite_notification_open_message" msgid="4149234979688273729">"Apri Messaggi"</string>
<string name="satellite_notification_how_it_works" msgid="3132069321977520519">"Come funziona"</string>
<string name="unarchival_session_app_label" msgid="6811856981546348205">"In attesa…"</string>
- <!-- no translation found for fingerprint_dangling_notification_title (7362075195588639989) -->
+ <string name="fingerprint_dangling_notification_title" msgid="7362075195588639989">"Riconfigura lo Sblocco con l\'Impronta"</string>
+ <!-- no translation found for fingerprint_dangling_notification_msg_1 (8517140433796229725) -->
<skip />
- <!-- no translation found for fingerprint_dangling_notification_msg_1 (6261149111900787302) -->
+ <!-- no translation found for fingerprint_dangling_notification_msg_2 (7578829498452127613) -->
<skip />
- <!-- no translation found for fingerprint_dangling_notification_msg_2 (7688302770424064884) -->
- <skip />
- <!-- no translation found for fingerprint_dangling_notification_msg_all_deleted_1 (2927018569542316055) -->
- <skip />
- <!-- no translation found for fingerprint_dangling_notification_msg_all_deleted_2 (6897989352716156176) -->
- <skip />
- <!-- no translation found for face_dangling_notification_title (947852541060975473) -->
- <skip />
- <!-- no translation found for face_dangling_notification_msg (8806849376915541655) -->
- <skip />
- <!-- no translation found for biometric_dangling_notification_action_set_up (8246885009807817961) -->
- <skip />
- <!-- no translation found for biometric_dangling_notification_action_not_now (8095249216864443491) -->
- <skip />
+ <string name="fingerprint_dangling_notification_msg_all_deleted_1" msgid="2927018569542316055">"<xliff:g id="FINGERPRINT">%s</xliff:g> non funzionava bene ed è stata eliminata. Riconfigurala per sbloccare lo smartphone con l\'impronta."</string>
+ <string name="fingerprint_dangling_notification_msg_all_deleted_2" msgid="6897989352716156176">"<xliff:g id="FINGERPRINT_0">%1$s</xliff:g> e <xliff:g id="FINGERPRINT_1">%2$s</xliff:g> non funzionavano bene e sono state eliminate. Riconfigurale per sbloccare lo smartphone con l\'impronta."</string>
+ <string name="face_dangling_notification_title" msgid="947852541060975473">"Riconfigura lo Sblocco con il Volto"</string>
+ <string name="face_dangling_notification_msg" msgid="8806849376915541655">"Il tuo modello del volto non funzionava bene ed è stato eliminato. Riconfiguralo per sbloccare lo smartphone con il volto."</string>
+ <string name="biometric_dangling_notification_action_set_up" msgid="8246885009807817961">"Configura"</string>
+ <string name="biometric_dangling_notification_action_not_now" msgid="8095249216864443491">"Non ora"</string>
</resources>
diff --git a/core/res/res/values-iw/strings.xml b/core/res/res/values-iw/strings.xml
index fee437a..3a924e1 100644
--- a/core/res/res/values-iw/strings.xml
+++ b/core/res/res/values-iw/strings.xml
@@ -284,6 +284,8 @@
<string name="global_action_voice_assist" msgid="6655788068555086695">"האסיסטנט"</string>
<string name="global_action_lockdown" msgid="2475471405907902963">"נעילה"</string>
<string name="status_bar_notification_info_overflow" msgid="3330152558746563475">"999+"</string>
+ <!-- no translation found for notification_compact_heads_up_reply (2425293958371284340) -->
+ <skip />
<string name="notification_hidden_text" msgid="2835519769868187223">"התראה חדשה"</string>
<string name="notification_channel_physical_keyboard" msgid="5417306456125988096">"מקלדת פיזית"</string>
<string name="notification_channel_security" msgid="8516754650348238057">"אבטחה"</string>
@@ -2395,7 +2397,7 @@
<string name="keyboard_layout_notification_more_than_three_selected_message" msgid="1581834181578206937">"פריסת המקלדת מוגדרת ל<xliff:g id="LAYOUT_1">%1$s</xliff:g>, <xliff:g id="LAYOUT_2">%2$s</xliff:g>, <xliff:g id="LAYOUT_3">%3$s</xliff:g>… אפשר להקיש כדי לשנות את ההגדרה הזו."</string>
<string name="keyboard_layout_notification_multiple_selected_title" msgid="5242444914367024499">"הוגדרו מקלדות פיזיות"</string>
<string name="keyboard_layout_notification_multiple_selected_message" msgid="6576533454124419202">"יש להקיש כדי להציג את המקלדות"</string>
- <string name="profile_label_private" msgid="6463418670715290696">"פרטי"</string>
+ <string name="profile_label_private" msgid="6463418670715290696">"פרופיל פרטי"</string>
<string name="profile_label_clone" msgid="769106052210954285">"שכפול"</string>
<string name="profile_label_work" msgid="3495359133038584618">"פרופיל עבודה"</string>
<string name="profile_label_work_2" msgid="4691533661598632135">"פרופיל עבודה 2"</string>
@@ -2414,22 +2416,15 @@
<string name="satellite_notification_open_message" msgid="4149234979688273729">"לפתיחת Messages"</string>
<string name="satellite_notification_how_it_works" msgid="3132069321977520519">"איך זה עובד"</string>
<string name="unarchival_session_app_label" msgid="6811856981546348205">"בהמתנה..."</string>
- <!-- no translation found for fingerprint_dangling_notification_title (7362075195588639989) -->
+ <string name="fingerprint_dangling_notification_title" msgid="7362075195588639989">"הגדרה חוזרת של \'ביטול הנעילה בטביעת אצבע\'"</string>
+ <!-- no translation found for fingerprint_dangling_notification_msg_1 (8517140433796229725) -->
<skip />
- <!-- no translation found for fingerprint_dangling_notification_msg_1 (6261149111900787302) -->
+ <!-- no translation found for fingerprint_dangling_notification_msg_2 (7578829498452127613) -->
<skip />
- <!-- no translation found for fingerprint_dangling_notification_msg_2 (7688302770424064884) -->
- <skip />
- <!-- no translation found for fingerprint_dangling_notification_msg_all_deleted_1 (2927018569542316055) -->
- <skip />
- <!-- no translation found for fingerprint_dangling_notification_msg_all_deleted_2 (6897989352716156176) -->
- <skip />
- <!-- no translation found for face_dangling_notification_title (947852541060975473) -->
- <skip />
- <!-- no translation found for face_dangling_notification_msg (8806849376915541655) -->
- <skip />
- <!-- no translation found for biometric_dangling_notification_action_set_up (8246885009807817961) -->
- <skip />
- <!-- no translation found for biometric_dangling_notification_action_not_now (8095249216864443491) -->
- <skip />
+ <string name="fingerprint_dangling_notification_msg_all_deleted_1" msgid="2927018569542316055">"<xliff:g id="FINGERPRINT">%s</xliff:g> לא פעלה היטב ולכן היא נמחקה. עליך להגדיר אותה שוב כדי שתהיה לך אפשרות לבטל את הנעילה של הטלפון באמצעות טביעת אצבע."</string>
+ <string name="fingerprint_dangling_notification_msg_all_deleted_2" msgid="6897989352716156176">"<xliff:g id="FINGERPRINT_0">%1$s</xliff:g> ו<xliff:g id="FINGERPRINT_1">%2$s</xliff:g> לא פעלו היטב ולכן הן נמחקו. עליך להגדיר אותן שוב כדי שתהיה לך אפשרות לבטל את הנעילה של הטלפון באמצעות טביעת אצבע."</string>
+ <string name="face_dangling_notification_title" msgid="947852541060975473">"הגדרה חוזרת של \'פתיחה ע\"י זיהוי הפנים\'"</string>
+ <string name="face_dangling_notification_msg" msgid="8806849376915541655">"התבנית לזיהוי הפנים לא פעלה היטב ולכן היא נמחקה. עליך להגדיר אותה שוב כדי שתהיה לך אפשרות לבטל את הנעילה של הטלפון באמצעות זיהוי הפנים."</string>
+ <string name="biometric_dangling_notification_action_set_up" msgid="8246885009807817961">"הגדרה"</string>
+ <string name="biometric_dangling_notification_action_not_now" msgid="8095249216864443491">"לא עכשיו"</string>
</resources>
diff --git a/core/res/res/values-ja/strings.xml b/core/res/res/values-ja/strings.xml
index 726db1c..e31d795 100644
--- a/core/res/res/values-ja/strings.xml
+++ b/core/res/res/values-ja/strings.xml
@@ -283,6 +283,8 @@
<string name="global_action_voice_assist" msgid="6655788068555086695">"音声アシスト"</string>
<string name="global_action_lockdown" msgid="2475471405907902963">"ロックダウン"</string>
<string name="status_bar_notification_info_overflow" msgid="3330152558746563475">"999+"</string>
+ <!-- no translation found for notification_compact_heads_up_reply (2425293958371284340) -->
+ <skip />
<string name="notification_hidden_text" msgid="2835519769868187223">"新しい通知"</string>
<string name="notification_channel_physical_keyboard" msgid="5417306456125988096">"物理キーボード"</string>
<string name="notification_channel_security" msgid="8516754650348238057">"セキュリティ"</string>
@@ -2413,22 +2415,15 @@
<string name="satellite_notification_open_message" msgid="4149234979688273729">"メッセージ アプリを開く"</string>
<string name="satellite_notification_how_it_works" msgid="3132069321977520519">"仕組み"</string>
<string name="unarchival_session_app_label" msgid="6811856981546348205">"保留中..."</string>
- <!-- no translation found for fingerprint_dangling_notification_title (7362075195588639989) -->
+ <string name="fingerprint_dangling_notification_title" msgid="7362075195588639989">"指紋認証をもう一度設定してください"</string>
+ <!-- no translation found for fingerprint_dangling_notification_msg_1 (8517140433796229725) -->
<skip />
- <!-- no translation found for fingerprint_dangling_notification_msg_1 (6261149111900787302) -->
+ <!-- no translation found for fingerprint_dangling_notification_msg_2 (7578829498452127613) -->
<skip />
- <!-- no translation found for fingerprint_dangling_notification_msg_2 (7688302770424064884) -->
- <skip />
- <!-- no translation found for fingerprint_dangling_notification_msg_all_deleted_1 (2927018569542316055) -->
- <skip />
- <!-- no translation found for fingerprint_dangling_notification_msg_all_deleted_2 (6897989352716156176) -->
- <skip />
- <!-- no translation found for face_dangling_notification_title (947852541060975473) -->
- <skip />
- <!-- no translation found for face_dangling_notification_msg (8806849376915541655) -->
- <skip />
- <!-- no translation found for biometric_dangling_notification_action_set_up (8246885009807817961) -->
- <skip />
- <!-- no translation found for biometric_dangling_notification_action_not_now (8095249216864443491) -->
- <skip />
+ <string name="fingerprint_dangling_notification_msg_all_deleted_1" msgid="2927018569542316055">"<xliff:g id="FINGERPRINT">%s</xliff:g> が正常に機能せず、削除されました。指紋認証でスマートフォンのロックを解除するには、設定し直してください。"</string>
+ <string name="fingerprint_dangling_notification_msg_all_deleted_2" msgid="6897989352716156176">"<xliff:g id="FINGERPRINT_0">%1$s</xliff:g> と <xliff:g id="FINGERPRINT_1">%2$s</xliff:g> が正常に機能せず、削除されました。指紋認証でスマートフォンのロックを解除するには、設定し直してください。"</string>
+ <string name="face_dangling_notification_title" msgid="947852541060975473">"顔認証をもう一度設定してください"</string>
+ <string name="face_dangling_notification_msg" msgid="8806849376915541655">"顔モデルが正常に機能せず、削除されました。顔認証でスマートフォンのロックを解除するには、設定し直してください。"</string>
+ <string name="biometric_dangling_notification_action_set_up" msgid="8246885009807817961">"設定"</string>
+ <string name="biometric_dangling_notification_action_not_now" msgid="8095249216864443491">"後で"</string>
</resources>
diff --git a/core/res/res/values-ka/strings.xml b/core/res/res/values-ka/strings.xml
index bdf6c48..a284719 100644
--- a/core/res/res/values-ka/strings.xml
+++ b/core/res/res/values-ka/strings.xml
@@ -283,6 +283,8 @@
<string name="global_action_voice_assist" msgid="6655788068555086695">"ხმოვანი ასისტ."</string>
<string name="global_action_lockdown" msgid="2475471405907902963">"დაბლოკვა"</string>
<string name="status_bar_notification_info_overflow" msgid="3330152558746563475">"999+"</string>
+ <!-- no translation found for notification_compact_heads_up_reply (2425293958371284340) -->
+ <skip />
<string name="notification_hidden_text" msgid="2835519769868187223">"ახალი შეტყობინება"</string>
<string name="notification_channel_physical_keyboard" msgid="5417306456125988096">"ფიზიკური კლავიატურა"</string>
<string name="notification_channel_security" msgid="8516754650348238057">"უსაფრთხოება"</string>
@@ -2394,7 +2396,7 @@
<string name="keyboard_layout_notification_more_than_three_selected_message" msgid="1581834181578206937">"დაყენდა კლავიატურის განლაგება: <xliff:g id="LAYOUT_1">%1$s</xliff:g>, <xliff:g id="LAYOUT_2">%2$s</xliff:g>, <xliff:g id="LAYOUT_3">%3$s</xliff:g>… შეეხეთ შესაცვლელად."</string>
<string name="keyboard_layout_notification_multiple_selected_title" msgid="5242444914367024499">"ფიზიკური კლავიატურები კონფიგურირებულია"</string>
<string name="keyboard_layout_notification_multiple_selected_message" msgid="6576533454124419202">"შეეხეთ კლავიატურების სანახავად"</string>
- <string name="profile_label_private" msgid="6463418670715290696">"პირადი"</string>
+ <string name="profile_label_private" msgid="6463418670715290696">"კერძო"</string>
<string name="profile_label_clone" msgid="769106052210954285">"კლონი"</string>
<string name="profile_label_work" msgid="3495359133038584618">"სამსახური"</string>
<string name="profile_label_work_2" msgid="4691533661598632135">"სამსახური 2"</string>
@@ -2413,22 +2415,15 @@
<string name="satellite_notification_open_message" msgid="4149234979688273729">"Messages-ის გახსნა"</string>
<string name="satellite_notification_how_it_works" msgid="3132069321977520519">"მუშაობის პრინციპი"</string>
<string name="unarchival_session_app_label" msgid="6811856981546348205">"მომლოდინე..."</string>
- <!-- no translation found for fingerprint_dangling_notification_title (7362075195588639989) -->
+ <string name="fingerprint_dangling_notification_title" msgid="7362075195588639989">"ანაბეჭდით განბლოკვის ხელახლა დაყენება"</string>
+ <!-- no translation found for fingerprint_dangling_notification_msg_1 (8517140433796229725) -->
<skip />
- <!-- no translation found for fingerprint_dangling_notification_msg_1 (6261149111900787302) -->
+ <!-- no translation found for fingerprint_dangling_notification_msg_2 (7578829498452127613) -->
<skip />
- <!-- no translation found for fingerprint_dangling_notification_msg_2 (7688302770424064884) -->
- <skip />
- <!-- no translation found for fingerprint_dangling_notification_msg_all_deleted_1 (2927018569542316055) -->
- <skip />
- <!-- no translation found for fingerprint_dangling_notification_msg_all_deleted_2 (6897989352716156176) -->
- <skip />
- <!-- no translation found for face_dangling_notification_title (947852541060975473) -->
- <skip />
- <!-- no translation found for face_dangling_notification_msg (8806849376915541655) -->
- <skip />
- <!-- no translation found for biometric_dangling_notification_action_set_up (8246885009807817961) -->
- <skip />
- <!-- no translation found for biometric_dangling_notification_action_not_now (8095249216864443491) -->
- <skip />
+ <string name="fingerprint_dangling_notification_msg_all_deleted_1" msgid="2927018569542316055">"<xliff:g id="FINGERPRINT">%s</xliff:g> კარგად არ მუშაობდა და წაიშალა. თავიდან დააყენეთ, რათა თქვენი ტელეფონი თითის ანაბეჭდის საშუალებით განბლოკოთ."</string>
+ <string name="fingerprint_dangling_notification_msg_all_deleted_2" msgid="6897989352716156176">"<xliff:g id="FINGERPRINT_0">%1$s</xliff:g> და <xliff:g id="FINGERPRINT_1">%2$s</xliff:g> კარგად არ მუშაობდნენ და წაიშალა. თავიდან დააყენეთ, რათა თქვენი ტელეფონი თითის ანაბეჭდის საშუალებით განბლოკოთ."</string>
+ <string name="face_dangling_notification_title" msgid="947852541060975473">"დააყენეთ სახით განბლოკვა ხელახლა"</string>
+ <string name="face_dangling_notification_msg" msgid="8806849376915541655">"თქვენი სახის მოდელი კარგად არ მუშაობდა და წაიშალა. თავიდან დააყენეთ, რათა თქვენი ტელეფონი სახის საშუალებით განბლოკოთ."</string>
+ <string name="biometric_dangling_notification_action_set_up" msgid="8246885009807817961">"დაყენება"</string>
+ <string name="biometric_dangling_notification_action_not_now" msgid="8095249216864443491">"ახლა არა"</string>
</resources>
diff --git a/core/res/res/values-kk/strings.xml b/core/res/res/values-kk/strings.xml
index 55cf92b..e407c09 100644
--- a/core/res/res/values-kk/strings.xml
+++ b/core/res/res/values-kk/strings.xml
@@ -283,6 +283,8 @@
<string name="global_action_voice_assist" msgid="6655788068555086695">"Дауыс көмекшісі"</string>
<string name="global_action_lockdown" msgid="2475471405907902963">"Құлыптау"</string>
<string name="status_bar_notification_info_overflow" msgid="3330152558746563475">"999+"</string>
+ <!-- no translation found for notification_compact_heads_up_reply (2425293958371284340) -->
+ <skip />
<string name="notification_hidden_text" msgid="2835519769868187223">"Жаңа хабарландыру"</string>
<string name="notification_channel_physical_keyboard" msgid="5417306456125988096">"Физикалық пернетақта"</string>
<string name="notification_channel_security" msgid="8516754650348238057">"Қауіпсіздік"</string>
@@ -2413,22 +2415,15 @@
<string name="satellite_notification_open_message" msgid="4149234979688273729">"Messages қолданбасын ашу"</string>
<string name="satellite_notification_how_it_works" msgid="3132069321977520519">"Бұл қалай орындалады?"</string>
<string name="unarchival_session_app_label" msgid="6811856981546348205">"Дайын емес…"</string>
- <!-- no translation found for fingerprint_dangling_notification_title (7362075195588639989) -->
+ <string name="fingerprint_dangling_notification_title" msgid="7362075195588639989">"Саусақ ізімен ашу функциясын қайта реттеу"</string>
+ <!-- no translation found for fingerprint_dangling_notification_msg_1 (8517140433796229725) -->
<skip />
- <!-- no translation found for fingerprint_dangling_notification_msg_1 (6261149111900787302) -->
+ <!-- no translation found for fingerprint_dangling_notification_msg_2 (7578829498452127613) -->
<skip />
- <!-- no translation found for fingerprint_dangling_notification_msg_2 (7688302770424064884) -->
- <skip />
- <!-- no translation found for fingerprint_dangling_notification_msg_all_deleted_1 (2927018569542316055) -->
- <skip />
- <!-- no translation found for fingerprint_dangling_notification_msg_all_deleted_2 (6897989352716156176) -->
- <skip />
- <!-- no translation found for face_dangling_notification_title (947852541060975473) -->
- <skip />
- <!-- no translation found for face_dangling_notification_msg (8806849376915541655) -->
- <skip />
- <!-- no translation found for biometric_dangling_notification_action_set_up (8246885009807817961) -->
- <skip />
- <!-- no translation found for biometric_dangling_notification_action_not_now (8095249216864443491) -->
- <skip />
+ <string name="fingerprint_dangling_notification_msg_all_deleted_1" msgid="2927018569542316055">"<xliff:g id="FINGERPRINT">%s</xliff:g> саусақ ізі дұрыс істемегендіктен жойылды. Телефонды саусақ ізімен ашу үшін оны қайта реттеңіз."</string>
+ <string name="fingerprint_dangling_notification_msg_all_deleted_2" msgid="6897989352716156176">"Саусақ іздері (<xliff:g id="FINGERPRINT_0">%1$s</xliff:g> және <xliff:g id="FINGERPRINT_1">%2$s</xliff:g>) дұрыс істемегендіктен жойылды. Телефонды саусақ ізімен ашу үшін оларды қайта реттеңіз."</string>
+ <string name="face_dangling_notification_title" msgid="947852541060975473">"Бет тану функциясын қайта реттеу"</string>
+ <string name="face_dangling_notification_msg" msgid="8806849376915541655">"Бет үлгісі дұрыс істемегендіктен жойылды. Телефонды бетпен ашу үшін оны қайта реттеңіз."</string>
+ <string name="biometric_dangling_notification_action_set_up" msgid="8246885009807817961">"Реттеу"</string>
+ <string name="biometric_dangling_notification_action_not_now" msgid="8095249216864443491">"Қазір емес"</string>
</resources>
diff --git a/core/res/res/values-km/strings.xml b/core/res/res/values-km/strings.xml
index 6a826db4..4fe4368 100644
--- a/core/res/res/values-km/strings.xml
+++ b/core/res/res/values-km/strings.xml
@@ -283,6 +283,8 @@
<string name="global_action_voice_assist" msgid="6655788068555086695">"ជំនួយសម្លេង"</string>
<string name="global_action_lockdown" msgid="2475471405907902963">"ការចាក់សោ"</string>
<string name="status_bar_notification_info_overflow" msgid="3330152558746563475">"999+"</string>
+ <!-- no translation found for notification_compact_heads_up_reply (2425293958371284340) -->
+ <skip />
<string name="notification_hidden_text" msgid="2835519769868187223">"ការជូនដំណឹងថ្មី"</string>
<string name="notification_channel_physical_keyboard" msgid="5417306456125988096">"ក្ដារចុចរូបវន្ត"</string>
<string name="notification_channel_security" msgid="8516754650348238057">"សុវត្ថិភាព"</string>
@@ -2413,22 +2415,15 @@
<string name="satellite_notification_open_message" msgid="4149234979688273729">"បើកកម្មវិធី Messages"</string>
<string name="satellite_notification_how_it_works" msgid="3132069321977520519">"របៀបដែលវាដំណើរការ"</string>
<string name="unarchival_session_app_label" msgid="6811856981546348205">"កំពុងរង់ចាំ..."</string>
- <!-- no translation found for fingerprint_dangling_notification_title (7362075195588639989) -->
+ <string name="fingerprint_dangling_notification_title" msgid="7362075195588639989">"រៀបចំការដោះសោដោយស្កេនស្នាមម្រាមដៃម្ដងទៀត"</string>
+ <!-- no translation found for fingerprint_dangling_notification_msg_1 (8517140433796229725) -->
<skip />
- <!-- no translation found for fingerprint_dangling_notification_msg_1 (6261149111900787302) -->
+ <!-- no translation found for fingerprint_dangling_notification_msg_2 (7578829498452127613) -->
<skip />
- <!-- no translation found for fingerprint_dangling_notification_msg_2 (7688302770424064884) -->
- <skip />
- <!-- no translation found for fingerprint_dangling_notification_msg_all_deleted_1 (2927018569542316055) -->
- <skip />
- <!-- no translation found for fingerprint_dangling_notification_msg_all_deleted_2 (6897989352716156176) -->
- <skip />
- <!-- no translation found for face_dangling_notification_title (947852541060975473) -->
- <skip />
- <!-- no translation found for face_dangling_notification_msg (8806849376915541655) -->
- <skip />
- <!-- no translation found for biometric_dangling_notification_action_set_up (8246885009807817961) -->
- <skip />
- <!-- no translation found for biometric_dangling_notification_action_not_now (8095249216864443491) -->
- <skip />
+ <string name="fingerprint_dangling_notification_msg_all_deleted_1" msgid="2927018569542316055">"<xliff:g id="FINGERPRINT">%s</xliff:g> មិនដំណើរការល្អទេ ហើយត្រូវបានលុបចេញហើយ។ រៀបចំវាម្ដងទៀត ដើម្បីដោះសោទូរសព្ទរបស់អ្នកដោយប្រើស្នាមម្រាមដៃ។"</string>
+ <string name="fingerprint_dangling_notification_msg_all_deleted_2" msgid="6897989352716156176">"<xliff:g id="FINGERPRINT_0">%1$s</xliff:g> និង <xliff:g id="FINGERPRINT_1">%2$s</xliff:g> មិនដំណើរការល្អទេ ហើយត្រូវបានលុបចេញហើយ។ រៀបចំវាម្ដងទៀត ដើម្បីដោះសោទូរសព្ទរបស់អ្នកដោយប្រើស្នាមម្រាមដៃ។"</string>
+ <string name="face_dangling_notification_title" msgid="947852541060975473">"រៀបចំការដោះសោដោយស្កេនមុខម្ដងទៀត"</string>
+ <string name="face_dangling_notification_msg" msgid="8806849376915541655">"គំរូមុខរបស់អ្នកមិនដំណើរការល្អទេ ហើយត្រូវបានលុបចេញហើយ។ រៀបចំវាម្ដងទៀត ដើម្បីដោះសោទូរសព្ទរបស់អ្នកដោយប្រើមុខ។"</string>
+ <string name="biometric_dangling_notification_action_set_up" msgid="8246885009807817961">"រៀបចំ"</string>
+ <string name="biometric_dangling_notification_action_not_now" msgid="8095249216864443491">"កុំទាន់"</string>
</resources>
diff --git a/core/res/res/values-kn/strings.xml b/core/res/res/values-kn/strings.xml
index 40d5138..3223c40 100644
--- a/core/res/res/values-kn/strings.xml
+++ b/core/res/res/values-kn/strings.xml
@@ -283,6 +283,8 @@
<string name="global_action_voice_assist" msgid="6655788068555086695">"ಧ್ವನಿ ಸಹಾಯಕ"</string>
<string name="global_action_lockdown" msgid="2475471405907902963">"ಲಾಕ್ಡೌನ್"</string>
<string name="status_bar_notification_info_overflow" msgid="3330152558746563475">"999+"</string>
+ <!-- no translation found for notification_compact_heads_up_reply (2425293958371284340) -->
+ <skip />
<string name="notification_hidden_text" msgid="2835519769868187223">"ಹೊಸ ನೋಟಿಫಿಕೇಶನ್"</string>
<string name="notification_channel_physical_keyboard" msgid="5417306456125988096">"ಭೌತಿಕ ಕೀಬೋರ್ಡ್"</string>
<string name="notification_channel_security" msgid="8516754650348238057">"ಭದ್ರತೆ"</string>
@@ -2413,22 +2415,15 @@
<string name="satellite_notification_open_message" msgid="4149234979688273729">"Messages ಅನ್ನು ತೆರೆಯಿರಿ"</string>
<string name="satellite_notification_how_it_works" msgid="3132069321977520519">"ಇದು ಹೇಗೆ ಕೆಲಸ ಮಾಡುತ್ತದೆ"</string>
<string name="unarchival_session_app_label" msgid="6811856981546348205">"ಬಾಕಿ ಉಳಿದಿದೆ..."</string>
- <!-- no translation found for fingerprint_dangling_notification_title (7362075195588639989) -->
+ <string name="fingerprint_dangling_notification_title" msgid="7362075195588639989">"ಫಿಂಗರ್ಪ್ರಿಂಟ್ ಅನ್ಲಾಕ್ ಅನ್ನು ಮತ್ತೊಮ್ಮೆ ಸೆಟಪ್ ಮಾಡಿ"</string>
+ <!-- no translation found for fingerprint_dangling_notification_msg_1 (8517140433796229725) -->
<skip />
- <!-- no translation found for fingerprint_dangling_notification_msg_1 (6261149111900787302) -->
+ <!-- no translation found for fingerprint_dangling_notification_msg_2 (7578829498452127613) -->
<skip />
- <!-- no translation found for fingerprint_dangling_notification_msg_2 (7688302770424064884) -->
- <skip />
- <!-- no translation found for fingerprint_dangling_notification_msg_all_deleted_1 (2927018569542316055) -->
- <skip />
- <!-- no translation found for fingerprint_dangling_notification_msg_all_deleted_2 (6897989352716156176) -->
- <skip />
- <!-- no translation found for face_dangling_notification_title (947852541060975473) -->
- <skip />
- <!-- no translation found for face_dangling_notification_msg (8806849376915541655) -->
- <skip />
- <!-- no translation found for biometric_dangling_notification_action_set_up (8246885009807817961) -->
- <skip />
- <!-- no translation found for biometric_dangling_notification_action_not_now (8095249216864443491) -->
- <skip />
+ <string name="fingerprint_dangling_notification_msg_all_deleted_1" msgid="2927018569542316055">"<xliff:g id="FINGERPRINT">%s</xliff:g> ಸರಿಯಾಗಿ ಕಾರ್ಯನಿರ್ವಹಿಸುತ್ತಿಲ್ಲ ಹಾಗೂ ಅದನ್ನು ಅಳಿಸಲಾಗಿದೆ. ಫಿಂಗರ್ ಪ್ರಿಂಟ್ ಮೂಲಕ ನಿಮ್ಮ ಫೋನ್ ಅನ್ನು ಅನ್ಲಾಕ್ ಮಾಡಲು ಅದನ್ನು ಪುನಃ ಸೆಟಪ್ ಮಾಡಿ."</string>
+ <string name="fingerprint_dangling_notification_msg_all_deleted_2" msgid="6897989352716156176">"<xliff:g id="FINGERPRINT_0">%1$s</xliff:g> ಮತ್ತು <xliff:g id="FINGERPRINT_1">%2$s</xliff:g> ಸರಿಯಾಗಿ ಕಾರ್ಯನಿರ್ವಹಿಸುತ್ತಿಲ್ಲ ಹಾಗೂ ಅವುಗಳನ್ನು ಅಳಿಸಲಾಗಿದೆ. ನಿಮ್ಮ ಫಿಂಗರ್ ಪ್ರಿಂಟ್ ಮೂಲಕ ನಿಮ್ಮ ಫೋನ್ ಅನ್ಲಾಕ್ ಮಾಡಲು ಅವುಗಳನ್ನು ಪುನಃ ಸೆಟಪ್ ಮಾಡಿ."</string>
+ <string name="face_dangling_notification_title" msgid="947852541060975473">"ಫೇಸ್ ಅನ್ಲಾಕ್ ಅನ್ನು ಮತ್ತೊಮ್ಮೆ ಸೆಟಪ್ ಮಾಡಿ"</string>
+ <string name="face_dangling_notification_msg" msgid="8806849376915541655">"ನಿಮ್ಮ ಫೇಸ್ ಮಾಡೆಲ್ ಸರಿಯಾಗಿ ಕಾರ್ಯನಿರ್ವಹಿಸುತ್ತಿಲ್ಲ ಹಾಗೂ ಅದನ್ನು ಅಳಿಸಲಾಗಿದೆ. ಫೇಸ್ ಮೂಲಕ ನಿಮ್ಮ ಫೋನ್ ಅನ್ನು ಅನ್ಲಾಕ್ ಮಾಡಲು ಅದನ್ನು ಪುನಃ ಸೆಟಪ್ ಮಾಡಿ."</string>
+ <string name="biometric_dangling_notification_action_set_up" msgid="8246885009807817961">"ಸೆಟಪ್ ಮಾಡಿ"</string>
+ <string name="biometric_dangling_notification_action_not_now" msgid="8095249216864443491">"ಈಗ ಬೇಡ"</string>
</resources>
diff --git a/core/res/res/values-ko/strings.xml b/core/res/res/values-ko/strings.xml
index dd47628..7a8d324 100644
--- a/core/res/res/values-ko/strings.xml
+++ b/core/res/res/values-ko/strings.xml
@@ -283,6 +283,8 @@
<string name="global_action_voice_assist" msgid="6655788068555086695">"음성 지원"</string>
<string name="global_action_lockdown" msgid="2475471405907902963">"잠금"</string>
<string name="status_bar_notification_info_overflow" msgid="3330152558746563475">"999+"</string>
+ <!-- no translation found for notification_compact_heads_up_reply (2425293958371284340) -->
+ <skip />
<string name="notification_hidden_text" msgid="2835519769868187223">"새 알림"</string>
<string name="notification_channel_physical_keyboard" msgid="5417306456125988096">"물리적 키보드"</string>
<string name="notification_channel_security" msgid="8516754650348238057">"보안"</string>
@@ -1388,7 +1390,7 @@
<string name="no_permissions" msgid="5729199278862516390">"권한 필요 없음"</string>
<string name="perm_costs_money" msgid="749054595022779685">"비용이 부과될 수 있습니다."</string>
<string name="dlg_ok" msgid="5103447663504839312">"확인"</string>
- <string name="usb_charging_notification_title" msgid="1674124518282666955">"이 기기를 USB로 충전 중."</string>
+ <string name="usb_charging_notification_title" msgid="1674124518282666955">"이 기기를 USB로 충전 중"</string>
<string name="usb_supplying_notification_title" msgid="5378546632408101811">"USB를 통해 연결된 기기 충전"</string>
<string name="usb_mtp_notification_title" msgid="1065989144124499810">"USB 파일 전송 사용 설정됨"</string>
<string name="usb_ptp_notification_title" msgid="5043437571863443281">"USB를 통해 PTP 사용 설정됨"</string>
@@ -2413,22 +2415,15 @@
<string name="satellite_notification_open_message" msgid="4149234979688273729">"메시지 열기"</string>
<string name="satellite_notification_how_it_works" msgid="3132069321977520519">"작동 방식"</string>
<string name="unarchival_session_app_label" msgid="6811856981546348205">"대기 중…"</string>
- <!-- no translation found for fingerprint_dangling_notification_title (7362075195588639989) -->
+ <string name="fingerprint_dangling_notification_title" msgid="7362075195588639989">"지문 잠금 해제 다시 설정"</string>
+ <!-- no translation found for fingerprint_dangling_notification_msg_1 (8517140433796229725) -->
<skip />
- <!-- no translation found for fingerprint_dangling_notification_msg_1 (6261149111900787302) -->
+ <!-- no translation found for fingerprint_dangling_notification_msg_2 (7578829498452127613) -->
<skip />
- <!-- no translation found for fingerprint_dangling_notification_msg_2 (7688302770424064884) -->
- <skip />
- <!-- no translation found for fingerprint_dangling_notification_msg_all_deleted_1 (2927018569542316055) -->
- <skip />
- <!-- no translation found for fingerprint_dangling_notification_msg_all_deleted_2 (6897989352716156176) -->
- <skip />
- <!-- no translation found for face_dangling_notification_title (947852541060975473) -->
- <skip />
- <!-- no translation found for face_dangling_notification_msg (8806849376915541655) -->
- <skip />
- <!-- no translation found for biometric_dangling_notification_action_set_up (8246885009807817961) -->
- <skip />
- <!-- no translation found for biometric_dangling_notification_action_not_now (8095249216864443491) -->
- <skip />
+ <string name="fingerprint_dangling_notification_msg_all_deleted_1" msgid="2927018569542316055">"<xliff:g id="FINGERPRINT">%s</xliff:g>이(가) 제대로 작동하지 않아 삭제되었습니다. 지문으로 휴대전화를 잠금 해제하려면 다시 설정하세요."</string>
+ <string name="fingerprint_dangling_notification_msg_all_deleted_2" msgid="6897989352716156176">"<xliff:g id="FINGERPRINT_0">%1$s</xliff:g> 및 <xliff:g id="FINGERPRINT_1">%2$s</xliff:g>이(가) 제대로 작동하지 않아 삭제되었습니다. 지문으로 휴대전화를 잠금 해제하려면 다시 설정하세요."</string>
+ <string name="face_dangling_notification_title" msgid="947852541060975473">"얼굴 인식 잠금 해제 다시 설정"</string>
+ <string name="face_dangling_notification_msg" msgid="8806849376915541655">"얼굴 모델이 제대로 작동하지 않아 삭제되었습니다. 얼굴로 휴대전화를 잠금 해제하려면 다시 설정하세요."</string>
+ <string name="biometric_dangling_notification_action_set_up" msgid="8246885009807817961">"설정"</string>
+ <string name="biometric_dangling_notification_action_not_now" msgid="8095249216864443491">"나중에"</string>
</resources>
diff --git a/core/res/res/values-ky/strings.xml b/core/res/res/values-ky/strings.xml
index bb29c36..8a233a3 100644
--- a/core/res/res/values-ky/strings.xml
+++ b/core/res/res/values-ky/strings.xml
@@ -283,6 +283,8 @@
<string name="global_action_voice_assist" msgid="6655788068555086695">"Үн жардамчысы"</string>
<string name="global_action_lockdown" msgid="2475471405907902963">"Бекем кулпулоо"</string>
<string name="status_bar_notification_info_overflow" msgid="3330152558746563475">"999+"</string>
+ <!-- no translation found for notification_compact_heads_up_reply (2425293958371284340) -->
+ <skip />
<string name="notification_hidden_text" msgid="2835519769868187223">"Жаңы эскертме"</string>
<string name="notification_channel_physical_keyboard" msgid="5417306456125988096">"Аппараттык баскычтоп"</string>
<string name="notification_channel_security" msgid="8516754650348238057">"Коопсуздук"</string>
@@ -2413,22 +2415,15 @@
<string name="satellite_notification_open_message" msgid="4149234979688273729">"Жазышуулар колдонмосун ачуу"</string>
<string name="satellite_notification_how_it_works" msgid="3132069321977520519">"Ал кантип иштейт"</string>
<string name="unarchival_session_app_label" msgid="6811856981546348205">"Кезекте турат..."</string>
- <!-- no translation found for fingerprint_dangling_notification_title (7362075195588639989) -->
+ <string name="fingerprint_dangling_notification_title" msgid="7362075195588639989">"Манжа изи менен ачуу функциясын кайра тууралаңыз"</string>
+ <!-- no translation found for fingerprint_dangling_notification_msg_1 (8517140433796229725) -->
<skip />
- <!-- no translation found for fingerprint_dangling_notification_msg_1 (6261149111900787302) -->
+ <!-- no translation found for fingerprint_dangling_notification_msg_2 (7578829498452127613) -->
<skip />
- <!-- no translation found for fingerprint_dangling_notification_msg_2 (7688302770424064884) -->
- <skip />
- <!-- no translation found for fingerprint_dangling_notification_msg_all_deleted_1 (2927018569542316055) -->
- <skip />
- <!-- no translation found for fingerprint_dangling_notification_msg_all_deleted_2 (6897989352716156176) -->
- <skip />
- <!-- no translation found for face_dangling_notification_title (947852541060975473) -->
- <skip />
- <!-- no translation found for face_dangling_notification_msg (8806849376915541655) -->
- <skip />
- <!-- no translation found for biometric_dangling_notification_action_set_up (8246885009807817961) -->
- <skip />
- <!-- no translation found for biometric_dangling_notification_action_not_now (8095249216864443491) -->
- <skip />
+ <string name="fingerprint_dangling_notification_msg_all_deleted_1" msgid="2927018569542316055">"<xliff:g id="FINGERPRINT">%s</xliff:g> ойдогудай иштебегендиктен, жок кылынды. Телефондо Манжа изи менен ачуу функциясын колдонуу үчүн аны кайра тууралаңыз."</string>
+ <string name="fingerprint_dangling_notification_msg_all_deleted_2" msgid="6897989352716156176">"<xliff:g id="FINGERPRINT_0">%1$s</xliff:g> жана <xliff:g id="FINGERPRINT_1">%2$s</xliff:g> ойдогудай иштебегендиктен, жок кылынды. Телефонду манжа изи менен ачуу үчүн аларды кайра тууралаңыз."</string>
+ <string name="face_dangling_notification_title" msgid="947852541060975473">"Жүзүнөн таанып ачуу функциясын кайрадан тууралаңыз"</string>
+ <string name="face_dangling_notification_msg" msgid="8806849376915541655">"Жүзүңүздүн үлгүсү ойдогудай иштебегендиктен, жок кылынды. Телефондо Жүзүнөн таанып ачуу функциясын колдонуу үчүн аны кайра тууралаңыз."</string>
+ <string name="biometric_dangling_notification_action_set_up" msgid="8246885009807817961">"Тууралоо"</string>
+ <string name="biometric_dangling_notification_action_not_now" msgid="8095249216864443491">"Азыр эмес"</string>
</resources>
diff --git a/core/res/res/values-lo/strings.xml b/core/res/res/values-lo/strings.xml
index 5fa0fc1..3e06b3d 100644
--- a/core/res/res/values-lo/strings.xml
+++ b/core/res/res/values-lo/strings.xml
@@ -283,6 +283,8 @@
<string name="global_action_voice_assist" msgid="6655788068555086695">"ຊ່ວຍເຫຼືອທາງສຽງ"</string>
<string name="global_action_lockdown" msgid="2475471405907902963">"ລັອກໄວ້"</string>
<string name="status_bar_notification_info_overflow" msgid="3330152558746563475">"999+"</string>
+ <!-- no translation found for notification_compact_heads_up_reply (2425293958371284340) -->
+ <skip />
<string name="notification_hidden_text" msgid="2835519769868187223">"ການແຈ້ງເຕືອນໃໝ່"</string>
<string name="notification_channel_physical_keyboard" msgid="5417306456125988096">"ແປ້ນພິມພາຍນອກ"</string>
<string name="notification_channel_security" msgid="8516754650348238057">"ຄວາມປອດໄພ"</string>
@@ -2394,7 +2396,7 @@
<string name="keyboard_layout_notification_more_than_three_selected_message" msgid="1581834181578206937">"ຕັ້ງໂຄງຮ່າງແປ້ນພິມເປັນ <xliff:g id="LAYOUT_1">%1$s</xliff:g>, <xliff:g id="LAYOUT_2">%2$s</xliff:g>, <xliff:g id="LAYOUT_3">%3$s</xliff:g>… ແຕະເພື່ອປ່ຽນແປງ."</string>
<string name="keyboard_layout_notification_multiple_selected_title" msgid="5242444914367024499">"ຕັ້ງຄ່າແປ້ນພິມແທ້ແລ້ວ"</string>
<string name="keyboard_layout_notification_multiple_selected_message" msgid="6576533454124419202">"ແຕະເພື່ອເບິ່ງແປ້ນພິມ"</string>
- <string name="profile_label_private" msgid="6463418670715290696">"ສ່ວນຕົວ"</string>
+ <string name="profile_label_private" msgid="6463418670715290696">"ສ່ວນບຸກຄົນ"</string>
<string name="profile_label_clone" msgid="769106052210954285">"ໂຄລນ"</string>
<string name="profile_label_work" msgid="3495359133038584618">"ວຽກ"</string>
<string name="profile_label_work_2" msgid="4691533661598632135">"ວຽກ 2"</string>
@@ -2413,22 +2415,15 @@
<string name="satellite_notification_open_message" msgid="4149234979688273729">"ເປີດ Messages"</string>
<string name="satellite_notification_how_it_works" msgid="3132069321977520519">"ມັນເຮັດວຽກແນວໃດ"</string>
<string name="unarchival_session_app_label" msgid="6811856981546348205">"ລໍຖ້າດຳເນີນການ..."</string>
- <!-- no translation found for fingerprint_dangling_notification_title (7362075195588639989) -->
+ <string name="fingerprint_dangling_notification_title" msgid="7362075195588639989">"ຕັ້ງຄ່າການປົດລັອກດ້ວຍລາຍນິ້ວມືຄືນໃໝ່"</string>
+ <!-- no translation found for fingerprint_dangling_notification_msg_1 (8517140433796229725) -->
<skip />
- <!-- no translation found for fingerprint_dangling_notification_msg_1 (6261149111900787302) -->
+ <!-- no translation found for fingerprint_dangling_notification_msg_2 (7578829498452127613) -->
<skip />
- <!-- no translation found for fingerprint_dangling_notification_msg_2 (7688302770424064884) -->
- <skip />
- <!-- no translation found for fingerprint_dangling_notification_msg_all_deleted_1 (2927018569542316055) -->
- <skip />
- <!-- no translation found for fingerprint_dangling_notification_msg_all_deleted_2 (6897989352716156176) -->
- <skip />
- <!-- no translation found for face_dangling_notification_title (947852541060975473) -->
- <skip />
- <!-- no translation found for face_dangling_notification_msg (8806849376915541655) -->
- <skip />
- <!-- no translation found for biometric_dangling_notification_action_set_up (8246885009807817961) -->
- <skip />
- <!-- no translation found for biometric_dangling_notification_action_not_now (8095249216864443491) -->
- <skip />
+ <string name="fingerprint_dangling_notification_msg_all_deleted_1" msgid="2927018569542316055">"<xliff:g id="FINGERPRINT">%s</xliff:g> ເຮັດວຽກໄດ້ບໍ່ດີ ແລະ ຖືກລຶບອອກແລ້ວ. ໃຫ້ຕັ້ງຄ່າມັນຄືນໃໝ່ເພື່ອປົດລັອກໂທລະສັບຂອງທ່ານດ້ວຍລາຍນິ້ວມື."</string>
+ <string name="fingerprint_dangling_notification_msg_all_deleted_2" msgid="6897989352716156176">"<xliff:g id="FINGERPRINT_0">%1$s</xliff:g> ແລະ <xliff:g id="FINGERPRINT_1">%2$s</xliff:g> ເຮັດວຽກໄດ້ບໍ່ດີ ແລະ ຖືກລຶບອອກແລ້ວ. ໃຫ້ຕັ້ງຄ່າພວກມັນຄືນໃໝ່ເພື່ອປົດລັອກໂທລະສັບຂອງທ່ານດ້ວຍລາຍນິ້ວມື."</string>
+ <string name="face_dangling_notification_title" msgid="947852541060975473">"ຕັ້ງຄ່າການປົດລັອກດ້ວຍໜ້າຄືນໃໝ່"</string>
+ <string name="face_dangling_notification_msg" msgid="8806849376915541655">"ຮູບແບບໃບໜ້າຂອງທ່ານເຮັດວຽກໄດ້ບໍ່ດີ ແລະ ຖືກລຶບອອກແລ້ວ. ໃຫ້ຕັ້ງຄ່າມັນຄືນໃໝ່ເພື່ອປົດລັອກໂທລະສັບຂອງທ່ານດ້ວຍໃບໜ້າ."</string>
+ <string name="biometric_dangling_notification_action_set_up" msgid="8246885009807817961">"ຕັ້ງຄ່າ"</string>
+ <string name="biometric_dangling_notification_action_not_now" msgid="8095249216864443491">"ບໍ່ຟ້າວເທື່ອ"</string>
</resources>
diff --git a/core/res/res/values-lt/strings.xml b/core/res/res/values-lt/strings.xml
index e06c2ef..50572f8 100644
--- a/core/res/res/values-lt/strings.xml
+++ b/core/res/res/values-lt/strings.xml
@@ -285,6 +285,8 @@
<string name="global_action_voice_assist" msgid="6655788068555086695">"Voice Assist"</string>
<string name="global_action_lockdown" msgid="2475471405907902963">"Užrakinimas"</string>
<string name="status_bar_notification_info_overflow" msgid="3330152558746563475">"999+"</string>
+ <!-- no translation found for notification_compact_heads_up_reply (2425293958371284340) -->
+ <skip />
<string name="notification_hidden_text" msgid="2835519769868187223">"Naujas pranešimas"</string>
<string name="notification_channel_physical_keyboard" msgid="5417306456125988096">"Fizinė klaviatūra"</string>
<string name="notification_channel_security" msgid="8516754650348238057">"Sauga"</string>
@@ -2415,22 +2417,15 @@
<string name="satellite_notification_open_message" msgid="4149234979688273729">"Atidaryti programą „Messages“"</string>
<string name="satellite_notification_how_it_works" msgid="3132069321977520519">"Kaip tai veikia"</string>
<string name="unarchival_session_app_label" msgid="6811856981546348205">"Laukiama..."</string>
- <!-- no translation found for fingerprint_dangling_notification_title (7362075195588639989) -->
+ <string name="fingerprint_dangling_notification_title" msgid="7362075195588639989">"Atrakinimo piršto atspaudu nustatymas dar kartą"</string>
+ <!-- no translation found for fingerprint_dangling_notification_msg_1 (8517140433796229725) -->
<skip />
- <!-- no translation found for fingerprint_dangling_notification_msg_1 (6261149111900787302) -->
+ <!-- no translation found for fingerprint_dangling_notification_msg_2 (7578829498452127613) -->
<skip />
- <!-- no translation found for fingerprint_dangling_notification_msg_2 (7688302770424064884) -->
- <skip />
- <!-- no translation found for fingerprint_dangling_notification_msg_all_deleted_1 (2927018569542316055) -->
- <skip />
- <!-- no translation found for fingerprint_dangling_notification_msg_all_deleted_2 (6897989352716156176) -->
- <skip />
- <!-- no translation found for face_dangling_notification_title (947852541060975473) -->
- <skip />
- <!-- no translation found for face_dangling_notification_msg (8806849376915541655) -->
- <skip />
- <!-- no translation found for biometric_dangling_notification_action_set_up (8246885009807817961) -->
- <skip />
- <!-- no translation found for biometric_dangling_notification_action_not_now (8095249216864443491) -->
- <skip />
+ <string name="fingerprint_dangling_notification_msg_all_deleted_1" msgid="2927018569542316055">"<xliff:g id="FINGERPRINT">%s</xliff:g> neveikė tinkamai ir buvo ištrintas. Nustatykite jį dar kartą, kad atrakintumėte telefoną piršto atspaudu."</string>
+ <string name="fingerprint_dangling_notification_msg_all_deleted_2" msgid="6897989352716156176">"<xliff:g id="FINGERPRINT_0">%1$s</xliff:g> ir <xliff:g id="FINGERPRINT_1">%2$s</xliff:g> neveikė tinkamai ir buvo ištrinti. Nustatykite juos dar kartą, kad atrakintumėte telefoną piršto atspaudu."</string>
+ <string name="face_dangling_notification_title" msgid="947852541060975473">"Atrakinimo pagal veidą nustatymas iš naujo"</string>
+ <string name="face_dangling_notification_msg" msgid="8806849376915541655">"Jūsų veido modelis neveikė tinkamai ir buvo ištrintas. Nustatykite jį dar kartą, kad atrakintumėte telefoną pagal veidą."</string>
+ <string name="biometric_dangling_notification_action_set_up" msgid="8246885009807817961">"Nustatyti"</string>
+ <string name="biometric_dangling_notification_action_not_now" msgid="8095249216864443491">"Ne dabar"</string>
</resources>
diff --git a/core/res/res/values-lv/strings.xml b/core/res/res/values-lv/strings.xml
index d2a6cd9..8be327d 100644
--- a/core/res/res/values-lv/strings.xml
+++ b/core/res/res/values-lv/strings.xml
@@ -284,6 +284,8 @@
<string name="global_action_voice_assist" msgid="6655788068555086695">"Balss palīgs"</string>
<string name="global_action_lockdown" msgid="2475471405907902963">"Bloķēšana"</string>
<string name="status_bar_notification_info_overflow" msgid="3330152558746563475">"Pārsniedz"</string>
+ <!-- no translation found for notification_compact_heads_up_reply (2425293958371284340) -->
+ <skip />
<string name="notification_hidden_text" msgid="2835519769868187223">"Jauns paziņojums"</string>
<string name="notification_channel_physical_keyboard" msgid="5417306456125988096">"Fiziskā tastatūra"</string>
<string name="notification_channel_security" msgid="8516754650348238057">"Drošība"</string>
@@ -2414,22 +2416,15 @@
<string name="satellite_notification_open_message" msgid="4149234979688273729">"Atvērt lietotni Ziņojumi"</string>
<string name="satellite_notification_how_it_works" msgid="3132069321977520519">"Darbības principi"</string>
<string name="unarchival_session_app_label" msgid="6811856981546348205">"Gaida…"</string>
- <!-- no translation found for fingerprint_dangling_notification_title (7362075195588639989) -->
+ <string name="fingerprint_dangling_notification_title" msgid="7362075195588639989">"Vēlreiz iestatiet autorizāciju ar pirksta nospiedumu"</string>
+ <!-- no translation found for fingerprint_dangling_notification_msg_1 (8517140433796229725) -->
<skip />
- <!-- no translation found for fingerprint_dangling_notification_msg_1 (6261149111900787302) -->
+ <!-- no translation found for fingerprint_dangling_notification_msg_2 (7578829498452127613) -->
<skip />
- <!-- no translation found for fingerprint_dangling_notification_msg_2 (7688302770424064884) -->
- <skip />
- <!-- no translation found for fingerprint_dangling_notification_msg_all_deleted_1 (2927018569542316055) -->
- <skip />
- <!-- no translation found for fingerprint_dangling_notification_msg_all_deleted_2 (6897989352716156176) -->
- <skip />
- <!-- no translation found for face_dangling_notification_title (947852541060975473) -->
- <skip />
- <!-- no translation found for face_dangling_notification_msg (8806849376915541655) -->
- <skip />
- <!-- no translation found for biometric_dangling_notification_action_set_up (8246885009807817961) -->
- <skip />
- <!-- no translation found for biometric_dangling_notification_action_not_now (8095249216864443491) -->
- <skip />
+ <string name="fingerprint_dangling_notification_msg_all_deleted_1" msgid="2927018569542316055">"<xliff:g id="FINGERPRINT">%s</xliff:g> nedarbojās pareizi un tika izdzēsts. Iestatiet to atkal, lai varētu atbloķēt tālruni ar pirksta nospiedumu."</string>
+ <string name="fingerprint_dangling_notification_msg_all_deleted_2" msgid="6897989352716156176">"<xliff:g id="FINGERPRINT_0">%1$s</xliff:g> un <xliff:g id="FINGERPRINT_1">%2$s</xliff:g> nedarbojās pareizi un tika izdzēsti. Iestatiet tos atkal, lai varētu atbloķētu tālruni ar pirksta nospiedumu."</string>
+ <string name="face_dangling_notification_title" msgid="947852541060975473">"Vēlreiz iestatiet autorizāciju pēc sejas"</string>
+ <string name="face_dangling_notification_msg" msgid="8806849376915541655">"Jūsu sejas modelis nedarbojās pareizi un tika izdzēsts. Iestatiet to atkal, lai varētu atbloķēt tālruni ar seju."</string>
+ <string name="biometric_dangling_notification_action_set_up" msgid="8246885009807817961">"Iestatīt"</string>
+ <string name="biometric_dangling_notification_action_not_now" msgid="8095249216864443491">"Ne tagad"</string>
</resources>
diff --git a/core/res/res/values-mk/strings.xml b/core/res/res/values-mk/strings.xml
index 796f9efe..680ff0a 100644
--- a/core/res/res/values-mk/strings.xml
+++ b/core/res/res/values-mk/strings.xml
@@ -153,7 +153,7 @@
<string name="cfTemplateForwardedTime" msgid="735042369233323609">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: <xliff:g id="DIALING_NUMBER">{1}</xliff:g> по <xliff:g id="TIME_DELAY">{2}</xliff:g> секунди"</string>
<string name="cfTemplateRegistered" msgid="5619930473441550596">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: не е препратено"</string>
<string name="cfTemplateRegisteredTime" msgid="5222794399642525045">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: не е проследен"</string>
- <string name="scCellularNetworkSecurityTitle" msgid="7752521808690294384">"Безбедност на мобилна мрежа"</string>
+ <string name="scCellularNetworkSecurityTitle" msgid="7752521808690294384">"Безбедност на мобилната мрежа"</string>
<string name="scCellularNetworkSecuritySummary" msgid="7042036754550545005">"Шифрирање, известувања за нешифрирани мрежи"</string>
<string name="scIdentifierDisclosureIssueTitle" msgid="2898888825129970328">"Пристапено е до ID на уредот"</string>
<string name="scIdentifierDisclosureIssueSummaryNotification" msgid="3699930821270580416">"Во <xliff:g id="DISCLOSURE_TIME">%1$s</xliff:g>, мрежа во близина го сними уникатниот ID (IMSI или IMEI) на вашиот телефон со користење на вашата SIM-картичка на <xliff:g id="DISCLOSURE_NETWORK">%2$s</xliff:g>"</string>
@@ -283,6 +283,8 @@
<string name="global_action_voice_assist" msgid="6655788068555086695">"Гласовна помош"</string>
<string name="global_action_lockdown" msgid="2475471405907902963">"Заклучување"</string>
<string name="status_bar_notification_info_overflow" msgid="3330152558746563475">"999+"</string>
+ <!-- no translation found for notification_compact_heads_up_reply (2425293958371284340) -->
+ <skip />
<string name="notification_hidden_text" msgid="2835519769868187223">"Ново известување"</string>
<string name="notification_channel_physical_keyboard" msgid="5417306456125988096">"Физичка тастатура"</string>
<string name="notification_channel_security" msgid="8516754650348238057">"Безбедност"</string>
@@ -2394,9 +2396,9 @@
<string name="keyboard_layout_notification_more_than_three_selected_message" msgid="1581834181578206937">"Распоредот на тастатурата е поставен на <xliff:g id="LAYOUT_1">%1$s</xliff:g>, <xliff:g id="LAYOUT_2">%2$s</xliff:g>, <xliff:g id="LAYOUT_3">%3$s</xliff:g>… Допрете за да промените."</string>
<string name="keyboard_layout_notification_multiple_selected_title" msgid="5242444914367024499">"Физичките тастатури се конфигурирани"</string>
<string name="keyboard_layout_notification_multiple_selected_message" msgid="6576533454124419202">"Допрете за да ги видите тастатурите"</string>
- <string name="profile_label_private" msgid="6463418670715290696">"Приватен профил"</string>
+ <string name="profile_label_private" msgid="6463418670715290696">"Приватно"</string>
<string name="profile_label_clone" msgid="769106052210954285">"Клониран профил"</string>
- <string name="profile_label_work" msgid="3495359133038584618">"Работен профил"</string>
+ <string name="profile_label_work" msgid="3495359133038584618">"Работно"</string>
<string name="profile_label_work_2" msgid="4691533661598632135">"Работен профил 2"</string>
<string name="profile_label_work_3" msgid="4834572253956798917">"Работен профил 3"</string>
<string name="profile_label_test" msgid="9168641926186071947">"Профил за тестирање"</string>
@@ -2413,22 +2415,15 @@
<string name="satellite_notification_open_message" msgid="4149234979688273729">"Отворете ја Messages"</string>
<string name="satellite_notification_how_it_works" msgid="3132069321977520519">"Дознајте како функционира"</string>
<string name="unarchival_session_app_label" msgid="6811856981546348205">"Во фаза на чекање…"</string>
- <!-- no translation found for fingerprint_dangling_notification_title (7362075195588639989) -->
+ <string name="fingerprint_dangling_notification_title" msgid="7362075195588639989">"Поставете „Отклучување со отпечаток“ повторно"</string>
+ <!-- no translation found for fingerprint_dangling_notification_msg_1 (8517140433796229725) -->
<skip />
- <!-- no translation found for fingerprint_dangling_notification_msg_1 (6261149111900787302) -->
+ <!-- no translation found for fingerprint_dangling_notification_msg_2 (7578829498452127613) -->
<skip />
- <!-- no translation found for fingerprint_dangling_notification_msg_2 (7688302770424064884) -->
- <skip />
- <!-- no translation found for fingerprint_dangling_notification_msg_all_deleted_1 (2927018569542316055) -->
- <skip />
- <!-- no translation found for fingerprint_dangling_notification_msg_all_deleted_2 (6897989352716156176) -->
- <skip />
- <!-- no translation found for face_dangling_notification_title (947852541060975473) -->
- <skip />
- <!-- no translation found for face_dangling_notification_msg (8806849376915541655) -->
- <skip />
- <!-- no translation found for biometric_dangling_notification_action_set_up (8246885009807817961) -->
- <skip />
- <!-- no translation found for biometric_dangling_notification_action_not_now (8095249216864443491) -->
- <skip />
+ <string name="fingerprint_dangling_notification_msg_all_deleted_1" msgid="2927018569542316055">"<xliff:g id="FINGERPRINT">%s</xliff:g> не функционираше добро, па се избриша. Поставете го повторно за да го отклучувате телефонот со отпечаток."</string>
+ <string name="fingerprint_dangling_notification_msg_all_deleted_2" msgid="6897989352716156176">"<xliff:g id="FINGERPRINT_0">%1$s</xliff:g> и <xliff:g id="FINGERPRINT_1">%2$s</xliff:g> не функционираа добро, па се избришаа. Поставете ги повторно за да го отклучувате телефонот со отпечаток."</string>
+ <string name="face_dangling_notification_title" msgid="947852541060975473">"Поставете „Отклучување со лик“ повторно"</string>
+ <string name="face_dangling_notification_msg" msgid="8806849376915541655">"Вашиот модел на лик не функционираше добро, па се избриша. Поставете го повторно за да го отклучите телефонот со лик."</string>
+ <string name="biometric_dangling_notification_action_set_up" msgid="8246885009807817961">"Поставете"</string>
+ <string name="biometric_dangling_notification_action_not_now" msgid="8095249216864443491">"Не сега"</string>
</resources>
diff --git a/core/res/res/values-ml/strings.xml b/core/res/res/values-ml/strings.xml
index a0b4c8d..eca7f28 100644
--- a/core/res/res/values-ml/strings.xml
+++ b/core/res/res/values-ml/strings.xml
@@ -283,6 +283,8 @@
<string name="global_action_voice_assist" msgid="6655788068555086695">"വോയ്സ് സഹായം"</string>
<string name="global_action_lockdown" msgid="2475471405907902963">"ലോക്ക്ഡൗൺ"</string>
<string name="status_bar_notification_info_overflow" msgid="3330152558746563475">"999+"</string>
+ <!-- no translation found for notification_compact_heads_up_reply (2425293958371284340) -->
+ <skip />
<string name="notification_hidden_text" msgid="2835519769868187223">"പുതിയ അറിയിപ്പ്"</string>
<string name="notification_channel_physical_keyboard" msgid="5417306456125988096">"ഫിസിക്കൽ കീബോഡ്"</string>
<string name="notification_channel_security" msgid="8516754650348238057">"സുരക്ഷ"</string>
@@ -1904,8 +1906,8 @@
<string name="package_updated_device_owner" msgid="7560272363805506941">"നിങ്ങളുടെ അഡ്മിൻ അപ്ഡേറ്റ് ചെയ്യുന്നത്"</string>
<string name="package_deleted_device_owner" msgid="2292335928930293023">"നിങ്ങളുടെ അഡ്മിൻ ഇല്ലാതാക്കുന്നത്"</string>
<string name="confirm_battery_saver" msgid="5247976246208245754">"ശരി"</string>
- <string name="battery_saver_description_with_learn_more" msgid="5444908404021316250">"\'ബാറ്ററി ലാഭിക്കൽ\' ഡാർക്ക് തീം ഓണാക്കുന്നു, ഒപ്പം പശ്ചാത്തല ആക്റ്റിവിറ്റിയും ചില വിഷ്വൽ ഇഫക്റ്റുകളും ചില ഫീച്ചറുകളും ചില നെറ്റ്വർക്ക് കണക്ഷനുകളും പരിമിതപ്പെടുത്തുകയോ ഓഫാക്കുകയോ ചെയ്യുന്നു."</string>
- <string name="battery_saver_description" msgid="8518809702138617167">"ബാറ്ററി ലാഭിക്കൽ ഡാർക്ക് തീം ഓണാക്കുന്നു, പശ്ചാത്തല ആക്റ്റിവിറ്റിയും ചില വിഷ്വൽ ഇഫക്റ്റുകളും ചില ഫീച്ചറുകളും ചില നെറ്റ്വർക്ക് കണക്ഷനുകളും അത് പരിമിതപ്പെടുത്തുകയോ ഓഫാക്കുകയോ ചെയ്യുന്നു."</string>
+ <string name="battery_saver_description_with_learn_more" msgid="5444908404021316250">"\'ബാറ്ററി സേവർ\' ഡാർക്ക് തീം ഓണാക്കുന്നു, ഒപ്പം പശ്ചാത്തല ആക്റ്റിവിറ്റിയും ചില വിഷ്വൽ ഇഫക്റ്റുകളും ചില ഫീച്ചറുകളും ചില നെറ്റ്വർക്ക് കണക്ഷനുകളും പരിമിതപ്പെടുത്തുകയോ ഓഫാക്കുകയോ ചെയ്യുന്നു."</string>
+ <string name="battery_saver_description" msgid="8518809702138617167">"ബാറ്ററി സേവർ ഡാർക്ക് തീം ഓണാക്കുന്നു, പശ്ചാത്തല ആക്റ്റിവിറ്റിയും ചില വിഷ്വൽ ഇഫക്റ്റുകളും ചില ഫീച്ചറുകളും ചില നെറ്റ്വർക്ക് കണക്ഷനുകളും അത് പരിമിതപ്പെടുത്തുകയോ ഓഫാക്കുകയോ ചെയ്യുന്നു."</string>
<string name="data_saver_description" msgid="4995164271550590517">"ഡാറ്റാ ഉപയോഗം കുറയ്ക്കാൻ സഹായിക്കുന്നതിനായി പശ്ചാത്തലത്തിൽ ഡാറ്റ അയയ്ക്കുകയോ സ്വീകരിക്കുകയോ ചെയ്യുന്നതിൽ നിന്ന് ചില ആപ്പുകളെ ഡാറ്റാ സേവർ തടയുന്നു. നിങ്ങൾ നിലവിൽ ഉപയോഗിക്കുന്ന ഒരു ആപ്പിന് ഡാറ്റ ആക്സസ് ചെയ്യാനാകും, എന്നാൽ വല്ലപ്പോഴും മാത്രമെ സംഭവിക്കുന്നുള്ളു. ഇതിനർത്ഥം, ഉദാഹരണമായി നിങ്ങൾ ടാപ്പ് ചെയ്യുന്നത് വരെ ചിത്രങ്ങൾ പ്രദർശിപ്പിക്കുകയില്ല എന്നാണ്."</string>
<string name="data_saver_enable_title" msgid="7080620065745260137">"ഡാറ്റാ സേവർ ഓണാക്കണോ?"</string>
<string name="data_saver_enable_button" msgid="4399405762586419726">"ഓണാക്കുക"</string>
@@ -2384,7 +2386,7 @@
<string name="concurrent_display_notification_thermal_title" msgid="5921609404644739229">"ഉപകരണത്തിന് ചൂട് കൂടുതലാണ്"</string>
<string name="concurrent_display_notification_thermal_content" msgid="2075484836527609319">"നിങ്ങളുടെ ഫോൺ വളരെയധികം ചൂടാകുന്നതിനാൽ ഡ്യുവൽ സ്ക്രീൻ ലഭ്യമല്ല"</string>
<string name="concurrent_display_notification_power_save_title" msgid="1794569070730736281">"ഡ്യുവൽ സ്ക്രീൻ ലഭ്യമല്ല"</string>
- <string name="concurrent_display_notification_power_save_content" msgid="2198116070583851493">"ബാറ്ററി ലാഭിക്കൽ ഓണായതിനാൽ ഡ്യുവൽ സ്ക്രീൻ ലഭ്യമല്ല. നിങ്ങൾക്ക് ഇത് ക്രമീകരണത്തിൽ ഓഫാക്കാം."</string>
+ <string name="concurrent_display_notification_power_save_content" msgid="2198116070583851493">"ബാറ്ററി സേവർ ഓണായതിനാൽ ഡ്യുവൽ സ്ക്രീൻ ലഭ്യമല്ല. നിങ്ങൾക്ക് ഇത് ക്രമീകരണത്തിൽ ഓഫാക്കാം."</string>
<string name="device_state_notification_settings_button" msgid="691937505741872749">"ക്രമീകരണത്തിലേക്ക് പോകുക"</string>
<string name="device_state_notification_turn_off_button" msgid="6327161707661689232">"ഓഫാക്കുക"</string>
<string name="keyboard_layout_notification_selected_title" msgid="1202560174252421219">"<xliff:g id="DEVICE_NAME">%s</xliff:g> കോൺഫിഗർ ചെയ്തു"</string>
@@ -2413,22 +2415,15 @@
<string name="satellite_notification_open_message" msgid="4149234979688273729">"Messages തുറക്കുക"</string>
<string name="satellite_notification_how_it_works" msgid="3132069321977520519">"ഇത് പ്രവർത്തിക്കുന്നത് എങ്ങനെയാണ്"</string>
<string name="unarchival_session_app_label" msgid="6811856981546348205">"തീർപ്പാക്കിയിട്ടില്ല..."</string>
- <!-- no translation found for fingerprint_dangling_notification_title (7362075195588639989) -->
+ <string name="fingerprint_dangling_notification_title" msgid="7362075195588639989">"ഫിംഗർപ്രിന്റ് അൺലോക്ക് വീണ്ടും സജ്ജീകരിക്കുക"</string>
+ <!-- no translation found for fingerprint_dangling_notification_msg_1 (8517140433796229725) -->
<skip />
- <!-- no translation found for fingerprint_dangling_notification_msg_1 (6261149111900787302) -->
+ <!-- no translation found for fingerprint_dangling_notification_msg_2 (7578829498452127613) -->
<skip />
- <!-- no translation found for fingerprint_dangling_notification_msg_2 (7688302770424064884) -->
- <skip />
- <!-- no translation found for fingerprint_dangling_notification_msg_all_deleted_1 (2927018569542316055) -->
- <skip />
- <!-- no translation found for fingerprint_dangling_notification_msg_all_deleted_2 (6897989352716156176) -->
- <skip />
- <!-- no translation found for face_dangling_notification_title (947852541060975473) -->
- <skip />
- <!-- no translation found for face_dangling_notification_msg (8806849376915541655) -->
- <skip />
- <!-- no translation found for biometric_dangling_notification_action_set_up (8246885009807817961) -->
- <skip />
- <!-- no translation found for biometric_dangling_notification_action_not_now (8095249216864443491) -->
- <skip />
+ <string name="fingerprint_dangling_notification_msg_all_deleted_1" msgid="2927018569542316055">"<xliff:g id="FINGERPRINT">%s</xliff:g> ശരിയായി പ്രവർത്തിക്കാത്തതിനാൽ അത് ഇല്ലാതാക്കി. നിങ്ങളുടെ ഫിംഗർപ്രിന്റ് ഉപയോഗിച്ച് ഫോൺ അൺലോക്ക് ചെയ്യുന്നതിനായി വീണ്ടും സജ്ജീകരിക്കുക."</string>
+ <string name="fingerprint_dangling_notification_msg_all_deleted_2" msgid="6897989352716156176">"<xliff:g id="FINGERPRINT_0">%1$s</xliff:g>, <xliff:g id="FINGERPRINT_1">%2$s</xliff:g> എന്നിവ ശരിയായി പ്രവർത്തിക്കാത്തതിനാൽ അവ ഇല്ലാതാക്കി. നിങ്ങളുടെ ഫിംഗർപ്രിന്റ് ഉപയോഗിച്ച് ഫോൺ അൺലോക്ക് ചെയ്യുന്നതിനായി അവ വീണ്ടും സജ്ജീകരിക്കുക."</string>
+ <string name="face_dangling_notification_title" msgid="947852541060975473">"ഫെയ്സ് അൺലോക്ക് വീണ്ടും സജ്ജീകരിക്കുക"</string>
+ <string name="face_dangling_notification_msg" msgid="8806849376915541655">"നിങ്ങളുടെ മുഖ മോഡൽ ശരിയായി പ്രവർത്തിക്കാത്തതിനാൽ അത് ഇല്ലാതാക്കി. നിങ്ങളുടെ മുഖം ഉപയോഗിച്ച് ഫോൺ അൺലോക്ക് ചെയ്യുന്നതിനായി വീണ്ടും സജ്ജീകരിക്കുക."</string>
+ <string name="biometric_dangling_notification_action_set_up" msgid="8246885009807817961">"സജ്ജീകരിക്കുക"</string>
+ <string name="biometric_dangling_notification_action_not_now" msgid="8095249216864443491">"ഇപ്പോൾ വേണ്ട"</string>
</resources>
diff --git a/core/res/res/values-mn/strings.xml b/core/res/res/values-mn/strings.xml
index 5ba16fe..8d430ee 100644
--- a/core/res/res/values-mn/strings.xml
+++ b/core/res/res/values-mn/strings.xml
@@ -283,6 +283,8 @@
<string name="global_action_voice_assist" msgid="6655788068555086695">"Дуут туслах"</string>
<string name="global_action_lockdown" msgid="2475471405907902963">"Түгжих"</string>
<string name="status_bar_notification_info_overflow" msgid="3330152558746563475">"999+"</string>
+ <!-- no translation found for notification_compact_heads_up_reply (2425293958371284340) -->
+ <skip />
<string name="notification_hidden_text" msgid="2835519769868187223">"Шинэ мэдэгдэл"</string>
<string name="notification_channel_physical_keyboard" msgid="5417306456125988096">"Биет гар"</string>
<string name="notification_channel_security" msgid="8516754650348238057">"Аюулгүй байдал"</string>
@@ -2394,7 +2396,7 @@
<string name="keyboard_layout_notification_more_than_three_selected_message" msgid="1581834181578206937">"Гарын бүдүүвчийг <xliff:g id="LAYOUT_1">%1$s</xliff:g>, <xliff:g id="LAYOUT_2">%2$s</xliff:g>, <xliff:g id="LAYOUT_3">%3$s</xliff:g> болгож тохируулсан… Өөрчлөхийн тулд товшино уу."</string>
<string name="keyboard_layout_notification_multiple_selected_title" msgid="5242444914367024499">"Биет гарыг тохируулсан"</string>
<string name="keyboard_layout_notification_multiple_selected_message" msgid="6576533454124419202">"Гарыг харахын тулд товшино уу"</string>
- <string name="profile_label_private" msgid="6463418670715290696">"Хувийн"</string>
+ <string name="profile_label_private" msgid="6463418670715290696">"Хаалттай"</string>
<string name="profile_label_clone" msgid="769106052210954285">"Клон"</string>
<string name="profile_label_work" msgid="3495359133038584618">"Ажил"</string>
<string name="profile_label_work_2" msgid="4691533661598632135">"Ажил 2"</string>
@@ -2413,22 +2415,15 @@
<string name="satellite_notification_open_message" msgid="4149234979688273729">"Мессежийг нээх"</string>
<string name="satellite_notification_how_it_works" msgid="3132069321977520519">"Энэ хэрхэн ажилладаг вэ?"</string>
<string name="unarchival_session_app_label" msgid="6811856981546348205">"Хүлээгдэж буй..."</string>
- <!-- no translation found for fingerprint_dangling_notification_title (7362075195588639989) -->
+ <string name="fingerprint_dangling_notification_title" msgid="7362075195588639989">"Хурууны хээгээр түгжээ тайлахыг дахин тохируулна уу"</string>
+ <!-- no translation found for fingerprint_dangling_notification_msg_1 (8517140433796229725) -->
<skip />
- <!-- no translation found for fingerprint_dangling_notification_msg_1 (6261149111900787302) -->
+ <!-- no translation found for fingerprint_dangling_notification_msg_2 (7578829498452127613) -->
<skip />
- <!-- no translation found for fingerprint_dangling_notification_msg_2 (7688302770424064884) -->
- <skip />
- <!-- no translation found for fingerprint_dangling_notification_msg_all_deleted_1 (2927018569542316055) -->
- <skip />
- <!-- no translation found for fingerprint_dangling_notification_msg_all_deleted_2 (6897989352716156176) -->
- <skip />
- <!-- no translation found for face_dangling_notification_title (947852541060975473) -->
- <skip />
- <!-- no translation found for face_dangling_notification_msg (8806849376915541655) -->
- <skip />
- <!-- no translation found for biometric_dangling_notification_action_set_up (8246885009807817961) -->
- <skip />
- <!-- no translation found for biometric_dangling_notification_action_not_now (8095249216864443491) -->
- <skip />
+ <string name="fingerprint_dangling_notification_msg_all_deleted_1" msgid="2927018569542316055">"<xliff:g id="FINGERPRINT">%s</xliff:g> сайн ажиллахгүй байсан тул үүнийг устгасан. Утасныхаа түгжээг хурууны хээгээр тайлахын тулд хурууны хээг дахин тохируулна уу."</string>
+ <string name="fingerprint_dangling_notification_msg_all_deleted_2" msgid="6897989352716156176">"<xliff:g id="FINGERPRINT_0">%1$s</xliff:g>, <xliff:g id="FINGERPRINT_1">%2$s</xliff:g> сайн ажиллахгүй байсан тул эдгээрийг устгасан. Утасныхаа түгжээг хурууныхаа хээгээр тайлахын тулд хоёр хурууны хээг дахин тохируулна уу."</string>
+ <string name="face_dangling_notification_title" msgid="947852541060975473">"Царайгаар түгжээ тайлахыг дахин тохируулна уу"</string>
+ <string name="face_dangling_notification_msg" msgid="8806849376915541655">"Таны нүүрний загвар сайн ажиллахгүй байсан бөгөөд үүнийг устгасан. Утасныхаа түгжээг царайгаар тайлахын тулд нүүрний загварыг дахин тохируулна уу."</string>
+ <string name="biometric_dangling_notification_action_set_up" msgid="8246885009807817961">"Тохируулах"</string>
+ <string name="biometric_dangling_notification_action_not_now" msgid="8095249216864443491">"Одоо биш"</string>
</resources>
diff --git a/core/res/res/values-mr/strings.xml b/core/res/res/values-mr/strings.xml
index 6c19cc5..d5f2bae 100644
--- a/core/res/res/values-mr/strings.xml
+++ b/core/res/res/values-mr/strings.xml
@@ -283,6 +283,8 @@
<string name="global_action_voice_assist" msgid="6655788068555086695">"व्हॉइस सहाय्य"</string>
<string name="global_action_lockdown" msgid="2475471405907902963">"लॉकडाउन"</string>
<string name="status_bar_notification_info_overflow" msgid="3330152558746563475">"999+"</string>
+ <!-- no translation found for notification_compact_heads_up_reply (2425293958371284340) -->
+ <skip />
<string name="notification_hidden_text" msgid="2835519769868187223">"नवीन सूचना"</string>
<string name="notification_channel_physical_keyboard" msgid="5417306456125988096">"वास्तविक कीबोर्ड"</string>
<string name="notification_channel_security" msgid="8516754650348238057">"सुरक्षा"</string>
@@ -2413,22 +2415,15 @@
<string name="satellite_notification_open_message" msgid="4149234979688273729">"Messages उघडा"</string>
<string name="satellite_notification_how_it_works" msgid="3132069321977520519">"ते कसे काम करते"</string>
<string name="unarchival_session_app_label" msgid="6811856981546348205">"प्रलंबित आहे..."</string>
- <!-- no translation found for fingerprint_dangling_notification_title (7362075195588639989) -->
+ <string name="fingerprint_dangling_notification_title" msgid="7362075195588639989">"फिंगरप्रिंट अनलॉक पुन्हा सेट करा"</string>
+ <!-- no translation found for fingerprint_dangling_notification_msg_1 (8517140433796229725) -->
<skip />
- <!-- no translation found for fingerprint_dangling_notification_msg_1 (6261149111900787302) -->
+ <!-- no translation found for fingerprint_dangling_notification_msg_2 (7578829498452127613) -->
<skip />
- <!-- no translation found for fingerprint_dangling_notification_msg_2 (7688302770424064884) -->
- <skip />
- <!-- no translation found for fingerprint_dangling_notification_msg_all_deleted_1 (2927018569542316055) -->
- <skip />
- <!-- no translation found for fingerprint_dangling_notification_msg_all_deleted_2 (6897989352716156176) -->
- <skip />
- <!-- no translation found for face_dangling_notification_title (947852541060975473) -->
- <skip />
- <!-- no translation found for face_dangling_notification_msg (8806849376915541655) -->
- <skip />
- <!-- no translation found for biometric_dangling_notification_action_set_up (8246885009807817961) -->
- <skip />
- <!-- no translation found for biometric_dangling_notification_action_not_now (8095249216864443491) -->
- <skip />
+ <string name="fingerprint_dangling_notification_msg_all_deleted_1" msgid="2927018569542316055">"योग्यरीत्या काम करत नसल्यामुळे <xliff:g id="FINGERPRINT">%s</xliff:g> हटवले गेले आहे. तुमचे फिंगरप्रिंट वापरून फोन अनलॉक करण्यासाठी ते पुन्हा सेट करा."</string>
+ <string name="fingerprint_dangling_notification_msg_all_deleted_2" msgid="6897989352716156176">"परफॉर्मन्समध्ये सुधारणा करण्यासाठी आणि योग्यरीत्या काम करत नसल्यामुळे <xliff:g id="FINGERPRINT_0">%1$s</xliff:g> व <xliff:g id="FINGERPRINT_1">%2$s</xliff:g> हटवली गेली आहेत. तुमचे फिंगरप्रिंट वापरून फोन अनलॉक करण्यासाठी ते पुन्हा सेट करा."</string>
+ <string name="face_dangling_notification_title" msgid="947852541060975473">"फेस अनलॉक पुन्हा सेट करा"</string>
+ <string name="face_dangling_notification_msg" msgid="8806849376915541655">"तुमचे फेस मॉडेल योग्यरीत्या काम करत नसल्यामुळे ते हटवले गेले आहे. तुमचा चेहरा वापरून फोन अनलॉक करण्यासाठी ते पुन्हा सेट करा."</string>
+ <string name="biometric_dangling_notification_action_set_up" msgid="8246885009807817961">"सेट करा"</string>
+ <string name="biometric_dangling_notification_action_not_now" msgid="8095249216864443491">"आताच नको"</string>
</resources>
diff --git a/core/res/res/values-ms/strings.xml b/core/res/res/values-ms/strings.xml
index 82c1a59..8018cb3 100644
--- a/core/res/res/values-ms/strings.xml
+++ b/core/res/res/values-ms/strings.xml
@@ -283,6 +283,8 @@
<string name="global_action_voice_assist" msgid="6655788068555086695">"Bantuan Suara"</string>
<string name="global_action_lockdown" msgid="2475471405907902963">"Kunci semua"</string>
<string name="status_bar_notification_info_overflow" msgid="3330152558746563475">"999+"</string>
+ <!-- no translation found for notification_compact_heads_up_reply (2425293958371284340) -->
+ <skip />
<string name="notification_hidden_text" msgid="2835519769868187223">"Pemberitahuan baharu"</string>
<string name="notification_channel_physical_keyboard" msgid="5417306456125988096">"Papan kekunci fizikal"</string>
<string name="notification_channel_security" msgid="8516754650348238057">"Keselamatan"</string>
@@ -2413,22 +2415,15 @@
<string name="satellite_notification_open_message" msgid="4149234979688273729">"Buka Messages"</string>
<string name="satellite_notification_how_it_works" msgid="3132069321977520519">"Cara ciri ini berfungsi"</string>
<string name="unarchival_session_app_label" msgid="6811856981546348205">"Belum selesai..."</string>
- <!-- no translation found for fingerprint_dangling_notification_title (7362075195588639989) -->
+ <string name="fingerprint_dangling_notification_title" msgid="7362075195588639989">"Sediakan Buka Kunci Cap Jari sekali lagi"</string>
+ <!-- no translation found for fingerprint_dangling_notification_msg_1 (8517140433796229725) -->
<skip />
- <!-- no translation found for fingerprint_dangling_notification_msg_1 (6261149111900787302) -->
+ <!-- no translation found for fingerprint_dangling_notification_msg_2 (7578829498452127613) -->
<skip />
- <!-- no translation found for fingerprint_dangling_notification_msg_2 (7688302770424064884) -->
- <skip />
- <!-- no translation found for fingerprint_dangling_notification_msg_all_deleted_1 (2927018569542316055) -->
- <skip />
- <!-- no translation found for fingerprint_dangling_notification_msg_all_deleted_2 (6897989352716156176) -->
- <skip />
- <!-- no translation found for face_dangling_notification_title (947852541060975473) -->
- <skip />
- <!-- no translation found for face_dangling_notification_msg (8806849376915541655) -->
- <skip />
- <!-- no translation found for biometric_dangling_notification_action_set_up (8246885009807817961) -->
- <skip />
- <!-- no translation found for biometric_dangling_notification_action_not_now (8095249216864443491) -->
- <skip />
+ <string name="fingerprint_dangling_notification_msg_all_deleted_1" msgid="2927018569542316055">"<xliff:g id="FINGERPRINT">%s</xliff:g> tidak berfungsi dengan baik dan telah dipadamkan. Sediakan cap jari sekali lagi untuk membuka kunci telefon anda menggunakan cap jari."</string>
+ <string name="fingerprint_dangling_notification_msg_all_deleted_2" msgid="6897989352716156176">"<xliff:g id="FINGERPRINT_0">%1$s</xliff:g> dan <xliff:g id="FINGERPRINT_1">%2$s</xliff:g> tidak berfungsi dengan baik dan telah dipadamkan. Sediakan kedua-dua cap jari tersebut sekali lagi untuk membuka kunci telefon anda menggunakan cap jari anda."</string>
+ <string name="face_dangling_notification_title" msgid="947852541060975473">"Sediakan semula Buka Kunci Wajah"</string>
+ <string name="face_dangling_notification_msg" msgid="8806849376915541655">"Model wajah anda tidak berfungsi dengan baik dan telah dipadamkan. Sediakan model wajah sekali lagi untuk membuka kunci telefon anda menggunakan wajah."</string>
+ <string name="biometric_dangling_notification_action_set_up" msgid="8246885009807817961">"Sediakan"</string>
+ <string name="biometric_dangling_notification_action_not_now" msgid="8095249216864443491">"Bukan sekarang"</string>
</resources>
diff --git a/core/res/res/values-my/strings.xml b/core/res/res/values-my/strings.xml
index 43b7ffa..488bdb0 100644
--- a/core/res/res/values-my/strings.xml
+++ b/core/res/res/values-my/strings.xml
@@ -283,6 +283,8 @@
<string name="global_action_voice_assist" msgid="6655788068555086695">"အသံ အကူအညီ"</string>
<string name="global_action_lockdown" msgid="2475471405907902963">"လော့ခ်ဒေါင်း"</string>
<string name="status_bar_notification_info_overflow" msgid="3330152558746563475">"၉၉၉+"</string>
+ <!-- no translation found for notification_compact_heads_up_reply (2425293958371284340) -->
+ <skip />
<string name="notification_hidden_text" msgid="2835519769868187223">"အကြောင်းကြားချက်အသစ်"</string>
<string name="notification_channel_physical_keyboard" msgid="5417306456125988096">"စက်၏ ကီးဘုတ်"</string>
<string name="notification_channel_security" msgid="8516754650348238057">"လုံခြုံရေး"</string>
@@ -2413,22 +2415,15 @@
<string name="satellite_notification_open_message" msgid="4149234979688273729">"Messages ဖွင့်ရန်"</string>
<string name="satellite_notification_how_it_works" msgid="3132069321977520519">"အလုပ်လုပ်ပုံ"</string>
<string name="unarchival_session_app_label" msgid="6811856981546348205">"ဆိုင်းငံ့ထားသည်…"</string>
- <!-- no translation found for fingerprint_dangling_notification_title (7362075195588639989) -->
+ <string name="fingerprint_dangling_notification_title" msgid="7362075195588639989">"‘လက်ဗွေသုံး လော့ခ်ဖွင့်ခြင်း’ ကို စနစ်ထပ်မံထည့်သွင်းပါ"</string>
+ <!-- no translation found for fingerprint_dangling_notification_msg_1 (8517140433796229725) -->
<skip />
- <!-- no translation found for fingerprint_dangling_notification_msg_1 (6261149111900787302) -->
+ <!-- no translation found for fingerprint_dangling_notification_msg_2 (7578829498452127613) -->
<skip />
- <!-- no translation found for fingerprint_dangling_notification_msg_2 (7688302770424064884) -->
- <skip />
- <!-- no translation found for fingerprint_dangling_notification_msg_all_deleted_1 (2927018569542316055) -->
- <skip />
- <!-- no translation found for fingerprint_dangling_notification_msg_all_deleted_2 (6897989352716156176) -->
- <skip />
- <!-- no translation found for face_dangling_notification_title (947852541060975473) -->
- <skip />
- <!-- no translation found for face_dangling_notification_msg (8806849376915541655) -->
- <skip />
- <!-- no translation found for biometric_dangling_notification_action_set_up (8246885009807817961) -->
- <skip />
- <!-- no translation found for biometric_dangling_notification_action_not_now (8095249216864443491) -->
- <skip />
+ <string name="fingerprint_dangling_notification_msg_all_deleted_1" msgid="2927018569542316055">"<xliff:g id="FINGERPRINT">%s</xliff:g> သိပ်အဆင်မပြေသဖြင့် ဖျက်ထားသည်။ သင့်ဖုန်းကို လက်ဗွေဖြင့်လော့ခ်ဖွင့်ရန် ၎င်းကို စနစ်ထပ်မံထည့်သွင်းပါ။"</string>
+ <string name="fingerprint_dangling_notification_msg_all_deleted_2" msgid="6897989352716156176">"<xliff:g id="FINGERPRINT_0">%1$s</xliff:g> နှင့် <xliff:g id="FINGERPRINT_1">%2$s</xliff:g> တို့ သိပ်အဆင်မပြေသဖြင့် ဖျက်ထားသည်။ သင့်ဖုန်းကို လက်ဗွေဖြင့်လော့ခ်ဖွင့်ရန် ၎င်းတို့ကို စနစ်ထပ်မံထည့်သွင်းပါ။"</string>
+ <string name="face_dangling_notification_title" msgid="947852541060975473">"‘မျက်နှာပြ လော့ခ်ဖွင့်ခြင်း’ ကို စနစ်ထပ်မံထည့်သွင်းပါ"</string>
+ <string name="face_dangling_notification_msg" msgid="8806849376915541655">"သင့်မျက်နှာနမူနာ သိပ်အဆင်မပြေသဖြင့် ဖျက်ထားသည်။ သင့်ဖုန်းကို မျက်နှာဖြင့်လော့ခ်ဖွင့်ရန် ၎င်းကို စနစ်ထပ်မံထည့်သွင်းပါ။"</string>
+ <string name="biometric_dangling_notification_action_set_up" msgid="8246885009807817961">"စနစ်ထည့်သွင်းရန်"</string>
+ <string name="biometric_dangling_notification_action_not_now" msgid="8095249216864443491">"ယခုမလုပ်ပါ"</string>
</resources>
diff --git a/core/res/res/values-nb/strings.xml b/core/res/res/values-nb/strings.xml
index ad4ffc2..3939f48 100644
--- a/core/res/res/values-nb/strings.xml
+++ b/core/res/res/values-nb/strings.xml
@@ -283,6 +283,8 @@
<string name="global_action_voice_assist" msgid="6655788068555086695">"Talehjelp"</string>
<string name="global_action_lockdown" msgid="2475471405907902963">"Låsing"</string>
<string name="status_bar_notification_info_overflow" msgid="3330152558746563475">"999+"</string>
+ <!-- no translation found for notification_compact_heads_up_reply (2425293958371284340) -->
+ <skip />
<string name="notification_hidden_text" msgid="2835519769868187223">"Nytt varsel"</string>
<string name="notification_channel_physical_keyboard" msgid="5417306456125988096">"Fysisk tastatur"</string>
<string name="notification_channel_security" msgid="8516754650348238057">"Sikkerhet"</string>
@@ -2413,22 +2415,15 @@
<string name="satellite_notification_open_message" msgid="4149234979688273729">"Åpne Meldinger"</string>
<string name="satellite_notification_how_it_works" msgid="3132069321977520519">"Slik fungerer det"</string>
<string name="unarchival_session_app_label" msgid="6811856981546348205">"Venter …"</string>
- <!-- no translation found for fingerprint_dangling_notification_title (7362075195588639989) -->
+ <string name="fingerprint_dangling_notification_title" msgid="7362075195588639989">"Konfigurer opplåsingen med fingeravtrykk på nytt"</string>
+ <!-- no translation found for fingerprint_dangling_notification_msg_1 (8517140433796229725) -->
<skip />
- <!-- no translation found for fingerprint_dangling_notification_msg_1 (6261149111900787302) -->
+ <!-- no translation found for fingerprint_dangling_notification_msg_2 (7578829498452127613) -->
<skip />
- <!-- no translation found for fingerprint_dangling_notification_msg_2 (7688302770424064884) -->
- <skip />
- <!-- no translation found for fingerprint_dangling_notification_msg_all_deleted_1 (2927018569542316055) -->
- <skip />
- <!-- no translation found for fingerprint_dangling_notification_msg_all_deleted_2 (6897989352716156176) -->
- <skip />
- <!-- no translation found for face_dangling_notification_title (947852541060975473) -->
- <skip />
- <!-- no translation found for face_dangling_notification_msg (8806849376915541655) -->
- <skip />
- <!-- no translation found for biometric_dangling_notification_action_set_up (8246885009807817961) -->
- <skip />
- <!-- no translation found for biometric_dangling_notification_action_not_now (8095249216864443491) -->
- <skip />
+ <string name="fingerprint_dangling_notification_msg_all_deleted_1" msgid="2927018569542316055">"<xliff:g id="FINGERPRINT">%s</xliff:g> fungerte ikke skikkelig og ble slettet. Du kan konfigurere det på nytt for å låse opp telefonen med fingeravtrykket."</string>
+ <string name="fingerprint_dangling_notification_msg_all_deleted_2" msgid="6897989352716156176">"<xliff:g id="FINGERPRINT_0">%1$s</xliff:g> og <xliff:g id="FINGERPRINT_1">%2$s</xliff:g> fungerte ikke skikkelig og ble slettet. Du kan konfigurere dem på nytt for å låse opp telefonen med fingeravtrykket."</string>
+ <string name="face_dangling_notification_title" msgid="947852541060975473">"Konfigurer ansiktslåsen på nytt"</string>
+ <string name="face_dangling_notification_msg" msgid="8806849376915541655">"Ansiktsmodellen din fungerte ikke skikkelig og ble slettet. Du kan konfigurere den på nytt for å låse opp telefonen med ansiktet."</string>
+ <string name="biometric_dangling_notification_action_set_up" msgid="8246885009807817961">"Konfigurer"</string>
+ <string name="biometric_dangling_notification_action_not_now" msgid="8095249216864443491">"Ikke nå"</string>
</resources>
diff --git a/core/res/res/values-ne/strings.xml b/core/res/res/values-ne/strings.xml
index 1cde247..c16a02c 100644
--- a/core/res/res/values-ne/strings.xml
+++ b/core/res/res/values-ne/strings.xml
@@ -283,6 +283,8 @@
<string name="global_action_voice_assist" msgid="6655788068555086695">"आवाज सहायता"</string>
<string name="global_action_lockdown" msgid="2475471405907902963">"लकडाउन गर्नु…"</string>
<string name="status_bar_notification_info_overflow" msgid="3330152558746563475">"९९९+"</string>
+ <!-- no translation found for notification_compact_heads_up_reply (2425293958371284340) -->
+ <skip />
<string name="notification_hidden_text" msgid="2835519769868187223">"नयाँ सूचना"</string>
<string name="notification_channel_physical_keyboard" msgid="5417306456125988096">"फिजिकल किबोर्ड"</string>
<string name="notification_channel_security" msgid="8516754650348238057">"सुरक्षा"</string>
@@ -873,30 +875,30 @@
<item msgid="4537253139152229577">"घरको फ्याक्स"</item>
<item msgid="6751245029698664340">"पेजर"</item>
<item msgid="1692790665884224905">"अन्य"</item>
- <item msgid="6216981255272016212">"आफू अनुकूल"</item>
+ <item msgid="6216981255272016212">" कस्टम"</item>
</string-array>
<string-array name="emailAddressTypes">
<item msgid="7786349763648997741">"गृह"</item>
<item msgid="435564470865989199">"काम"</item>
<item msgid="4199433197875490373">"अन्य"</item>
- <item msgid="3233938986670468328">"आफू अनुकूल"</item>
+ <item msgid="3233938986670468328">" कस्टम"</item>
</string-array>
<string-array name="postalAddressTypes">
<item msgid="3861463339764243038">"गृह"</item>
<item msgid="5472578890164979109">"काम"</item>
<item msgid="5718921296646594739">"अन्य"</item>
- <item msgid="5523122236731783179">"आफू अनुकूल"</item>
+ <item msgid="5523122236731783179">" कस्टम"</item>
</string-array>
<string-array name="imAddressTypes">
<item msgid="588088543406993772">"गृह"</item>
<item msgid="5503060422020476757">"काम"</item>
<item msgid="2530391194653760297">"अन्य"</item>
- <item msgid="7640927178025203330">"आफू अनुकूल"</item>
+ <item msgid="7640927178025203330">" कस्टम"</item>
</string-array>
<string-array name="organizationTypes">
<item msgid="6144047813304847762">"काम गर्नुहोस्"</item>
<item msgid="7402720230065674193">"अन्य"</item>
- <item msgid="808230403067569648">"आफू अनुकूल"</item>
+ <item msgid="808230403067569648">" कस्टम"</item>
</string-array>
<string-array name="imProtocols">
<item msgid="7535761744432206400">"AIM"</item>
@@ -908,7 +910,7 @@
<item msgid="4717545739447438044">"ICQ"</item>
<item msgid="8293711853624033835">"Jabber"</item>
</string-array>
- <string name="phoneTypeCustom" msgid="5120365721260686814">"आफू अनुकूल"</string>
+ <string name="phoneTypeCustom" msgid="5120365721260686814">" कस्टम"</string>
<string name="phoneTypeHome" msgid="3880132427643623588">"गृह"</string>
<string name="phoneTypeMobile" msgid="1178852541462086735">"मोबाइल"</string>
<string name="phoneTypeWork" msgid="6604967163358864607">"काम"</string>
@@ -929,24 +931,24 @@
<string name="phoneTypeWorkPager" msgid="3748332310638505234">"कार्य पेजर"</string>
<string name="phoneTypeAssistant" msgid="757550783842231039">"सहायक"</string>
<string name="phoneTypeMms" msgid="1799747455131365989">"MMS"</string>
- <string name="eventTypeCustom" msgid="3257367158986466481">"आफू अनुकूल"</string>
+ <string name="eventTypeCustom" msgid="3257367158986466481">" कस्टम"</string>
<string name="eventTypeBirthday" msgid="7770026752793912283">"जन्मदिन"</string>
<string name="eventTypeAnniversary" msgid="4684702412407916888">"वार्षिक समारोह"</string>
<string name="eventTypeOther" msgid="530671238533887997">"अन्य"</string>
- <string name="emailTypeCustom" msgid="1809435350482181786">"आफू अनुकूल"</string>
+ <string name="emailTypeCustom" msgid="1809435350482181786">" कस्टम"</string>
<string name="emailTypeHome" msgid="1597116303154775999">"गृह"</string>
<string name="emailTypeWork" msgid="2020095414401882111">"काम"</string>
<string name="emailTypeOther" msgid="5131130857030897465">"अन्य"</string>
<string name="emailTypeMobile" msgid="787155077375364230">"मोबाइल"</string>
- <string name="postalTypeCustom" msgid="5645590470242939129">"आफू अनुकूल"</string>
+ <string name="postalTypeCustom" msgid="5645590470242939129">" कस्टम"</string>
<string name="postalTypeHome" msgid="7562272480949727912">"गृह"</string>
<string name="postalTypeWork" msgid="8553425424652012826">"काम"</string>
<string name="postalTypeOther" msgid="7094245413678857420">"अन्य"</string>
- <string name="imTypeCustom" msgid="5653384545085765570">"आफू अनुकूल"</string>
+ <string name="imTypeCustom" msgid="5653384545085765570">" कस्टम"</string>
<string name="imTypeHome" msgid="6996507981044278216">"गृह"</string>
<string name="imTypeWork" msgid="2099668940169903123">"काम"</string>
<string name="imTypeOther" msgid="8068447383276219810">"अन्य"</string>
- <string name="imProtocolCustom" msgid="4437878287653764692">"आफू अनुकूल"</string>
+ <string name="imProtocolCustom" msgid="4437878287653764692">" कस्टम"</string>
<string name="imProtocolAim" msgid="4050198236506604378">"AIM"</string>
<string name="imProtocolMsn" msgid="2257148557766499232">"Windows Live"</string>
<string name="imProtocolYahoo" msgid="5373338758093392231">"Yahoo"</string>
@@ -958,8 +960,8 @@
<string name="imProtocolNetMeeting" msgid="4985002408136148256">"NetMeeting"</string>
<string name="orgTypeWork" msgid="8684458700669564172">"काम"</string>
<string name="orgTypeOther" msgid="5450675258408005553">"अन्य"</string>
- <string name="orgTypeCustom" msgid="1126322047677329218">"आफू अनुकूल"</string>
- <string name="relationTypeCustom" msgid="282938315217441351">"आफू अनुकूल"</string>
+ <string name="orgTypeCustom" msgid="1126322047677329218">" कस्टम"</string>
+ <string name="relationTypeCustom" msgid="282938315217441351">" कस्टम"</string>
<string name="relationTypeAssistant" msgid="4057605157116589315">"सहायक"</string>
<string name="relationTypeBrother" msgid="7141662427379247820">"भाइ"</string>
<string name="relationTypeChild" msgid="9076258911292693601">"सन्तान"</string>
@@ -974,7 +976,7 @@
<string name="relationTypeRelative" msgid="3396498519818009134">"आफन्त"</string>
<string name="relationTypeSister" msgid="3721676005094140671">"बहिनी"</string>
<string name="relationTypeSpouse" msgid="6916682664436031703">"पति-पत्नी"</string>
- <string name="sipAddressTypeCustom" msgid="6283889809842649336">"आफू अनुकूल"</string>
+ <string name="sipAddressTypeCustom" msgid="6283889809842649336">" कस्टम"</string>
<string name="sipAddressTypeHome" msgid="5918441930656878367">"गृह"</string>
<string name="sipAddressTypeWork" msgid="7873967986701216770">"काम गर्नुहोस्"</string>
<string name="sipAddressTypeOther" msgid="6317012577345187275">"अन्य"</string>
@@ -2413,22 +2415,15 @@
<string name="satellite_notification_open_message" msgid="4149234979688273729">"Messages खोल्नुहोस्"</string>
<string name="satellite_notification_how_it_works" msgid="3132069321977520519">"यसले काम गर्ने तरिका"</string>
<string name="unarchival_session_app_label" msgid="6811856981546348205">"विचाराधीन..."</string>
- <!-- no translation found for fingerprint_dangling_notification_title (7362075195588639989) -->
+ <string name="fingerprint_dangling_notification_title" msgid="7362075195588639989">"फिंगरप्रिन्ट अनलक फेरि सेटअप गर्नुहोस्"</string>
+ <!-- no translation found for fingerprint_dangling_notification_msg_1 (8517140433796229725) -->
<skip />
- <!-- no translation found for fingerprint_dangling_notification_msg_1 (6261149111900787302) -->
+ <!-- no translation found for fingerprint_dangling_notification_msg_2 (7578829498452127613) -->
<skip />
- <!-- no translation found for fingerprint_dangling_notification_msg_2 (7688302770424064884) -->
- <skip />
- <!-- no translation found for fingerprint_dangling_notification_msg_all_deleted_1 (2927018569542316055) -->
- <skip />
- <!-- no translation found for fingerprint_dangling_notification_msg_all_deleted_2 (6897989352716156176) -->
- <skip />
- <!-- no translation found for face_dangling_notification_title (947852541060975473) -->
- <skip />
- <!-- no translation found for face_dangling_notification_msg (8806849376915541655) -->
- <skip />
- <!-- no translation found for biometric_dangling_notification_action_set_up (8246885009807817961) -->
- <skip />
- <!-- no translation found for biometric_dangling_notification_action_not_now (8095249216864443491) -->
- <skip />
+ <string name="fingerprint_dangling_notification_msg_all_deleted_1" msgid="2927018569542316055">"<xliff:g id="FINGERPRINT">%s</xliff:g> ले काम गरिरहेको थिएन र त्यसलाई मेटाइयो। फिंगरप्रिन्ट प्रयोग गरी आफ्नो फोन अनलक गर्न त्यसलाई फेरि सेट अप गर्नुहोस्।"</string>
+ <string name="fingerprint_dangling_notification_msg_all_deleted_2" msgid="6897989352716156176">"<xliff:g id="FINGERPRINT_0">%1$s</xliff:g> र <xliff:g id="FINGERPRINT_1">%2$s</xliff:g> ले राम्ररी काम गरिरहेका थिएनन् र तिनलाई मेटाइयो। फिंगरप्रिन्ट प्रयोग गरी आफ्नो फोन अनलक गर्न तिनलाई फेरि सेट अप गर्नुहोस्।"</string>
+ <string name="face_dangling_notification_title" msgid="947852541060975473">"फेस अनलक फेरि सेटअप गर्नुहोस्"</string>
+ <string name="face_dangling_notification_msg" msgid="8806849376915541655">"तपाईंको फेस मोडेलले राम्ररी काम गरिरहेको थिएन र त्यसलाई मेटाइयो। अनुहार प्रयोग गरी आफ्नो फोन अनलक गर्न फेस मोडेल फेरि सेट अप गर्नुहोस्।"</string>
+ <string name="biometric_dangling_notification_action_set_up" msgid="8246885009807817961">"सेटअप गर्नुहोस्"</string>
+ <string name="biometric_dangling_notification_action_not_now" msgid="8095249216864443491">"अहिले होइन"</string>
</resources>
diff --git a/core/res/res/values-nl/strings.xml b/core/res/res/values-nl/strings.xml
index 7bae96e..f8b0f9e 100644
--- a/core/res/res/values-nl/strings.xml
+++ b/core/res/res/values-nl/strings.xml
@@ -283,6 +283,8 @@
<string name="global_action_voice_assist" msgid="6655788068555086695">"Spraakassistent"</string>
<string name="global_action_lockdown" msgid="2475471405907902963">"Lockdown"</string>
<string name="status_bar_notification_info_overflow" msgid="3330152558746563475">"999 +"</string>
+ <!-- no translation found for notification_compact_heads_up_reply (2425293958371284340) -->
+ <skip />
<string name="notification_hidden_text" msgid="2835519769868187223">"Nieuwe melding"</string>
<string name="notification_channel_physical_keyboard" msgid="5417306456125988096">"Fysiek toetsenbord"</string>
<string name="notification_channel_security" msgid="8516754650348238057">"Beveiliging"</string>
@@ -2413,22 +2415,15 @@
<string name="satellite_notification_open_message" msgid="4149234979688273729">"Berichten openen"</string>
<string name="satellite_notification_how_it_works" msgid="3132069321977520519">"Hoe het werkt"</string>
<string name="unarchival_session_app_label" msgid="6811856981546348205">"In behandeling…"</string>
- <!-- no translation found for fingerprint_dangling_notification_title (7362075195588639989) -->
+ <string name="fingerprint_dangling_notification_title" msgid="7362075195588639989">"Ontgrendelen met vingerafdruk weer instellen"</string>
+ <!-- no translation found for fingerprint_dangling_notification_msg_1 (8517140433796229725) -->
<skip />
- <!-- no translation found for fingerprint_dangling_notification_msg_1 (6261149111900787302) -->
+ <!-- no translation found for fingerprint_dangling_notification_msg_2 (7578829498452127613) -->
<skip />
- <!-- no translation found for fingerprint_dangling_notification_msg_2 (7688302770424064884) -->
- <skip />
- <!-- no translation found for fingerprint_dangling_notification_msg_all_deleted_1 (2927018569542316055) -->
- <skip />
- <!-- no translation found for fingerprint_dangling_notification_msg_all_deleted_2 (6897989352716156176) -->
- <skip />
- <!-- no translation found for face_dangling_notification_title (947852541060975473) -->
- <skip />
- <!-- no translation found for face_dangling_notification_msg (8806849376915541655) -->
- <skip />
- <!-- no translation found for biometric_dangling_notification_action_set_up (8246885009807817961) -->
- <skip />
- <!-- no translation found for biometric_dangling_notification_action_not_now (8095249216864443491) -->
- <skip />
+ <string name="fingerprint_dangling_notification_msg_all_deleted_1" msgid="2927018569542316055">"<xliff:g id="FINGERPRINT">%s</xliff:g> werkte niet goed en is verwijderd. Stel deze opnieuw in om de telefoon met je vingerafdruk te ontgrendelen."</string>
+ <string name="fingerprint_dangling_notification_msg_all_deleted_2" msgid="6897989352716156176">"<xliff:g id="FINGERPRINT_0">%1$s</xliff:g> en <xliff:g id="FINGERPRINT_1">%2$s</xliff:g> werkten niet goed en zijn verwijderd. Stel ze opnieuw in om de telefoon met je vingerafdruk te ontgrendelen."</string>
+ <string name="face_dangling_notification_title" msgid="947852541060975473">"Ontgrendelen via gezichtsherkenning weer instellen"</string>
+ <string name="face_dangling_notification_msg" msgid="8806849376915541655">"Je gezichtsmodel werkte niet goed en is verwijderd. Stel het opnieuw in om de telefoon met je gezicht te ontgrendelen."</string>
+ <string name="biometric_dangling_notification_action_set_up" msgid="8246885009807817961">"Instellen"</string>
+ <string name="biometric_dangling_notification_action_not_now" msgid="8095249216864443491">"Niet nu"</string>
</resources>
diff --git a/core/res/res/values-or/strings.xml b/core/res/res/values-or/strings.xml
index c00998a..9abed2b 100644
--- a/core/res/res/values-or/strings.xml
+++ b/core/res/res/values-or/strings.xml
@@ -283,6 +283,8 @@
<string name="global_action_voice_assist" msgid="6655788068555086695">"ଭଏସ୍ ସହାୟକ"</string>
<string name="global_action_lockdown" msgid="2475471405907902963">"ଲକ୍ କରନ୍ତୁ"</string>
<string name="status_bar_notification_info_overflow" msgid="3330152558746563475">"999+"</string>
+ <!-- no translation found for notification_compact_heads_up_reply (2425293958371284340) -->
+ <skip />
<string name="notification_hidden_text" msgid="2835519769868187223">"ନୂଆ ବିଜ୍ଞପ୍ତି"</string>
<string name="notification_channel_physical_keyboard" msgid="5417306456125988096">"ଫିଜିକଲ୍ କୀ’ବୋର୍ଡ"</string>
<string name="notification_channel_security" msgid="8516754650348238057">"ସୁରକ୍ଷା"</string>
@@ -2144,7 +2146,7 @@
<string name="dynamic_mode_notification_channel_name" msgid="2986926422100223328">"ନିୟମିତ ମୋଡ୍ ସୂଚନା ବିଜ୍ଞପ୍ତି"</string>
<string name="dynamic_mode_notification_title" msgid="1388718452788985481">"ବେଟେରୀ ସେଭର ଚାଲୁ କରାଯାଇଛି"</string>
<string name="dynamic_mode_notification_summary" msgid="1639031262484979689">"ବ୍ୟାଟେରୀ ଲାଇଫ ବଢ଼ାଇବା ପାଇଁ ବ୍ୟାଟେରୀ ବ୍ୟବହାର କମ୍ କରିବା"</string>
- <string name="dynamic_mode_notification_title_v2" msgid="5072385242078021152">"ବ୍ୟାଟେରୀ ସେଭର୍ ଚାଲୁ ଅଛି"</string>
+ <string name="dynamic_mode_notification_title_v2" msgid="5072385242078021152">"ବେଟେରୀ ସେଭର ଚାଲୁ ଅଛି"</string>
<string name="dynamic_mode_notification_summary_v2" msgid="2142444344663147938">"ବେଟେରୀ ଲାଇଫକୁ ବଢ଼ାଇବା ପାଇଁ ବେଟେରୀ ସେଭରକୁ ଚାଲୁ କରାଯାଇଛି"</string>
<string name="battery_saver_notification_channel_name" msgid="3918243458067916913">"ବେଟେରୀ ସେଭର"</string>
<string name="battery_saver_off_notification_title" msgid="7637255960468032515">"ବ୍ୟାଟେରୀ ସେଭର୍ ବନ୍ଦ ଅଛି"</string>
@@ -2413,22 +2415,15 @@
<string name="satellite_notification_open_message" msgid="4149234979688273729">"Messages ଖୋଲନ୍ତୁ"</string>
<string name="satellite_notification_how_it_works" msgid="3132069321977520519">"ଏହା କିପରି କାମ କରେ"</string>
<string name="unarchival_session_app_label" msgid="6811856981546348205">"ବାକି ଅଛି…"</string>
- <!-- no translation found for fingerprint_dangling_notification_title (7362075195588639989) -->
+ <string name="fingerprint_dangling_notification_title" msgid="7362075195588639989">"ଫିଙ୍ଗରପ୍ରିଣ୍ଟ ଅନଲକ ପୁଣି ସେଟ ଅପ କରନ୍ତୁ"</string>
+ <!-- no translation found for fingerprint_dangling_notification_msg_1 (8517140433796229725) -->
<skip />
- <!-- no translation found for fingerprint_dangling_notification_msg_1 (6261149111900787302) -->
+ <!-- no translation found for fingerprint_dangling_notification_msg_2 (7578829498452127613) -->
<skip />
- <!-- no translation found for fingerprint_dangling_notification_msg_2 (7688302770424064884) -->
- <skip />
- <!-- no translation found for fingerprint_dangling_notification_msg_all_deleted_1 (2927018569542316055) -->
- <skip />
- <!-- no translation found for fingerprint_dangling_notification_msg_all_deleted_2 (6897989352716156176) -->
- <skip />
- <!-- no translation found for face_dangling_notification_title (947852541060975473) -->
- <skip />
- <!-- no translation found for face_dangling_notification_msg (8806849376915541655) -->
- <skip />
- <!-- no translation found for biometric_dangling_notification_action_set_up (8246885009807817961) -->
- <skip />
- <!-- no translation found for biometric_dangling_notification_action_not_now (8095249216864443491) -->
- <skip />
+ <string name="fingerprint_dangling_notification_msg_all_deleted_1" msgid="2927018569542316055">"<xliff:g id="FINGERPRINT">%s</xliff:g> ସଠିକ ଭାବେ କାମ କରୁନାହିଁ ଏବଂ ଏହାକୁ ଡିଲିଟ କରାଯାଇଛି। ଟିପଚିହ୍ନ ମାଧ୍ୟମରେ ଆପଣଙ୍କ ଫୋନକୁ ଅନଲକ କରିବାକୁ ଏହାକୁ ପୁଣି ସେଟ ଅପ କରନ୍ତୁ।"</string>
+ <string name="fingerprint_dangling_notification_msg_all_deleted_2" msgid="6897989352716156176">"<xliff:g id="FINGERPRINT_0">%1$s</xliff:g> ଏବଂ <xliff:g id="FINGERPRINT_1">%2$s</xliff:g> ସଠିକ ଭାବେ କାମ କରୁନାହିଁ ଏବଂ ଏଗୁଡ଼ିକୁ ଡିଲିଟ କରାଯାଇଛି। ଆପଣଙ୍କ ଟିପଚିହ୍ନ ମାଧ୍ୟମରେ ଆପଣଙ୍କ ଫୋନକୁ ଅନଲକ କରିବାକୁ ଏଗୁଡ଼ିକୁ ପୁଣି ସେଟ ଅପ କରନ୍ତୁ।"</string>
+ <string name="face_dangling_notification_title" msgid="947852541060975473">"ଫେସ୍ ଅନଲକ୍ ପୁଣି ସେଟ୍ ଅପ୍ କରନ୍ତୁ"</string>
+ <string name="face_dangling_notification_msg" msgid="8806849376915541655">"ଆପଣଙ୍କ ଫେସ ମଡେଲ ସଠିକ ଭାବେ କାମ କରୁନାହିଁ ଏବଂ ଏହାକୁ ଡିଲିଟ କରାଯାଇଛି। ଫେସ ମାଧ୍ୟମରେ ଆପଣଙ୍କ ଫୋନକୁ ଅନଲକ କରିବାକୁ ଏହାକୁ ପୁଣି ସେଟ ଅପ କରନ୍ତୁ।"</string>
+ <string name="biometric_dangling_notification_action_set_up" msgid="8246885009807817961">"ସେଟ ଅପ କରନ୍ତୁ"</string>
+ <string name="biometric_dangling_notification_action_not_now" msgid="8095249216864443491">"ବର୍ତ୍ତମାନ ନୁହେଁ"</string>
</resources>
diff --git a/core/res/res/values-pa/strings.xml b/core/res/res/values-pa/strings.xml
index 77020ac..64b326c 100644
--- a/core/res/res/values-pa/strings.xml
+++ b/core/res/res/values-pa/strings.xml
@@ -283,6 +283,8 @@
<string name="global_action_voice_assist" msgid="6655788068555086695">"ਅਵਾਜ਼ੀ ਸਹਾਇਕ"</string>
<string name="global_action_lockdown" msgid="2475471405907902963">"ਲਾਕਡਾਊਨ"</string>
<string name="status_bar_notification_info_overflow" msgid="3330152558746563475">"999+"</string>
+ <!-- no translation found for notification_compact_heads_up_reply (2425293958371284340) -->
+ <skip />
<string name="notification_hidden_text" msgid="2835519769868187223">"ਨਵੀਂ ਸੂਚਨਾ"</string>
<string name="notification_channel_physical_keyboard" msgid="5417306456125988096">"ਭੌਤਿਕ ਕੀ-ਬੋਰਡ"</string>
<string name="notification_channel_security" msgid="8516754650348238057">"ਸੁਰੱਖਿਆ"</string>
@@ -2413,22 +2415,15 @@
<string name="satellite_notification_open_message" msgid="4149234979688273729">"Messages ਐਪ ਖੋਲ੍ਹੋ"</string>
<string name="satellite_notification_how_it_works" msgid="3132069321977520519">"ਇਹ ਕਿਵੇਂ ਕੰਮ ਕਰਦਾ ਹੈ"</string>
<string name="unarchival_session_app_label" msgid="6811856981546348205">"ਵਿਚਾਰ-ਅਧੀਨ..."</string>
- <!-- no translation found for fingerprint_dangling_notification_title (7362075195588639989) -->
+ <string name="fingerprint_dangling_notification_title" msgid="7362075195588639989">"ਫਿੰਗਰਪ੍ਰਿੰਟ ਅਣਲਾਕ ਦਾ ਦੁਬਾਰਾ ਸੈੱਟਅੱਪ ਕਰੋ"</string>
+ <!-- no translation found for fingerprint_dangling_notification_msg_1 (8517140433796229725) -->
<skip />
- <!-- no translation found for fingerprint_dangling_notification_msg_1 (6261149111900787302) -->
+ <!-- no translation found for fingerprint_dangling_notification_msg_2 (7578829498452127613) -->
<skip />
- <!-- no translation found for fingerprint_dangling_notification_msg_2 (7688302770424064884) -->
- <skip />
- <!-- no translation found for fingerprint_dangling_notification_msg_all_deleted_1 (2927018569542316055) -->
- <skip />
- <!-- no translation found for fingerprint_dangling_notification_msg_all_deleted_2 (6897989352716156176) -->
- <skip />
- <!-- no translation found for face_dangling_notification_title (947852541060975473) -->
- <skip />
- <!-- no translation found for face_dangling_notification_msg (8806849376915541655) -->
- <skip />
- <!-- no translation found for biometric_dangling_notification_action_set_up (8246885009807817961) -->
- <skip />
- <!-- no translation found for biometric_dangling_notification_action_not_now (8095249216864443491) -->
- <skip />
+ <string name="fingerprint_dangling_notification_msg_all_deleted_1" msgid="2927018569542316055">"<xliff:g id="FINGERPRINT">%s</xliff:g> ਚੰਗੀ ਤਰ੍ਹਾਂ ਕੰਮ ਨਹੀਂ ਕਰ ਰਿਹਾ ਸੀ ਅਤੇ ਉਸਨੂੰ ਮਿਟਾਇਆ ਗਿਆ ਸੀ। ਆਪਣੇ ਫ਼ੋਨ ਨੂੰ ਫਿੰਗਰਪ੍ਰਿੰਟ ਨਾਲ ਅਣਲਾਕ ਕਰਨ ਲਈ ਇਸਦਾ ਦੁਬਾਰਾ ਸੈੱਟਅੱਪ ਕਰੋ।"</string>
+ <string name="fingerprint_dangling_notification_msg_all_deleted_2" msgid="6897989352716156176">"<xliff:g id="FINGERPRINT_0">%1$s</xliff:g> ਅਤੇ <xliff:g id="FINGERPRINT_1">%2$s</xliff:g> ਚੰਗੀ ਤਰ੍ਹਾਂ ਕੰਮ ਨਹੀਂ ਕਰ ਰਹੇ ਸੀ ਅਤੇ ਉਨ੍ਹਾਂ ਨੂੰ ਮਿਟਾਇਆ ਗਿਆ ਸੀ। ਆਪਣੇ ਫ਼ੋਨ ਨੂੰ ਆਪਣੇ ਫਿੰਗਰਪ੍ਰਿੰਟ ਨਾਲ ਅਣਲਾਕ ਕਰਨ ਲਈ ਇਨ੍ਹਾਂ ਦਾ ਦੁਬਾਰਾ ਸੈੱਟਅੱਪ ਕਰੋ।"</string>
+ <string name="face_dangling_notification_title" msgid="947852541060975473">"ਫ਼ੇਸ ਅਣਲਾਕ ਦਾ ਦੁਬਾਰਾ ਸੈੱਟਅੱਪ ਕਰੋ"</string>
+ <string name="face_dangling_notification_msg" msgid="8806849376915541655">"ਤੁਹਾਡਾ ਚਿਹਰੇ ਦਾ ਮਾਡਲ ਚੰਗੀ ਤਰ੍ਹਾਂ ਕੰਮ ਨਹੀਂ ਕਰ ਰਿਹਾ ਸੀ ਅਤੇ ਉਸਨੂੰ ਮਿਟਾਇਆ ਗਿਆ ਸੀ। ਆਪਣੇ ਫ਼ੋਨ ਨੂੰ ਚਿਹਰੇ ਨਾਲ ਅਣਲਾਕ ਕਰਨ ਲਈ ਇਸਦਾ ਦੁਬਾਰਾ ਸੈੱਟਅੱਪ ਕਰੋ।"</string>
+ <string name="biometric_dangling_notification_action_set_up" msgid="8246885009807817961">"ਸੈੱਟਅੱਪ ਕਰੋ"</string>
+ <string name="biometric_dangling_notification_action_not_now" msgid="8095249216864443491">"ਹੁਣੇ ਨਹੀਂ"</string>
</resources>
diff --git a/core/res/res/values-pl/strings.xml b/core/res/res/values-pl/strings.xml
index 0971ae4..9cc9916 100644
--- a/core/res/res/values-pl/strings.xml
+++ b/core/res/res/values-pl/strings.xml
@@ -285,6 +285,8 @@
<string name="global_action_voice_assist" msgid="6655788068555086695">"Asystent głosowy"</string>
<string name="global_action_lockdown" msgid="2475471405907902963">"Blokada"</string>
<string name="status_bar_notification_info_overflow" msgid="3330152558746563475">">999"</string>
+ <!-- no translation found for notification_compact_heads_up_reply (2425293958371284340) -->
+ <skip />
<string name="notification_hidden_text" msgid="2835519769868187223">"Nowe powiadomienie"</string>
<string name="notification_channel_physical_keyboard" msgid="5417306456125988096">"Klawiatura fizyczna"</string>
<string name="notification_channel_security" msgid="8516754650348238057">"Bezpieczeństwo"</string>
@@ -2415,22 +2417,15 @@
<string name="satellite_notification_open_message" msgid="4149234979688273729">"Otwórz Wiadomości"</string>
<string name="satellite_notification_how_it_works" msgid="3132069321977520519">"Jak to działa"</string>
<string name="unarchival_session_app_label" msgid="6811856981546348205">"Oczekiwanie…"</string>
- <!-- no translation found for fingerprint_dangling_notification_title (7362075195588639989) -->
+ <string name="fingerprint_dangling_notification_title" msgid="7362075195588639989">"Skonfiguruj ponownie odblokowywanie odciskiem palca"</string>
+ <!-- no translation found for fingerprint_dangling_notification_msg_1 (8517140433796229725) -->
<skip />
- <!-- no translation found for fingerprint_dangling_notification_msg_1 (6261149111900787302) -->
+ <!-- no translation found for fingerprint_dangling_notification_msg_2 (7578829498452127613) -->
<skip />
- <!-- no translation found for fingerprint_dangling_notification_msg_2 (7688302770424064884) -->
- <skip />
- <!-- no translation found for fingerprint_dangling_notification_msg_all_deleted_1 (2927018569542316055) -->
- <skip />
- <!-- no translation found for fingerprint_dangling_notification_msg_all_deleted_2 (6897989352716156176) -->
- <skip />
- <!-- no translation found for face_dangling_notification_title (947852541060975473) -->
- <skip />
- <!-- no translation found for face_dangling_notification_msg (8806849376915541655) -->
- <skip />
- <!-- no translation found for biometric_dangling_notification_action_set_up (8246885009807817961) -->
- <skip />
- <!-- no translation found for biometric_dangling_notification_action_not_now (8095249216864443491) -->
- <skip />
+ <string name="fingerprint_dangling_notification_msg_all_deleted_1" msgid="2927018569542316055">"Odcisk palca <xliff:g id="FINGERPRINT">%s</xliff:g> nie sprawdzał się dobrze i został usunięty. Skonfiguruj go ponownie, aby odblokowywać telefon odciskiem palca."</string>
+ <string name="fingerprint_dangling_notification_msg_all_deleted_2" msgid="6897989352716156176">"Odciski palca <xliff:g id="FINGERPRINT_0">%1$s</xliff:g> i <xliff:g id="FINGERPRINT_1">%2$s</xliff:g> nie sprawdzały się dobrze i zostały usunięte. Skonfiguruj je ponownie, aby odblokowywać telefon odciskiem palca."</string>
+ <string name="face_dangling_notification_title" msgid="947852541060975473">"Skonfiguruj ponownie rozpoznawanie twarzy"</string>
+ <string name="face_dangling_notification_msg" msgid="8806849376915541655">"Twój model twarzy nie sprawdzał się dobrze i został usunięty. Skonfiguruj go ponownie, aby odblokowywać telefon za pomocą skanu twarzy."</string>
+ <string name="biometric_dangling_notification_action_set_up" msgid="8246885009807817961">"Skonfiguruj"</string>
+ <string name="biometric_dangling_notification_action_not_now" msgid="8095249216864443491">"Nie teraz"</string>
</resources>
diff --git a/core/res/res/values-pt-rBR/strings.xml b/core/res/res/values-pt-rBR/strings.xml
index b04cd4c..e3b49a0 100644
--- a/core/res/res/values-pt-rBR/strings.xml
+++ b/core/res/res/values-pt-rBR/strings.xml
@@ -284,6 +284,8 @@
<string name="global_action_voice_assist" msgid="6655788068555086695">"Ajuda de voz"</string>
<string name="global_action_lockdown" msgid="2475471405907902963">"Bloqueio total"</string>
<string name="status_bar_notification_info_overflow" msgid="3330152558746563475">">999"</string>
+ <!-- no translation found for notification_compact_heads_up_reply (2425293958371284340) -->
+ <skip />
<string name="notification_hidden_text" msgid="2835519769868187223">"Nova notificação"</string>
<string name="notification_channel_physical_keyboard" msgid="5417306456125988096">"Teclado físico"</string>
<string name="notification_channel_security" msgid="8516754650348238057">"Segurança"</string>
@@ -2395,7 +2397,7 @@
<string name="keyboard_layout_notification_more_than_three_selected_message" msgid="1581834181578206937">"Layout do teclado definido como <xliff:g id="LAYOUT_1">%1$s</xliff:g>, <xliff:g id="LAYOUT_2">%2$s</xliff:g>, <xliff:g id="LAYOUT_3">%3$s</xliff:g>… Toque para mudar."</string>
<string name="keyboard_layout_notification_multiple_selected_title" msgid="5242444914367024499">"Teclados físicos configurados"</string>
<string name="keyboard_layout_notification_multiple_selected_message" msgid="6576533454124419202">"Toque para conferir os teclados"</string>
- <string name="profile_label_private" msgid="6463418670715290696">"Particular"</string>
+ <string name="profile_label_private" msgid="6463418670715290696">"Privado"</string>
<string name="profile_label_clone" msgid="769106052210954285">"Clone"</string>
<string name="profile_label_work" msgid="3495359133038584618">"Trabalho"</string>
<string name="profile_label_work_2" msgid="4691533661598632135">"Trabalho 2"</string>
@@ -2414,22 +2416,15 @@
<string name="satellite_notification_open_message" msgid="4149234979688273729">"Abrir o app Mensagens"</string>
<string name="satellite_notification_how_it_works" msgid="3132069321977520519">"Como funciona"</string>
<string name="unarchival_session_app_label" msgid="6811856981546348205">"Pendente…"</string>
- <!-- no translation found for fingerprint_dangling_notification_title (7362075195588639989) -->
+ <string name="fingerprint_dangling_notification_title" msgid="7362075195588639989">"Configurar o Desbloqueio por impressão digital de novo"</string>
+ <!-- no translation found for fingerprint_dangling_notification_msg_1 (8517140433796229725) -->
<skip />
- <!-- no translation found for fingerprint_dangling_notification_msg_1 (6261149111900787302) -->
+ <!-- no translation found for fingerprint_dangling_notification_msg_2 (7578829498452127613) -->
<skip />
- <!-- no translation found for fingerprint_dangling_notification_msg_2 (7688302770424064884) -->
- <skip />
- <!-- no translation found for fingerprint_dangling_notification_msg_all_deleted_1 (2927018569542316055) -->
- <skip />
- <!-- no translation found for fingerprint_dangling_notification_msg_all_deleted_2 (6897989352716156176) -->
- <skip />
- <!-- no translation found for face_dangling_notification_title (947852541060975473) -->
- <skip />
- <!-- no translation found for face_dangling_notification_msg (8806849376915541655) -->
- <skip />
- <!-- no translation found for biometric_dangling_notification_action_set_up (8246885009807817961) -->
- <skip />
- <!-- no translation found for biometric_dangling_notification_action_not_now (8095249216864443491) -->
- <skip />
+ <string name="fingerprint_dangling_notification_msg_all_deleted_1" msgid="2927018569542316055">"A impressão digital <xliff:g id="FINGERPRINT">%s</xliff:g> não estava funcionando bem e foi excluída. Configure de novo para desbloquear o smartphone com a impressão digital."</string>
+ <string name="fingerprint_dangling_notification_msg_all_deleted_2" msgid="6897989352716156176">"As impressões digitais <xliff:g id="FINGERPRINT_0">%1$s</xliff:g> e <xliff:g id="FINGERPRINT_1">%2$s</xliff:g> não estavam funcionando bem e foram excluídas. Configure de novo para desbloquear o smartphone com a impressão digital."</string>
+ <string name="face_dangling_notification_title" msgid="947852541060975473">"Configure o Desbloqueio facial de novo"</string>
+ <string name="face_dangling_notification_msg" msgid="8806849376915541655">"Seu modelo de rosto não estava funcionando bem e foi excluído. Configure de novo para desbloquear o smartphone com o rosto."</string>
+ <string name="biometric_dangling_notification_action_set_up" msgid="8246885009807817961">"Configuração"</string>
+ <string name="biometric_dangling_notification_action_not_now" msgid="8095249216864443491">"Agora não"</string>
</resources>
diff --git a/core/res/res/values-pt-rPT/strings.xml b/core/res/res/values-pt-rPT/strings.xml
index 90f9eda..d8c1f72 100644
--- a/core/res/res/values-pt-rPT/strings.xml
+++ b/core/res/res/values-pt-rPT/strings.xml
@@ -284,6 +284,8 @@
<string name="global_action_voice_assist" msgid="6655788068555086695">"Assist. de voz"</string>
<string name="global_action_lockdown" msgid="2475471405907902963">"Bloquear"</string>
<string name="status_bar_notification_info_overflow" msgid="3330152558746563475">"999+"</string>
+ <!-- no translation found for notification_compact_heads_up_reply (2425293958371284340) -->
+ <skip />
<string name="notification_hidden_text" msgid="2835519769868187223">"Nova notificação"</string>
<string name="notification_channel_physical_keyboard" msgid="5417306456125988096">"Teclado físico"</string>
<string name="notification_channel_security" msgid="8516754650348238057">"Segurança"</string>
@@ -327,7 +329,7 @@
<string name="permgroupdesc_storage" msgid="5378659041354582769">"aceder aos ficheiros no seu dispositivo"</string>
<string name="permgrouplab_readMediaAural" msgid="1858331312624942053">"Música e áudio"</string>
<string name="permgroupdesc_readMediaAural" msgid="7565467343667089595">"aceder a música e áudio no seu dispositivo"</string>
- <string name="permgrouplab_readMediaVisual" msgid="4724874717811908660">"fotos e vídeos"</string>
+ <string name="permgrouplab_readMediaVisual" msgid="4724874717811908660">"Fotos e vídeos"</string>
<string name="permgroupdesc_readMediaVisual" msgid="4080463241903508688">"aceder a fotos e vídeos no seu dispositivo"</string>
<string name="permgrouplab_microphone" msgid="2480597427667420076">"Microfone"</string>
<string name="permgroupdesc_microphone" msgid="1047786732792487722">"gravar áudio"</string>
@@ -2414,22 +2416,15 @@
<string name="satellite_notification_open_message" msgid="4149234979688273729">"Abre a app Mensagens"</string>
<string name="satellite_notification_how_it_works" msgid="3132069321977520519">"Como funciona"</string>
<string name="unarchival_session_app_label" msgid="6811856981546348205">"Pendente…"</string>
- <!-- no translation found for fingerprint_dangling_notification_title (7362075195588639989) -->
+ <string name="fingerprint_dangling_notification_title" msgid="7362075195588639989">"Configure o Desbloqueio por impressão digital novamente"</string>
+ <!-- no translation found for fingerprint_dangling_notification_msg_1 (8517140433796229725) -->
<skip />
- <!-- no translation found for fingerprint_dangling_notification_msg_1 (6261149111900787302) -->
+ <!-- no translation found for fingerprint_dangling_notification_msg_2 (7578829498452127613) -->
<skip />
- <!-- no translation found for fingerprint_dangling_notification_msg_2 (7688302770424064884) -->
- <skip />
- <!-- no translation found for fingerprint_dangling_notification_msg_all_deleted_1 (2927018569542316055) -->
- <skip />
- <!-- no translation found for fingerprint_dangling_notification_msg_all_deleted_2 (6897989352716156176) -->
- <skip />
- <!-- no translation found for face_dangling_notification_title (947852541060975473) -->
- <skip />
- <!-- no translation found for face_dangling_notification_msg (8806849376915541655) -->
- <skip />
- <!-- no translation found for biometric_dangling_notification_action_set_up (8246885009807817961) -->
- <skip />
- <!-- no translation found for biometric_dangling_notification_action_not_now (8095249216864443491) -->
- <skip />
+ <string name="fingerprint_dangling_notification_msg_all_deleted_1" msgid="2927018569542316055">"A <xliff:g id="FINGERPRINT">%s</xliff:g> não estava a funcionar bem e foi eliminada. Configure-a novamente para desbloquear o telemóvel com a impressão digital."</string>
+ <string name="fingerprint_dangling_notification_msg_all_deleted_2" msgid="6897989352716156176">"A <xliff:g id="FINGERPRINT_0">%1$s</xliff:g> e a <xliff:g id="FINGERPRINT_1">%2$s</xliff:g> não estavam a funcionar bem e foram eliminadas. Configure-as novamente para desbloquear o telemóvel com a sua impressão digital."</string>
+ <string name="face_dangling_notification_title" msgid="947852541060975473">"Configure o Desbloqueio facial novamente"</string>
+ <string name="face_dangling_notification_msg" msgid="8806849376915541655">"O seu modelo de rosto não estava a funcionar bem e foi eliminado. Configure-o novamente para desbloquear o telemóvel com o rosto."</string>
+ <string name="biometric_dangling_notification_action_set_up" msgid="8246885009807817961">"Configurar"</string>
+ <string name="biometric_dangling_notification_action_not_now" msgid="8095249216864443491">"Agora não"</string>
</resources>
diff --git a/core/res/res/values-pt/strings.xml b/core/res/res/values-pt/strings.xml
index b04cd4c..e3b49a0 100644
--- a/core/res/res/values-pt/strings.xml
+++ b/core/res/res/values-pt/strings.xml
@@ -284,6 +284,8 @@
<string name="global_action_voice_assist" msgid="6655788068555086695">"Ajuda de voz"</string>
<string name="global_action_lockdown" msgid="2475471405907902963">"Bloqueio total"</string>
<string name="status_bar_notification_info_overflow" msgid="3330152558746563475">">999"</string>
+ <!-- no translation found for notification_compact_heads_up_reply (2425293958371284340) -->
+ <skip />
<string name="notification_hidden_text" msgid="2835519769868187223">"Nova notificação"</string>
<string name="notification_channel_physical_keyboard" msgid="5417306456125988096">"Teclado físico"</string>
<string name="notification_channel_security" msgid="8516754650348238057">"Segurança"</string>
@@ -2395,7 +2397,7 @@
<string name="keyboard_layout_notification_more_than_three_selected_message" msgid="1581834181578206937">"Layout do teclado definido como <xliff:g id="LAYOUT_1">%1$s</xliff:g>, <xliff:g id="LAYOUT_2">%2$s</xliff:g>, <xliff:g id="LAYOUT_3">%3$s</xliff:g>… Toque para mudar."</string>
<string name="keyboard_layout_notification_multiple_selected_title" msgid="5242444914367024499">"Teclados físicos configurados"</string>
<string name="keyboard_layout_notification_multiple_selected_message" msgid="6576533454124419202">"Toque para conferir os teclados"</string>
- <string name="profile_label_private" msgid="6463418670715290696">"Particular"</string>
+ <string name="profile_label_private" msgid="6463418670715290696">"Privado"</string>
<string name="profile_label_clone" msgid="769106052210954285">"Clone"</string>
<string name="profile_label_work" msgid="3495359133038584618">"Trabalho"</string>
<string name="profile_label_work_2" msgid="4691533661598632135">"Trabalho 2"</string>
@@ -2414,22 +2416,15 @@
<string name="satellite_notification_open_message" msgid="4149234979688273729">"Abrir o app Mensagens"</string>
<string name="satellite_notification_how_it_works" msgid="3132069321977520519">"Como funciona"</string>
<string name="unarchival_session_app_label" msgid="6811856981546348205">"Pendente…"</string>
- <!-- no translation found for fingerprint_dangling_notification_title (7362075195588639989) -->
+ <string name="fingerprint_dangling_notification_title" msgid="7362075195588639989">"Configurar o Desbloqueio por impressão digital de novo"</string>
+ <!-- no translation found for fingerprint_dangling_notification_msg_1 (8517140433796229725) -->
<skip />
- <!-- no translation found for fingerprint_dangling_notification_msg_1 (6261149111900787302) -->
+ <!-- no translation found for fingerprint_dangling_notification_msg_2 (7578829498452127613) -->
<skip />
- <!-- no translation found for fingerprint_dangling_notification_msg_2 (7688302770424064884) -->
- <skip />
- <!-- no translation found for fingerprint_dangling_notification_msg_all_deleted_1 (2927018569542316055) -->
- <skip />
- <!-- no translation found for fingerprint_dangling_notification_msg_all_deleted_2 (6897989352716156176) -->
- <skip />
- <!-- no translation found for face_dangling_notification_title (947852541060975473) -->
- <skip />
- <!-- no translation found for face_dangling_notification_msg (8806849376915541655) -->
- <skip />
- <!-- no translation found for biometric_dangling_notification_action_set_up (8246885009807817961) -->
- <skip />
- <!-- no translation found for biometric_dangling_notification_action_not_now (8095249216864443491) -->
- <skip />
+ <string name="fingerprint_dangling_notification_msg_all_deleted_1" msgid="2927018569542316055">"A impressão digital <xliff:g id="FINGERPRINT">%s</xliff:g> não estava funcionando bem e foi excluída. Configure de novo para desbloquear o smartphone com a impressão digital."</string>
+ <string name="fingerprint_dangling_notification_msg_all_deleted_2" msgid="6897989352716156176">"As impressões digitais <xliff:g id="FINGERPRINT_0">%1$s</xliff:g> e <xliff:g id="FINGERPRINT_1">%2$s</xliff:g> não estavam funcionando bem e foram excluídas. Configure de novo para desbloquear o smartphone com a impressão digital."</string>
+ <string name="face_dangling_notification_title" msgid="947852541060975473">"Configure o Desbloqueio facial de novo"</string>
+ <string name="face_dangling_notification_msg" msgid="8806849376915541655">"Seu modelo de rosto não estava funcionando bem e foi excluído. Configure de novo para desbloquear o smartphone com o rosto."</string>
+ <string name="biometric_dangling_notification_action_set_up" msgid="8246885009807817961">"Configuração"</string>
+ <string name="biometric_dangling_notification_action_not_now" msgid="8095249216864443491">"Agora não"</string>
</resources>
diff --git a/core/res/res/values-ro/strings.xml b/core/res/res/values-ro/strings.xml
index c43df4f..7de9952 100644
--- a/core/res/res/values-ro/strings.xml
+++ b/core/res/res/values-ro/strings.xml
@@ -284,6 +284,8 @@
<string name="global_action_voice_assist" msgid="6655788068555086695">"Asistent vocal"</string>
<string name="global_action_lockdown" msgid="2475471405907902963">"Blocare strictă"</string>
<string name="status_bar_notification_info_overflow" msgid="3330152558746563475">"˃999"</string>
+ <!-- no translation found for notification_compact_heads_up_reply (2425293958371284340) -->
+ <skip />
<string name="notification_hidden_text" msgid="2835519769868187223">"Notificare nouă"</string>
<string name="notification_channel_physical_keyboard" msgid="5417306456125988096">"Tastatură fizică"</string>
<string name="notification_channel_security" msgid="8516754650348238057">"Securitate"</string>
@@ -2414,22 +2416,15 @@
<string name="satellite_notification_open_message" msgid="4149234979688273729">"Deschide Mesaje"</string>
<string name="satellite_notification_how_it_works" msgid="3132069321977520519">"Cum funcționează"</string>
<string name="unarchival_session_app_label" msgid="6811856981546348205">"În așteptare..."</string>
- <!-- no translation found for fingerprint_dangling_notification_title (7362075195588639989) -->
+ <string name="fingerprint_dangling_notification_title" msgid="7362075195588639989">"Configurează din nou Deblocarea cu amprenta"</string>
+ <!-- no translation found for fingerprint_dangling_notification_msg_1 (8517140433796229725) -->
<skip />
- <!-- no translation found for fingerprint_dangling_notification_msg_1 (6261149111900787302) -->
+ <!-- no translation found for fingerprint_dangling_notification_msg_2 (7578829498452127613) -->
<skip />
- <!-- no translation found for fingerprint_dangling_notification_msg_2 (7688302770424064884) -->
- <skip />
- <!-- no translation found for fingerprint_dangling_notification_msg_all_deleted_1 (2927018569542316055) -->
- <skip />
- <!-- no translation found for fingerprint_dangling_notification_msg_all_deleted_2 (6897989352716156176) -->
- <skip />
- <!-- no translation found for face_dangling_notification_title (947852541060975473) -->
- <skip />
- <!-- no translation found for face_dangling_notification_msg (8806849376915541655) -->
- <skip />
- <!-- no translation found for biometric_dangling_notification_action_set_up (8246885009807817961) -->
- <skip />
- <!-- no translation found for biometric_dangling_notification_action_not_now (8095249216864443491) -->
- <skip />
+ <string name="fingerprint_dangling_notification_msg_all_deleted_1" msgid="2927018569542316055">"<xliff:g id="FINGERPRINT">%s</xliff:g> nu funcționa bine și s-a șters. Configureaz-o din nou pentru a-ți debloca telefonul cu amprenta."</string>
+ <string name="fingerprint_dangling_notification_msg_all_deleted_2" msgid="6897989352716156176">"<xliff:g id="FINGERPRINT_0">%1$s</xliff:g> și <xliff:g id="FINGERPRINT_1">%2$s</xliff:g> nu funcționau bine și s-au șters. Configurează-le din nou pentru a-ți debloca telefonul cu amprenta."</string>
+ <string name="face_dangling_notification_title" msgid="947852541060975473">"Reconfigurează Deblocarea facială"</string>
+ <string name="face_dangling_notification_msg" msgid="8806849376915541655">"Modelul tău facial nu funcționa bine și s-a șters. Configurează-l din nou pentru a-ți debloca telefonul folosindu-ți chipul."</string>
+ <string name="biometric_dangling_notification_action_set_up" msgid="8246885009807817961">"Configurează"</string>
+ <string name="biometric_dangling_notification_action_not_now" msgid="8095249216864443491">"Nu acum"</string>
</resources>
diff --git a/core/res/res/values-ru/strings.xml b/core/res/res/values-ru/strings.xml
index 32a2338..d999bf5 100644
--- a/core/res/res/values-ru/strings.xml
+++ b/core/res/res/values-ru/strings.xml
@@ -285,6 +285,8 @@
<string name="global_action_voice_assist" msgid="6655788068555086695">"Аудиоподсказки"</string>
<string name="global_action_lockdown" msgid="2475471405907902963">"Блокировка входа"</string>
<string name="status_bar_notification_info_overflow" msgid="3330152558746563475">">999"</string>
+ <!-- no translation found for notification_compact_heads_up_reply (2425293958371284340) -->
+ <skip />
<string name="notification_hidden_text" msgid="2835519769868187223">"Новое уведомление"</string>
<string name="notification_channel_physical_keyboard" msgid="5417306456125988096">"Физическая клавиатура"</string>
<string name="notification_channel_security" msgid="8516754650348238057">"Безопасность"</string>
@@ -668,7 +670,7 @@
</string-array>
<string name="fingerprint_error_not_match" msgid="4599441812893438961">"Отпечаток не распознан."</string>
<string name="fingerprint_udfps_error_not_match" msgid="8236930793223158856">"Отпечаток не распознан."</string>
- <string name="fingerprint_dialog_use_fingerprint_instead" msgid="5590293588784953188">"Лицо не распознано. Используйте отпечаток."</string>
+ <string name="fingerprint_dialog_use_fingerprint_instead" msgid="5590293588784953188">"Лицо не распознано. Сканируйте отпечаток пальца."</string>
<string name="fingerprint_authenticated" msgid="2024862866860283100">"Отпечаток пальца проверен"</string>
<string name="face_authenticated_no_confirmation_required" msgid="8867889115112348167">"Лицо распознано"</string>
<string name="face_authenticated_confirmation_required" msgid="6872632732508013755">"Лицо распознано, нажмите кнопку \"Подтвердить\""</string>
@@ -2396,7 +2398,7 @@
<string name="keyboard_layout_notification_more_than_three_selected_message" msgid="1581834181578206937">"Настроены раскладки клавиатуры для яз.: <xliff:g id="LAYOUT_1">%1$s</xliff:g>, <xliff:g id="LAYOUT_2">%2$s</xliff:g>, <xliff:g id="LAYOUT_3">%3$s</xliff:g> и др. Нажмите, чтобы изменить."</string>
<string name="keyboard_layout_notification_multiple_selected_title" msgid="5242444914367024499">"Физические клавиатуры настроены"</string>
<string name="keyboard_layout_notification_multiple_selected_message" msgid="6576533454124419202">"Нажмите, чтобы посмотреть подключенные клавиатуры."</string>
- <string name="profile_label_private" msgid="6463418670715290696">"Личный"</string>
+ <string name="profile_label_private" msgid="6463418670715290696">"Частный"</string>
<string name="profile_label_clone" msgid="769106052210954285">"Клон"</string>
<string name="profile_label_work" msgid="3495359133038584618">"Рабочий"</string>
<string name="profile_label_work_2" msgid="4691533661598632135">"Рабочий 2"</string>
@@ -2415,22 +2417,15 @@
<string name="satellite_notification_open_message" msgid="4149234979688273729">"Открыть Сообщения"</string>
<string name="satellite_notification_how_it_works" msgid="3132069321977520519">"Узнать принцип работы"</string>
<string name="unarchival_session_app_label" msgid="6811856981546348205">"Обработка…"</string>
- <!-- no translation found for fingerprint_dangling_notification_title (7362075195588639989) -->
+ <string name="fingerprint_dangling_notification_title" msgid="7362075195588639989">"Настройте разблокировку по отпечатку пальца заново"</string>
+ <!-- no translation found for fingerprint_dangling_notification_msg_1 (8517140433796229725) -->
<skip />
- <!-- no translation found for fingerprint_dangling_notification_msg_1 (6261149111900787302) -->
+ <!-- no translation found for fingerprint_dangling_notification_msg_2 (7578829498452127613) -->
<skip />
- <!-- no translation found for fingerprint_dangling_notification_msg_2 (7688302770424064884) -->
- <skip />
- <!-- no translation found for fingerprint_dangling_notification_msg_all_deleted_1 (2927018569542316055) -->
- <skip />
- <!-- no translation found for fingerprint_dangling_notification_msg_all_deleted_2 (6897989352716156176) -->
- <skip />
- <!-- no translation found for face_dangling_notification_title (947852541060975473) -->
- <skip />
- <!-- no translation found for face_dangling_notification_msg (8806849376915541655) -->
- <skip />
- <!-- no translation found for biometric_dangling_notification_action_set_up (8246885009807817961) -->
- <skip />
- <!-- no translation found for biometric_dangling_notification_action_not_now (8095249216864443491) -->
- <skip />
+ <string name="fingerprint_dangling_notification_msg_all_deleted_1" msgid="2927018569542316055">"Отпечаток пальца \"<xliff:g id="FINGERPRINT">%s</xliff:g>\" оказался неудачным и был удален. Чтобы использовать разблокировку с помощью отпечатка пальца, настройте ее заново."</string>
+ <string name="fingerprint_dangling_notification_msg_all_deleted_2" msgid="6897989352716156176">"Отпечатки пальцев \"<xliff:g id="FINGERPRINT_0">%1$s</xliff:g>\" и \"<xliff:g id="FINGERPRINT_1">%2$s</xliff:g>\" оказались неудачными и были удалены. Чтобы использовать разблокировку с помощью отпечатка пальца, настройте ее заново."</string>
+ <string name="face_dangling_notification_title" msgid="947852541060975473">"Настройте фейсконтроль заново"</string>
+ <string name="face_dangling_notification_msg" msgid="8806849376915541655">"Модель лица оказалась неудачной и была удалена. Чтобы пользоваться фейсконтролем, настройте его заново."</string>
+ <string name="biometric_dangling_notification_action_set_up" msgid="8246885009807817961">"Настроить"</string>
+ <string name="biometric_dangling_notification_action_not_now" msgid="8095249216864443491">"Не сейчас"</string>
</resources>
diff --git a/core/res/res/values-si/strings.xml b/core/res/res/values-si/strings.xml
index 9539cef..a8eda4f 100644
--- a/core/res/res/values-si/strings.xml
+++ b/core/res/res/values-si/strings.xml
@@ -283,6 +283,8 @@
<string name="global_action_voice_assist" msgid="6655788068555086695">"හඬ සහායක"</string>
<string name="global_action_lockdown" msgid="2475471405907902963">"අගුලු දැමීම"</string>
<string name="status_bar_notification_info_overflow" msgid="3330152558746563475">"999+"</string>
+ <!-- no translation found for notification_compact_heads_up_reply (2425293958371284340) -->
+ <skip />
<string name="notification_hidden_text" msgid="2835519769868187223">"නව දැනුම්දීම"</string>
<string name="notification_channel_physical_keyboard" msgid="5417306456125988096">"භෞතික යතුරු පුවරුව"</string>
<string name="notification_channel_security" msgid="8516754650348238057">"ආරක්ෂාව"</string>
@@ -2413,22 +2415,15 @@
<string name="satellite_notification_open_message" msgid="4149234979688273729">"Messages විවෘත කරන්න"</string>
<string name="satellite_notification_how_it_works" msgid="3132069321977520519">"එය ක්රියා කරන ආකාරය"</string>
<string name="unarchival_session_app_label" msgid="6811856981546348205">"පොරොත්තුයි..."</string>
- <!-- no translation found for fingerprint_dangling_notification_title (7362075195588639989) -->
+ <string name="fingerprint_dangling_notification_title" msgid="7362075195588639989">"ඇඟිලි සලකුණු අගුලු හැරීම නැවත සකසන්න"</string>
+ <!-- no translation found for fingerprint_dangling_notification_msg_1 (8517140433796229725) -->
<skip />
- <!-- no translation found for fingerprint_dangling_notification_msg_1 (6261149111900787302) -->
+ <!-- no translation found for fingerprint_dangling_notification_msg_2 (7578829498452127613) -->
<skip />
- <!-- no translation found for fingerprint_dangling_notification_msg_2 (7688302770424064884) -->
- <skip />
- <!-- no translation found for fingerprint_dangling_notification_msg_all_deleted_1 (2927018569542316055) -->
- <skip />
- <!-- no translation found for fingerprint_dangling_notification_msg_all_deleted_2 (6897989352716156176) -->
- <skip />
- <!-- no translation found for face_dangling_notification_title (947852541060975473) -->
- <skip />
- <!-- no translation found for face_dangling_notification_msg (8806849376915541655) -->
- <skip />
- <!-- no translation found for biometric_dangling_notification_action_set_up (8246885009807817961) -->
- <skip />
- <!-- no translation found for biometric_dangling_notification_action_not_now (8095249216864443491) -->
- <skip />
+ <string name="fingerprint_dangling_notification_msg_all_deleted_1" msgid="2927018569542316055">"<xliff:g id="FINGERPRINT">%s</xliff:g> හොඳින් ක්රියා නොකළේය, එය මකන ලදි ඇඟිලි සලකුණ මගින් ඔබේ දුරකථනය අගුලු හැරීමට එය නැවත සකසන්න."</string>
+ <string name="fingerprint_dangling_notification_msg_all_deleted_2" msgid="6897989352716156176">"<xliff:g id="FINGERPRINT_0">%1$s</xliff:g> සහ <xliff:g id="FINGERPRINT_1">%2$s</xliff:g> හොඳින් ක්රියා නොකළේය, කාර්යසාධනය දියුණූ කිරීමට ඒවා මකන ලදි. ඔබේ ඇඟිලි සලකුණ මගින් ඔබේ දුරකථනය අගුලු හැරීමට ඒවා නැවත සකසන්න."</string>
+ <string name="face_dangling_notification_title" msgid="947852541060975473">"මුහුණෙන් අගුලු හැරීම නැවත සකසන්න"</string>
+ <string name="face_dangling_notification_msg" msgid="8806849376915541655">"ඔබේ මුහුණු මාදිලිය හොඳින් ක්රියා නොකරයි, එය මකන ලදි. මුහුණ මගින් ඔබේ දුරකථනය අගුලු හැරීමට එය නැවත සකසන්න."</string>
+ <string name="biometric_dangling_notification_action_set_up" msgid="8246885009807817961">"සකසන්න"</string>
+ <string name="biometric_dangling_notification_action_not_now" msgid="8095249216864443491">"දැන් නොවේ"</string>
</resources>
diff --git a/core/res/res/values-sk/strings.xml b/core/res/res/values-sk/strings.xml
index c72a426d..46897cb 100644
--- a/core/res/res/values-sk/strings.xml
+++ b/core/res/res/values-sk/strings.xml
@@ -285,6 +285,8 @@
<string name="global_action_voice_assist" msgid="6655788068555086695">"Hlasový asistent"</string>
<string name="global_action_lockdown" msgid="2475471405907902963">"Uzamknúť"</string>
<string name="status_bar_notification_info_overflow" msgid="3330152558746563475">"999+"</string>
+ <!-- no translation found for notification_compact_heads_up_reply (2425293958371284340) -->
+ <skip />
<string name="notification_hidden_text" msgid="2835519769868187223">"Nové upozornenie"</string>
<string name="notification_channel_physical_keyboard" msgid="5417306456125988096">"Fyzická klávesnica"</string>
<string name="notification_channel_security" msgid="8516754650348238057">"Zabezpečenie"</string>
@@ -2415,22 +2417,15 @@
<string name="satellite_notification_open_message" msgid="4149234979688273729">"Otvoriť Správy"</string>
<string name="satellite_notification_how_it_works" msgid="3132069321977520519">"Ako to funguje"</string>
<string name="unarchival_session_app_label" msgid="6811856981546348205">"Nespracovaná…"</string>
- <!-- no translation found for fingerprint_dangling_notification_title (7362075195588639989) -->
+ <string name="fingerprint_dangling_notification_title" msgid="7362075195588639989">"Znova nastavte odomknutie odtlačkom prsta"</string>
+ <!-- no translation found for fingerprint_dangling_notification_msg_1 (8517140433796229725) -->
<skip />
- <!-- no translation found for fingerprint_dangling_notification_msg_1 (6261149111900787302) -->
+ <!-- no translation found for fingerprint_dangling_notification_msg_2 (7578829498452127613) -->
<skip />
- <!-- no translation found for fingerprint_dangling_notification_msg_2 (7688302770424064884) -->
- <skip />
- <!-- no translation found for fingerprint_dangling_notification_msg_all_deleted_1 (2927018569542316055) -->
- <skip />
- <!-- no translation found for fingerprint_dangling_notification_msg_all_deleted_2 (6897989352716156176) -->
- <skip />
- <!-- no translation found for face_dangling_notification_title (947852541060975473) -->
- <skip />
- <!-- no translation found for face_dangling_notification_msg (8806849376915541655) -->
- <skip />
- <!-- no translation found for biometric_dangling_notification_action_set_up (8246885009807817961) -->
- <skip />
- <!-- no translation found for biometric_dangling_notification_action_not_now (8095249216864443491) -->
- <skip />
+ <string name="fingerprint_dangling_notification_msg_all_deleted_1" msgid="2927018569542316055">"Odtlačok <xliff:g id="FINGERPRINT">%s</xliff:g> nefungoval správne a bol odstránený. Ak chcete odomykať telefón odtlačkom prsta, nastavte ho znova."</string>
+ <string name="fingerprint_dangling_notification_msg_all_deleted_2" msgid="6897989352716156176">"Odtlačky <xliff:g id="FINGERPRINT_0">%1$s</xliff:g> a <xliff:g id="FINGERPRINT_1">%2$s</xliff:g> nefungovali správne a boli odstránené. Ak chcete odomykať telefón odtlačkom prsta, nastavte ich znova."</string>
+ <string name="face_dangling_notification_title" msgid="947852541060975473">"Znova nastavte odomknutie tvárou"</string>
+ <string name="face_dangling_notification_msg" msgid="8806849376915541655">"Váš model tváre nefungoval správne a bol odstránený. Ak chcete odomykať telefón tvárou, nastavte ho znova."</string>
+ <string name="biometric_dangling_notification_action_set_up" msgid="8246885009807817961">"Nastaviť"</string>
+ <string name="biometric_dangling_notification_action_not_now" msgid="8095249216864443491">"Teraz nie"</string>
</resources>
diff --git a/core/res/res/values-sl/strings.xml b/core/res/res/values-sl/strings.xml
index e1be803..2f10d58 100644
--- a/core/res/res/values-sl/strings.xml
+++ b/core/res/res/values-sl/strings.xml
@@ -285,6 +285,8 @@
<string name="global_action_voice_assist" msgid="6655788068555086695">"Glas. pomočnik"</string>
<string name="global_action_lockdown" msgid="2475471405907902963">"Zakleni"</string>
<string name="status_bar_notification_info_overflow" msgid="3330152558746563475">"999 +"</string>
+ <!-- no translation found for notification_compact_heads_up_reply (2425293958371284340) -->
+ <skip />
<string name="notification_hidden_text" msgid="2835519769868187223">"Novo obvestilo"</string>
<string name="notification_channel_physical_keyboard" msgid="5417306456125988096">"Fizična tipkovnica"</string>
<string name="notification_channel_security" msgid="8516754650348238057">"Varnost"</string>
@@ -1733,7 +1735,7 @@
<string name="accessibility_service_screen_control_title" msgid="190017412626919776">"Ogledovanje in upravljanje zaslona"</string>
<string name="accessibility_service_screen_control_description" msgid="6946315917771791525">"Bere lahko vso vsebino na zaslonu ter prikaže vsebino prek drugih aplikacij."</string>
<string name="accessibility_service_action_perform_title" msgid="779670378951658160">"Ogledovanje in izvajanje dejanj"</string>
- <string name="accessibility_service_action_perform_description" msgid="2718852014003170558">"Spremlja lahko vaše interakcije z aplikacijo ali tipalom strojne opreme ter komunicira z aplikacijami v vašem imenu."</string>
+ <string name="accessibility_service_action_perform_description" msgid="2718852014003170558">"Spremlja lahko vaše interakcije z aplikacijo ali tipalom ter komunicira z aplikacijami v vašem imenu."</string>
<string name="accessibility_dialog_button_allow" msgid="2092558122987144530">"Dovoli"</string>
<string name="accessibility_dialog_button_deny" msgid="4129575637812472671">"Zavrni"</string>
<string name="accessibility_dialog_button_uninstall" msgid="2952465517671708108">"Odmesti"</string>
@@ -2415,22 +2417,15 @@
<string name="satellite_notification_open_message" msgid="4149234979688273729">"Odpri Sporočila"</string>
<string name="satellite_notification_how_it_works" msgid="3132069321977520519">"Kako deluje"</string>
<string name="unarchival_session_app_label" msgid="6811856981546348205">"V teku …"</string>
- <!-- no translation found for fingerprint_dangling_notification_title (7362075195588639989) -->
+ <string name="fingerprint_dangling_notification_title" msgid="7362075195588639989">"Vnovična nastavitev odklepanja s prstnim odtisom"</string>
+ <!-- no translation found for fingerprint_dangling_notification_msg_1 (8517140433796229725) -->
<skip />
- <!-- no translation found for fingerprint_dangling_notification_msg_1 (6261149111900787302) -->
+ <!-- no translation found for fingerprint_dangling_notification_msg_2 (7578829498452127613) -->
<skip />
- <!-- no translation found for fingerprint_dangling_notification_msg_2 (7688302770424064884) -->
- <skip />
- <!-- no translation found for fingerprint_dangling_notification_msg_all_deleted_1 (2927018569542316055) -->
- <skip />
- <!-- no translation found for fingerprint_dangling_notification_msg_all_deleted_2 (6897989352716156176) -->
- <skip />
- <!-- no translation found for face_dangling_notification_title (947852541060975473) -->
- <skip />
- <!-- no translation found for face_dangling_notification_msg (8806849376915541655) -->
- <skip />
- <!-- no translation found for biometric_dangling_notification_action_set_up (8246885009807817961) -->
- <skip />
- <!-- no translation found for biometric_dangling_notification_action_not_now (8095249216864443491) -->
- <skip />
+ <string name="fingerprint_dangling_notification_msg_all_deleted_1" msgid="2927018569542316055">"<xliff:g id="FINGERPRINT">%s</xliff:g> ni deloval pravilno in je bil izbrisan. Znova ga nastavite, če želite telefon odklepati s prstnim odtisom."</string>
+ <string name="fingerprint_dangling_notification_msg_all_deleted_2" msgid="6897989352716156176">"<xliff:g id="FINGERPRINT_0">%1$s</xliff:g> in <xliff:g id="FINGERPRINT_1">%2$s</xliff:g> nista delovala pravilno in sta bila izbrisana. Znova ju nastavite, če želite telefon odklepati s prstnim odtisom."</string>
+ <string name="face_dangling_notification_title" msgid="947852541060975473">"Vnovična nastavitev odklepanja z obrazom"</string>
+ <string name="face_dangling_notification_msg" msgid="8806849376915541655">"Model obraza ni deloval pravilno in je bil izbrisan. Znova ga nastavite, če želite telefon odklepati z obrazom."</string>
+ <string name="biometric_dangling_notification_action_set_up" msgid="8246885009807817961">"Nastavi"</string>
+ <string name="biometric_dangling_notification_action_not_now" msgid="8095249216864443491">"Ne zdaj"</string>
</resources>
diff --git a/core/res/res/values-sq/strings.xml b/core/res/res/values-sq/strings.xml
index 4d79ce7..70980b6 100644
--- a/core/res/res/values-sq/strings.xml
+++ b/core/res/res/values-sq/strings.xml
@@ -283,6 +283,8 @@
<string name="global_action_voice_assist" msgid="6655788068555086695">"Ndihma zanore"</string>
<string name="global_action_lockdown" msgid="2475471405907902963">"Blloko"</string>
<string name="status_bar_notification_info_overflow" msgid="3330152558746563475">"999+"</string>
+ <!-- no translation found for notification_compact_heads_up_reply (2425293958371284340) -->
+ <skip />
<string name="notification_hidden_text" msgid="2835519769868187223">"Njoftim i ri"</string>
<string name="notification_channel_physical_keyboard" msgid="5417306456125988096">"Tastiera fizike"</string>
<string name="notification_channel_security" msgid="8516754650348238057">"Siguria"</string>
@@ -2415,9 +2417,9 @@
<string name="unarchival_session_app_label" msgid="6811856981546348205">"Në pritje..."</string>
<!-- no translation found for fingerprint_dangling_notification_title (7362075195588639989) -->
<skip />
- <!-- no translation found for fingerprint_dangling_notification_msg_1 (6261149111900787302) -->
+ <!-- no translation found for fingerprint_dangling_notification_msg_1 (8517140433796229725) -->
<skip />
- <!-- no translation found for fingerprint_dangling_notification_msg_2 (7688302770424064884) -->
+ <!-- no translation found for fingerprint_dangling_notification_msg_2 (7578829498452127613) -->
<skip />
<!-- no translation found for fingerprint_dangling_notification_msg_all_deleted_1 (2927018569542316055) -->
<skip />
diff --git a/core/res/res/values-sr/strings.xml b/core/res/res/values-sr/strings.xml
index 95ccead..86387c9 100644
--- a/core/res/res/values-sr/strings.xml
+++ b/core/res/res/values-sr/strings.xml
@@ -284,6 +284,8 @@
<string name="global_action_voice_assist" msgid="6655788068555086695">"Гласовна помоћ"</string>
<string name="global_action_lockdown" msgid="2475471405907902963">"Закључавање"</string>
<string name="status_bar_notification_info_overflow" msgid="3330152558746563475">"999+"</string>
+ <!-- no translation found for notification_compact_heads_up_reply (2425293958371284340) -->
+ <skip />
<string name="notification_hidden_text" msgid="2835519769868187223">"Ново обавештење"</string>
<string name="notification_channel_physical_keyboard" msgid="5417306456125988096">"Физичка тастатура"</string>
<string name="notification_channel_security" msgid="8516754650348238057">"Безбедност"</string>
@@ -835,11 +837,11 @@
<string name="policylab_watchLogin" msgid="7599669460083719504">"Надзор покушаја откључавања екрана"</string>
<string name="policydesc_watchLogin" product="tablet" msgid="2388436408621909298">"Прати број нетачно унетих лозинки приликом откључавања екрана и закључава таблет или брише податке са таблета ако је нетачна лозинка унета превише пута."</string>
<string name="policydesc_watchLogin" product="tv" msgid="2140588224468517507">"Надгледа број нетачних лозинки унетих при откључавању екрана и закључава Android TV уређај или брише све податке са Android TV уређаја ако се унесе превише нетачних лозинки."</string>
- <string name="policydesc_watchLogin" product="automotive" msgid="7011438994051251521">"Прати број нетачно унетих лозинки при откључавању екрана и закључава систем за инфо-забаву или брише све податке са система за инфо-забаву ако је нетачна лозинка унета превише пута."</string>
+ <string name="policydesc_watchLogin" product="automotive" msgid="7011438994051251521">"Прати број нетачно унетих лозинки при откључавању екрана и закључава систем за информације и забаву или брише све податке са система за информације и забаву ако је нетачна лозинка унета превише пута."</string>
<string name="policydesc_watchLogin" product="default" msgid="4885030206253600299">"Прати број нетачно унетих лозинки при откључавању екрана и закључава телефон или брише све податке са телефона ако је нетачна лозинка унета превише пута."</string>
<string name="policydesc_watchLogin_secondaryUser" product="tablet" msgid="2049038943004297474">"Надгледа број нетачних лозинки унетих при откључавању екрана и закључава таблет или брише све податке овог корисника ако се унесе превише нетачних лозинки."</string>
<string name="policydesc_watchLogin_secondaryUser" product="tv" msgid="8965224107449407052">"Надгледа број нетачних лозинки унетих при откључавању екрана и закључава Android TV уређај или брише све податке овог корисника ако се унесе превише нетачних лозинки."</string>
- <string name="policydesc_watchLogin_secondaryUser" product="automotive" msgid="7180857406058327941">"Надгледа број нетачних лозинки унетих при откључавању екрана и закључава систем за инфо-забаву или брише све податке овог профила ако се унесе превише нетачних лозинки."</string>
+ <string name="policydesc_watchLogin_secondaryUser" product="automotive" msgid="7180857406058327941">"Надгледа број нетачних лозинки унетих при откључавању екрана и закључава систем за информације и забаву или брише све податке овог профила ако се унесе превише нетачних лозинки."</string>
<string name="policydesc_watchLogin_secondaryUser" product="default" msgid="9177645136475155924">"Надгледа број нетачних лозинки унетих при откључавању екрана и закључава телефон или брише све податке овог корисника ако се унесе превише нетачних лозинки."</string>
<string name="policylab_resetPassword" msgid="214556238645096520">"Промена закључавања екрана"</string>
<string name="policydesc_resetPassword" msgid="4626419138439341851">"Мења откључавање екрана."</string>
@@ -848,13 +850,13 @@
<string name="policylab_wipeData" msgid="1359485247727537311">"Брисање свих података"</string>
<string name="policydesc_wipeData" product="tablet" msgid="7245372676261947507">"Брисање података на таблету без упозорења ресетовањем на фабричка подешавања."</string>
<string name="policydesc_wipeData" product="tv" msgid="513862488950801261">"Брише податке Android TV уређаја без упозорења помоћу ресетовања на фабричка подешавања."</string>
- <string name="policydesc_wipeData" product="automotive" msgid="660804547737323300">"Брише податке на систему за инфо-забаву без упозорења ресетовањем на фабричка подешавања."</string>
+ <string name="policydesc_wipeData" product="automotive" msgid="660804547737323300">"Брише податке на систему за информације и забаву без упозорења ресетовањем на фабричка подешавања."</string>
<string name="policydesc_wipeData" product="default" msgid="8036084184768379022">"Брисање података на телефону без упозорења ресетовањем на фабричка подешавања."</string>
<string name="policylab_wipeData_secondaryUser" product="automotive" msgid="115034358520328373">"Брисање података профила"</string>
<string name="policylab_wipeData_secondaryUser" product="default" msgid="413813645323433166">"Обриши податке корисника"</string>
<string name="policydesc_wipeData_secondaryUser" product="tablet" msgid="2336676480090926470">"Брише податке овог корисника на овом таблету без упозорења."</string>
<string name="policydesc_wipeData_secondaryUser" product="tv" msgid="2293713284515865200">"Брише податке овог корисника на овом Android TV уређају без упозорења."</string>
- <string name="policydesc_wipeData_secondaryUser" product="automotive" msgid="4658832487305780879">"Брише податке овог профила на овом систему за инфо-забаву без упозорења."</string>
+ <string name="policydesc_wipeData_secondaryUser" product="automotive" msgid="4658832487305780879">"Брише податке овог профила на овом систему за информације и забаву без упозорења."</string>
<string name="policydesc_wipeData_secondaryUser" product="default" msgid="2788325512167208654">"Брише податке овог корисника на овом телефону без упозорења."</string>
<string name="policylab_setGlobalProxy" msgid="215332221188670221">"Подесите глобални прокси сервер уређаја"</string>
<string name="policydesc_setGlobalProxy" msgid="7149665222705519604">"Подешава глобални прокси уређаја који ће се користити док су смернице омогућене. Само власник уређаја може да подеси глобални прокси."</string>
@@ -2414,22 +2416,15 @@
<string name="satellite_notification_open_message" msgid="4149234979688273729">"Отвори Messages"</string>
<string name="satellite_notification_how_it_works" msgid="3132069321977520519">"Принцип рада"</string>
<string name="unarchival_session_app_label" msgid="6811856981546348205">"На чекању..."</string>
- <!-- no translation found for fingerprint_dangling_notification_title (7362075195588639989) -->
+ <string name="fingerprint_dangling_notification_title" msgid="7362075195588639989">"Поново подесите откључавање отиском прста"</string>
+ <!-- no translation found for fingerprint_dangling_notification_msg_1 (8517140433796229725) -->
<skip />
- <!-- no translation found for fingerprint_dangling_notification_msg_1 (6261149111900787302) -->
+ <!-- no translation found for fingerprint_dangling_notification_msg_2 (7578829498452127613) -->
<skip />
- <!-- no translation found for fingerprint_dangling_notification_msg_2 (7688302770424064884) -->
- <skip />
- <!-- no translation found for fingerprint_dangling_notification_msg_all_deleted_1 (2927018569542316055) -->
- <skip />
- <!-- no translation found for fingerprint_dangling_notification_msg_all_deleted_2 (6897989352716156176) -->
- <skip />
- <!-- no translation found for face_dangling_notification_title (947852541060975473) -->
- <skip />
- <!-- no translation found for face_dangling_notification_msg (8806849376915541655) -->
- <skip />
- <!-- no translation found for biometric_dangling_notification_action_set_up (8246885009807817961) -->
- <skip />
- <!-- no translation found for biometric_dangling_notification_action_not_now (8095249216864443491) -->
- <skip />
+ <string name="fingerprint_dangling_notification_msg_all_deleted_1" msgid="2927018569542316055">"<xliff:g id="FINGERPRINT">%s</xliff:g> није функционисао и избрисали смо га. Поново га подесите да бисте телефон откључавали отиском прста."</string>
+ <string name="fingerprint_dangling_notification_msg_all_deleted_2" msgid="6897989352716156176">"<xliff:g id="FINGERPRINT_0">%1$s</xliff:g> и <xliff:g id="FINGERPRINT_1">%2$s</xliff:g> нису функционисали и избрисали смо их. Поново их подесите да бисте телефон откључавали отиском прста."</string>
+ <string name="face_dangling_notification_title" msgid="947852541060975473">"Поново подесите откључавање лицем"</string>
+ <string name="face_dangling_notification_msg" msgid="8806849376915541655">"Ваш модел лица није функционисао и избрисали смо га. Поново га подесите да бисте телефон откључавали лицем."</string>
+ <string name="biometric_dangling_notification_action_set_up" msgid="8246885009807817961">"Подеси"</string>
+ <string name="biometric_dangling_notification_action_not_now" msgid="8095249216864443491">"Не сада"</string>
</resources>
diff --git a/core/res/res/values-sv/strings.xml b/core/res/res/values-sv/strings.xml
index 0cbdda1..6c15ad8 100644
--- a/core/res/res/values-sv/strings.xml
+++ b/core/res/res/values-sv/strings.xml
@@ -283,6 +283,8 @@
<string name="global_action_voice_assist" msgid="6655788068555086695">"Voice Assist"</string>
<string name="global_action_lockdown" msgid="2475471405907902963">"Låsning"</string>
<string name="status_bar_notification_info_overflow" msgid="3330152558746563475">"999+"</string>
+ <!-- no translation found for notification_compact_heads_up_reply (2425293958371284340) -->
+ <skip />
<string name="notification_hidden_text" msgid="2835519769868187223">"Ny avisering"</string>
<string name="notification_channel_physical_keyboard" msgid="5417306456125988096">"Fysiskt tangentbord"</string>
<string name="notification_channel_security" msgid="8516754650348238057">"Säkerhet"</string>
@@ -623,12 +625,12 @@
<string name="permdesc_postNotification" msgid="5974977162462877075">"Tillåter att appen visar aviseringar"</string>
<string name="permlab_turnScreenOn" msgid="219344053664171492">"Slå på skärmen"</string>
<string name="permdesc_turnScreenOn" msgid="4394606875897601559">"Tillåter att appen slår på skärmen."</string>
- <string name="permlab_useBiometric" msgid="6314741124749633786">"använd biometrisk maskinvara"</string>
- <string name="permdesc_useBiometric" msgid="7502858732677143410">"Tillåter att appen använder biometrisk maskinvara vid autentisering"</string>
- <string name="permlab_manageFingerprint" msgid="7432667156322821178">"hantera maskinvara för fingeravtryck"</string>
+ <string name="permlab_useBiometric" msgid="6314741124749633786">"använd biometrisk hårdvara"</string>
+ <string name="permdesc_useBiometric" msgid="7502858732677143410">"Tillåter att appen använder biometrisk hårdvara vid autentisering"</string>
+ <string name="permlab_manageFingerprint" msgid="7432667156322821178">"hantera hårdvara för fingeravtryck"</string>
<string name="permdesc_manageFingerprint" msgid="2025616816437339865">"Tillåter att appen anropar metoder för att lägga till och radera fingeravtrycksmallar."</string>
- <string name="permlab_useFingerprint" msgid="1001421069766751922">"använda maskinvara för fingeravtryck"</string>
- <string name="permdesc_useFingerprint" msgid="412463055059323742">"Tillåter att appen använder maskinvara för fingeravtryck vid autentisering"</string>
+ <string name="permlab_useFingerprint" msgid="1001421069766751922">"använda hårdvara för fingeravtryck"</string>
+ <string name="permdesc_useFingerprint" msgid="412463055059323742">"Tillåter att appen använder hårdvara för fingeravtryck vid autentisering"</string>
<string name="permlab_audioWrite" msgid="8501705294265669405">"göra ändringar i din musiksamling"</string>
<string name="permdesc_audioWrite" msgid="8057399517013412431">"Tillåter att appen gör ändringar i din musiksamling."</string>
<string name="permlab_videoWrite" msgid="5940738769586451318">"göra ändringar i din videosamling"</string>
@@ -642,7 +644,7 @@
<string name="biometric_dialog_default_title" msgid="55026799173208210">"Verifiera din identitet"</string>
<string name="biometric_dialog_default_subtitle" msgid="8457232339298571992">"Fortsätt med hjälp av din biometriska data"</string>
<string name="biometric_or_screen_lock_dialog_default_subtitle" msgid="159539678371552009">"Fortsätt med hjälp av din biometriska data eller skärmlåset"</string>
- <string name="biometric_error_hw_unavailable" msgid="2494077380540615216">"Biometrisk maskinvara är inte tillgänglig"</string>
+ <string name="biometric_error_hw_unavailable" msgid="2494077380540615216">"Biometrisk hårdvara är inte tillgänglig"</string>
<string name="biometric_error_user_canceled" msgid="6732303949695293730">"Autentiseringen avbröts"</string>
<string name="biometric_not_recognized" msgid="5106687642694635888">"Identifierades inte"</string>
<string name="biometric_face_not_recognized" msgid="5535599455744525200">"Ansiktet känns inte igen"</string>
@@ -670,7 +672,7 @@
<string name="fingerprint_authenticated" msgid="2024862866860283100">"Fingeravtrycket har autentiserats"</string>
<string name="face_authenticated_no_confirmation_required" msgid="8867889115112348167">"Ansiktet har autentiserats"</string>
<string name="face_authenticated_confirmation_required" msgid="6872632732508013755">"Ansiktet har autentiserats. Tryck på Bekräfta"</string>
- <string name="fingerprint_error_hw_not_available" msgid="7755729484334001137">"Det finns ingen maskinvara för fingeravtryck"</string>
+ <string name="fingerprint_error_hw_not_available" msgid="7755729484334001137">"Det finns ingen hårdvara för fingeravtryck"</string>
<string name="fingerprint_error_no_space" msgid="7285481581905967580">"Det gick inte att konfigurera fingeravtryck"</string>
<string name="fingerprint_error_timeout" msgid="7361192266621252164">"Tiden för fingeravtrycksinställning gick ut. Försök igen."</string>
<string name="fingerprint_error_canceled" msgid="5541771463159727513">"Fingeravtrycksåtgärden avbröts"</string>
@@ -732,7 +734,7 @@
<string name="face_acquired_mouth_covering_detected_alt" msgid="1122294982850589766">"Något täcker ansiktet. Hela ansiktet måste synas."</string>
<string-array name="face_acquired_vendor">
</string-array>
- <string name="face_error_hw_not_available" msgid="5085202213036026288">"Ansiktsverifiering går ej. Otillgänglig maskinvara."</string>
+ <string name="face_error_hw_not_available" msgid="5085202213036026288">"Ansiktsverifiering går ej. Otillgänglig hårdvara."</string>
<string name="face_error_timeout" msgid="2598544068593889762">"Försök att använda ansiktslåset igen"</string>
<string name="face_error_no_space" msgid="5649264057026021723">"Kan inte lagra ny ansiktsdata. Radera först gammal data."</string>
<string name="face_error_canceled" msgid="2164434737103802131">"Ansiktsåtgärden har avbrutits."</string>
@@ -1731,7 +1733,7 @@
<string name="accessibility_service_screen_control_title" msgid="190017412626919776">"Visa och styra skärmen"</string>
<string name="accessibility_service_screen_control_description" msgid="6946315917771791525">"Den kan läsa allt innehåll på skärmen och visa innehåll över andra appar."</string>
<string name="accessibility_service_action_perform_title" msgid="779670378951658160">"Visa och vidta åtgärder"</string>
- <string name="accessibility_service_action_perform_description" msgid="2718852014003170558">"Den kan registrera din användning av en app eller maskinvarusensor och interagera med appar åt dig."</string>
+ <string name="accessibility_service_action_perform_description" msgid="2718852014003170558">"Den kan registrera din användning av en app eller hårdvarusensor och interagera med appar åt dig."</string>
<string name="accessibility_dialog_button_allow" msgid="2092558122987144530">"Tillåt"</string>
<string name="accessibility_dialog_button_deny" msgid="4129575637812472671">"Neka"</string>
<string name="accessibility_dialog_button_uninstall" msgid="2952465517671708108">"Avinstallera"</string>
@@ -2413,22 +2415,15 @@
<string name="satellite_notification_open_message" msgid="4149234979688273729">"Öppna Messages"</string>
<string name="satellite_notification_how_it_works" msgid="3132069321977520519">"Så fungerar det"</string>
<string name="unarchival_session_app_label" msgid="6811856981546348205">"Väntar …"</string>
- <!-- no translation found for fingerprint_dangling_notification_title (7362075195588639989) -->
+ <string name="fingerprint_dangling_notification_title" msgid="7362075195588639989">"Konfigurera fingeravtryckslås igen"</string>
+ <!-- no translation found for fingerprint_dangling_notification_msg_1 (8517140433796229725) -->
<skip />
- <!-- no translation found for fingerprint_dangling_notification_msg_1 (6261149111900787302) -->
+ <!-- no translation found for fingerprint_dangling_notification_msg_2 (7578829498452127613) -->
<skip />
- <!-- no translation found for fingerprint_dangling_notification_msg_2 (7688302770424064884) -->
- <skip />
- <!-- no translation found for fingerprint_dangling_notification_msg_all_deleted_1 (2927018569542316055) -->
- <skip />
- <!-- no translation found for fingerprint_dangling_notification_msg_all_deleted_2 (6897989352716156176) -->
- <skip />
- <!-- no translation found for face_dangling_notification_title (947852541060975473) -->
- <skip />
- <!-- no translation found for face_dangling_notification_msg (8806849376915541655) -->
- <skip />
- <!-- no translation found for biometric_dangling_notification_action_set_up (8246885009807817961) -->
- <skip />
- <!-- no translation found for biometric_dangling_notification_action_not_now (8095249216864443491) -->
- <skip />
+ <string name="fingerprint_dangling_notification_msg_all_deleted_1" msgid="2927018569542316055">"<xliff:g id="FINGERPRINT">%s</xliff:g> fungerade inte bra och har raderats. Konfigurera det igen för att låsa upp telefonen med fingeravtryck."</string>
+ <string name="fingerprint_dangling_notification_msg_all_deleted_2" msgid="6897989352716156176">"<xliff:g id="FINGERPRINT_0">%1$s</xliff:g> och <xliff:g id="FINGERPRINT_1">%2$s</xliff:g> fungerade inte bra och har raderats. Konfigurera dem igen för att låsa upp telefonen med fingeravtryck."</string>
+ <string name="face_dangling_notification_title" msgid="947852541060975473">"Konfigurera ansiktslås igen"</string>
+ <string name="face_dangling_notification_msg" msgid="8806849376915541655">"Ansiktsmodellen fungerade inte bra och har raderats. Konfigurera den igen för att låsa upp telefonen med ansiktet."</string>
+ <string name="biometric_dangling_notification_action_set_up" msgid="8246885009807817961">"Ställ in"</string>
+ <string name="biometric_dangling_notification_action_not_now" msgid="8095249216864443491">"Inte nu"</string>
</resources>
diff --git a/core/res/res/values-sw/strings.xml b/core/res/res/values-sw/strings.xml
index 10f55af..1437a38 100644
--- a/core/res/res/values-sw/strings.xml
+++ b/core/res/res/values-sw/strings.xml
@@ -283,6 +283,8 @@
<string name="global_action_voice_assist" msgid="6655788068555086695">"Usaidizi wa Sauti"</string>
<string name="global_action_lockdown" msgid="2475471405907902963">"Funga"</string>
<string name="status_bar_notification_info_overflow" msgid="3330152558746563475">"999+"</string>
+ <!-- no translation found for notification_compact_heads_up_reply (2425293958371284340) -->
+ <skip />
<string name="notification_hidden_text" msgid="2835519769868187223">"Arifa mpya"</string>
<string name="notification_channel_physical_keyboard" msgid="5417306456125988096">"Kibodi halisi"</string>
<string name="notification_channel_security" msgid="8516754650348238057">"Usalama"</string>
@@ -2413,22 +2415,15 @@
<string name="satellite_notification_open_message" msgid="4149234979688273729">"Fungua Programu ya Messages"</string>
<string name="satellite_notification_how_it_works" msgid="3132069321977520519">"Utaratibu wake"</string>
<string name="unarchival_session_app_label" msgid="6811856981546348205">"Inashughulikiwa..."</string>
- <!-- no translation found for fingerprint_dangling_notification_title (7362075195588639989) -->
+ <string name="fingerprint_dangling_notification_title" msgid="7362075195588639989">"Weka tena mipangilio ya Kufungua kwa Alama ya Kidole"</string>
+ <!-- no translation found for fingerprint_dangling_notification_msg_1 (8517140433796229725) -->
<skip />
- <!-- no translation found for fingerprint_dangling_notification_msg_1 (6261149111900787302) -->
+ <!-- no translation found for fingerprint_dangling_notification_msg_2 (7578829498452127613) -->
<skip />
- <!-- no translation found for fingerprint_dangling_notification_msg_2 (7688302770424064884) -->
- <skip />
- <!-- no translation found for fingerprint_dangling_notification_msg_all_deleted_1 (2927018569542316055) -->
- <skip />
- <!-- no translation found for fingerprint_dangling_notification_msg_all_deleted_2 (6897989352716156176) -->
- <skip />
- <!-- no translation found for face_dangling_notification_title (947852541060975473) -->
- <skip />
- <!-- no translation found for face_dangling_notification_msg (8806849376915541655) -->
- <skip />
- <!-- no translation found for biometric_dangling_notification_action_set_up (8246885009807817961) -->
- <skip />
- <!-- no translation found for biometric_dangling_notification_action_not_now (8095249216864443491) -->
- <skip />
+ <string name="fingerprint_dangling_notification_msg_all_deleted_1" msgid="2927018569542316055">"Alama ya <xliff:g id="FINGERPRINT">%s</xliff:g> ilikuwa na hitilafu na imefutwa. Iweke tena ili ufungue simu yako kwa alama ya kidole."</string>
+ <string name="fingerprint_dangling_notification_msg_all_deleted_2" msgid="6897989352716156176">"Alama za <xliff:g id="FINGERPRINT_0">%1$s</xliff:g> na <xliff:g id="FINGERPRINT_1">%2$s</xliff:g> zilikuwa na hitilafu na zimefutwa. Ziweke tena ili ufungue simu yako kwa alama ya kidole."</string>
+ <string name="face_dangling_notification_title" msgid="947852541060975473">"Weka tena mipangilio ya Kufungua kwa Uso"</string>
+ <string name="face_dangling_notification_msg" msgid="8806849376915541655">"Muundo wako wa uso ulikuwa na hitilafu na umefutwa. Uweke tena ili ufungue simu yako kwa uso."</string>
+ <string name="biometric_dangling_notification_action_set_up" msgid="8246885009807817961">"Weka mipangilio"</string>
+ <string name="biometric_dangling_notification_action_not_now" msgid="8095249216864443491">"Si sasa"</string>
</resources>
diff --git a/core/res/res/values-ta/strings.xml b/core/res/res/values-ta/strings.xml
index 466e29a..231b14c 100644
--- a/core/res/res/values-ta/strings.xml
+++ b/core/res/res/values-ta/strings.xml
@@ -283,6 +283,8 @@
<string name="global_action_voice_assist" msgid="6655788068555086695">"குரல் உதவி"</string>
<string name="global_action_lockdown" msgid="2475471405907902963">"பூட்டு"</string>
<string name="status_bar_notification_info_overflow" msgid="3330152558746563475">"999+"</string>
+ <!-- no translation found for notification_compact_heads_up_reply (2425293958371284340) -->
+ <skip />
<string name="notification_hidden_text" msgid="2835519769868187223">"புதிய அறிவிப்பு"</string>
<string name="notification_channel_physical_keyboard" msgid="5417306456125988096">"கைமுறை கீபோர்டு"</string>
<string name="notification_channel_security" msgid="8516754650348238057">"பாதுகாப்பு"</string>
@@ -2413,22 +2415,15 @@
<string name="satellite_notification_open_message" msgid="4149234979688273729">"Messages ஆப்ஸைத் திறக்கவும்"</string>
<string name="satellite_notification_how_it_works" msgid="3132069321977520519">"இது செயல்படும் விதம்"</string>
<string name="unarchival_session_app_label" msgid="6811856981546348205">"நிலுவையிலுள்ளது..."</string>
- <!-- no translation found for fingerprint_dangling_notification_title (7362075195588639989) -->
+ <string name="fingerprint_dangling_notification_title" msgid="7362075195588639989">"கைரேகை அன்லாக் அம்சத்தை மீண்டும் அமையுங்கள்"</string>
+ <!-- no translation found for fingerprint_dangling_notification_msg_1 (8517140433796229725) -->
<skip />
- <!-- no translation found for fingerprint_dangling_notification_msg_1 (6261149111900787302) -->
+ <!-- no translation found for fingerprint_dangling_notification_msg_2 (7578829498452127613) -->
<skip />
- <!-- no translation found for fingerprint_dangling_notification_msg_2 (7688302770424064884) -->
- <skip />
- <!-- no translation found for fingerprint_dangling_notification_msg_all_deleted_1 (2927018569542316055) -->
- <skip />
- <!-- no translation found for fingerprint_dangling_notification_msg_all_deleted_2 (6897989352716156176) -->
- <skip />
- <!-- no translation found for face_dangling_notification_title (947852541060975473) -->
- <skip />
- <!-- no translation found for face_dangling_notification_msg (8806849376915541655) -->
- <skip />
- <!-- no translation found for biometric_dangling_notification_action_set_up (8246885009807817961) -->
- <skip />
- <!-- no translation found for biometric_dangling_notification_action_not_now (8095249216864443491) -->
- <skip />
+ <string name="fingerprint_dangling_notification_msg_all_deleted_1" msgid="2927018569542316055">"<xliff:g id="FINGERPRINT">%s</xliff:g> சரியாகச் செயல்படவில்லை என்பதால் அது நீக்கபட்டது. கைரேகை மூலம் உங்கள் மொபைலை அன்லாக் செய்ய அதை மீண்டும் அமையுங்கள்."</string>
+ <string name="fingerprint_dangling_notification_msg_all_deleted_2" msgid="6897989352716156176">"<xliff:g id="FINGERPRINT_0">%1$s</xliff:g> மற்றும் <xliff:g id="FINGERPRINT_1">%2$s</xliff:g> சரியாகச் செயல்படவில்லை என்பதால் அவை நீக்கப்பட்டன. கைரேகை மூலம் உங்கள் மொபைலை அன்லாக் செய்ய அவற்றை மீண்டும் அமையுங்கள்."</string>
+ <string name="face_dangling_notification_title" msgid="947852541060975473">"\'முகம் காட்டித் திறத்தல்\' அம்சத்தை மீண்டும் அமையுங்கள்"</string>
+ <string name="face_dangling_notification_msg" msgid="8806849376915541655">"உங்கள் முகத் தோற்றப் பதிவு சரியாகச் செயல்படவில்லை என்பதால் அது நீக்கப்பட்டது. உங்கள் முகத்தைப் பயன்படுத்தி மொபைலை அன்லாக் செய்ய அதை மீண்டும் அமையுங்கள்."</string>
+ <string name="biometric_dangling_notification_action_set_up" msgid="8246885009807817961">"அமை"</string>
+ <string name="biometric_dangling_notification_action_not_now" msgid="8095249216864443491">"இப்போது வேண்டாம்"</string>
</resources>
diff --git a/core/res/res/values-te/strings.xml b/core/res/res/values-te/strings.xml
index db75aac..cf4e194 100644
--- a/core/res/res/values-te/strings.xml
+++ b/core/res/res/values-te/strings.xml
@@ -283,6 +283,8 @@
<string name="global_action_voice_assist" msgid="6655788068555086695">"వాయిస్ అసిస్టెంట్"</string>
<string name="global_action_lockdown" msgid="2475471405907902963">"లాక్ చేయండి"</string>
<string name="status_bar_notification_info_overflow" msgid="3330152558746563475">"999+"</string>
+ <!-- no translation found for notification_compact_heads_up_reply (2425293958371284340) -->
+ <skip />
<string name="notification_hidden_text" msgid="2835519769868187223">"కొత్త నోటిఫికేషన్"</string>
<string name="notification_channel_physical_keyboard" msgid="5417306456125988096">"భౌతిక కీబోర్డ్"</string>
<string name="notification_channel_security" msgid="8516754650348238057">"సెక్యూరిటీ"</string>
@@ -2413,22 +2415,15 @@
<string name="satellite_notification_open_message" msgid="4149234979688273729">"Messagesను తెరవండి"</string>
<string name="satellite_notification_how_it_works" msgid="3132069321977520519">"ఇది ఎలా పని చేస్తుంది"</string>
<string name="unarchival_session_app_label" msgid="6811856981546348205">"పెండింగ్లో ఉంది..."</string>
- <!-- no translation found for fingerprint_dangling_notification_title (7362075195588639989) -->
+ <string name="fingerprint_dangling_notification_title" msgid="7362075195588639989">"వేలిముద్ర అన్లాక్ను మళ్లీ సెటప్ చేయండి"</string>
+ <!-- no translation found for fingerprint_dangling_notification_msg_1 (8517140433796229725) -->
<skip />
- <!-- no translation found for fingerprint_dangling_notification_msg_1 (6261149111900787302) -->
+ <!-- no translation found for fingerprint_dangling_notification_msg_2 (7578829498452127613) -->
<skip />
- <!-- no translation found for fingerprint_dangling_notification_msg_2 (7688302770424064884) -->
- <skip />
- <!-- no translation found for fingerprint_dangling_notification_msg_all_deleted_1 (2927018569542316055) -->
- <skip />
- <!-- no translation found for fingerprint_dangling_notification_msg_all_deleted_2 (6897989352716156176) -->
- <skip />
- <!-- no translation found for face_dangling_notification_title (947852541060975473) -->
- <skip />
- <!-- no translation found for face_dangling_notification_msg (8806849376915541655) -->
- <skip />
- <!-- no translation found for biometric_dangling_notification_action_set_up (8246885009807817961) -->
- <skip />
- <!-- no translation found for biometric_dangling_notification_action_not_now (8095249216864443491) -->
- <skip />
+ <string name="fingerprint_dangling_notification_msg_all_deleted_1" msgid="2927018569542316055">"<xliff:g id="FINGERPRINT">%s</xliff:g> సరిగ్గా పని చేయడం లేదు, తొలగించబడింది. వేలిముద్రతో మీ ఫోన్ను అన్లాక్ చేయడానికి దాన్ని మళ్లీ సెటప్ చేయండి."</string>
+ <string name="fingerprint_dangling_notification_msg_all_deleted_2" msgid="6897989352716156176">"<xliff:g id="FINGERPRINT_0">%1$s</xliff:g>, <xliff:g id="FINGERPRINT_1">%2$s</xliff:g> బాగా పని చేయడం లేదు, తొలగించబడ్డాయి. మీ వేలిముద్రతో మీ ఫోన్ను అన్లాక్ చేయడానికి వాటిని మళ్లీ సెటప్ చేయండి."</string>
+ <string name="face_dangling_notification_title" msgid="947852541060975473">"ఫేస్ అన్లాక్ను మళ్లీ సెటప్ చేయండి"</string>
+ <string name="face_dangling_notification_msg" msgid="8806849376915541655">"మీ ఫేస్ మోడల్ సరిగ్గా పని చేయడం లేదు, తొలగించబడింది. ఫేస్తో మీ ఫోన్ను అన్లాక్ చేయడానికి దాన్ని మళ్లీ సెటప్ చేయండి."</string>
+ <string name="biometric_dangling_notification_action_set_up" msgid="8246885009807817961">"సెటప్ చేయండి"</string>
+ <string name="biometric_dangling_notification_action_not_now" msgid="8095249216864443491">"ఇప్పుడు కాదు"</string>
</resources>
diff --git a/core/res/res/values-th/strings.xml b/core/res/res/values-th/strings.xml
index 284a5f2..59809c9 100644
--- a/core/res/res/values-th/strings.xml
+++ b/core/res/res/values-th/strings.xml
@@ -283,6 +283,8 @@
<string name="global_action_voice_assist" msgid="6655788068555086695">"ตัวช่วยเสียง"</string>
<string name="global_action_lockdown" msgid="2475471405907902963">"ปิดล็อก"</string>
<string name="status_bar_notification_info_overflow" msgid="3330152558746563475">"999+"</string>
+ <!-- no translation found for notification_compact_heads_up_reply (2425293958371284340) -->
+ <skip />
<string name="notification_hidden_text" msgid="2835519769868187223">"การแจ้งเตือนใหม่"</string>
<string name="notification_channel_physical_keyboard" msgid="5417306456125988096">"แป้นพิมพ์จริง"</string>
<string name="notification_channel_security" msgid="8516754650348238057">"ความปลอดภัย"</string>
@@ -2413,22 +2415,15 @@
<string name="satellite_notification_open_message" msgid="4149234979688273729">"เปิด Messages"</string>
<string name="satellite_notification_how_it_works" msgid="3132069321977520519">"วิธีการทำงาน"</string>
<string name="unarchival_session_app_label" msgid="6811856981546348205">"รอดำเนินการ..."</string>
- <!-- no translation found for fingerprint_dangling_notification_title (7362075195588639989) -->
+ <string name="fingerprint_dangling_notification_title" msgid="7362075195588639989">"ตั้งค่าการปลดล็อกด้วยลายนิ้วมืออีกครั้ง"</string>
+ <!-- no translation found for fingerprint_dangling_notification_msg_1 (8517140433796229725) -->
<skip />
- <!-- no translation found for fingerprint_dangling_notification_msg_1 (6261149111900787302) -->
+ <!-- no translation found for fingerprint_dangling_notification_msg_2 (7578829498452127613) -->
<skip />
- <!-- no translation found for fingerprint_dangling_notification_msg_2 (7688302770424064884) -->
- <skip />
- <!-- no translation found for fingerprint_dangling_notification_msg_all_deleted_1 (2927018569542316055) -->
- <skip />
- <!-- no translation found for fingerprint_dangling_notification_msg_all_deleted_2 (6897989352716156176) -->
- <skip />
- <!-- no translation found for face_dangling_notification_title (947852541060975473) -->
- <skip />
- <!-- no translation found for face_dangling_notification_msg (8806849376915541655) -->
- <skip />
- <!-- no translation found for biometric_dangling_notification_action_set_up (8246885009807817961) -->
- <skip />
- <!-- no translation found for biometric_dangling_notification_action_not_now (8095249216864443491) -->
- <skip />
+ <string name="fingerprint_dangling_notification_msg_all_deleted_1" msgid="2927018569542316055">"<xliff:g id="FINGERPRINT">%s</xliff:g>ทำงานได้ไม่ดีและถูกลบออกไปแล้ว ตั้งค่าอีกครั้งเพื่อปลดล็อกโทรศัพท์ด้วยลายนิ้วมือ"</string>
+ <string name="fingerprint_dangling_notification_msg_all_deleted_2" msgid="6897989352716156176">"<xliff:g id="FINGERPRINT_0">%1$s</xliff:g> และ <xliff:g id="FINGERPRINT_1">%2$s</xliff:g> ทำงานได้ไม่ดีและถูกลบออกไปแล้ว ตั้งค่าอีกครั้งเพื่อปลดล็อกโทรศัพท์ด้วยลายนิ้วมือ"</string>
+ <string name="face_dangling_notification_title" msgid="947852541060975473">"ตั้งค่าการปลดล็อกด้วยใบหน้าอีกครั้ง"</string>
+ <string name="face_dangling_notification_msg" msgid="8806849376915541655">"รูปแบบใบหน้าของคุณทำงานได้ไม่ดีและถูกลบออกไปแล้ว ตั้งค่าอีกครั้งเพื่อปลดล็อกโทรศัพท์ด้วยใบหน้า"</string>
+ <string name="biometric_dangling_notification_action_set_up" msgid="8246885009807817961">"ตั้งค่า"</string>
+ <string name="biometric_dangling_notification_action_not_now" msgid="8095249216864443491">"ไว้ทีหลัง"</string>
</resources>
diff --git a/core/res/res/values-tl/strings.xml b/core/res/res/values-tl/strings.xml
index 4dd4886..7ba81ad 100644
--- a/core/res/res/values-tl/strings.xml
+++ b/core/res/res/values-tl/strings.xml
@@ -283,6 +283,8 @@
<string name="global_action_voice_assist" msgid="6655788068555086695">"Voice Assist"</string>
<string name="global_action_lockdown" msgid="2475471405907902963">"I-lockdown"</string>
<string name="status_bar_notification_info_overflow" msgid="3330152558746563475">"999+"</string>
+ <!-- no translation found for notification_compact_heads_up_reply (2425293958371284340) -->
+ <skip />
<string name="notification_hidden_text" msgid="2835519769868187223">"Bagong notification"</string>
<string name="notification_channel_physical_keyboard" msgid="5417306456125988096">"Pisikal na keyboard"</string>
<string name="notification_channel_security" msgid="8516754650348238057">"Seguridad"</string>
@@ -2413,22 +2415,15 @@
<string name="satellite_notification_open_message" msgid="4149234979688273729">"Buksan ang Messages"</string>
<string name="satellite_notification_how_it_works" msgid="3132069321977520519">"Paano ito gumagana"</string>
<string name="unarchival_session_app_label" msgid="6811856981546348205">"Nakabinbin..."</string>
- <!-- no translation found for fingerprint_dangling_notification_title (7362075195588639989) -->
+ <string name="fingerprint_dangling_notification_title" msgid="7362075195588639989">"I-set up ulit ang Pag-unlock Gamit ang Fingerprint"</string>
+ <!-- no translation found for fingerprint_dangling_notification_msg_1 (8517140433796229725) -->
<skip />
- <!-- no translation found for fingerprint_dangling_notification_msg_1 (6261149111900787302) -->
+ <!-- no translation found for fingerprint_dangling_notification_msg_2 (7578829498452127613) -->
<skip />
- <!-- no translation found for fingerprint_dangling_notification_msg_2 (7688302770424064884) -->
- <skip />
- <!-- no translation found for fingerprint_dangling_notification_msg_all_deleted_1 (2927018569542316055) -->
- <skip />
- <!-- no translation found for fingerprint_dangling_notification_msg_all_deleted_2 (6897989352716156176) -->
- <skip />
- <!-- no translation found for face_dangling_notification_title (947852541060975473) -->
- <skip />
- <!-- no translation found for face_dangling_notification_msg (8806849376915541655) -->
- <skip />
- <!-- no translation found for biometric_dangling_notification_action_set_up (8246885009807817961) -->
- <skip />
- <!-- no translation found for biometric_dangling_notification_action_not_now (8095249216864443491) -->
- <skip />
+ <string name="fingerprint_dangling_notification_msg_all_deleted_1" msgid="2927018569542316055">"Hindi gumagana nang maayos ang <xliff:g id="FINGERPRINT">%s</xliff:g> at na-delete na ito. I-set up ulit ito para ma-unlock ang iyong telepono sa pamamagitan ng fingerprint."</string>
+ <string name="fingerprint_dangling_notification_msg_all_deleted_2" msgid="6897989352716156176">"Hindi gumagana nang maayos ang <xliff:g id="FINGERPRINT_0">%1$s</xliff:g> at <xliff:g id="FINGERPRINT_1">%2$s</xliff:g> at na-delete na ang mga ito. I-set up ulit ang mga ito para ma-unlock ang iyong telepono gamit ang fingerprint mo."</string>
+ <string name="face_dangling_notification_title" msgid="947852541060975473">"I-set up ulit ang Pag-unlock Gamit ang Mukha"</string>
+ <string name="face_dangling_notification_msg" msgid="8806849376915541655">"Hindi gumagana nang maayos ang iyong face model at na-delete na ito. I-set up ulit ito para ma-unlock ang iyong telepono sa pamamagitan ng mukha."</string>
+ <string name="biometric_dangling_notification_action_set_up" msgid="8246885009807817961">"I-set up"</string>
+ <string name="biometric_dangling_notification_action_not_now" msgid="8095249216864443491">"Huwag muna"</string>
</resources>
diff --git a/core/res/res/values-tr/strings.xml b/core/res/res/values-tr/strings.xml
index 2b616de..64498b5 100644
--- a/core/res/res/values-tr/strings.xml
+++ b/core/res/res/values-tr/strings.xml
@@ -283,6 +283,8 @@
<string name="global_action_voice_assist" msgid="6655788068555086695">"Sesli Yardım"</string>
<string name="global_action_lockdown" msgid="2475471405907902963">"Tam kilitleme"</string>
<string name="status_bar_notification_info_overflow" msgid="3330152558746563475">"999+"</string>
+ <!-- no translation found for notification_compact_heads_up_reply (2425293958371284340) -->
+ <skip />
<string name="notification_hidden_text" msgid="2835519769868187223">"Yeni bildirim"</string>
<string name="notification_channel_physical_keyboard" msgid="5417306456125988096">"Fiziksel klavye"</string>
<string name="notification_channel_security" msgid="8516754650348238057">"Güvenlik"</string>
@@ -2413,22 +2415,15 @@
<string name="satellite_notification_open_message" msgid="4149234979688273729">"Mesajlar\'ı aç"</string>
<string name="satellite_notification_how_it_works" msgid="3132069321977520519">"İşleyiş şekli"</string>
<string name="unarchival_session_app_label" msgid="6811856981546348205">"Bekliyor..."</string>
- <!-- no translation found for fingerprint_dangling_notification_title (7362075195588639989) -->
+ <string name="fingerprint_dangling_notification_title" msgid="7362075195588639989">"Parmak İzi Kilidi\'ni tekrar kurun"</string>
+ <!-- no translation found for fingerprint_dangling_notification_msg_1 (8517140433796229725) -->
<skip />
- <!-- no translation found for fingerprint_dangling_notification_msg_1 (6261149111900787302) -->
+ <!-- no translation found for fingerprint_dangling_notification_msg_2 (7578829498452127613) -->
<skip />
- <!-- no translation found for fingerprint_dangling_notification_msg_2 (7688302770424064884) -->
- <skip />
- <!-- no translation found for fingerprint_dangling_notification_msg_all_deleted_1 (2927018569542316055) -->
- <skip />
- <!-- no translation found for fingerprint_dangling_notification_msg_all_deleted_2 (6897989352716156176) -->
- <skip />
- <!-- no translation found for face_dangling_notification_title (947852541060975473) -->
- <skip />
- <!-- no translation found for face_dangling_notification_msg (8806849376915541655) -->
- <skip />
- <!-- no translation found for biometric_dangling_notification_action_set_up (8246885009807817961) -->
- <skip />
- <!-- no translation found for biometric_dangling_notification_action_not_now (8095249216864443491) -->
- <skip />
+ <string name="fingerprint_dangling_notification_msg_all_deleted_1" msgid="2927018569542316055">"<xliff:g id="FINGERPRINT">%s</xliff:g> iyi çalışmadığı için silindi. Telefonunuzun kilidini parmak iziyle açmak için tekrar kurun."</string>
+ <string name="fingerprint_dangling_notification_msg_all_deleted_2" msgid="6897989352716156176">"<xliff:g id="FINGERPRINT_0">%1$s</xliff:g> ve <xliff:g id="FINGERPRINT_1">%2$s</xliff:g> iyi çalışmadığı için silindi. Telefonunuzun kilidini parmak izinizle açmak için tekrar kurun."</string>
+ <string name="face_dangling_notification_title" msgid="947852541060975473">"Yüz Tanıma Kilidi\'ni tekrar kurun"</string>
+ <string name="face_dangling_notification_msg" msgid="8806849376915541655">"Yüz modeliniz iyi çalışmadığı için silindi. Telefonunuzun kilidini yüzünüzle açmak için tekrar kurun."</string>
+ <string name="biometric_dangling_notification_action_set_up" msgid="8246885009807817961">"Ayarla"</string>
+ <string name="biometric_dangling_notification_action_not_now" msgid="8095249216864443491">"Şimdi değil"</string>
</resources>
diff --git a/core/res/res/values-uk/strings.xml b/core/res/res/values-uk/strings.xml
index 850e21b..fcea7bb 100644
--- a/core/res/res/values-uk/strings.xml
+++ b/core/res/res/values-uk/strings.xml
@@ -285,6 +285,8 @@
<string name="global_action_voice_assist" msgid="6655788068555086695">"Голос. підказки"</string>
<string name="global_action_lockdown" msgid="2475471405907902963">"Блокування"</string>
<string name="status_bar_notification_info_overflow" msgid="3330152558746563475">"999+"</string>
+ <!-- no translation found for notification_compact_heads_up_reply (2425293958371284340) -->
+ <skip />
<string name="notification_hidden_text" msgid="2835519769868187223">"Нове сповіщення"</string>
<string name="notification_channel_physical_keyboard" msgid="5417306456125988096">"Фізична клавіатура"</string>
<string name="notification_channel_security" msgid="8516754650348238057">"Безпека"</string>
@@ -2415,22 +2417,15 @@
<string name="satellite_notification_open_message" msgid="4149234979688273729">"Відкрийте Повідомлення"</string>
<string name="satellite_notification_how_it_works" msgid="3132069321977520519">"Як це працює"</string>
<string name="unarchival_session_app_label" msgid="6811856981546348205">"Обробка…"</string>
- <!-- no translation found for fingerprint_dangling_notification_title (7362075195588639989) -->
+ <string name="fingerprint_dangling_notification_title" msgid="7362075195588639989">"Налаштуйте розблокування відбитком пальця повторно"</string>
+ <!-- no translation found for fingerprint_dangling_notification_msg_1 (8517140433796229725) -->
<skip />
- <!-- no translation found for fingerprint_dangling_notification_msg_1 (6261149111900787302) -->
+ <!-- no translation found for fingerprint_dangling_notification_msg_2 (7578829498452127613) -->
<skip />
- <!-- no translation found for fingerprint_dangling_notification_msg_2 (7688302770424064884) -->
- <skip />
- <!-- no translation found for fingerprint_dangling_notification_msg_all_deleted_1 (2927018569542316055) -->
- <skip />
- <!-- no translation found for fingerprint_dangling_notification_msg_all_deleted_2 (6897989352716156176) -->
- <skip />
- <!-- no translation found for face_dangling_notification_title (947852541060975473) -->
- <skip />
- <!-- no translation found for face_dangling_notification_msg (8806849376915541655) -->
- <skip />
- <!-- no translation found for biometric_dangling_notification_action_set_up (8246885009807817961) -->
- <skip />
- <!-- no translation found for biometric_dangling_notification_action_not_now (8095249216864443491) -->
- <skip />
+ <string name="fingerprint_dangling_notification_msg_all_deleted_1" msgid="2927018569542316055">"Відбиток \"<xliff:g id="FINGERPRINT">%s</xliff:g>\" працював неналежним чином, і його видалено. Налаштуйте його ще раз, щоб розблоковувати телефон за допомогою відбитка пальця."</string>
+ <string name="fingerprint_dangling_notification_msg_all_deleted_2" msgid="6897989352716156176">"Відбитки \"<xliff:g id="FINGERPRINT_0">%1$s</xliff:g>\" і \"<xliff:g id="FINGERPRINT_1">%2$s</xliff:g>\" працювали неналежним чином, і їх видалено. Налаштуйте їх ще раз, щоб розблоковувати телефон за допомогою відбитка пальця."</string>
+ <string name="face_dangling_notification_title" msgid="947852541060975473">"Налаштуйте фейс-контроль повторно"</string>
+ <string name="face_dangling_notification_msg" msgid="8806849376915541655">"Модель обличчя працювала неналежним чином, і її видалено. Налаштуйте її ще раз, щоб розблоковувати телефон за допомогою фейс-контролю."</string>
+ <string name="biometric_dangling_notification_action_set_up" msgid="8246885009807817961">"Налаштувати"</string>
+ <string name="biometric_dangling_notification_action_not_now" msgid="8095249216864443491">"Не зараз"</string>
</resources>
diff --git a/core/res/res/values-ur/strings.xml b/core/res/res/values-ur/strings.xml
index d3fde3c..fc23d99 100644
--- a/core/res/res/values-ur/strings.xml
+++ b/core/res/res/values-ur/strings.xml
@@ -283,6 +283,8 @@
<string name="global_action_voice_assist" msgid="6655788068555086695">"Voice Assist"</string>
<string name="global_action_lockdown" msgid="2475471405907902963">"مقفل"</string>
<string name="status_bar_notification_info_overflow" msgid="3330152558746563475">"999+"</string>
+ <!-- no translation found for notification_compact_heads_up_reply (2425293958371284340) -->
+ <skip />
<string name="notification_hidden_text" msgid="2835519769868187223">"نئی اطلاع"</string>
<string name="notification_channel_physical_keyboard" msgid="5417306456125988096">"فزیکل کی بورڈ"</string>
<string name="notification_channel_security" msgid="8516754650348238057">"سیکیورٹی"</string>
@@ -2413,22 +2415,15 @@
<string name="satellite_notification_open_message" msgid="4149234979688273729">"پیغامات ایپ کو کھولیں"</string>
<string name="satellite_notification_how_it_works" msgid="3132069321977520519">"اس کے کام کرنے کا طریقہ"</string>
<string name="unarchival_session_app_label" msgid="6811856981546348205">"زیر التواء..."</string>
- <!-- no translation found for fingerprint_dangling_notification_title (7362075195588639989) -->
+ <string name="fingerprint_dangling_notification_title" msgid="7362075195588639989">"فنگر پرنٹ اَن لاک کو دوبارہ سیٹ اپ کریں"</string>
+ <!-- no translation found for fingerprint_dangling_notification_msg_1 (8517140433796229725) -->
<skip />
- <!-- no translation found for fingerprint_dangling_notification_msg_1 (6261149111900787302) -->
+ <!-- no translation found for fingerprint_dangling_notification_msg_2 (7578829498452127613) -->
<skip />
- <!-- no translation found for fingerprint_dangling_notification_msg_2 (7688302770424064884) -->
- <skip />
- <!-- no translation found for fingerprint_dangling_notification_msg_all_deleted_1 (2927018569542316055) -->
- <skip />
- <!-- no translation found for fingerprint_dangling_notification_msg_all_deleted_2 (6897989352716156176) -->
- <skip />
- <!-- no translation found for face_dangling_notification_title (947852541060975473) -->
- <skip />
- <!-- no translation found for face_dangling_notification_msg (8806849376915541655) -->
- <skip />
- <!-- no translation found for biometric_dangling_notification_action_set_up (8246885009807817961) -->
- <skip />
- <!-- no translation found for biometric_dangling_notification_action_not_now (8095249216864443491) -->
- <skip />
+ <string name="fingerprint_dangling_notification_msg_all_deleted_1" msgid="2927018569542316055">"<xliff:g id="FINGERPRINT">%s</xliff:g> اچھی طرح کام نہیں کر رہا تھا اور حذف کر دیا گیا تھا۔ اپنے فون کو فنگر پرنٹ سے غیر مقفل کرنے کے لیے، اسے دوبارہ سیٹ اپ کریں۔"</string>
+ <string name="fingerprint_dangling_notification_msg_all_deleted_2" msgid="6897989352716156176">"<xliff:g id="FINGERPRINT_0">%1$s</xliff:g> اور <xliff:g id="FINGERPRINT_1">%2$s</xliff:g> اچھی طرح کام نہیں کر رہے تھے اور انہیں حذف کر دیا گیا تھا۔ اپنے فون کو اپنے فنگر پرنٹ سے غیر مقفل کرنے کے لیے انہیں دوبارہ سیٹ اپ کریں۔"</string>
+ <string name="face_dangling_notification_title" msgid="947852541060975473">"فیس اَن لاک کو دوبارہ سیٹ اپ کریں"</string>
+ <string name="face_dangling_notification_msg" msgid="8806849376915541655">"آپ کے چہرے کا ماڈل اچھی طرح کام نہیں کر رہا تھا اور حذف کر دیا گیا تھا۔ اپنے فون کو چہرے سے غیر مقفل کرنے کے لیے، اسے دوبارہ سیٹ اپ کریں۔"</string>
+ <string name="biometric_dangling_notification_action_set_up" msgid="8246885009807817961">"سیٹ اپ کریں"</string>
+ <string name="biometric_dangling_notification_action_not_now" msgid="8095249216864443491">"ابھی نہیں"</string>
</resources>
diff --git a/core/res/res/values-uz/strings.xml b/core/res/res/values-uz/strings.xml
index c143244..0ef4ddc 100644
--- a/core/res/res/values-uz/strings.xml
+++ b/core/res/res/values-uz/strings.xml
@@ -283,6 +283,8 @@
<string name="global_action_voice_assist" msgid="6655788068555086695">"Ovozli yordam"</string>
<string name="global_action_lockdown" msgid="2475471405907902963">"Qulflash"</string>
<string name="status_bar_notification_info_overflow" msgid="3330152558746563475">"999+"</string>
+ <!-- no translation found for notification_compact_heads_up_reply (2425293958371284340) -->
+ <skip />
<string name="notification_hidden_text" msgid="2835519769868187223">"Yangi bildirishnoma"</string>
<string name="notification_channel_physical_keyboard" msgid="5417306456125988096">"Tashqi klaviatura"</string>
<string name="notification_channel_security" msgid="8516754650348238057">"Xavfsizlik"</string>
@@ -2413,22 +2415,15 @@
<string name="satellite_notification_open_message" msgid="4149234979688273729">"Xabarlar ilovasini ochish"</string>
<string name="satellite_notification_how_it_works" msgid="3132069321977520519">"Ishlash tartibi"</string>
<string name="unarchival_session_app_label" msgid="6811856981546348205">"Kutilmoqda..."</string>
- <!-- no translation found for fingerprint_dangling_notification_title (7362075195588639989) -->
+ <string name="fingerprint_dangling_notification_title" msgid="7362075195588639989">"Barmoq izi bilan ochish funksiyasini qayta sozlang"</string>
+ <!-- no translation found for fingerprint_dangling_notification_msg_1 (8517140433796229725) -->
<skip />
- <!-- no translation found for fingerprint_dangling_notification_msg_1 (6261149111900787302) -->
+ <!-- no translation found for fingerprint_dangling_notification_msg_2 (7578829498452127613) -->
<skip />
- <!-- no translation found for fingerprint_dangling_notification_msg_2 (7688302770424064884) -->
- <skip />
- <!-- no translation found for fingerprint_dangling_notification_msg_all_deleted_1 (2927018569542316055) -->
- <skip />
- <!-- no translation found for fingerprint_dangling_notification_msg_all_deleted_2 (6897989352716156176) -->
- <skip />
- <!-- no translation found for face_dangling_notification_title (947852541060975473) -->
- <skip />
- <!-- no translation found for face_dangling_notification_msg (8806849376915541655) -->
- <skip />
- <!-- no translation found for biometric_dangling_notification_action_set_up (8246885009807817961) -->
- <skip />
- <!-- no translation found for biometric_dangling_notification_action_not_now (8095249216864443491) -->
- <skip />
+ <string name="fingerprint_dangling_notification_msg_all_deleted_1" msgid="2927018569542316055">"<xliff:g id="FINGERPRINT">%s</xliff:g> yaxshi ishlamadi va oʻchirib tashlandi. Telefonni barmoq izi bilan ochish uchun uni qayta sozlang."</string>
+ <string name="fingerprint_dangling_notification_msg_all_deleted_2" msgid="6897989352716156176">"<xliff:g id="FINGERPRINT_0">%1$s</xliff:g> va <xliff:g id="FINGERPRINT_1">%2$s</xliff:g> yaxshi ishlamadi va oʻchirib tashlandi. Telefonni barmoq izi bilan ochish uchun ularni qayta sozlang."</string>
+ <string name="face_dangling_notification_title" msgid="947852541060975473">"Yuz bilan ochishni qayta sozlash"</string>
+ <string name="face_dangling_notification_msg" msgid="8806849376915541655">"Yuzingiz mobeli yaxshi ishlamadi va oʻchirib tashlandi. Telefonni yuz bilan ochish uchun uni qayta sozlang."</string>
+ <string name="biometric_dangling_notification_action_set_up" msgid="8246885009807817961">"Sozlash"</string>
+ <string name="biometric_dangling_notification_action_not_now" msgid="8095249216864443491">"Hozir emas"</string>
</resources>
diff --git a/core/res/res/values-vi/strings.xml b/core/res/res/values-vi/strings.xml
index a0775d4..6ff6528 100644
--- a/core/res/res/values-vi/strings.xml
+++ b/core/res/res/values-vi/strings.xml
@@ -283,6 +283,8 @@
<string name="global_action_voice_assist" msgid="6655788068555086695">"Trợ lý thoại"</string>
<string name="global_action_lockdown" msgid="2475471405907902963">"Khóa"</string>
<string name="status_bar_notification_info_overflow" msgid="3330152558746563475">"999+"</string>
+ <!-- no translation found for notification_compact_heads_up_reply (2425293958371284340) -->
+ <skip />
<string name="notification_hidden_text" msgid="2835519769868187223">"Thông báo mới"</string>
<string name="notification_channel_physical_keyboard" msgid="5417306456125988096">"Bàn phím vật lý"</string>
<string name="notification_channel_security" msgid="8516754650348238057">"Bảo mật"</string>
@@ -2413,22 +2415,15 @@
<string name="satellite_notification_open_message" msgid="4149234979688273729">"Mở ứng dụng Tin nhắn"</string>
<string name="satellite_notification_how_it_works" msgid="3132069321977520519">"Cách hoạt động"</string>
<string name="unarchival_session_app_label" msgid="6811856981546348205">"Đang chờ xử lý..."</string>
- <!-- no translation found for fingerprint_dangling_notification_title (7362075195588639989) -->
+ <string name="fingerprint_dangling_notification_title" msgid="7362075195588639989">"Thiết lập lại tính năng Mở khoá bằng vân tay"</string>
+ <!-- no translation found for fingerprint_dangling_notification_msg_1 (8517140433796229725) -->
<skip />
- <!-- no translation found for fingerprint_dangling_notification_msg_1 (6261149111900787302) -->
+ <!-- no translation found for fingerprint_dangling_notification_msg_2 (7578829498452127613) -->
<skip />
- <!-- no translation found for fingerprint_dangling_notification_msg_2 (7688302770424064884) -->
- <skip />
- <!-- no translation found for fingerprint_dangling_notification_msg_all_deleted_1 (2927018569542316055) -->
- <skip />
- <!-- no translation found for fingerprint_dangling_notification_msg_all_deleted_2 (6897989352716156176) -->
- <skip />
- <!-- no translation found for face_dangling_notification_title (947852541060975473) -->
- <skip />
- <!-- no translation found for face_dangling_notification_msg (8806849376915541655) -->
- <skip />
- <!-- no translation found for biometric_dangling_notification_action_set_up (8246885009807817961) -->
- <skip />
- <!-- no translation found for biometric_dangling_notification_action_not_now (8095249216864443491) -->
- <skip />
+ <string name="fingerprint_dangling_notification_msg_all_deleted_1" msgid="2927018569542316055">"<xliff:g id="FINGERPRINT">%s</xliff:g> không dùng được và đã bị xoá. Hãy thiết lập lại để mở khoá điện thoại bằng vân tay."</string>
+ <string name="fingerprint_dangling_notification_msg_all_deleted_2" msgid="6897989352716156176">"<xliff:g id="FINGERPRINT_0">%1$s</xliff:g> và <xliff:g id="FINGERPRINT_1">%2$s</xliff:g> không dùng được và đã bị xoá. Hãy thiết lập lại để mở khoá điện thoại bằng vân tay."</string>
+ <string name="face_dangling_notification_title" msgid="947852541060975473">"Thiết lập lại tính năng Mở khoá bằng khuôn mặt"</string>
+ <string name="face_dangling_notification_msg" msgid="8806849376915541655">"Mẫu khuôn mặt của bạn không dùng được và đã bị xoá. Hãy thiết lập lại để mở khoá điện thoại bằng khuôn mặt."</string>
+ <string name="biometric_dangling_notification_action_set_up" msgid="8246885009807817961">"Thiết lập"</string>
+ <string name="biometric_dangling_notification_action_not_now" msgid="8095249216864443491">"Để sau"</string>
</resources>
diff --git a/core/res/res/values-watch/config.xml b/core/res/res/values-watch/config.xml
index 31acd9a..5266214 100644
--- a/core/res/res/values-watch/config.xml
+++ b/core/res/res/values-watch/config.xml
@@ -93,4 +93,7 @@
<!-- If this is true, allow wake from theater mode from motion. -->
<bool name="config_allowTheaterModeWakeFromMotion">true</bool>
+
+ <!-- True if the device supports system decorations on secondary displays. -->
+ <bool name="config_supportsSystemDecorsOnSecondaryDisplays">false</bool>
</resources>
diff --git a/core/res/res/values-zh-rCN/strings.xml b/core/res/res/values-zh-rCN/strings.xml
index 00dd3db..1024322 100644
--- a/core/res/res/values-zh-rCN/strings.xml
+++ b/core/res/res/values-zh-rCN/strings.xml
@@ -283,6 +283,8 @@
<string name="global_action_voice_assist" msgid="6655788068555086695">"语音助理"</string>
<string name="global_action_lockdown" msgid="2475471405907902963">"锁定"</string>
<string name="status_bar_notification_info_overflow" msgid="3330152558746563475">"999+"</string>
+ <!-- no translation found for notification_compact_heads_up_reply (2425293958371284340) -->
+ <skip />
<string name="notification_hidden_text" msgid="2835519769868187223">"新通知"</string>
<string name="notification_channel_physical_keyboard" msgid="5417306456125988096">"实体键盘"</string>
<string name="notification_channel_security" msgid="8516754650348238057">"安全性"</string>
@@ -2413,22 +2415,15 @@
<string name="satellite_notification_open_message" msgid="4149234979688273729">"打开“信息”应用"</string>
<string name="satellite_notification_how_it_works" msgid="3132069321977520519">"运作方式"</string>
<string name="unarchival_session_app_label" msgid="6811856981546348205">"待归档…"</string>
- <!-- no translation found for fingerprint_dangling_notification_title (7362075195588639989) -->
+ <string name="fingerprint_dangling_notification_title" msgid="7362075195588639989">"重新设置指纹解锁功能"</string>
+ <!-- no translation found for fingerprint_dangling_notification_msg_1 (8517140433796229725) -->
<skip />
- <!-- no translation found for fingerprint_dangling_notification_msg_1 (6261149111900787302) -->
+ <!-- no translation found for fingerprint_dangling_notification_msg_2 (7578829498452127613) -->
<skip />
- <!-- no translation found for fingerprint_dangling_notification_msg_2 (7688302770424064884) -->
- <skip />
- <!-- no translation found for fingerprint_dangling_notification_msg_all_deleted_1 (2927018569542316055) -->
- <skip />
- <!-- no translation found for fingerprint_dangling_notification_msg_all_deleted_2 (6897989352716156176) -->
- <skip />
- <!-- no translation found for face_dangling_notification_title (947852541060975473) -->
- <skip />
- <!-- no translation found for face_dangling_notification_msg (8806849376915541655) -->
- <skip />
- <!-- no translation found for biometric_dangling_notification_action_set_up (8246885009807817961) -->
- <skip />
- <!-- no translation found for biometric_dangling_notification_action_not_now (8095249216864443491) -->
- <skip />
+ <string name="fingerprint_dangling_notification_msg_all_deleted_1" msgid="2927018569542316055">"“<xliff:g id="FINGERPRINT">%s</xliff:g>”无法正常使用,系统已将其删除。如要通过指纹解锁功能来解锁手机,请重新设置。"</string>
+ <string name="fingerprint_dangling_notification_msg_all_deleted_2" msgid="6897989352716156176">"“<xliff:g id="FINGERPRINT_0">%1$s</xliff:g>”和“<xliff:g id="FINGERPRINT_1">%2$s</xliff:g>”无法正常使用,系统已将它们删除。如要通过指纹解锁功能来解锁手机,请重新设置。"</string>
+ <string name="face_dangling_notification_title" msgid="947852541060975473">"重新设置“人脸解锁”功能"</string>
+ <string name="face_dangling_notification_msg" msgid="8806849376915541655">"您的脸部模型无法正常使用,系统已将其删除。如要通过人脸解锁功能来解锁手机,请重新设置。"</string>
+ <string name="biometric_dangling_notification_action_set_up" msgid="8246885009807817961">"设置"</string>
+ <string name="biometric_dangling_notification_action_not_now" msgid="8095249216864443491">"以后再说"</string>
</resources>
diff --git a/core/res/res/values-zh-rHK/strings.xml b/core/res/res/values-zh-rHK/strings.xml
index 142940a..b1157f7 100644
--- a/core/res/res/values-zh-rHK/strings.xml
+++ b/core/res/res/values-zh-rHK/strings.xml
@@ -283,6 +283,8 @@
<string name="global_action_voice_assist" msgid="6655788068555086695">"語音助手"</string>
<string name="global_action_lockdown" msgid="2475471405907902963">"鎖定"</string>
<string name="status_bar_notification_info_overflow" msgid="3330152558746563475">"999+"</string>
+ <!-- no translation found for notification_compact_heads_up_reply (2425293958371284340) -->
+ <skip />
<string name="notification_hidden_text" msgid="2835519769868187223">"新通知"</string>
<string name="notification_channel_physical_keyboard" msgid="5417306456125988096">"實體鍵盤"</string>
<string name="notification_channel_security" msgid="8516754650348238057">"安全性"</string>
@@ -651,11 +653,11 @@
<string name="biometric_error_generic" msgid="6784371929985434439">"驗證時發生錯誤"</string>
<string name="screen_lock_app_setting_name" msgid="6054944352976789228">"使用螢幕鎖定"</string>
<string name="screen_lock_dialog_default_subtitle" msgid="120359538048533695">"如要繼續操作,請輸入螢幕鎖定解鎖憑證"</string>
- <string name="fingerprint_acquired_partial" msgid="4323789264604479684">"請用力按住感應器"</string>
+ <string name="fingerprint_acquired_partial" msgid="4323789264604479684">"請按住感應器"</string>
<string name="fingerprint_acquired_insufficient" msgid="2410176550915730974">"無法辨識指紋,請再試一次。"</string>
<string name="fingerprint_acquired_imager_dirty" msgid="1770676120848224250">"請清潔指紋感應器,然後再試一次"</string>
<string name="fingerprint_acquired_imager_dirty_alt" msgid="9169582140486372897">"請清潔感應器,然後再試一次"</string>
- <string name="fingerprint_acquired_too_fast" msgid="1628459767349116104">"請用力按住感應器"</string>
+ <string name="fingerprint_acquired_too_fast" msgid="1628459767349116104">"請按住感應器"</string>
<string name="fingerprint_acquired_too_slow" msgid="6683510291554497580">"手指移動太慢,請重試。"</string>
<string name="fingerprint_acquired_already_enrolled" msgid="2285166003936206785">"改用其他指紋"</string>
<string name="fingerprint_acquired_too_bright" msgid="3863560181670915607">"太亮"</string>
@@ -2413,22 +2415,15 @@
<string name="satellite_notification_open_message" msgid="4149234979688273729">"開啟「訊息」"</string>
<string name="satellite_notification_how_it_works" msgid="3132069321977520519">"運作方式"</string>
<string name="unarchival_session_app_label" msgid="6811856981546348205">"待處理…"</string>
- <!-- no translation found for fingerprint_dangling_notification_title (7362075195588639989) -->
+ <string name="fingerprint_dangling_notification_title" msgid="7362075195588639989">"重新設定「指紋解鎖」功能"</string>
+ <!-- no translation found for fingerprint_dangling_notification_msg_1 (8517140433796229725) -->
<skip />
- <!-- no translation found for fingerprint_dangling_notification_msg_1 (6261149111900787302) -->
+ <!-- no translation found for fingerprint_dangling_notification_msg_2 (7578829498452127613) -->
<skip />
- <!-- no translation found for fingerprint_dangling_notification_msg_2 (7688302770424064884) -->
- <skip />
- <!-- no translation found for fingerprint_dangling_notification_msg_all_deleted_1 (2927018569542316055) -->
- <skip />
- <!-- no translation found for fingerprint_dangling_notification_msg_all_deleted_2 (6897989352716156176) -->
- <skip />
- <!-- no translation found for face_dangling_notification_title (947852541060975473) -->
- <skip />
- <!-- no translation found for face_dangling_notification_msg (8806849376915541655) -->
- <skip />
- <!-- no translation found for biometric_dangling_notification_action_set_up (8246885009807817961) -->
- <skip />
- <!-- no translation found for biometric_dangling_notification_action_not_now (8095249216864443491) -->
- <skip />
+ <string name="fingerprint_dangling_notification_msg_all_deleted_1" msgid="2927018569542316055">"由於「<xliff:g id="FINGERPRINT">%s</xliff:g>」無法正常運作,因此系統已將其刪除。請重新設定,才能使用指紋解鎖手機。"</string>
+ <string name="fingerprint_dangling_notification_msg_all_deleted_2" msgid="6897989352716156176">"由於「<xliff:g id="FINGERPRINT_0">%1$s</xliff:g>」和「<xliff:g id="FINGERPRINT_1">%2$s</xliff:g>」無法正常運作,因此系統已將其刪除。請重新設定,才能使用指紋解鎖手機。"</string>
+ <string name="face_dangling_notification_title" msgid="947852541060975473">"重新設定「面孔解鎖」功能"</string>
+ <string name="face_dangling_notification_msg" msgid="8806849376915541655">"由於面部模型無法正常運作,因此系統已將其刪除。請重新設定,才能使用面孔解鎖手機。"</string>
+ <string name="biometric_dangling_notification_action_set_up" msgid="8246885009807817961">"設定"</string>
+ <string name="biometric_dangling_notification_action_not_now" msgid="8095249216864443491">"暫時不要"</string>
</resources>
diff --git a/core/res/res/values-zh-rTW/strings.xml b/core/res/res/values-zh-rTW/strings.xml
index b7ee23c..da43a12 100644
--- a/core/res/res/values-zh-rTW/strings.xml
+++ b/core/res/res/values-zh-rTW/strings.xml
@@ -283,6 +283,8 @@
<string name="global_action_voice_assist" msgid="6655788068555086695">"語音小幫手"</string>
<string name="global_action_lockdown" msgid="2475471405907902963">"鎖定"</string>
<string name="status_bar_notification_info_overflow" msgid="3330152558746563475">"超過 999"</string>
+ <!-- no translation found for notification_compact_heads_up_reply (2425293958371284340) -->
+ <skip />
<string name="notification_hidden_text" msgid="2835519769868187223">"新通知"</string>
<string name="notification_channel_physical_keyboard" msgid="5417306456125988096">"實體鍵盤"</string>
<string name="notification_channel_security" msgid="8516754650348238057">"安全性"</string>
@@ -2413,22 +2415,15 @@
<string name="satellite_notification_open_message" msgid="4149234979688273729">"開啟「訊息」應用程式"</string>
<string name="satellite_notification_how_it_works" msgid="3132069321977520519">"運作方式"</string>
<string name="unarchival_session_app_label" msgid="6811856981546348205">"待處理…"</string>
- <!-- no translation found for fingerprint_dangling_notification_title (7362075195588639989) -->
+ <string name="fingerprint_dangling_notification_title" msgid="7362075195588639989">"重新設定指紋解鎖"</string>
+ <!-- no translation found for fingerprint_dangling_notification_msg_1 (8517140433796229725) -->
<skip />
- <!-- no translation found for fingerprint_dangling_notification_msg_1 (6261149111900787302) -->
+ <!-- no translation found for fingerprint_dangling_notification_msg_2 (7578829498452127613) -->
<skip />
- <!-- no translation found for fingerprint_dangling_notification_msg_2 (7688302770424064884) -->
- <skip />
- <!-- no translation found for fingerprint_dangling_notification_msg_all_deleted_1 (2927018569542316055) -->
- <skip />
- <!-- no translation found for fingerprint_dangling_notification_msg_all_deleted_2 (6897989352716156176) -->
- <skip />
- <!-- no translation found for face_dangling_notification_title (947852541060975473) -->
- <skip />
- <!-- no translation found for face_dangling_notification_msg (8806849376915541655) -->
- <skip />
- <!-- no translation found for biometric_dangling_notification_action_set_up (8246885009807817961) -->
- <skip />
- <!-- no translation found for biometric_dangling_notification_action_not_now (8095249216864443491) -->
- <skip />
+ <string name="fingerprint_dangling_notification_msg_all_deleted_1" msgid="2927018569542316055">"「<xliff:g id="FINGERPRINT">%s</xliff:g>」無法正常運作,因此系統已將其刪除。請重新設定,才能用指紋解鎖手機。"</string>
+ <string name="fingerprint_dangling_notification_msg_all_deleted_2" msgid="6897989352716156176">"「<xliff:g id="FINGERPRINT_0">%1$s</xliff:g>」和「<xliff:g id="FINGERPRINT_1">%2$s</xliff:g>」無法正常運作,因此系統已將其刪除。請重新設定,才能用指紋解鎖手機。"</string>
+ <string name="face_dangling_notification_title" msgid="947852541060975473">"重新設定人臉解鎖"</string>
+ <string name="face_dangling_notification_msg" msgid="8806849376915541655">"臉部模型無法正常運作,因此系統已將其刪除。請重新設定,才能用臉解鎖手機。"</string>
+ <string name="biometric_dangling_notification_action_set_up" msgid="8246885009807817961">"設定"</string>
+ <string name="biometric_dangling_notification_action_not_now" msgid="8095249216864443491">"暫時不要"</string>
</resources>
diff --git a/core/res/res/values-zu/strings.xml b/core/res/res/values-zu/strings.xml
index a967fb6..7749b1b 100644
--- a/core/res/res/values-zu/strings.xml
+++ b/core/res/res/values-zu/strings.xml
@@ -283,6 +283,8 @@
<string name="global_action_voice_assist" msgid="6655788068555086695">"Isisekeli sezwi"</string>
<string name="global_action_lockdown" msgid="2475471405907902963">"Khiya"</string>
<string name="status_bar_notification_info_overflow" msgid="3330152558746563475">"999+"</string>
+ <!-- no translation found for notification_compact_heads_up_reply (2425293958371284340) -->
+ <skip />
<string name="notification_hidden_text" msgid="2835519769868187223">"Isaziso esisha"</string>
<string name="notification_channel_physical_keyboard" msgid="5417306456125988096">"Ikhibhodi ephathekayo"</string>
<string name="notification_channel_security" msgid="8516754650348238057">"Ukuphepha"</string>
@@ -2413,22 +2415,15 @@
<string name="satellite_notification_open_message" msgid="4149234979688273729">"Vula Imilayezo"</string>
<string name="satellite_notification_how_it_works" msgid="3132069321977520519">"Indlela esebenza ngayo"</string>
<string name="unarchival_session_app_label" msgid="6811856981546348205">"Ilindile..."</string>
- <!-- no translation found for fingerprint_dangling_notification_title (7362075195588639989) -->
+ <string name="fingerprint_dangling_notification_title" msgid="7362075195588639989">"Setha Ukuvula ngesigxivizo somunwe futhi"</string>
+ <!-- no translation found for fingerprint_dangling_notification_msg_1 (8517140433796229725) -->
<skip />
- <!-- no translation found for fingerprint_dangling_notification_msg_1 (6261149111900787302) -->
+ <!-- no translation found for fingerprint_dangling_notification_msg_2 (7578829498452127613) -->
<skip />
- <!-- no translation found for fingerprint_dangling_notification_msg_2 (7688302770424064884) -->
- <skip />
- <!-- no translation found for fingerprint_dangling_notification_msg_all_deleted_1 (2927018569542316055) -->
- <skip />
- <!-- no translation found for fingerprint_dangling_notification_msg_all_deleted_2 (6897989352716156176) -->
- <skip />
- <!-- no translation found for face_dangling_notification_title (947852541060975473) -->
- <skip />
- <!-- no translation found for face_dangling_notification_msg (8806849376915541655) -->
- <skip />
- <!-- no translation found for biometric_dangling_notification_action_set_up (8246885009807817961) -->
- <skip />
- <!-- no translation found for biometric_dangling_notification_action_not_now (8095249216864443491) -->
- <skip />
+ <string name="fingerprint_dangling_notification_msg_all_deleted_1" msgid="2927018569542316055">"I-<xliff:g id="FINGERPRINT">%s</xliff:g> ibingasebenzi kahle futhi isuliwe. Phinde uyisethe ukuze uvule ifoni yakho ngesigxivizo somunwe."</string>
+ <string name="fingerprint_dangling_notification_msg_all_deleted_2" msgid="6897989352716156176">"I-<xliff:g id="FINGERPRINT_0">%1$s</xliff:g> kanye ne-<xliff:g id="FINGERPRINT_1">%2$s</xliff:g> ibingasebenzi kahle futhi isuliwe. Phinde uyisethe ukuze uvule ifoni yakho ngesigxivizo somunwe wakho"</string>
+ <string name="face_dangling_notification_title" msgid="947852541060975473">"Setha Ukuvula Ngobuso futhi"</string>
+ <string name="face_dangling_notification_msg" msgid="8806849376915541655">"Imodeli yobuso yakho ibingasebenzi kahle futhi isuliwe. Phinde uyisethe ukuze uvule ifoni yakho ngobuso."</string>
+ <string name="biometric_dangling_notification_action_set_up" msgid="8246885009807817961">"Setha"</string>
+ <string name="biometric_dangling_notification_action_not_now" msgid="8095249216864443491">"Hhayi manje"</string>
</resources>
diff --git a/core/res/res/values/attrs.xml b/core/res/res/values/attrs.xml
index 37d39a7..405324b 100644
--- a/core/res/res/values/attrs.xml
+++ b/core/res/res/values/attrs.xml
@@ -1346,6 +1346,51 @@
<attr name="materialColorTertiary" format="color"/>
<!-- The error color for the app, intended to draw attention to error conditions. @hide -->
<attr name="materialColorError" format="color"/>
+
+ <!-- System Custom Tokens-->
+ <!-- @hide -->
+ <attr name="customColorWidgetBackground" format="color"/>
+ <!-- @hide -->
+ <attr name="customColorClockHour" format="color"/>
+ <!-- @hide -->
+ <attr name="customColorClockMinute" format="color"/>
+ <!-- @hide -->
+ <attr name="customColorClockSecond" format="color"/>
+ <!-- @hide -->
+ <attr name="customColorThemeApp" format="color"/>
+ <!-- @hide -->
+ <attr name="customColorOnThemeApp" format="color"/>
+ <!-- @hide -->
+ <attr name="customColorThemeAppRing" format="color"/>
+ <!-- @hide -->
+ <attr name="customColorOnThemeAppRing" format="color"/>
+ <!-- @hide -->
+ <attr name="customColorBrandA" format="color"/>
+ <!-- @hide -->
+ <attr name="customColorBrandB" format="color"/>
+ <!-- @hide -->
+ <attr name="customColorBrandC" format="color"/>
+ <!-- @hide -->
+ <attr name="customColorBrandD" format="color"/>
+ <!-- @hide -->
+ <attr name="customColorUnderSurface" format="color"/>
+ <!-- @hide -->
+ <attr name="customColorShadeActive" format="color"/>
+ <!-- @hide -->
+ <attr name="customColorOnShadeActive" format="color"/>
+ <!-- @hide -->
+ <attr name="customColorOnShadeActiveVariant" format="color"/>
+ <!-- @hide -->
+ <attr name="customColorShadeInactive" format="color"/>
+ <!-- @hide -->
+ <attr name="customColorOnShadeInactive" format="color"/>
+ <!-- @hide -->
+ <attr name="customColorOnShadeInactiveVariant" format="color"/>
+ <!-- @hide -->
+ <attr name="customColorShadeDisabled" format="color"/>
+ <!-- @hide -->
+ <attr name="customColorOverviewBackground" format="color"/>
+
</declare-styleable>
<!-- **************************************************************** -->
@@ -2544,6 +2589,8 @@
<li>The framework will set {@link android.R.attr#statusBarColor},
{@link android.R.attr#navigationBarColor}, and
{@link android.R.attr#navigationBarDividerColor} to transparent.
+ <li>The frameworks will send Configuration no longer considering system insets.
+ The Configuration will be stable regardless of the system insets change.
</ul>
<p>If this is true, the edge-to-edge enforcement won't be applied. However, this
diff --git a/core/res/res/values/colors.xml b/core/res/res/values/colors.xml
index e671919..5e039b5 100644
--- a/core/res/res/values/colors.xml
+++ b/core/res/res/values/colors.xml
@@ -581,6 +581,51 @@
<color name="system_on_tertiary_fixed">#FFFFFF</color>
<color name="system_on_tertiary_fixed_variant">#FFFFFF</color>
+ <!--Colors used in Android system, from design system. These values can be overlaid at runtime
+ by OverlayManager RROs.-->
+ <color name="system_widget_background_light">#EEF0FF</color>
+ <color name="system_clock_hour_light">#1D2435</color>
+ <color name="system_clock_minute_light">#20386A</color>
+ <color name="system_clock_second_light">#000000</color>
+ <color name="system_theme_app_light">#2F4578</color>
+ <color name="system_on_theme_app_light">#D6DFFF</color>
+ <color name="system_theme_app_ring_light">#94AAE4</color>
+ <color name="system_on_theme_app_ring_light">#FDD7FA</color>
+ <color name="system_brand_a_light">#3A5084</color>
+ <color name="system_brand_b_light">#6E7488</color>
+ <color name="system_brand_c_light">#6076AC</color>
+ <color name="system_brand_d_light">#8C6D8C</color>
+ <color name="system_under_surface_light">#000000</color>
+ <color name="system_shade_active_light">#D9E2FF</color>
+ <color name="system_on_shade_active_light">#152E60</color>
+ <color name="system_on_shade_active_variant_light">#2F4578</color>
+ <color name="system_shade_inactive_light">#2F3036</color>
+ <color name="system_on_shade_inactive_light">#E1E2EC</color>
+ <color name="system_on_shade_inactive_variant_light">#C5C6D0</color>
+ <color name="system_shade_disabled_light">#0C0E13</color>
+ <color name="system_overview_background_light">#50525A</color>
+ <color name="system_widget_background_dark">#152E60</color>
+ <color name="system_clock_hour_dark">#9AA0B6</color>
+ <color name="system_clock_minute_dark">#D8E1FF</color>
+ <color name="system_clock_second_dark">#FFFFFF</color>
+ <color name="system_theme_app_dark">#D9E2FF</color>
+ <color name="system_on_theme_app_dark">#304679</color>
+ <color name="system_theme_app_ring_dark">#94AAE4</color>
+ <color name="system_on_theme_app_ring_dark">#E0BBDD</color>
+ <color name="system_brand_a_dark">#90A6DF</color>
+ <color name="system_brand_b_dark">#A4ABC1</color>
+ <color name="system_brand_c_dark">#7A90C8</color>
+ <color name="system_brand_d_dark">#A886A6</color>
+ <color name="system_under_surface_dark">#000000</color>
+ <color name="system_shade_active_dark">#D9E2FF</color>
+ <color name="system_on_shade_active_dark">#001945</color>
+ <color name="system_on_shade_active_variant_dark">#2F4578</color>
+ <color name="system_shade_inactive_dark">#2F3036</color>
+ <color name="system_on_shade_inactive_dark">#E1E2EC</color>
+ <color name="system_on_shade_inactive_variant_dark">#C5C6D0</color>
+ <color name="system_shade_disabled_dark">#0C0E13</color>
+ <color name="system_overview_background_dark">#C5C6D0</color>
+
<!-- Accessibility shortcut icon background color -->
<color name="accessibility_feature_background">#5F6368</color> <!-- Google grey 700 -->
<color name="accessibility_magnification_background">#F50D60</color>
diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml
index 37771a2..0676f72 100644
--- a/core/res/res/values/config.xml
+++ b/core/res/res/values/config.xml
@@ -4717,6 +4717,15 @@
<!-- The component name for the default system on-device sandboxed inference service. -->
<string name="config_defaultOnDeviceSandboxedInferenceService" translatable="false"></string>
+ <!-- The broadcast intent name for notifying when the on-device model is loading -->
+ <string name="config_onDeviceIntelligenceModelLoadedBroadcastKey" translatable="false"></string>
+
+ <!-- The broadcast intent name for notifying when the on-device model has been unloaded -->
+ <string name="config_onDeviceIntelligenceModelUnloadedBroadcastKey" translatable="false"></string>
+
+ <!-- The DeviceConfig namespace for the default system on-device sandboxed inference service. -->
+ <string name="config_defaultOnDeviceIntelligenceDeviceConfigNamespace" translatable="false"></string>
+
<!-- Component name that accepts ACTION_SEND intents for requesting ambient context consent for
wearable sensing. -->
<string translatable="false" name="config_defaultWearableSensingConsentComponent"></string>
@@ -6944,9 +6953,6 @@
an app is not changed during subsequent reboots. -->
<bool name="config_stopSystemPackagesByDefault">true</bool>
- <!-- Whether to show weather on the lock screen by default. -->
- <bool name="config_lockscreenWeatherEnabledByDefault">false</bool>
-
<!-- Whether we should persist the brightness value in nits for the default display even if
the underlying display device changes. -->
<bool name="config_persistBrightnessNitsForDefaultDisplay">false</bool>
diff --git a/core/res/res/values/config_battery_stats.xml b/core/res/res/values/config_battery_stats.xml
index 8d97362..80cf088 100644
--- a/core/res/res/values/config_battery_stats.xml
+++ b/core/res/res/values/config_battery_stats.xml
@@ -27,16 +27,18 @@
<!-- Whether to reset Battery Stats on unplug if the battery was significantly charged -->
<bool name="config_batteryStatsResetOnUnplugAfterSignificantCharge">true</bool>
- <!-- CPU power stats collection throttle period in milliseconds. Since power stats collection
- is a relatively expensive operation, this throttle period may need to be adjusted for low-power
- devices-->
- <integer name="config_defaultPowerStatsThrottlePeriodCpu">60000</integer>
+ <!-- Power stats collection throttle periods in milliseconds. Since power stats collection
+ is a relatively expensive operation, these throttle period may need to be adjusted for low-power
+ devices.
- <!-- Mobile Radio power stats collection throttle period in milliseconds. -->
- <integer name="config_defaultPowerStatsThrottlePeriodMobileRadio">3600000</integer>
-
- <!-- Mobile Radio power stats collection throttle period in milliseconds. -->
- <integer name="config_defaultPowerStatsThrottlePeriodWifi">3600000</integer>
+ The syntax of this config string is as follows:
+ <pre>
+ power-component-name1:throttle-period-millis1 power-component-name2:throttle-period-ms2 ...
+ </pre>
+ Use "*" for the power-component-name to represent the default for all power components
+ not mentioned by name.
+ -->
+ <string name="config_powerStatsThrottlePeriods">cpu:60000 *:300000</string>
<!-- PowerStats aggregation period in milliseconds. This is the interval at which the power
stats aggregation procedure is performed and the results stored in PowerStatsStore. -->
diff --git a/core/res/res/values/config_telephony.xml b/core/res/res/values/config_telephony.xml
index cc02a7e..e420ffe 100644
--- a/core/res/res/values/config_telephony.xml
+++ b/core/res/res/values/config_telephony.xml
@@ -365,4 +365,23 @@
<item>xcap</item>
</string-array>
<java-symbol type="array" name="config_force_cellular_transport_capabilities" />
+
+ <!-- The time duration in millis after which DemoSimulator will move to CONNECTED state from
+ NOT_CONNECTED state if the device is aligned to satellite.
+ -->
+ <integer name="config_demo_pointing_aligned_duration_millis">15000</integer>
+ <java-symbol type="integer" name="config_demo_pointing_aligned_duration_millis" />
+
+ <!-- The time duration in millis after which DemoSimulator will move to NOT_CONNECTED state from
+ CONNECTED state if the device is not aligned to satellite.
+ -->
+ <integer name="config_demo_pointing_not_aligned_duration_millis">30000</integer>
+ <java-symbol type="integer" name="config_demo_pointing_not_aligned_duration_millis" />
+
+ <!-- Boolean indicating whether Telephony should wait for device alignment with satellite
+ before sending or receiving datagrams in demo mode.
+ -->
+ <bool name="config_wait_for_device_alignment_in_demo_datagram">false</bool>
+ <java-symbol type="bool" name="config_wait_for_device_alignment_in_demo_datagram" />
+
</resources>
diff --git a/core/res/res/values/dimens.xml b/core/res/res/values/dimens.xml
index 52ce993..b885e03 100644
--- a/core/res/res/values/dimens.xml
+++ b/core/res/res/values/dimens.xml
@@ -846,6 +846,12 @@
<dimen name="conversation_face_pile_protection_width">2dp</dimen>
<!-- The width of the protection of the face pile layout when expanded-->
<dimen name="conversation_face_pile_protection_width_expanded">@dimen/conversation_face_pile_protection_width</dimen>
+ <!-- size of the compact face pile -->
+ <dimen name="conversation_compact_face_pile_size">24dp</dimen>
+ <!-- size of the face pile avatar -->
+ <dimen name="conversation_compact_face_pile_avatar_size">17dp</dimen>
+ <!-- size of the face pile protection -->
+ <dimen name="conversation_compact_face_pile_protection_width">1dp</dimen>
<!-- The padding of the expanded message container-->
<dimen name="expanded_group_conversation_message_padding">32dp</dimen>
<!-- The stroke width of the ring used to visually mark a conversation as important -->
diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml
index 59e4161..2da5e9a 100644
--- a/core/res/res/values/strings.xml
+++ b/core/res/res/values/strings.xml
@@ -794,6 +794,9 @@
<!-- The divider symbol between different parts of the notification header including spaces. not translatable [CHAR LIMIT=3] -->
<string name="notification_header_divider_symbol_with_spaces" translatable="false">" • "</string>
+ <!-- Text for inline reply button for compact conversation heads ups -->
+ <string name="notification_compact_heads_up_reply">Reply</string>
+
<!-- Text shown in place of notification contents when the notification is hidden on a secure lockscreen -->
<string name="notification_hidden_text">New notification</string>
@@ -6488,9 +6491,9 @@
<!-- Fingerprint dangling notification title -->
<string name="fingerprint_dangling_notification_title">Set up Fingerprint Unlock again</string>
<!-- Fingerprint dangling notification content for only 1 fingerprint deleted -->
- <string name="fingerprint_dangling_notification_msg_1"><xliff:g id="fingerprint">%s</xliff:g> wasn\'t working well and was deleted to improve performance</string>
+ <string name="fingerprint_dangling_notification_msg_1"><xliff:g id="fingerprint">%s</xliff:g> wasn\'t working well and was deleted</string>
<!-- Fingerprint dangling notification content for more than 1 fingerprints deleted -->
- <string name="fingerprint_dangling_notification_msg_2"><xliff:g id="fingerprint">%1$s</xliff:g> and <xliff:g id="fingerprint">%2$s</xliff:g> weren\'t working well and were deleted to improve performance</string>
+ <string name="fingerprint_dangling_notification_msg_2"><xliff:g id="fingerprint">%1$s</xliff:g> and <xliff:g id="fingerprint">%2$s</xliff:g> weren\'t working well and were deleted</string>
<!-- Fingerprint dangling notification content for only 1 fingerprint deleted and no fingerprint left-->
<string name="fingerprint_dangling_notification_msg_all_deleted_1"><xliff:g id="fingerprint">%s</xliff:g> wasn\'t working well and was deleted. Set it up again to unlock your phone with fingerprint.</string>
<!-- Fingerprint dangling notification content for more than 1 fingerprints deleted and no fingerprint left -->
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index e5768e4..5a3eaeb 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -2353,6 +2353,7 @@
<java-symbol type="layout" name="notification_template_material_base" />
<java-symbol type="layout" name="notification_template_material_heads_up_base" />
<java-symbol type="layout" name="notification_template_material_compact_heads_up_base" />
+ <java-symbol type="layout" name="notification_template_material_messaging_compact_heads_up" />
<java-symbol type="layout" name="notification_template_material_big_base" />
<java-symbol type="layout" name="notification_template_material_big_picture" />
<java-symbol type="layout" name="notification_template_material_inbox" />
@@ -3628,6 +3629,7 @@
<java-symbol type="drawable" name="lockscreen_selected" />
<java-symbol type="string" name="notification_header_divider_symbol_with_spaces" />
+ <java-symbol type="string" name="notification_compact_heads_up_reply" />
<java-symbol type="color" name="notification_primary_text_color_light" />
<java-symbol type="color" name="notification_primary_text_color_dark" />
@@ -3943,6 +3945,9 @@
<java-symbol type="string" name="config_defaultWearableSensingService" />
<java-symbol type="string" name="config_defaultOnDeviceIntelligenceService" />
<java-symbol type="string" name="config_defaultOnDeviceSandboxedInferenceService" />
+ <java-symbol type="string" name="config_onDeviceIntelligenceModelLoadedBroadcastKey" />
+ <java-symbol type="string" name="config_onDeviceIntelligenceModelUnloadedBroadcastKey" />
+ <java-symbol type="string" name="config_defaultOnDeviceIntelligenceDeviceConfigNamespace" />
<java-symbol type="string" name="config_retailDemoPackage" />
<java-symbol type="string" name="config_retailDemoPackageSignature" />
@@ -4520,6 +4525,7 @@
<java-symbol type="id" name="expand_button_container" />
<java-symbol type="id" name="expand_button_a11y_container" />
<java-symbol type="id" name="expand_button_touch_container" />
+ <java-symbol type="id" name="reply_action_container" />
<java-symbol type="id" name="messaging_group_content_container" />
<java-symbol type="id" name="expand_button_and_content_container" />
<java-symbol type="id" name="conversation_header" />
@@ -4537,6 +4543,9 @@
<java-symbol type="dimen" name="conversation_avatar_size_group_expanded" />
<java-symbol type="dimen" name="conversation_face_pile_avatar_size" />
<java-symbol type="dimen" name="conversation_face_pile_avatar_size_group_expanded" />
+ <java-symbol type="dimen" name="conversation_compact_face_pile_size" />
+ <java-symbol type="dimen" name="conversation_compact_face_pile_avatar_size" />
+ <java-symbol type="dimen" name="conversation_compact_face_pile_protection_width" />
<java-symbol type="dimen" name="conversation_face_pile_protection_width" />
<java-symbol type="dimen" name="conversation_face_pile_protection_width_expanded" />
<java-symbol type="dimen" name="conversation_badge_protrusion_group_expanded" />
@@ -5219,9 +5228,6 @@
<java-symbol type="bool" name="config_hotspotNetworksEnabledForService"/>
<java-symbol type="bool" name="config_knownNetworksEnabledForService"/>
- <!-- Whether to show weather on the lockscreen by default. -->
- <java-symbol type="bool" name="config_lockscreenWeatherEnabledByDefault" />
-
<!-- For keyboard notification -->
<java-symbol type="string" name="keyboard_layout_notification_selected_title"/>
<java-symbol type="string" name="keyboard_layout_notification_one_selected_message"/>
@@ -5238,9 +5244,7 @@
<java-symbol type="bool" name="config_batteryStatsResetOnUnplugHighBatteryLevel" />
<java-symbol type="bool" name="config_batteryStatsResetOnUnplugAfterSignificantCharge" />
- <java-symbol type="integer" name="config_defaultPowerStatsThrottlePeriodCpu" />
- <java-symbol type="integer" name="config_defaultPowerStatsThrottlePeriodMobileRadio" />
- <java-symbol type="integer" name="config_defaultPowerStatsThrottlePeriodWifi" />
+ <java-symbol type="string" name="config_powerStatsThrottlePeriods" />
<java-symbol type="integer" name="config_powerStatsAggregationPeriod" />
<java-symbol type="integer" name="config_aggregatedPowerStatsSpanDuration" />
@@ -5290,6 +5294,71 @@
<java-symbol name="materialColorTertiary" type="attr"/>
<java-symbol name="materialColorError" type="attr"/>
+ <java-symbol name="customColorWidgetBackground" type="attr"/>
+ <java-symbol name="customColorClockHour" type="attr"/>
+ <java-symbol name="customColorClockMinute" type="attr"/>
+ <java-symbol name="customColorClockSecond" type="attr"/>
+ <java-symbol name="customColorThemeApp" type="attr"/>
+ <java-symbol name="customColorOnThemeApp" type="attr"/>
+ <java-symbol name="customColorThemeAppRing" type="attr"/>
+ <java-symbol name="customColorOnThemeAppRing" type="attr"/>
+ <java-symbol name="customColorBrandA" type="attr"/>
+ <java-symbol name="customColorBrandB" type="attr"/>
+ <java-symbol name="customColorBrandC" type="attr"/>
+ <java-symbol name="customColorBrandD" type="attr"/>
+ <java-symbol name="customColorUnderSurface" type="attr"/>
+ <java-symbol name="customColorShadeActive" type="attr"/>
+ <java-symbol name="customColorOnShadeActive" type="attr"/>
+ <java-symbol name="customColorOnShadeActiveVariant" type="attr"/>
+ <java-symbol name="customColorShadeInactive" type="attr"/>
+ <java-symbol name="customColorOnShadeInactive" type="attr"/>
+ <java-symbol name="customColorOnShadeInactiveVariant" type="attr"/>
+ <java-symbol name="customColorShadeDisabled" type="attr"/>
+ <java-symbol name="customColorOverviewBackground" type="attr"/>
+
+ <java-symbol name="system_widget_background_light" type="color"/>
+ <java-symbol name="system_clock_hour_light" type="color"/>
+ <java-symbol name="system_clock_minute_light" type="color"/>
+ <java-symbol name="system_clock_second_light" type="color"/>
+ <java-symbol name="system_theme_app_light" type="color"/>
+ <java-symbol name="system_on_theme_app_light" type="color"/>
+ <java-symbol name="system_theme_app_ring_light" type="color"/>
+ <java-symbol name="system_on_theme_app_ring_light" type="color"/>
+ <java-symbol name="system_brand_a_light" type="color"/>
+ <java-symbol name="system_brand_b_light" type="color"/>
+ <java-symbol name="system_brand_c_light" type="color"/>
+ <java-symbol name="system_brand_d_light" type="color"/>
+ <java-symbol name="system_under_surface_light" type="color"/>
+ <java-symbol name="system_shade_active_light" type="color"/>
+ <java-symbol name="system_on_shade_active_light" type="color"/>
+ <java-symbol name="system_on_shade_active_variant_light" type="color"/>
+ <java-symbol name="system_shade_inactive_light" type="color"/>
+ <java-symbol name="system_on_shade_inactive_light" type="color"/>
+ <java-symbol name="system_on_shade_inactive_variant_light" type="color"/>
+ <java-symbol name="system_shade_disabled_light" type="color"/>
+ <java-symbol name="system_overview_background_light" type="color"/>
+ <java-symbol name="system_widget_background_dark" type="color"/>
+ <java-symbol name="system_clock_hour_dark" type="color"/>
+ <java-symbol name="system_clock_minute_dark" type="color"/>
+ <java-symbol name="system_clock_second_dark" type="color"/>
+ <java-symbol name="system_theme_app_dark" type="color"/>
+ <java-symbol name="system_on_theme_app_dark" type="color"/>
+ <java-symbol name="system_theme_app_ring_dark" type="color"/>
+ <java-symbol name="system_on_theme_app_ring_dark" type="color"/>
+ <java-symbol name="system_brand_a_dark" type="color"/>
+ <java-symbol name="system_brand_b_dark" type="color"/>
+ <java-symbol name="system_brand_c_dark" type="color"/>
+ <java-symbol name="system_brand_d_dark" type="color"/>
+ <java-symbol name="system_under_surface_dark" type="color"/>
+ <java-symbol name="system_shade_active_dark" type="color"/>
+ <java-symbol name="system_on_shade_active_dark" type="color"/>
+ <java-symbol name="system_on_shade_active_variant_dark" type="color"/>
+ <java-symbol name="system_shade_inactive_dark" type="color"/>
+ <java-symbol name="system_on_shade_inactive_dark" type="color"/>
+ <java-symbol name="system_on_shade_inactive_variant_dark" type="color"/>
+ <java-symbol name="system_shade_disabled_dark" type="color"/>
+ <java-symbol name="system_overview_background_dark" type="color"/>
+
<java-symbol type="attr" name="actionModeUndoDrawable" />
<java-symbol type="attr" name="actionModeRedoDrawable" />
diff --git a/core/res/res/values/themes_device_defaults.xml b/core/res/res/values/themes_device_defaults.xml
index ee19144..24d4938 100644
--- a/core/res/res/values/themes_device_defaults.xml
+++ b/core/res/res/values/themes_device_defaults.xml
@@ -284,6 +284,28 @@
<item name="materialColorSecondary">@color/system_secondary_dark</item>
<item name="materialColorTertiary">@color/system_tertiary_dark</item>
<item name="materialColorError">@color/system_error_dark</item>
+
+ <item name="customColorWidgetBackground">@color/system_widget_background_dark</item>
+ <item name="customColorClockHour">@color/system_clock_hour_dark</item>
+ <item name="customColorClockMinute">@color/system_clock_minute_dark</item>
+ <item name="customColorClockSecond">@color/system_clock_second_dark</item>
+ <item name="customColorThemeApp">@color/system_theme_app_dark</item>
+ <item name="customColorOnThemeApp">@color/system_on_theme_app_dark</item>
+ <item name="customColorThemeAppRing">@color/system_theme_app_ring_dark</item>
+ <item name="customColorOnThemeAppRing">@color/system_on_theme_app_ring_dark</item>
+ <item name="customColorBrandA">@color/system_brand_a_dark</item>
+ <item name="customColorBrandB">@color/system_brand_b_dark</item>
+ <item name="customColorBrandC">@color/system_brand_c_dark</item>
+ <item name="customColorBrandD">@color/system_brand_d_dark</item>
+ <item name="customColorUnderSurface">@color/system_under_surface_dark</item>
+ <item name="customColorShadeActive">@color/system_shade_active_dark</item>
+ <item name="customColorOnShadeActive">@color/system_on_shade_active_dark</item>
+ <item name="customColorOnShadeActiveVariant">@color/system_on_shade_active_variant_dark</item>
+ <item name="customColorShadeInactive">@color/system_shade_inactive_dark</item>
+ <item name="customColorOnShadeInactive">@color/system_on_shade_inactive_dark</item>
+ <item name="customColorOnShadeInactiveVariant">@color/system_on_shade_inactive_variant_dark</item>
+ <item name="customColorShadeDisabled">@color/system_shade_disabled_dark</item>
+ <item name="customColorOverviewBackground">@color/system_overview_background_dark</item>
</style>
<style name="Theme.DeviceDefault" parent="Theme.DeviceDefaultBase" />
@@ -380,6 +402,28 @@
<item name="materialColorSecondary">@color/system_secondary_dark</item>
<item name="materialColorTertiary">@color/system_tertiary_dark</item>
<item name="materialColorError">@color/system_error_dark</item>
+
+ <item name="customColorWidgetBackground">@color/system_widget_background_dark</item>
+ <item name="customColorClockHour">@color/system_clock_hour_dark</item>
+ <item name="customColorClockMinute">@color/system_clock_minute_dark</item>
+ <item name="customColorClockSecond">@color/system_clock_second_dark</item>
+ <item name="customColorThemeApp">@color/system_theme_app_dark</item>
+ <item name="customColorOnThemeApp">@color/system_on_theme_app_dark</item>
+ <item name="customColorThemeAppRing">@color/system_theme_app_ring_dark</item>
+ <item name="customColorOnThemeAppRing">@color/system_on_theme_app_ring_dark</item>
+ <item name="customColorBrandA">@color/system_brand_a_dark</item>
+ <item name="customColorBrandB">@color/system_brand_b_dark</item>
+ <item name="customColorBrandC">@color/system_brand_c_dark</item>
+ <item name="customColorBrandD">@color/system_brand_d_dark</item>
+ <item name="customColorUnderSurface">@color/system_under_surface_dark</item>
+ <item name="customColorShadeActive">@color/system_shade_active_dark</item>
+ <item name="customColorOnShadeActive">@color/system_on_shade_active_dark</item>
+ <item name="customColorOnShadeActiveVariant">@color/system_on_shade_active_variant_dark</item>
+ <item name="customColorShadeInactive">@color/system_shade_inactive_dark</item>
+ <item name="customColorOnShadeInactive">@color/system_on_shade_inactive_dark</item>
+ <item name="customColorOnShadeInactiveVariant">@color/system_on_shade_inactive_variant_dark</item>
+ <item name="customColorShadeDisabled">@color/system_shade_disabled_dark</item>
+ <item name="customColorOverviewBackground">@color/system_overview_background_dark</item>
</style>
<!-- Variant of {@link #Theme_DeviceDefault} with no action bar and no status bar. This theme
@@ -475,6 +519,28 @@
<item name="materialColorSecondary">@color/system_secondary_dark</item>
<item name="materialColorTertiary">@color/system_tertiary_dark</item>
<item name="materialColorError">@color/system_error_dark</item>
+
+ <item name="customColorWidgetBackground">@color/system_widget_background_dark</item>
+ <item name="customColorClockHour">@color/system_clock_hour_dark</item>
+ <item name="customColorClockMinute">@color/system_clock_minute_dark</item>
+ <item name="customColorClockSecond">@color/system_clock_second_dark</item>
+ <item name="customColorThemeApp">@color/system_theme_app_dark</item>
+ <item name="customColorOnThemeApp">@color/system_on_theme_app_dark</item>
+ <item name="customColorThemeAppRing">@color/system_theme_app_ring_dark</item>
+ <item name="customColorOnThemeAppRing">@color/system_on_theme_app_ring_dark</item>
+ <item name="customColorBrandA">@color/system_brand_a_dark</item>
+ <item name="customColorBrandB">@color/system_brand_b_dark</item>
+ <item name="customColorBrandC">@color/system_brand_c_dark</item>
+ <item name="customColorBrandD">@color/system_brand_d_dark</item>
+ <item name="customColorUnderSurface">@color/system_under_surface_dark</item>
+ <item name="customColorShadeActive">@color/system_shade_active_dark</item>
+ <item name="customColorOnShadeActive">@color/system_on_shade_active_dark</item>
+ <item name="customColorOnShadeActiveVariant">@color/system_on_shade_active_variant_dark</item>
+ <item name="customColorShadeInactive">@color/system_shade_inactive_dark</item>
+ <item name="customColorOnShadeInactive">@color/system_on_shade_inactive_dark</item>
+ <item name="customColorOnShadeInactiveVariant">@color/system_on_shade_inactive_variant_dark</item>
+ <item name="customColorShadeDisabled">@color/system_shade_disabled_dark</item>
+ <item name="customColorOverviewBackground">@color/system_overview_background_dark</item>
</style>
<!-- Variant of {@link #Theme_DeviceDefault} with no action bar and no status bar and
@@ -572,6 +638,28 @@
<item name="materialColorSecondary">@color/system_secondary_dark</item>
<item name="materialColorTertiary">@color/system_tertiary_dark</item>
<item name="materialColorError">@color/system_error_dark</item>
+
+ <item name="customColorWidgetBackground">@color/system_widget_background_dark</item>
+ <item name="customColorClockHour">@color/system_clock_hour_dark</item>
+ <item name="customColorClockMinute">@color/system_clock_minute_dark</item>
+ <item name="customColorClockSecond">@color/system_clock_second_dark</item>
+ <item name="customColorThemeApp">@color/system_theme_app_dark</item>
+ <item name="customColorOnThemeApp">@color/system_on_theme_app_dark</item>
+ <item name="customColorThemeAppRing">@color/system_theme_app_ring_dark</item>
+ <item name="customColorOnThemeAppRing">@color/system_on_theme_app_ring_dark</item>
+ <item name="customColorBrandA">@color/system_brand_a_dark</item>
+ <item name="customColorBrandB">@color/system_brand_b_dark</item>
+ <item name="customColorBrandC">@color/system_brand_c_dark</item>
+ <item name="customColorBrandD">@color/system_brand_d_dark</item>
+ <item name="customColorUnderSurface">@color/system_under_surface_dark</item>
+ <item name="customColorShadeActive">@color/system_shade_active_dark</item>
+ <item name="customColorOnShadeActive">@color/system_on_shade_active_dark</item>
+ <item name="customColorOnShadeActiveVariant">@color/system_on_shade_active_variant_dark</item>
+ <item name="customColorShadeInactive">@color/system_shade_inactive_dark</item>
+ <item name="customColorOnShadeInactive">@color/system_on_shade_inactive_dark</item>
+ <item name="customColorOnShadeInactiveVariant">@color/system_on_shade_inactive_variant_dark</item>
+ <item name="customColorShadeDisabled">@color/system_shade_disabled_dark</item>
+ <item name="customColorOverviewBackground">@color/system_overview_background_dark</item>
</style>
<!-- Variant of {@link #Theme_DeviceDefault} that has no title bar and translucent
@@ -668,6 +756,28 @@
<item name="materialColorSecondary">@color/system_secondary_dark</item>
<item name="materialColorTertiary">@color/system_tertiary_dark</item>
<item name="materialColorError">@color/system_error_dark</item>
+
+ <item name="customColorWidgetBackground">@color/system_widget_background_dark</item>
+ <item name="customColorClockHour">@color/system_clock_hour_dark</item>
+ <item name="customColorClockMinute">@color/system_clock_minute_dark</item>
+ <item name="customColorClockSecond">@color/system_clock_second_dark</item>
+ <item name="customColorThemeApp">@color/system_theme_app_dark</item>
+ <item name="customColorOnThemeApp">@color/system_on_theme_app_dark</item>
+ <item name="customColorThemeAppRing">@color/system_theme_app_ring_dark</item>
+ <item name="customColorOnThemeAppRing">@color/system_on_theme_app_ring_dark</item>
+ <item name="customColorBrandA">@color/system_brand_a_dark</item>
+ <item name="customColorBrandB">@color/system_brand_b_dark</item>
+ <item name="customColorBrandC">@color/system_brand_c_dark</item>
+ <item name="customColorBrandD">@color/system_brand_d_dark</item>
+ <item name="customColorUnderSurface">@color/system_under_surface_dark</item>
+ <item name="customColorShadeActive">@color/system_shade_active_dark</item>
+ <item name="customColorOnShadeActive">@color/system_on_shade_active_dark</item>
+ <item name="customColorOnShadeActiveVariant">@color/system_on_shade_active_variant_dark</item>
+ <item name="customColorShadeInactive">@color/system_shade_inactive_dark</item>
+ <item name="customColorOnShadeInactive">@color/system_on_shade_inactive_dark</item>
+ <item name="customColorOnShadeInactiveVariant">@color/system_on_shade_inactive_variant_dark</item>
+ <item name="customColorShadeDisabled">@color/system_shade_disabled_dark</item>
+ <item name="customColorOverviewBackground">@color/system_overview_background_dark</item>
</style>
<!-- DeviceDefault theme for dialog windows and activities. This changes the window to be
@@ -772,6 +882,28 @@
<item name="materialColorSecondary">@color/system_secondary_dark</item>
<item name="materialColorTertiary">@color/system_tertiary_dark</item>
<item name="materialColorError">@color/system_error_dark</item>
+
+ <item name="customColorWidgetBackground">@color/system_widget_background_dark</item>
+ <item name="customColorClockHour">@color/system_clock_hour_dark</item>
+ <item name="customColorClockMinute">@color/system_clock_minute_dark</item>
+ <item name="customColorClockSecond">@color/system_clock_second_dark</item>
+ <item name="customColorThemeApp">@color/system_theme_app_dark</item>
+ <item name="customColorOnThemeApp">@color/system_on_theme_app_dark</item>
+ <item name="customColorThemeAppRing">@color/system_theme_app_ring_dark</item>
+ <item name="customColorOnThemeAppRing">@color/system_on_theme_app_ring_dark</item>
+ <item name="customColorBrandA">@color/system_brand_a_dark</item>
+ <item name="customColorBrandB">@color/system_brand_b_dark</item>
+ <item name="customColorBrandC">@color/system_brand_c_dark</item>
+ <item name="customColorBrandD">@color/system_brand_d_dark</item>
+ <item name="customColorUnderSurface">@color/system_under_surface_dark</item>
+ <item name="customColorShadeActive">@color/system_shade_active_dark</item>
+ <item name="customColorOnShadeActive">@color/system_on_shade_active_dark</item>
+ <item name="customColorOnShadeActiveVariant">@color/system_on_shade_active_variant_dark</item>
+ <item name="customColorShadeInactive">@color/system_shade_inactive_dark</item>
+ <item name="customColorOnShadeInactive">@color/system_on_shade_inactive_dark</item>
+ <item name="customColorOnShadeInactiveVariant">@color/system_on_shade_inactive_variant_dark</item>
+ <item name="customColorShadeDisabled">@color/system_shade_disabled_dark</item>
+ <item name="customColorOverviewBackground">@color/system_overview_background_dark</item>
</style>
<!-- Variant of {@link #Theme_DeviceDefault_Dialog} that has a nice minimum width for a
@@ -867,6 +999,28 @@
<item name="materialColorSecondary">@color/system_secondary_dark</item>
<item name="materialColorTertiary">@color/system_tertiary_dark</item>
<item name="materialColorError">@color/system_error_dark</item>
+
+ <item name="customColorWidgetBackground">@color/system_widget_background_dark</item>
+ <item name="customColorClockHour">@color/system_clock_hour_dark</item>
+ <item name="customColorClockMinute">@color/system_clock_minute_dark</item>
+ <item name="customColorClockSecond">@color/system_clock_second_dark</item>
+ <item name="customColorThemeApp">@color/system_theme_app_dark</item>
+ <item name="customColorOnThemeApp">@color/system_on_theme_app_dark</item>
+ <item name="customColorThemeAppRing">@color/system_theme_app_ring_dark</item>
+ <item name="customColorOnThemeAppRing">@color/system_on_theme_app_ring_dark</item>
+ <item name="customColorBrandA">@color/system_brand_a_dark</item>
+ <item name="customColorBrandB">@color/system_brand_b_dark</item>
+ <item name="customColorBrandC">@color/system_brand_c_dark</item>
+ <item name="customColorBrandD">@color/system_brand_d_dark</item>
+ <item name="customColorUnderSurface">@color/system_under_surface_dark</item>
+ <item name="customColorShadeActive">@color/system_shade_active_dark</item>
+ <item name="customColorOnShadeActive">@color/system_on_shade_active_dark</item>
+ <item name="customColorOnShadeActiveVariant">@color/system_on_shade_active_variant_dark</item>
+ <item name="customColorShadeInactive">@color/system_shade_inactive_dark</item>
+ <item name="customColorOnShadeInactive">@color/system_on_shade_inactive_dark</item>
+ <item name="customColorOnShadeInactiveVariant">@color/system_on_shade_inactive_variant_dark</item>
+ <item name="customColorShadeDisabled">@color/system_shade_disabled_dark</item>
+ <item name="customColorOverviewBackground">@color/system_overview_background_dark</item>
</style>
<!-- Variant of {@link #Theme_DeviceDefault_Dialog} without an action bar -->
@@ -961,6 +1115,28 @@
<item name="materialColorSecondary">@color/system_secondary_dark</item>
<item name="materialColorTertiary">@color/system_tertiary_dark</item>
<item name="materialColorError">@color/system_error_dark</item>
+
+ <item name="customColorWidgetBackground">@color/system_widget_background_dark</item>
+ <item name="customColorClockHour">@color/system_clock_hour_dark</item>
+ <item name="customColorClockMinute">@color/system_clock_minute_dark</item>
+ <item name="customColorClockSecond">@color/system_clock_second_dark</item>
+ <item name="customColorThemeApp">@color/system_theme_app_dark</item>
+ <item name="customColorOnThemeApp">@color/system_on_theme_app_dark</item>
+ <item name="customColorThemeAppRing">@color/system_theme_app_ring_dark</item>
+ <item name="customColorOnThemeAppRing">@color/system_on_theme_app_ring_dark</item>
+ <item name="customColorBrandA">@color/system_brand_a_dark</item>
+ <item name="customColorBrandB">@color/system_brand_b_dark</item>
+ <item name="customColorBrandC">@color/system_brand_c_dark</item>
+ <item name="customColorBrandD">@color/system_brand_d_dark</item>
+ <item name="customColorUnderSurface">@color/system_under_surface_dark</item>
+ <item name="customColorShadeActive">@color/system_shade_active_dark</item>
+ <item name="customColorOnShadeActive">@color/system_on_shade_active_dark</item>
+ <item name="customColorOnShadeActiveVariant">@color/system_on_shade_active_variant_dark</item>
+ <item name="customColorShadeInactive">@color/system_shade_inactive_dark</item>
+ <item name="customColorOnShadeInactive">@color/system_on_shade_inactive_dark</item>
+ <item name="customColorOnShadeInactiveVariant">@color/system_on_shade_inactive_variant_dark</item>
+ <item name="customColorShadeDisabled">@color/system_shade_disabled_dark</item>
+ <item name="customColorOverviewBackground">@color/system_overview_background_dark</item>
</style>
<!-- Variant of {@link #Theme_DeviceDefault_Dialog_NoActionBar} that has a nice minimum width
@@ -1056,6 +1232,28 @@
<item name="materialColorSecondary">@color/system_secondary_dark</item>
<item name="materialColorTertiary">@color/system_tertiary_dark</item>
<item name="materialColorError">@color/system_error_dark</item>
+
+ <item name="customColorWidgetBackground">@color/system_widget_background_dark</item>
+ <item name="customColorClockHour">@color/system_clock_hour_dark</item>
+ <item name="customColorClockMinute">@color/system_clock_minute_dark</item>
+ <item name="customColorClockSecond">@color/system_clock_second_dark</item>
+ <item name="customColorThemeApp">@color/system_theme_app_dark</item>
+ <item name="customColorOnThemeApp">@color/system_on_theme_app_dark</item>
+ <item name="customColorThemeAppRing">@color/system_theme_app_ring_dark</item>
+ <item name="customColorOnThemeAppRing">@color/system_on_theme_app_ring_dark</item>
+ <item name="customColorBrandA">@color/system_brand_a_dark</item>
+ <item name="customColorBrandB">@color/system_brand_b_dark</item>
+ <item name="customColorBrandC">@color/system_brand_c_dark</item>
+ <item name="customColorBrandD">@color/system_brand_d_dark</item>
+ <item name="customColorUnderSurface">@color/system_under_surface_dark</item>
+ <item name="customColorShadeActive">@color/system_shade_active_dark</item>
+ <item name="customColorOnShadeActive">@color/system_on_shade_active_dark</item>
+ <item name="customColorOnShadeActiveVariant">@color/system_on_shade_active_variant_dark</item>
+ <item name="customColorShadeInactive">@color/system_shade_inactive_dark</item>
+ <item name="customColorOnShadeInactive">@color/system_on_shade_inactive_dark</item>
+ <item name="customColorOnShadeInactiveVariant">@color/system_on_shade_inactive_variant_dark</item>
+ <item name="customColorShadeDisabled">@color/system_shade_disabled_dark</item>
+ <item name="customColorOverviewBackground">@color/system_overview_background_dark</item>
</style>
<!-- Variant of Theme.DeviceDefault.Dialog that has a fixed size. -->
@@ -1167,6 +1365,28 @@
<item name="materialColorSecondary">@color/system_secondary_dark</item>
<item name="materialColorTertiary">@color/system_tertiary_dark</item>
<item name="materialColorError">@color/system_error_dark</item>
+
+ <item name="customColorWidgetBackground">@color/system_widget_background_dark</item>
+ <item name="customColorClockHour">@color/system_clock_hour_dark</item>
+ <item name="customColorClockMinute">@color/system_clock_minute_dark</item>
+ <item name="customColorClockSecond">@color/system_clock_second_dark</item>
+ <item name="customColorThemeApp">@color/system_theme_app_dark</item>
+ <item name="customColorOnThemeApp">@color/system_on_theme_app_dark</item>
+ <item name="customColorThemeAppRing">@color/system_theme_app_ring_dark</item>
+ <item name="customColorOnThemeAppRing">@color/system_on_theme_app_ring_dark</item>
+ <item name="customColorBrandA">@color/system_brand_a_dark</item>
+ <item name="customColorBrandB">@color/system_brand_b_dark</item>
+ <item name="customColorBrandC">@color/system_brand_c_dark</item>
+ <item name="customColorBrandD">@color/system_brand_d_dark</item>
+ <item name="customColorUnderSurface">@color/system_under_surface_dark</item>
+ <item name="customColorShadeActive">@color/system_shade_active_dark</item>
+ <item name="customColorOnShadeActive">@color/system_on_shade_active_dark</item>
+ <item name="customColorOnShadeActiveVariant">@color/system_on_shade_active_variant_dark</item>
+ <item name="customColorShadeInactive">@color/system_shade_inactive_dark</item>
+ <item name="customColorOnShadeInactive">@color/system_on_shade_inactive_dark</item>
+ <item name="customColorOnShadeInactiveVariant">@color/system_on_shade_inactive_variant_dark</item>
+ <item name="customColorShadeDisabled">@color/system_shade_disabled_dark</item>
+ <item name="customColorOverviewBackground">@color/system_overview_background_dark</item>
</style>
<!-- DeviceDefault theme for a window without an action bar that will be displayed either
@@ -1263,6 +1483,28 @@
<item name="materialColorSecondary">@color/system_secondary_dark</item>
<item name="materialColorTertiary">@color/system_tertiary_dark</item>
<item name="materialColorError">@color/system_error_dark</item>
+
+ <item name="customColorWidgetBackground">@color/system_widget_background_dark</item>
+ <item name="customColorClockHour">@color/system_clock_hour_dark</item>
+ <item name="customColorClockMinute">@color/system_clock_minute_dark</item>
+ <item name="customColorClockSecond">@color/system_clock_second_dark</item>
+ <item name="customColorThemeApp">@color/system_theme_app_dark</item>
+ <item name="customColorOnThemeApp">@color/system_on_theme_app_dark</item>
+ <item name="customColorThemeAppRing">@color/system_theme_app_ring_dark</item>
+ <item name="customColorOnThemeAppRing">@color/system_on_theme_app_ring_dark</item>
+ <item name="customColorBrandA">@color/system_brand_a_dark</item>
+ <item name="customColorBrandB">@color/system_brand_b_dark</item>
+ <item name="customColorBrandC">@color/system_brand_c_dark</item>
+ <item name="customColorBrandD">@color/system_brand_d_dark</item>
+ <item name="customColorUnderSurface">@color/system_under_surface_dark</item>
+ <item name="customColorShadeActive">@color/system_shade_active_dark</item>
+ <item name="customColorOnShadeActive">@color/system_on_shade_active_dark</item>
+ <item name="customColorOnShadeActiveVariant">@color/system_on_shade_active_variant_dark</item>
+ <item name="customColorShadeInactive">@color/system_shade_inactive_dark</item>
+ <item name="customColorOnShadeInactive">@color/system_on_shade_inactive_dark</item>
+ <item name="customColorOnShadeInactiveVariant">@color/system_on_shade_inactive_variant_dark</item>
+ <item name="customColorShadeDisabled">@color/system_shade_disabled_dark</item>
+ <item name="customColorOverviewBackground">@color/system_overview_background_dark</item>
</style>
<!-- DeviceDefault theme for a presentation window on a secondary display. -->
@@ -1357,6 +1599,28 @@
<item name="materialColorSecondary">@color/system_secondary_dark</item>
<item name="materialColorTertiary">@color/system_tertiary_dark</item>
<item name="materialColorError">@color/system_error_dark</item>
+
+ <item name="customColorWidgetBackground">@color/system_widget_background_dark</item>
+ <item name="customColorClockHour">@color/system_clock_hour_dark</item>
+ <item name="customColorClockMinute">@color/system_clock_minute_dark</item>
+ <item name="customColorClockSecond">@color/system_clock_second_dark</item>
+ <item name="customColorThemeApp">@color/system_theme_app_dark</item>
+ <item name="customColorOnThemeApp">@color/system_on_theme_app_dark</item>
+ <item name="customColorThemeAppRing">@color/system_theme_app_ring_dark</item>
+ <item name="customColorOnThemeAppRing">@color/system_on_theme_app_ring_dark</item>
+ <item name="customColorBrandA">@color/system_brand_a_dark</item>
+ <item name="customColorBrandB">@color/system_brand_b_dark</item>
+ <item name="customColorBrandC">@color/system_brand_c_dark</item>
+ <item name="customColorBrandD">@color/system_brand_d_dark</item>
+ <item name="customColorUnderSurface">@color/system_under_surface_dark</item>
+ <item name="customColorShadeActive">@color/system_shade_active_dark</item>
+ <item name="customColorOnShadeActive">@color/system_on_shade_active_dark</item>
+ <item name="customColorOnShadeActiveVariant">@color/system_on_shade_active_variant_dark</item>
+ <item name="customColorShadeInactive">@color/system_shade_inactive_dark</item>
+ <item name="customColorOnShadeInactive">@color/system_on_shade_inactive_dark</item>
+ <item name="customColorOnShadeInactiveVariant">@color/system_on_shade_inactive_variant_dark</item>
+ <item name="customColorShadeDisabled">@color/system_shade_disabled_dark</item>
+ <item name="customColorOverviewBackground">@color/system_overview_background_dark</item>
</style>
<!-- DeviceDefault theme for panel windows. This removes all extraneous window
@@ -1453,6 +1717,28 @@
<item name="materialColorSecondary">@color/system_secondary_dark</item>
<item name="materialColorTertiary">@color/system_tertiary_dark</item>
<item name="materialColorError">@color/system_error_dark</item>
+
+ <item name="customColorWidgetBackground">@color/system_widget_background_dark</item>
+ <item name="customColorClockHour">@color/system_clock_hour_dark</item>
+ <item name="customColorClockMinute">@color/system_clock_minute_dark</item>
+ <item name="customColorClockSecond">@color/system_clock_second_dark</item>
+ <item name="customColorThemeApp">@color/system_theme_app_dark</item>
+ <item name="customColorOnThemeApp">@color/system_on_theme_app_dark</item>
+ <item name="customColorThemeAppRing">@color/system_theme_app_ring_dark</item>
+ <item name="customColorOnThemeAppRing">@color/system_on_theme_app_ring_dark</item>
+ <item name="customColorBrandA">@color/system_brand_a_dark</item>
+ <item name="customColorBrandB">@color/system_brand_b_dark</item>
+ <item name="customColorBrandC">@color/system_brand_c_dark</item>
+ <item name="customColorBrandD">@color/system_brand_d_dark</item>
+ <item name="customColorUnderSurface">@color/system_under_surface_dark</item>
+ <item name="customColorShadeActive">@color/system_shade_active_dark</item>
+ <item name="customColorOnShadeActive">@color/system_on_shade_active_dark</item>
+ <item name="customColorOnShadeActiveVariant">@color/system_on_shade_active_variant_dark</item>
+ <item name="customColorShadeInactive">@color/system_shade_inactive_dark</item>
+ <item name="customColorOnShadeInactive">@color/system_on_shade_inactive_dark</item>
+ <item name="customColorOnShadeInactiveVariant">@color/system_on_shade_inactive_variant_dark</item>
+ <item name="customColorShadeDisabled">@color/system_shade_disabled_dark</item>
+ <item name="customColorOverviewBackground">@color/system_overview_background_dark</item>
</style>
<!-- DeviceDefault theme for windows that want to have the user's selected wallpaper appear
@@ -1548,6 +1834,28 @@
<item name="materialColorSecondary">@color/system_secondary_dark</item>
<item name="materialColorTertiary">@color/system_tertiary_dark</item>
<item name="materialColorError">@color/system_error_dark</item>
+
+ <item name="customColorWidgetBackground">@color/system_widget_background_dark</item>
+ <item name="customColorClockHour">@color/system_clock_hour_dark</item>
+ <item name="customColorClockMinute">@color/system_clock_minute_dark</item>
+ <item name="customColorClockSecond">@color/system_clock_second_dark</item>
+ <item name="customColorThemeApp">@color/system_theme_app_dark</item>
+ <item name="customColorOnThemeApp">@color/system_on_theme_app_dark</item>
+ <item name="customColorThemeAppRing">@color/system_theme_app_ring_dark</item>
+ <item name="customColorOnThemeAppRing">@color/system_on_theme_app_ring_dark</item>
+ <item name="customColorBrandA">@color/system_brand_a_dark</item>
+ <item name="customColorBrandB">@color/system_brand_b_dark</item>
+ <item name="customColorBrandC">@color/system_brand_c_dark</item>
+ <item name="customColorBrandD">@color/system_brand_d_dark</item>
+ <item name="customColorUnderSurface">@color/system_under_surface_dark</item>
+ <item name="customColorShadeActive">@color/system_shade_active_dark</item>
+ <item name="customColorOnShadeActive">@color/system_on_shade_active_dark</item>
+ <item name="customColorOnShadeActiveVariant">@color/system_on_shade_active_variant_dark</item>
+ <item name="customColorShadeInactive">@color/system_shade_inactive_dark</item>
+ <item name="customColorOnShadeInactive">@color/system_on_shade_inactive_dark</item>
+ <item name="customColorOnShadeInactiveVariant">@color/system_on_shade_inactive_variant_dark</item>
+ <item name="customColorShadeDisabled">@color/system_shade_disabled_dark</item>
+ <item name="customColorOverviewBackground">@color/system_overview_background_dark</item>
</style>
<!-- DeviceDefault theme for windows that want to have the user's selected wallpaper appear
@@ -1643,6 +1951,28 @@
<item name="materialColorSecondary">@color/system_secondary_dark</item>
<item name="materialColorTertiary">@color/system_tertiary_dark</item>
<item name="materialColorError">@color/system_error_dark</item>
+
+ <item name="customColorWidgetBackground">@color/system_widget_background_dark</item>
+ <item name="customColorClockHour">@color/system_clock_hour_dark</item>
+ <item name="customColorClockMinute">@color/system_clock_minute_dark</item>
+ <item name="customColorClockSecond">@color/system_clock_second_dark</item>
+ <item name="customColorThemeApp">@color/system_theme_app_dark</item>
+ <item name="customColorOnThemeApp">@color/system_on_theme_app_dark</item>
+ <item name="customColorThemeAppRing">@color/system_theme_app_ring_dark</item>
+ <item name="customColorOnThemeAppRing">@color/system_on_theme_app_ring_dark</item>
+ <item name="customColorBrandA">@color/system_brand_a_dark</item>
+ <item name="customColorBrandB">@color/system_brand_b_dark</item>
+ <item name="customColorBrandC">@color/system_brand_c_dark</item>
+ <item name="customColorBrandD">@color/system_brand_d_dark</item>
+ <item name="customColorUnderSurface">@color/system_under_surface_dark</item>
+ <item name="customColorShadeActive">@color/system_shade_active_dark</item>
+ <item name="customColorOnShadeActive">@color/system_on_shade_active_dark</item>
+ <item name="customColorOnShadeActiveVariant">@color/system_on_shade_active_variant_dark</item>
+ <item name="customColorShadeInactive">@color/system_shade_inactive_dark</item>
+ <item name="customColorOnShadeInactive">@color/system_on_shade_inactive_dark</item>
+ <item name="customColorOnShadeInactiveVariant">@color/system_on_shade_inactive_variant_dark</item>
+ <item name="customColorShadeDisabled">@color/system_shade_disabled_dark</item>
+ <item name="customColorOverviewBackground">@color/system_overview_background_dark</item>
</style>
<!-- DeviceDefault style for input methods, which is used by the
@@ -1738,6 +2068,28 @@
<item name="materialColorSecondary">@color/system_secondary_light</item>
<item name="materialColorTertiary">@color/system_tertiary_light</item>
<item name="materialColorError">@color/system_error_light</item>
+
+ <item name="customColorWidgetBackground">@color/system_widget_background_light</item>
+ <item name="customColorClockHour">@color/system_clock_hour_light</item>
+ <item name="customColorClockMinute">@color/system_clock_minute_light</item>
+ <item name="customColorClockSecond">@color/system_clock_second_light</item>
+ <item name="customColorThemeApp">@color/system_theme_app_light</item>
+ <item name="customColorOnThemeApp">@color/system_on_theme_app_light</item>
+ <item name="customColorThemeAppRing">@color/system_theme_app_ring_light</item>
+ <item name="customColorOnThemeAppRing">@color/system_on_theme_app_ring_light</item>
+ <item name="customColorBrandA">@color/system_brand_a_light</item>
+ <item name="customColorBrandB">@color/system_brand_b_light</item>
+ <item name="customColorBrandC">@color/system_brand_c_light</item>
+ <item name="customColorBrandD">@color/system_brand_d_light</item>
+ <item name="customColorUnderSurface">@color/system_under_surface_light</item>
+ <item name="customColorShadeActive">@color/system_shade_active_light</item>
+ <item name="customColorOnShadeActive">@color/system_on_shade_active_light</item>
+ <item name="customColorOnShadeActiveVariant">@color/system_on_shade_active_variant_light</item>
+ <item name="customColorShadeInactive">@color/system_shade_inactive_light</item>
+ <item name="customColorOnShadeInactive">@color/system_on_shade_inactive_light</item>
+ <item name="customColorOnShadeInactiveVariant">@color/system_on_shade_inactive_variant_light</item>
+ <item name="customColorShadeDisabled">@color/system_shade_disabled_light</item>
+ <item name="customColorOverviewBackground">@color/system_overview_background_light</item>
</style>
<!-- DeviceDefault style for input methods, which is used by the
@@ -1833,6 +2185,28 @@
<item name="materialColorSecondary">@color/system_secondary_light</item>
<item name="materialColorTertiary">@color/system_tertiary_light</item>
<item name="materialColorError">@color/system_error_light</item>
+
+ <item name="customColorWidgetBackground">@color/system_widget_background_light</item>
+ <item name="customColorClockHour">@color/system_clock_hour_light</item>
+ <item name="customColorClockMinute">@color/system_clock_minute_light</item>
+ <item name="customColorClockSecond">@color/system_clock_second_light</item>
+ <item name="customColorThemeApp">@color/system_theme_app_light</item>
+ <item name="customColorOnThemeApp">@color/system_on_theme_app_light</item>
+ <item name="customColorThemeAppRing">@color/system_theme_app_ring_light</item>
+ <item name="customColorOnThemeAppRing">@color/system_on_theme_app_ring_light</item>
+ <item name="customColorBrandA">@color/system_brand_a_light</item>
+ <item name="customColorBrandB">@color/system_brand_b_light</item>
+ <item name="customColorBrandC">@color/system_brand_c_light</item>
+ <item name="customColorBrandD">@color/system_brand_d_light</item>
+ <item name="customColorUnderSurface">@color/system_under_surface_light</item>
+ <item name="customColorShadeActive">@color/system_shade_active_light</item>
+ <item name="customColorOnShadeActive">@color/system_on_shade_active_light</item>
+ <item name="customColorOnShadeActiveVariant">@color/system_on_shade_active_variant_light</item>
+ <item name="customColorShadeInactive">@color/system_shade_inactive_light</item>
+ <item name="customColorOnShadeInactive">@color/system_on_shade_inactive_light</item>
+ <item name="customColorOnShadeInactiveVariant">@color/system_on_shade_inactive_variant_light</item>
+ <item name="customColorShadeDisabled">@color/system_shade_disabled_light</item>
+ <item name="customColorOverviewBackground">@color/system_overview_background_light</item>
</style>
<style name="Theme.DeviceDefault.Dialog.Alert" parent="Theme.Material.Dialog.Alert">
@@ -1928,6 +2302,28 @@
<item name="materialColorSecondary">@color/system_secondary_dark</item>
<item name="materialColorTertiary">@color/system_tertiary_dark</item>
<item name="materialColorError">@color/system_error_dark</item>
+
+ <item name="customColorWidgetBackground">@color/system_widget_background_dark</item>
+ <item name="customColorClockHour">@color/system_clock_hour_dark</item>
+ <item name="customColorClockMinute">@color/system_clock_minute_dark</item>
+ <item name="customColorClockSecond">@color/system_clock_second_dark</item>
+ <item name="customColorThemeApp">@color/system_theme_app_dark</item>
+ <item name="customColorOnThemeApp">@color/system_on_theme_app_dark</item>
+ <item name="customColorThemeAppRing">@color/system_theme_app_ring_dark</item>
+ <item name="customColorOnThemeAppRing">@color/system_on_theme_app_ring_dark</item>
+ <item name="customColorBrandA">@color/system_brand_a_dark</item>
+ <item name="customColorBrandB">@color/system_brand_b_dark</item>
+ <item name="customColorBrandC">@color/system_brand_c_dark</item>
+ <item name="customColorBrandD">@color/system_brand_d_dark</item>
+ <item name="customColorUnderSurface">@color/system_under_surface_dark</item>
+ <item name="customColorShadeActive">@color/system_shade_active_dark</item>
+ <item name="customColorOnShadeActive">@color/system_on_shade_active_dark</item>
+ <item name="customColorOnShadeActiveVariant">@color/system_on_shade_active_variant_dark</item>
+ <item name="customColorShadeInactive">@color/system_shade_inactive_dark</item>
+ <item name="customColorOnShadeInactive">@color/system_on_shade_inactive_dark</item>
+ <item name="customColorOnShadeInactiveVariant">@color/system_on_shade_inactive_variant_dark</item>
+ <item name="customColorShadeDisabled">@color/system_shade_disabled_dark</item>
+ <item name="customColorOverviewBackground">@color/system_overview_background_dark</item>
</style>
<!-- Theme for the dialog shown when an app crashes or ANRs. -->
@@ -2028,6 +2424,28 @@
<item name="materialColorSecondary">@color/system_secondary_dark</item>
<item name="materialColorTertiary">@color/system_tertiary_dark</item>
<item name="materialColorError">@color/system_error_dark</item>
+
+ <item name="customColorWidgetBackground">@color/system_widget_background_dark</item>
+ <item name="customColorClockHour">@color/system_clock_hour_dark</item>
+ <item name="customColorClockMinute">@color/system_clock_minute_dark</item>
+ <item name="customColorClockSecond">@color/system_clock_second_dark</item>
+ <item name="customColorThemeApp">@color/system_theme_app_dark</item>
+ <item name="customColorOnThemeApp">@color/system_on_theme_app_dark</item>
+ <item name="customColorThemeAppRing">@color/system_theme_app_ring_dark</item>
+ <item name="customColorOnThemeAppRing">@color/system_on_theme_app_ring_dark</item>
+ <item name="customColorBrandA">@color/system_brand_a_dark</item>
+ <item name="customColorBrandB">@color/system_brand_b_dark</item>
+ <item name="customColorBrandC">@color/system_brand_c_dark</item>
+ <item name="customColorBrandD">@color/system_brand_d_dark</item>
+ <item name="customColorUnderSurface">@color/system_under_surface_dark</item>
+ <item name="customColorShadeActive">@color/system_shade_active_dark</item>
+ <item name="customColorOnShadeActive">@color/system_on_shade_active_dark</item>
+ <item name="customColorOnShadeActiveVariant">@color/system_on_shade_active_variant_dark</item>
+ <item name="customColorShadeInactive">@color/system_shade_inactive_dark</item>
+ <item name="customColorOnShadeInactive">@color/system_on_shade_inactive_dark</item>
+ <item name="customColorOnShadeInactiveVariant">@color/system_on_shade_inactive_variant_dark</item>
+ <item name="customColorShadeDisabled">@color/system_shade_disabled_dark</item>
+ <item name="customColorOverviewBackground">@color/system_overview_background_dark</item>
</style>
<style name="Theme.DeviceDefault.Dialog.NoFrame" parent="Theme.Material.Dialog.NoFrame">
@@ -2121,6 +2539,28 @@
<item name="materialColorSecondary">@color/system_secondary_dark</item>
<item name="materialColorTertiary">@color/system_tertiary_dark</item>
<item name="materialColorError">@color/system_error_dark</item>
+
+ <item name="customColorWidgetBackground">@color/system_widget_background_dark</item>
+ <item name="customColorClockHour">@color/system_clock_hour_dark</item>
+ <item name="customColorClockMinute">@color/system_clock_minute_dark</item>
+ <item name="customColorClockSecond">@color/system_clock_second_dark</item>
+ <item name="customColorThemeApp">@color/system_theme_app_dark</item>
+ <item name="customColorOnThemeApp">@color/system_on_theme_app_dark</item>
+ <item name="customColorThemeAppRing">@color/system_theme_app_ring_dark</item>
+ <item name="customColorOnThemeAppRing">@color/system_on_theme_app_ring_dark</item>
+ <item name="customColorBrandA">@color/system_brand_a_dark</item>
+ <item name="customColorBrandB">@color/system_brand_b_dark</item>
+ <item name="customColorBrandC">@color/system_brand_c_dark</item>
+ <item name="customColorBrandD">@color/system_brand_d_dark</item>
+ <item name="customColorUnderSurface">@color/system_under_surface_dark</item>
+ <item name="customColorShadeActive">@color/system_shade_active_dark</item>
+ <item name="customColorOnShadeActive">@color/system_on_shade_active_dark</item>
+ <item name="customColorOnShadeActiveVariant">@color/system_on_shade_active_variant_dark</item>
+ <item name="customColorShadeInactive">@color/system_shade_inactive_dark</item>
+ <item name="customColorOnShadeInactive">@color/system_on_shade_inactive_dark</item>
+ <item name="customColorOnShadeInactiveVariant">@color/system_on_shade_inactive_variant_dark</item>
+ <item name="customColorShadeDisabled">@color/system_shade_disabled_dark</item>
+ <item name="customColorOverviewBackground">@color/system_overview_background_dark</item>
</style>
<!-- Variant of {@link #Theme_DeviceDefault} with a light-colored style -->
@@ -2352,6 +2792,28 @@
<item name="materialColorSecondary">@color/system_secondary_light</item>
<item name="materialColorTertiary">@color/system_tertiary_light</item>
<item name="materialColorError">@color/system_error_light</item>
+
+ <item name="customColorWidgetBackground">@color/system_widget_background_light</item>
+ <item name="customColorClockHour">@color/system_clock_hour_light</item>
+ <item name="customColorClockMinute">@color/system_clock_minute_light</item>
+ <item name="customColorClockSecond">@color/system_clock_second_light</item>
+ <item name="customColorThemeApp">@color/system_theme_app_light</item>
+ <item name="customColorOnThemeApp">@color/system_on_theme_app_light</item>
+ <item name="customColorThemeAppRing">@color/system_theme_app_ring_light</item>
+ <item name="customColorOnThemeAppRing">@color/system_on_theme_app_ring_light</item>
+ <item name="customColorBrandA">@color/system_brand_a_light</item>
+ <item name="customColorBrandB">@color/system_brand_b_light</item>
+ <item name="customColorBrandC">@color/system_brand_c_light</item>
+ <item name="customColorBrandD">@color/system_brand_d_light</item>
+ <item name="customColorUnderSurface">@color/system_under_surface_light</item>
+ <item name="customColorShadeActive">@color/system_shade_active_light</item>
+ <item name="customColorOnShadeActive">@color/system_on_shade_active_light</item>
+ <item name="customColorOnShadeActiveVariant">@color/system_on_shade_active_variant_light</item>
+ <item name="customColorShadeInactive">@color/system_shade_inactive_light</item>
+ <item name="customColorOnShadeInactive">@color/system_on_shade_inactive_light</item>
+ <item name="customColorOnShadeInactiveVariant">@color/system_on_shade_inactive_variant_light</item>
+ <item name="customColorShadeDisabled">@color/system_shade_disabled_light</item>
+ <item name="customColorOverviewBackground">@color/system_overview_background_light</item>
</style>
<!-- Variant of the DeviceDefault (light) theme that has a solid (opaque) action bar with an
@@ -2447,6 +2909,28 @@
<item name="materialColorSecondary">@color/system_secondary_light</item>
<item name="materialColorTertiary">@color/system_tertiary_light</item>
<item name="materialColorError">@color/system_error_light</item>
+
+ <item name="customColorWidgetBackground">@color/system_widget_background_light</item>
+ <item name="customColorClockHour">@color/system_clock_hour_light</item>
+ <item name="customColorClockMinute">@color/system_clock_minute_light</item>
+ <item name="customColorClockSecond">@color/system_clock_second_light</item>
+ <item name="customColorThemeApp">@color/system_theme_app_light</item>
+ <item name="customColorOnThemeApp">@color/system_on_theme_app_light</item>
+ <item name="customColorThemeAppRing">@color/system_theme_app_ring_light</item>
+ <item name="customColorOnThemeAppRing">@color/system_on_theme_app_ring_light</item>
+ <item name="customColorBrandA">@color/system_brand_a_light</item>
+ <item name="customColorBrandB">@color/system_brand_b_light</item>
+ <item name="customColorBrandC">@color/system_brand_c_light</item>
+ <item name="customColorBrandD">@color/system_brand_d_light</item>
+ <item name="customColorUnderSurface">@color/system_under_surface_light</item>
+ <item name="customColorShadeActive">@color/system_shade_active_light</item>
+ <item name="customColorOnShadeActive">@color/system_on_shade_active_light</item>
+ <item name="customColorOnShadeActiveVariant">@color/system_on_shade_active_variant_light</item>
+ <item name="customColorShadeInactive">@color/system_shade_inactive_light</item>
+ <item name="customColorOnShadeInactive">@color/system_on_shade_inactive_light</item>
+ <item name="customColorOnShadeInactiveVariant">@color/system_on_shade_inactive_variant_light</item>
+ <item name="customColorShadeDisabled">@color/system_shade_disabled_light</item>
+ <item name="customColorOverviewBackground">@color/system_overview_background_light</item>
</style>
<!-- Variant of {@link #Theme_DeviceDefault_Light} with no action bar -->
@@ -2541,6 +3025,28 @@
<item name="materialColorSecondary">@color/system_secondary_light</item>
<item name="materialColorTertiary">@color/system_tertiary_light</item>
<item name="materialColorError">@color/system_error_light</item>
+
+ <item name="customColorWidgetBackground">@color/system_widget_background_light</item>
+ <item name="customColorClockHour">@color/system_clock_hour_light</item>
+ <item name="customColorClockMinute">@color/system_clock_minute_light</item>
+ <item name="customColorClockSecond">@color/system_clock_second_light</item>
+ <item name="customColorThemeApp">@color/system_theme_app_light</item>
+ <item name="customColorOnThemeApp">@color/system_on_theme_app_light</item>
+ <item name="customColorThemeAppRing">@color/system_theme_app_ring_light</item>
+ <item name="customColorOnThemeAppRing">@color/system_on_theme_app_ring_light</item>
+ <item name="customColorBrandA">@color/system_brand_a_light</item>
+ <item name="customColorBrandB">@color/system_brand_b_light</item>
+ <item name="customColorBrandC">@color/system_brand_c_light</item>
+ <item name="customColorBrandD">@color/system_brand_d_light</item>
+ <item name="customColorUnderSurface">@color/system_under_surface_light</item>
+ <item name="customColorShadeActive">@color/system_shade_active_light</item>
+ <item name="customColorOnShadeActive">@color/system_on_shade_active_light</item>
+ <item name="customColorOnShadeActiveVariant">@color/system_on_shade_active_variant_light</item>
+ <item name="customColorShadeInactive">@color/system_shade_inactive_light</item>
+ <item name="customColorOnShadeInactive">@color/system_on_shade_inactive_light</item>
+ <item name="customColorOnShadeInactiveVariant">@color/system_on_shade_inactive_variant_light</item>
+ <item name="customColorShadeDisabled">@color/system_shade_disabled_light</item>
+ <item name="customColorOverviewBackground">@color/system_overview_background_light</item>
</style>
<!-- Variant of {@link #Theme_DeviceDefault_Light} with no action bar and no status bar.
@@ -2636,6 +3142,28 @@
<item name="materialColorSecondary">@color/system_secondary_light</item>
<item name="materialColorTertiary">@color/system_tertiary_light</item>
<item name="materialColorError">@color/system_error_light</item>
+
+ <item name="customColorWidgetBackground">@color/system_widget_background_light</item>
+ <item name="customColorClockHour">@color/system_clock_hour_light</item>
+ <item name="customColorClockMinute">@color/system_clock_minute_light</item>
+ <item name="customColorClockSecond">@color/system_clock_second_light</item>
+ <item name="customColorThemeApp">@color/system_theme_app_light</item>
+ <item name="customColorOnThemeApp">@color/system_on_theme_app_light</item>
+ <item name="customColorThemeAppRing">@color/system_theme_app_ring_light</item>
+ <item name="customColorOnThemeAppRing">@color/system_on_theme_app_ring_light</item>
+ <item name="customColorBrandA">@color/system_brand_a_light</item>
+ <item name="customColorBrandB">@color/system_brand_b_light</item>
+ <item name="customColorBrandC">@color/system_brand_c_light</item>
+ <item name="customColorBrandD">@color/system_brand_d_light</item>
+ <item name="customColorUnderSurface">@color/system_under_surface_light</item>
+ <item name="customColorShadeActive">@color/system_shade_active_light</item>
+ <item name="customColorOnShadeActive">@color/system_on_shade_active_light</item>
+ <item name="customColorOnShadeActiveVariant">@color/system_on_shade_active_variant_light</item>
+ <item name="customColorShadeInactive">@color/system_shade_inactive_light</item>
+ <item name="customColorOnShadeInactive">@color/system_on_shade_inactive_light</item>
+ <item name="customColorOnShadeInactiveVariant">@color/system_on_shade_inactive_variant_light</item>
+ <item name="customColorShadeDisabled">@color/system_shade_disabled_light</item>
+ <item name="customColorOverviewBackground">@color/system_overview_background_light</item>
</style>
<!-- Variant of {@link #Theme_DeviceDefault_Light} with no action bar and no status bar
@@ -2733,6 +3261,28 @@
<item name="materialColorSecondary">@color/system_secondary_light</item>
<item name="materialColorTertiary">@color/system_tertiary_light</item>
<item name="materialColorError">@color/system_error_light</item>
+
+ <item name="customColorWidgetBackground">@color/system_widget_background_light</item>
+ <item name="customColorClockHour">@color/system_clock_hour_light</item>
+ <item name="customColorClockMinute">@color/system_clock_minute_light</item>
+ <item name="customColorClockSecond">@color/system_clock_second_light</item>
+ <item name="customColorThemeApp">@color/system_theme_app_light</item>
+ <item name="customColorOnThemeApp">@color/system_on_theme_app_light</item>
+ <item name="customColorThemeAppRing">@color/system_theme_app_ring_light</item>
+ <item name="customColorOnThemeAppRing">@color/system_on_theme_app_ring_light</item>
+ <item name="customColorBrandA">@color/system_brand_a_light</item>
+ <item name="customColorBrandB">@color/system_brand_b_light</item>
+ <item name="customColorBrandC">@color/system_brand_c_light</item>
+ <item name="customColorBrandD">@color/system_brand_d_light</item>
+ <item name="customColorUnderSurface">@color/system_under_surface_light</item>
+ <item name="customColorShadeActive">@color/system_shade_active_light</item>
+ <item name="customColorOnShadeActive">@color/system_on_shade_active_light</item>
+ <item name="customColorOnShadeActiveVariant">@color/system_on_shade_active_variant_light</item>
+ <item name="customColorShadeInactive">@color/system_shade_inactive_light</item>
+ <item name="customColorOnShadeInactive">@color/system_on_shade_inactive_light</item>
+ <item name="customColorOnShadeInactiveVariant">@color/system_on_shade_inactive_variant_light</item>
+ <item name="customColorShadeDisabled">@color/system_shade_disabled_light</item>
+ <item name="customColorOverviewBackground">@color/system_overview_background_light</item>
</style>
<!-- Variant of {@link #Theme_DeviceDefault_Light} that has no title bar and translucent
@@ -2829,6 +3379,28 @@
<item name="materialColorSecondary">@color/system_secondary_light</item>
<item name="materialColorTertiary">@color/system_tertiary_light</item>
<item name="materialColorError">@color/system_error_light</item>
+
+ <item name="customColorWidgetBackground">@color/system_widget_background_light</item>
+ <item name="customColorClockHour">@color/system_clock_hour_light</item>
+ <item name="customColorClockMinute">@color/system_clock_minute_light</item>
+ <item name="customColorClockSecond">@color/system_clock_second_light</item>
+ <item name="customColorThemeApp">@color/system_theme_app_light</item>
+ <item name="customColorOnThemeApp">@color/system_on_theme_app_light</item>
+ <item name="customColorThemeAppRing">@color/system_theme_app_ring_light</item>
+ <item name="customColorOnThemeAppRing">@color/system_on_theme_app_ring_light</item>
+ <item name="customColorBrandA">@color/system_brand_a_light</item>
+ <item name="customColorBrandB">@color/system_brand_b_light</item>
+ <item name="customColorBrandC">@color/system_brand_c_light</item>
+ <item name="customColorBrandD">@color/system_brand_d_light</item>
+ <item name="customColorUnderSurface">@color/system_under_surface_light</item>
+ <item name="customColorShadeActive">@color/system_shade_active_light</item>
+ <item name="customColorOnShadeActive">@color/system_on_shade_active_light</item>
+ <item name="customColorOnShadeActiveVariant">@color/system_on_shade_active_variant_light</item>
+ <item name="customColorShadeInactive">@color/system_shade_inactive_light</item>
+ <item name="customColorOnShadeInactive">@color/system_on_shade_inactive_light</item>
+ <item name="customColorOnShadeInactiveVariant">@color/system_on_shade_inactive_variant_light</item>
+ <item name="customColorShadeDisabled">@color/system_shade_disabled_light</item>
+ <item name="customColorOverviewBackground">@color/system_overview_background_light</item>
</style>
<!-- DeviceDefault light theme for dialog windows and activities. This changes the window to be
@@ -2931,6 +3503,28 @@
<item name="materialColorSecondary">@color/system_secondary_light</item>
<item name="materialColorTertiary">@color/system_tertiary_light</item>
<item name="materialColorError">@color/system_error_light</item>
+
+ <item name="customColorWidgetBackground">@color/system_widget_background_light</item>
+ <item name="customColorClockHour">@color/system_clock_hour_light</item>
+ <item name="customColorClockMinute">@color/system_clock_minute_light</item>
+ <item name="customColorClockSecond">@color/system_clock_second_light</item>
+ <item name="customColorThemeApp">@color/system_theme_app_light</item>
+ <item name="customColorOnThemeApp">@color/system_on_theme_app_light</item>
+ <item name="customColorThemeAppRing">@color/system_theme_app_ring_light</item>
+ <item name="customColorOnThemeAppRing">@color/system_on_theme_app_ring_light</item>
+ <item name="customColorBrandA">@color/system_brand_a_light</item>
+ <item name="customColorBrandB">@color/system_brand_b_light</item>
+ <item name="customColorBrandC">@color/system_brand_c_light</item>
+ <item name="customColorBrandD">@color/system_brand_d_light</item>
+ <item name="customColorUnderSurface">@color/system_under_surface_light</item>
+ <item name="customColorShadeActive">@color/system_shade_active_light</item>
+ <item name="customColorOnShadeActive">@color/system_on_shade_active_light</item>
+ <item name="customColorOnShadeActiveVariant">@color/system_on_shade_active_variant_light</item>
+ <item name="customColorShadeInactive">@color/system_shade_inactive_light</item>
+ <item name="customColorOnShadeInactive">@color/system_on_shade_inactive_light</item>
+ <item name="customColorOnShadeInactiveVariant">@color/system_on_shade_inactive_variant_light</item>
+ <item name="customColorShadeDisabled">@color/system_shade_disabled_light</item>
+ <item name="customColorOverviewBackground">@color/system_overview_background_light</item>
</style>
<!-- Variant of {@link #Theme_DeviceDefault_Light_Dialog} that has a nice minimum width for a
@@ -3029,6 +3623,28 @@
<item name="materialColorSecondary">@color/system_secondary_light</item>
<item name="materialColorTertiary">@color/system_tertiary_light</item>
<item name="materialColorError">@color/system_error_light</item>
+
+ <item name="customColorWidgetBackground">@color/system_widget_background_light</item>
+ <item name="customColorClockHour">@color/system_clock_hour_light</item>
+ <item name="customColorClockMinute">@color/system_clock_minute_light</item>
+ <item name="customColorClockSecond">@color/system_clock_second_light</item>
+ <item name="customColorThemeApp">@color/system_theme_app_light</item>
+ <item name="customColorOnThemeApp">@color/system_on_theme_app_light</item>
+ <item name="customColorThemeAppRing">@color/system_theme_app_ring_light</item>
+ <item name="customColorOnThemeAppRing">@color/system_on_theme_app_ring_light</item>
+ <item name="customColorBrandA">@color/system_brand_a_light</item>
+ <item name="customColorBrandB">@color/system_brand_b_light</item>
+ <item name="customColorBrandC">@color/system_brand_c_light</item>
+ <item name="customColorBrandD">@color/system_brand_d_light</item>
+ <item name="customColorUnderSurface">@color/system_under_surface_light</item>
+ <item name="customColorShadeActive">@color/system_shade_active_light</item>
+ <item name="customColorOnShadeActive">@color/system_on_shade_active_light</item>
+ <item name="customColorOnShadeActiveVariant">@color/system_on_shade_active_variant_light</item>
+ <item name="customColorShadeInactive">@color/system_shade_inactive_light</item>
+ <item name="customColorOnShadeInactive">@color/system_on_shade_inactive_light</item>
+ <item name="customColorOnShadeInactiveVariant">@color/system_on_shade_inactive_variant_light</item>
+ <item name="customColorShadeDisabled">@color/system_shade_disabled_light</item>
+ <item name="customColorOverviewBackground">@color/system_overview_background_light</item>
</style>
<!-- Variant of {@link #Theme_DeviceDefault_Light_Dialog} without an action bar -->
@@ -3126,6 +3742,28 @@
<item name="materialColorSecondary">@color/system_secondary_light</item>
<item name="materialColorTertiary">@color/system_tertiary_light</item>
<item name="materialColorError">@color/system_error_light</item>
+
+ <item name="customColorWidgetBackground">@color/system_widget_background_light</item>
+ <item name="customColorClockHour">@color/system_clock_hour_light</item>
+ <item name="customColorClockMinute">@color/system_clock_minute_light</item>
+ <item name="customColorClockSecond">@color/system_clock_second_light</item>
+ <item name="customColorThemeApp">@color/system_theme_app_light</item>
+ <item name="customColorOnThemeApp">@color/system_on_theme_app_light</item>
+ <item name="customColorThemeAppRing">@color/system_theme_app_ring_light</item>
+ <item name="customColorOnThemeAppRing">@color/system_on_theme_app_ring_light</item>
+ <item name="customColorBrandA">@color/system_brand_a_light</item>
+ <item name="customColorBrandB">@color/system_brand_b_light</item>
+ <item name="customColorBrandC">@color/system_brand_c_light</item>
+ <item name="customColorBrandD">@color/system_brand_d_light</item>
+ <item name="customColorUnderSurface">@color/system_under_surface_light</item>
+ <item name="customColorShadeActive">@color/system_shade_active_light</item>
+ <item name="customColorOnShadeActive">@color/system_on_shade_active_light</item>
+ <item name="customColorOnShadeActiveVariant">@color/system_on_shade_active_variant_light</item>
+ <item name="customColorShadeInactive">@color/system_shade_inactive_light</item>
+ <item name="customColorOnShadeInactive">@color/system_on_shade_inactive_light</item>
+ <item name="customColorOnShadeInactiveVariant">@color/system_on_shade_inactive_variant_light</item>
+ <item name="customColorShadeDisabled">@color/system_shade_disabled_light</item>
+ <item name="customColorOverviewBackground">@color/system_overview_background_light</item>
</style>
<!-- Variant of {@link #Theme_DeviceDefault_Light_Dialog_NoActionBar} that has a nice minimum
@@ -3224,6 +3862,28 @@
<item name="materialColorSecondary">@color/system_secondary_light</item>
<item name="materialColorTertiary">@color/system_tertiary_light</item>
<item name="materialColorError">@color/system_error_light</item>
+
+ <item name="customColorWidgetBackground">@color/system_widget_background_light</item>
+ <item name="customColorClockHour">@color/system_clock_hour_light</item>
+ <item name="customColorClockMinute">@color/system_clock_minute_light</item>
+ <item name="customColorClockSecond">@color/system_clock_second_light</item>
+ <item name="customColorThemeApp">@color/system_theme_app_light</item>
+ <item name="customColorOnThemeApp">@color/system_on_theme_app_light</item>
+ <item name="customColorThemeAppRing">@color/system_theme_app_ring_light</item>
+ <item name="customColorOnThemeAppRing">@color/system_on_theme_app_ring_light</item>
+ <item name="customColorBrandA">@color/system_brand_a_light</item>
+ <item name="customColorBrandB">@color/system_brand_b_light</item>
+ <item name="customColorBrandC">@color/system_brand_c_light</item>
+ <item name="customColorBrandD">@color/system_brand_d_light</item>
+ <item name="customColorUnderSurface">@color/system_under_surface_light</item>
+ <item name="customColorShadeActive">@color/system_shade_active_light</item>
+ <item name="customColorOnShadeActive">@color/system_on_shade_active_light</item>
+ <item name="customColorOnShadeActiveVariant">@color/system_on_shade_active_variant_light</item>
+ <item name="customColorShadeInactive">@color/system_shade_inactive_light</item>
+ <item name="customColorOnShadeInactive">@color/system_on_shade_inactive_light</item>
+ <item name="customColorOnShadeInactiveVariant">@color/system_on_shade_inactive_variant_light</item>
+ <item name="customColorShadeDisabled">@color/system_shade_disabled_light</item>
+ <item name="customColorOverviewBackground">@color/system_overview_background_light</item>
</style>
<!-- Variant of Theme.DeviceDefault.Dialog that has a fixed size. -->
@@ -3303,6 +3963,28 @@
<item name="materialColorSecondary">@color/system_secondary_light</item>
<item name="materialColorTertiary">@color/system_tertiary_light</item>
<item name="materialColorError">@color/system_error_light</item>
+
+ <item name="customColorWidgetBackground">@color/system_widget_background_light</item>
+ <item name="customColorClockHour">@color/system_clock_hour_light</item>
+ <item name="customColorClockMinute">@color/system_clock_minute_light</item>
+ <item name="customColorClockSecond">@color/system_clock_second_light</item>
+ <item name="customColorThemeApp">@color/system_theme_app_light</item>
+ <item name="customColorOnThemeApp">@color/system_on_theme_app_light</item>
+ <item name="customColorThemeAppRing">@color/system_theme_app_ring_light</item>
+ <item name="customColorOnThemeAppRing">@color/system_on_theme_app_ring_light</item>
+ <item name="customColorBrandA">@color/system_brand_a_light</item>
+ <item name="customColorBrandB">@color/system_brand_b_light</item>
+ <item name="customColorBrandC">@color/system_brand_c_light</item>
+ <item name="customColorBrandD">@color/system_brand_d_light</item>
+ <item name="customColorUnderSurface">@color/system_under_surface_light</item>
+ <item name="customColorShadeActive">@color/system_shade_active_light</item>
+ <item name="customColorOnShadeActive">@color/system_on_shade_active_light</item>
+ <item name="customColorOnShadeActiveVariant">@color/system_on_shade_active_variant_light</item>
+ <item name="customColorShadeInactive">@color/system_shade_inactive_light</item>
+ <item name="customColorOnShadeInactive">@color/system_on_shade_inactive_light</item>
+ <item name="customColorOnShadeInactiveVariant">@color/system_on_shade_inactive_variant_light</item>
+ <item name="customColorShadeDisabled">@color/system_shade_disabled_light</item>
+ <item name="customColorOverviewBackground">@color/system_overview_background_light</item>
</style>
<!-- Variant of Theme.DeviceDefault.Dialog.NoActionBar that has a fixed size. -->
@@ -3382,6 +4064,28 @@
<item name="materialColorSecondary">@color/system_secondary_light</item>
<item name="materialColorTertiary">@color/system_tertiary_light</item>
<item name="materialColorError">@color/system_error_light</item>
+
+ <item name="customColorWidgetBackground">@color/system_widget_background_light</item>
+ <item name="customColorClockHour">@color/system_clock_hour_light</item>
+ <item name="customColorClockMinute">@color/system_clock_minute_light</item>
+ <item name="customColorClockSecond">@color/system_clock_second_light</item>
+ <item name="customColorThemeApp">@color/system_theme_app_light</item>
+ <item name="customColorOnThemeApp">@color/system_on_theme_app_light</item>
+ <item name="customColorThemeAppRing">@color/system_theme_app_ring_light</item>
+ <item name="customColorOnThemeAppRing">@color/system_on_theme_app_ring_light</item>
+ <item name="customColorBrandA">@color/system_brand_a_light</item>
+ <item name="customColorBrandB">@color/system_brand_b_light</item>
+ <item name="customColorBrandC">@color/system_brand_c_light</item>
+ <item name="customColorBrandD">@color/system_brand_d_light</item>
+ <item name="customColorUnderSurface">@color/system_under_surface_light</item>
+ <item name="customColorShadeActive">@color/system_shade_active_light</item>
+ <item name="customColorOnShadeActive">@color/system_on_shade_active_light</item>
+ <item name="customColorOnShadeActiveVariant">@color/system_on_shade_active_variant_light</item>
+ <item name="customColorShadeInactive">@color/system_shade_inactive_light</item>
+ <item name="customColorOnShadeInactive">@color/system_on_shade_inactive_light</item>
+ <item name="customColorOnShadeInactiveVariant">@color/system_on_shade_inactive_variant_light</item>
+ <item name="customColorShadeDisabled">@color/system_shade_disabled_light</item>
+ <item name="customColorOverviewBackground">@color/system_overview_background_light</item>
</style>
<!-- DeviceDefault light theme for a window that will be displayed either full-screen on smaller
@@ -3480,6 +4184,28 @@
<item name="materialColorSecondary">@color/system_secondary_light</item>
<item name="materialColorTertiary">@color/system_tertiary_light</item>
<item name="materialColorError">@color/system_error_light</item>
+
+ <item name="customColorWidgetBackground">@color/system_widget_background_light</item>
+ <item name="customColorClockHour">@color/system_clock_hour_light</item>
+ <item name="customColorClockMinute">@color/system_clock_minute_light</item>
+ <item name="customColorClockSecond">@color/system_clock_second_light</item>
+ <item name="customColorThemeApp">@color/system_theme_app_light</item>
+ <item name="customColorOnThemeApp">@color/system_on_theme_app_light</item>
+ <item name="customColorThemeAppRing">@color/system_theme_app_ring_light</item>
+ <item name="customColorOnThemeAppRing">@color/system_on_theme_app_ring_light</item>
+ <item name="customColorBrandA">@color/system_brand_a_light</item>
+ <item name="customColorBrandB">@color/system_brand_b_light</item>
+ <item name="customColorBrandC">@color/system_brand_c_light</item>
+ <item name="customColorBrandD">@color/system_brand_d_light</item>
+ <item name="customColorUnderSurface">@color/system_under_surface_light</item>
+ <item name="customColorShadeActive">@color/system_shade_active_light</item>
+ <item name="customColorOnShadeActive">@color/system_on_shade_active_light</item>
+ <item name="customColorOnShadeActiveVariant">@color/system_on_shade_active_variant_light</item>
+ <item name="customColorShadeInactive">@color/system_shade_inactive_light</item>
+ <item name="customColorOnShadeInactive">@color/system_on_shade_inactive_light</item>
+ <item name="customColorOnShadeInactiveVariant">@color/system_on_shade_inactive_variant_light</item>
+ <item name="customColorShadeDisabled">@color/system_shade_disabled_light</item>
+ <item name="customColorOverviewBackground">@color/system_overview_background_light</item>
</style>
<!-- DeviceDefault light theme for a window without an action bar that will be displayed either
@@ -3579,6 +4305,28 @@
<item name="materialColorSecondary">@color/system_secondary_light</item>
<item name="materialColorTertiary">@color/system_tertiary_light</item>
<item name="materialColorError">@color/system_error_light</item>
+
+ <item name="customColorWidgetBackground">@color/system_widget_background_light</item>
+ <item name="customColorClockHour">@color/system_clock_hour_light</item>
+ <item name="customColorClockMinute">@color/system_clock_minute_light</item>
+ <item name="customColorClockSecond">@color/system_clock_second_light</item>
+ <item name="customColorThemeApp">@color/system_theme_app_light</item>
+ <item name="customColorOnThemeApp">@color/system_on_theme_app_light</item>
+ <item name="customColorThemeAppRing">@color/system_theme_app_ring_light</item>
+ <item name="customColorOnThemeAppRing">@color/system_on_theme_app_ring_light</item>
+ <item name="customColorBrandA">@color/system_brand_a_light</item>
+ <item name="customColorBrandB">@color/system_brand_b_light</item>
+ <item name="customColorBrandC">@color/system_brand_c_light</item>
+ <item name="customColorBrandD">@color/system_brand_d_light</item>
+ <item name="customColorUnderSurface">@color/system_under_surface_light</item>
+ <item name="customColorShadeActive">@color/system_shade_active_light</item>
+ <item name="customColorOnShadeActive">@color/system_on_shade_active_light</item>
+ <item name="customColorOnShadeActiveVariant">@color/system_on_shade_active_variant_light</item>
+ <item name="customColorShadeInactive">@color/system_shade_inactive_light</item>
+ <item name="customColorOnShadeInactive">@color/system_on_shade_inactive_light</item>
+ <item name="customColorOnShadeInactiveVariant">@color/system_on_shade_inactive_variant_light</item>
+ <item name="customColorShadeDisabled">@color/system_shade_disabled_light</item>
+ <item name="customColorOverviewBackground">@color/system_overview_background_light</item>
</style>
<!-- DeviceDefault light theme for a presentation window on a secondary display. -->
@@ -3676,6 +4424,28 @@
<item name="materialColorSecondary">@color/system_secondary_light</item>
<item name="materialColorTertiary">@color/system_tertiary_light</item>
<item name="materialColorError">@color/system_error_light</item>
+
+ <item name="customColorWidgetBackground">@color/system_widget_background_light</item>
+ <item name="customColorClockHour">@color/system_clock_hour_light</item>
+ <item name="customColorClockMinute">@color/system_clock_minute_light</item>
+ <item name="customColorClockSecond">@color/system_clock_second_light</item>
+ <item name="customColorThemeApp">@color/system_theme_app_light</item>
+ <item name="customColorOnThemeApp">@color/system_on_theme_app_light</item>
+ <item name="customColorThemeAppRing">@color/system_theme_app_ring_light</item>
+ <item name="customColorOnThemeAppRing">@color/system_on_theme_app_ring_light</item>
+ <item name="customColorBrandA">@color/system_brand_a_light</item>
+ <item name="customColorBrandB">@color/system_brand_b_light</item>
+ <item name="customColorBrandC">@color/system_brand_c_light</item>
+ <item name="customColorBrandD">@color/system_brand_d_light</item>
+ <item name="customColorUnderSurface">@color/system_under_surface_light</item>
+ <item name="customColorShadeActive">@color/system_shade_active_light</item>
+ <item name="customColorOnShadeActive">@color/system_on_shade_active_light</item>
+ <item name="customColorOnShadeActiveVariant">@color/system_on_shade_active_variant_light</item>
+ <item name="customColorShadeInactive">@color/system_shade_inactive_light</item>
+ <item name="customColorOnShadeInactive">@color/system_on_shade_inactive_light</item>
+ <item name="customColorOnShadeInactiveVariant">@color/system_on_shade_inactive_variant_light</item>
+ <item name="customColorShadeDisabled">@color/system_shade_disabled_light</item>
+ <item name="customColorOverviewBackground">@color/system_overview_background_light</item>
</style>
<!-- DeviceDefault light theme for panel windows. This removes all extraneous window
@@ -3772,6 +4542,28 @@
<item name="materialColorSecondary">@color/system_secondary_light</item>
<item name="materialColorTertiary">@color/system_tertiary_light</item>
<item name="materialColorError">@color/system_error_light</item>
+
+ <item name="customColorWidgetBackground">@color/system_widget_background_light</item>
+ <item name="customColorClockHour">@color/system_clock_hour_light</item>
+ <item name="customColorClockMinute">@color/system_clock_minute_light</item>
+ <item name="customColorClockSecond">@color/system_clock_second_light</item>
+ <item name="customColorThemeApp">@color/system_theme_app_light</item>
+ <item name="customColorOnThemeApp">@color/system_on_theme_app_light</item>
+ <item name="customColorThemeAppRing">@color/system_theme_app_ring_light</item>
+ <item name="customColorOnThemeAppRing">@color/system_on_theme_app_ring_light</item>
+ <item name="customColorBrandA">@color/system_brand_a_light</item>
+ <item name="customColorBrandB">@color/system_brand_b_light</item>
+ <item name="customColorBrandC">@color/system_brand_c_light</item>
+ <item name="customColorBrandD">@color/system_brand_d_light</item>
+ <item name="customColorUnderSurface">@color/system_under_surface_light</item>
+ <item name="customColorShadeActive">@color/system_shade_active_light</item>
+ <item name="customColorOnShadeActive">@color/system_on_shade_active_light</item>
+ <item name="customColorOnShadeActiveVariant">@color/system_on_shade_active_variant_light</item>
+ <item name="customColorShadeInactive">@color/system_shade_inactive_light</item>
+ <item name="customColorOnShadeInactive">@color/system_on_shade_inactive_light</item>
+ <item name="customColorOnShadeInactiveVariant">@color/system_on_shade_inactive_variant_light</item>
+ <item name="customColorShadeDisabled">@color/system_shade_disabled_light</item>
+ <item name="customColorOverviewBackground">@color/system_overview_background_light</item>
</style>
<style name="Theme.DeviceDefault.Light.Dialog.Alert" parent="Theme.Material.Light.Dialog.Alert">
@@ -3867,6 +4659,28 @@
<item name="materialColorSecondary">@color/system_secondary_light</item>
<item name="materialColorTertiary">@color/system_tertiary_light</item>
<item name="materialColorError">@color/system_error_light</item>
+
+ <item name="customColorWidgetBackground">@color/system_widget_background_light</item>
+ <item name="customColorClockHour">@color/system_clock_hour_light</item>
+ <item name="customColorClockMinute">@color/system_clock_minute_light</item>
+ <item name="customColorClockSecond">@color/system_clock_second_light</item>
+ <item name="customColorThemeApp">@color/system_theme_app_light</item>
+ <item name="customColorOnThemeApp">@color/system_on_theme_app_light</item>
+ <item name="customColorThemeAppRing">@color/system_theme_app_ring_light</item>
+ <item name="customColorOnThemeAppRing">@color/system_on_theme_app_ring_light</item>
+ <item name="customColorBrandA">@color/system_brand_a_light</item>
+ <item name="customColorBrandB">@color/system_brand_b_light</item>
+ <item name="customColorBrandC">@color/system_brand_c_light</item>
+ <item name="customColorBrandD">@color/system_brand_d_light</item>
+ <item name="customColorUnderSurface">@color/system_under_surface_light</item>
+ <item name="customColorShadeActive">@color/system_shade_active_light</item>
+ <item name="customColorOnShadeActive">@color/system_on_shade_active_light</item>
+ <item name="customColorOnShadeActiveVariant">@color/system_on_shade_active_variant_light</item>
+ <item name="customColorShadeInactive">@color/system_shade_inactive_light</item>
+ <item name="customColorOnShadeInactive">@color/system_on_shade_inactive_light</item>
+ <item name="customColorOnShadeInactiveVariant">@color/system_on_shade_inactive_variant_light</item>
+ <item name="customColorShadeDisabled">@color/system_shade_disabled_light</item>
+ <item name="customColorOverviewBackground">@color/system_overview_background_light</item>
</style>
<style name="Theme.DeviceDefault.Dialog.Alert.DayNight" parent="Theme.DeviceDefault.Light.Dialog.Alert" />
@@ -3962,6 +4776,28 @@
<item name="materialColorSecondary">@color/system_secondary_light</item>
<item name="materialColorTertiary">@color/system_tertiary_light</item>
<item name="materialColorError">@color/system_error_light</item>
+
+ <item name="customColorWidgetBackground">@color/system_widget_background_light</item>
+ <item name="customColorClockHour">@color/system_clock_hour_light</item>
+ <item name="customColorClockMinute">@color/system_clock_minute_light</item>
+ <item name="customColorClockSecond">@color/system_clock_second_light</item>
+ <item name="customColorThemeApp">@color/system_theme_app_light</item>
+ <item name="customColorOnThemeApp">@color/system_on_theme_app_light</item>
+ <item name="customColorThemeAppRing">@color/system_theme_app_ring_light</item>
+ <item name="customColorOnThemeAppRing">@color/system_on_theme_app_ring_light</item>
+ <item name="customColorBrandA">@color/system_brand_a_light</item>
+ <item name="customColorBrandB">@color/system_brand_b_light</item>
+ <item name="customColorBrandC">@color/system_brand_c_light</item>
+ <item name="customColorBrandD">@color/system_brand_d_light</item>
+ <item name="customColorUnderSurface">@color/system_under_surface_light</item>
+ <item name="customColorShadeActive">@color/system_shade_active_light</item>
+ <item name="customColorOnShadeActive">@color/system_on_shade_active_light</item>
+ <item name="customColorOnShadeActiveVariant">@color/system_on_shade_active_variant_light</item>
+ <item name="customColorShadeInactive">@color/system_shade_inactive_light</item>
+ <item name="customColorOnShadeInactive">@color/system_on_shade_inactive_light</item>
+ <item name="customColorOnShadeInactiveVariant">@color/system_on_shade_inactive_variant_light</item>
+ <item name="customColorShadeDisabled">@color/system_shade_disabled_light</item>
+ <item name="customColorOverviewBackground">@color/system_overview_background_light</item>
</style>
<style name="Theme.DeviceDefault.Light.Voice" parent="Theme.Material.Light.Voice">
@@ -4055,6 +4891,28 @@
<item name="materialColorSecondary">@color/system_secondary_light</item>
<item name="materialColorTertiary">@color/system_tertiary_light</item>
<item name="materialColorError">@color/system_error_light</item>
+
+ <item name="customColorWidgetBackground">@color/system_widget_background_light</item>
+ <item name="customColorClockHour">@color/system_clock_hour_light</item>
+ <item name="customColorClockMinute">@color/system_clock_minute_light</item>
+ <item name="customColorClockSecond">@color/system_clock_second_light</item>
+ <item name="customColorThemeApp">@color/system_theme_app_light</item>
+ <item name="customColorOnThemeApp">@color/system_on_theme_app_light</item>
+ <item name="customColorThemeAppRing">@color/system_theme_app_ring_light</item>
+ <item name="customColorOnThemeAppRing">@color/system_on_theme_app_ring_light</item>
+ <item name="customColorBrandA">@color/system_brand_a_light</item>
+ <item name="customColorBrandB">@color/system_brand_b_light</item>
+ <item name="customColorBrandC">@color/system_brand_c_light</item>
+ <item name="customColorBrandD">@color/system_brand_d_light</item>
+ <item name="customColorUnderSurface">@color/system_under_surface_light</item>
+ <item name="customColorShadeActive">@color/system_shade_active_light</item>
+ <item name="customColorOnShadeActive">@color/system_on_shade_active_light</item>
+ <item name="customColorOnShadeActiveVariant">@color/system_on_shade_active_variant_light</item>
+ <item name="customColorShadeInactive">@color/system_shade_inactive_light</item>
+ <item name="customColorOnShadeInactive">@color/system_on_shade_inactive_light</item>
+ <item name="customColorOnShadeInactiveVariant">@color/system_on_shade_inactive_variant_light</item>
+ <item name="customColorShadeDisabled">@color/system_shade_disabled_light</item>
+ <item name="customColorOverviewBackground">@color/system_overview_background_light</item>
</style>
<!-- DeviceDefault theme for a window that should look like the Settings app. -->
@@ -4156,6 +5014,28 @@
<item name="materialColorSecondary">@color/system_secondary_light</item>
<item name="materialColorTertiary">@color/system_tertiary_light</item>
<item name="materialColorError">@color/system_error_light</item>
+
+ <item name="customColorWidgetBackground">@color/system_widget_background_light</item>
+ <item name="customColorClockHour">@color/system_clock_hour_light</item>
+ <item name="customColorClockMinute">@color/system_clock_minute_light</item>
+ <item name="customColorClockSecond">@color/system_clock_second_light</item>
+ <item name="customColorThemeApp">@color/system_theme_app_light</item>
+ <item name="customColorOnThemeApp">@color/system_on_theme_app_light</item>
+ <item name="customColorThemeAppRing">@color/system_theme_app_ring_light</item>
+ <item name="customColorOnThemeAppRing">@color/system_on_theme_app_ring_light</item>
+ <item name="customColorBrandA">@color/system_brand_a_light</item>
+ <item name="customColorBrandB">@color/system_brand_b_light</item>
+ <item name="customColorBrandC">@color/system_brand_c_light</item>
+ <item name="customColorBrandD">@color/system_brand_d_light</item>
+ <item name="customColorUnderSurface">@color/system_under_surface_light</item>
+ <item name="customColorShadeActive">@color/system_shade_active_light</item>
+ <item name="customColorOnShadeActive">@color/system_on_shade_active_light</item>
+ <item name="customColorOnShadeActiveVariant">@color/system_on_shade_active_variant_light</item>
+ <item name="customColorShadeInactive">@color/system_shade_inactive_light</item>
+ <item name="customColorOnShadeInactive">@color/system_on_shade_inactive_light</item>
+ <item name="customColorOnShadeInactiveVariant">@color/system_on_shade_inactive_variant_light</item>
+ <item name="customColorShadeDisabled">@color/system_shade_disabled_light</item>
+ <item name="customColorOverviewBackground">@color/system_overview_background_light</item>
</style>
<style name="Theme.DeviceDefault.SystemUI" parent="Theme.DeviceDefault.Light">
@@ -4238,6 +5118,28 @@
<item name="materialColorSecondary">@color/system_secondary_light</item>
<item name="materialColorTertiary">@color/system_tertiary_light</item>
<item name="materialColorError">@color/system_error_light</item>
+
+ <item name="customColorWidgetBackground">@color/system_widget_background_light</item>
+ <item name="customColorClockHour">@color/system_clock_hour_light</item>
+ <item name="customColorClockMinute">@color/system_clock_minute_light</item>
+ <item name="customColorClockSecond">@color/system_clock_second_light</item>
+ <item name="customColorThemeApp">@color/system_theme_app_light</item>
+ <item name="customColorOnThemeApp">@color/system_on_theme_app_light</item>
+ <item name="customColorThemeAppRing">@color/system_theme_app_ring_light</item>
+ <item name="customColorOnThemeAppRing">@color/system_on_theme_app_ring_light</item>
+ <item name="customColorBrandA">@color/system_brand_a_light</item>
+ <item name="customColorBrandB">@color/system_brand_b_light</item>
+ <item name="customColorBrandC">@color/system_brand_c_light</item>
+ <item name="customColorBrandD">@color/system_brand_d_light</item>
+ <item name="customColorUnderSurface">@color/system_under_surface_light</item>
+ <item name="customColorShadeActive">@color/system_shade_active_light</item>
+ <item name="customColorOnShadeActive">@color/system_on_shade_active_light</item>
+ <item name="customColorOnShadeActiveVariant">@color/system_on_shade_active_variant_light</item>
+ <item name="customColorShadeInactive">@color/system_shade_inactive_light</item>
+ <item name="customColorOnShadeInactive">@color/system_on_shade_inactive_light</item>
+ <item name="customColorOnShadeInactiveVariant">@color/system_on_shade_inactive_variant_light</item>
+ <item name="customColorShadeDisabled">@color/system_shade_disabled_light</item>
+ <item name="customColorOverviewBackground">@color/system_overview_background_light</item>
</style>
<style name="Theme.DeviceDefault.SystemUI.Dialog" parent="Theme.DeviceDefault.Light.Dialog">
@@ -4312,6 +5214,28 @@
<item name="materialColorSecondary">@color/system_secondary_light</item>
<item name="materialColorTertiary">@color/system_tertiary_light</item>
<item name="materialColorError">@color/system_error_light</item>
+
+ <item name="customColorWidgetBackground">@color/system_widget_background_light</item>
+ <item name="customColorClockHour">@color/system_clock_hour_light</item>
+ <item name="customColorClockMinute">@color/system_clock_minute_light</item>
+ <item name="customColorClockSecond">@color/system_clock_second_light</item>
+ <item name="customColorThemeApp">@color/system_theme_app_light</item>
+ <item name="customColorOnThemeApp">@color/system_on_theme_app_light</item>
+ <item name="customColorThemeAppRing">@color/system_theme_app_ring_light</item>
+ <item name="customColorOnThemeAppRing">@color/system_on_theme_app_ring_light</item>
+ <item name="customColorBrandA">@color/system_brand_a_light</item>
+ <item name="customColorBrandB">@color/system_brand_b_light</item>
+ <item name="customColorBrandC">@color/system_brand_c_light</item>
+ <item name="customColorBrandD">@color/system_brand_d_light</item>
+ <item name="customColorUnderSurface">@color/system_under_surface_light</item>
+ <item name="customColorShadeActive">@color/system_shade_active_light</item>
+ <item name="customColorOnShadeActive">@color/system_on_shade_active_light</item>
+ <item name="customColorOnShadeActiveVariant">@color/system_on_shade_active_variant_light</item>
+ <item name="customColorShadeInactive">@color/system_shade_inactive_light</item>
+ <item name="customColorOnShadeInactive">@color/system_on_shade_inactive_light</item>
+ <item name="customColorOnShadeInactiveVariant">@color/system_on_shade_inactive_variant_light</item>
+ <item name="customColorShadeDisabled">@color/system_shade_disabled_light</item>
+ <item name="customColorOverviewBackground">@color/system_overview_background_light</item>
</style>
<!-- Variant of {@link #Theme_DeviceDefault_Settings_Dark} with no action bar -->
@@ -4407,6 +5331,28 @@
<item name="materialColorSecondary">@color/system_secondary_dark</item>
<item name="materialColorTertiary">@color/system_tertiary_dark</item>
<item name="materialColorError">@color/system_error_dark</item>
+
+ <item name="customColorWidgetBackground">@color/system_widget_background_dark</item>
+ <item name="customColorClockHour">@color/system_clock_hour_dark</item>
+ <item name="customColorClockMinute">@color/system_clock_minute_dark</item>
+ <item name="customColorClockSecond">@color/system_clock_second_dark</item>
+ <item name="customColorThemeApp">@color/system_theme_app_dark</item>
+ <item name="customColorOnThemeApp">@color/system_on_theme_app_dark</item>
+ <item name="customColorThemeAppRing">@color/system_theme_app_ring_dark</item>
+ <item name="customColorOnThemeAppRing">@color/system_on_theme_app_ring_dark</item>
+ <item name="customColorBrandA">@color/system_brand_a_dark</item>
+ <item name="customColorBrandB">@color/system_brand_b_dark</item>
+ <item name="customColorBrandC">@color/system_brand_c_dark</item>
+ <item name="customColorBrandD">@color/system_brand_d_dark</item>
+ <item name="customColorUnderSurface">@color/system_under_surface_dark</item>
+ <item name="customColorShadeActive">@color/system_shade_active_dark</item>
+ <item name="customColorOnShadeActive">@color/system_on_shade_active_dark</item>
+ <item name="customColorOnShadeActiveVariant">@color/system_on_shade_active_variant_dark</item>
+ <item name="customColorShadeInactive">@color/system_shade_inactive_dark</item>
+ <item name="customColorOnShadeInactive">@color/system_on_shade_inactive_dark</item>
+ <item name="customColorOnShadeInactiveVariant">@color/system_on_shade_inactive_variant_dark</item>
+ <item name="customColorShadeDisabled">@color/system_shade_disabled_dark</item>
+ <item name="customColorOverviewBackground">@color/system_overview_background_dark</item>
</style>
<style name="Theme.DeviceDefault.Settings.DialogBase" parent="Theme.Material.Light.BaseDialog">
@@ -4486,6 +5432,28 @@
<item name="materialColorSecondary">@color/system_secondary_light</item>
<item name="materialColorTertiary">@color/system_tertiary_light</item>
<item name="materialColorError">@color/system_error_light</item>
+
+ <item name="customColorWidgetBackground">@color/system_widget_background_light</item>
+ <item name="customColorClockHour">@color/system_clock_hour_light</item>
+ <item name="customColorClockMinute">@color/system_clock_minute_light</item>
+ <item name="customColorClockSecond">@color/system_clock_second_light</item>
+ <item name="customColorThemeApp">@color/system_theme_app_light</item>
+ <item name="customColorOnThemeApp">@color/system_on_theme_app_light</item>
+ <item name="customColorThemeAppRing">@color/system_theme_app_ring_light</item>
+ <item name="customColorOnThemeAppRing">@color/system_on_theme_app_ring_light</item>
+ <item name="customColorBrandA">@color/system_brand_a_light</item>
+ <item name="customColorBrandB">@color/system_brand_b_light</item>
+ <item name="customColorBrandC">@color/system_brand_c_light</item>
+ <item name="customColorBrandD">@color/system_brand_d_light</item>
+ <item name="customColorUnderSurface">@color/system_under_surface_light</item>
+ <item name="customColorShadeActive">@color/system_shade_active_light</item>
+ <item name="customColorOnShadeActive">@color/system_on_shade_active_light</item>
+ <item name="customColorOnShadeActiveVariant">@color/system_on_shade_active_variant_light</item>
+ <item name="customColorShadeInactive">@color/system_shade_inactive_light</item>
+ <item name="customColorOnShadeInactive">@color/system_on_shade_inactive_light</item>
+ <item name="customColorOnShadeInactiveVariant">@color/system_on_shade_inactive_variant_light</item>
+ <item name="customColorShadeDisabled">@color/system_shade_disabled_light</item>
+ <item name="customColorOverviewBackground">@color/system_overview_background_light</item>
</style>
<style name="Theme.DeviceDefault.Settings.Dialog" parent="Theme.DeviceDefault.Settings.DialogBase">
@@ -4605,6 +5573,28 @@
<item name="materialColorSecondary">@color/system_secondary_light</item>
<item name="materialColorTertiary">@color/system_tertiary_light</item>
<item name="materialColorError">@color/system_error_light</item>
+
+ <item name="customColorWidgetBackground">@color/system_widget_background_light</item>
+ <item name="customColorClockHour">@color/system_clock_hour_light</item>
+ <item name="customColorClockMinute">@color/system_clock_minute_light</item>
+ <item name="customColorClockSecond">@color/system_clock_second_light</item>
+ <item name="customColorThemeApp">@color/system_theme_app_light</item>
+ <item name="customColorOnThemeApp">@color/system_on_theme_app_light</item>
+ <item name="customColorThemeAppRing">@color/system_theme_app_ring_light</item>
+ <item name="customColorOnThemeAppRing">@color/system_on_theme_app_ring_light</item>
+ <item name="customColorBrandA">@color/system_brand_a_light</item>
+ <item name="customColorBrandB">@color/system_brand_b_light</item>
+ <item name="customColorBrandC">@color/system_brand_c_light</item>
+ <item name="customColorBrandD">@color/system_brand_d_light</item>
+ <item name="customColorUnderSurface">@color/system_under_surface_light</item>
+ <item name="customColorShadeActive">@color/system_shade_active_light</item>
+ <item name="customColorOnShadeActive">@color/system_on_shade_active_light</item>
+ <item name="customColorOnShadeActiveVariant">@color/system_on_shade_active_variant_light</item>
+ <item name="customColorShadeInactive">@color/system_shade_inactive_light</item>
+ <item name="customColorOnShadeInactive">@color/system_on_shade_inactive_light</item>
+ <item name="customColorOnShadeInactiveVariant">@color/system_on_shade_inactive_variant_light</item>
+ <item name="customColorShadeDisabled">@color/system_shade_disabled_light</item>
+ <item name="customColorOverviewBackground">@color/system_overview_background_light</item>
</style>
<style name="Theme.DeviceDefault.Settings.Dialog.Alert" parent="Theme.Material.Settings.Dialog.Alert">
@@ -4702,6 +5692,28 @@
<item name="materialColorSecondary">@color/system_secondary_light</item>
<item name="materialColorTertiary">@color/system_tertiary_light</item>
<item name="materialColorError">@color/system_error_light</item>
+
+ <item name="customColorWidgetBackground">@color/system_widget_background_light</item>
+ <item name="customColorClockHour">@color/system_clock_hour_light</item>
+ <item name="customColorClockMinute">@color/system_clock_minute_light</item>
+ <item name="customColorClockSecond">@color/system_clock_second_light</item>
+ <item name="customColorThemeApp">@color/system_theme_app_light</item>
+ <item name="customColorOnThemeApp">@color/system_on_theme_app_light</item>
+ <item name="customColorThemeAppRing">@color/system_theme_app_ring_light</item>
+ <item name="customColorOnThemeAppRing">@color/system_on_theme_app_ring_light</item>
+ <item name="customColorBrandA">@color/system_brand_a_light</item>
+ <item name="customColorBrandB">@color/system_brand_b_light</item>
+ <item name="customColorBrandC">@color/system_brand_c_light</item>
+ <item name="customColorBrandD">@color/system_brand_d_light</item>
+ <item name="customColorUnderSurface">@color/system_under_surface_light</item>
+ <item name="customColorShadeActive">@color/system_shade_active_light</item>
+ <item name="customColorOnShadeActive">@color/system_on_shade_active_light</item>
+ <item name="customColorOnShadeActiveVariant">@color/system_on_shade_active_variant_light</item>
+ <item name="customColorShadeInactive">@color/system_shade_inactive_light</item>
+ <item name="customColorOnShadeInactive">@color/system_on_shade_inactive_light</item>
+ <item name="customColorOnShadeInactiveVariant">@color/system_on_shade_inactive_variant_light</item>
+ <item name="customColorShadeDisabled">@color/system_shade_disabled_light</item>
+ <item name="customColorOverviewBackground">@color/system_overview_background_light</item>
</style>
<style name="Theme.DeviceDefault.Settings.Dialog.NoActionBar" parent="Theme.DeviceDefault.Light.Dialog.NoActionBar" />
@@ -4825,6 +5837,28 @@
<item name="materialColorSecondary">@color/system_secondary_dark</item>
<item name="materialColorTertiary">@color/system_tertiary_dark</item>
<item name="materialColorError">@color/system_error_dark</item>
+
+ <item name="customColorWidgetBackground">@color/system_widget_background_dark</item>
+ <item name="customColorClockHour">@color/system_clock_hour_dark</item>
+ <item name="customColorClockMinute">@color/system_clock_minute_dark</item>
+ <item name="customColorClockSecond">@color/system_clock_second_dark</item>
+ <item name="customColorThemeApp">@color/system_theme_app_dark</item>
+ <item name="customColorOnThemeApp">@color/system_on_theme_app_dark</item>
+ <item name="customColorThemeAppRing">@color/system_theme_app_ring_dark</item>
+ <item name="customColorOnThemeAppRing">@color/system_on_theme_app_ring_dark</item>
+ <item name="customColorBrandA">@color/system_brand_a_dark</item>
+ <item name="customColorBrandB">@color/system_brand_b_dark</item>
+ <item name="customColorBrandC">@color/system_brand_c_dark</item>
+ <item name="customColorBrandD">@color/system_brand_d_dark</item>
+ <item name="customColorUnderSurface">@color/system_under_surface_dark</item>
+ <item name="customColorShadeActive">@color/system_shade_active_dark</item>
+ <item name="customColorOnShadeActive">@color/system_on_shade_active_dark</item>
+ <item name="customColorOnShadeActiveVariant">@color/system_on_shade_active_variant_dark</item>
+ <item name="customColorShadeInactive">@color/system_shade_inactive_dark</item>
+ <item name="customColorOnShadeInactive">@color/system_on_shade_inactive_dark</item>
+ <item name="customColorOnShadeInactiveVariant">@color/system_on_shade_inactive_variant_dark</item>
+ <item name="customColorShadeDisabled">@color/system_shade_disabled_dark</item>
+ <item name="customColorOverviewBackground">@color/system_overview_background_dark</item>
</style>
<style name="ThemeOverlay.DeviceDefault.Accent.Light">
@@ -4878,6 +5912,28 @@
<item name="materialColorSecondary">@color/system_secondary_light</item>
<item name="materialColorTertiary">@color/system_tertiary_light</item>
<item name="materialColorError">@color/system_error_light</item>
+
+ <item name="customColorWidgetBackground">@color/system_widget_background_light</item>
+ <item name="customColorClockHour">@color/system_clock_hour_light</item>
+ <item name="customColorClockMinute">@color/system_clock_minute_light</item>
+ <item name="customColorClockSecond">@color/system_clock_second_light</item>
+ <item name="customColorThemeApp">@color/system_theme_app_light</item>
+ <item name="customColorOnThemeApp">@color/system_on_theme_app_light</item>
+ <item name="customColorThemeAppRing">@color/system_theme_app_ring_light</item>
+ <item name="customColorOnThemeAppRing">@color/system_on_theme_app_ring_light</item>
+ <item name="customColorBrandA">@color/system_brand_a_light</item>
+ <item name="customColorBrandB">@color/system_brand_b_light</item>
+ <item name="customColorBrandC">@color/system_brand_c_light</item>
+ <item name="customColorBrandD">@color/system_brand_d_light</item>
+ <item name="customColorUnderSurface">@color/system_under_surface_light</item>
+ <item name="customColorShadeActive">@color/system_shade_active_light</item>
+ <item name="customColorOnShadeActive">@color/system_on_shade_active_light</item>
+ <item name="customColorOnShadeActiveVariant">@color/system_on_shade_active_variant_light</item>
+ <item name="customColorShadeInactive">@color/system_shade_inactive_light</item>
+ <item name="customColorOnShadeInactive">@color/system_on_shade_inactive_light</item>
+ <item name="customColorOnShadeInactiveVariant">@color/system_on_shade_inactive_variant_light</item>
+ <item name="customColorShadeDisabled">@color/system_shade_disabled_light</item>
+ <item name="customColorOverviewBackground">@color/system_overview_background_light</item>
</style>
<!-- Theme overlay that replaces colorAccent with the colorAccent from {@link #Theme_DeviceDefault_DayNight}. -->
@@ -4935,6 +5991,28 @@
<item name="materialColorSecondary">@color/system_secondary_dark</item>
<item name="materialColorTertiary">@color/system_tertiary_dark</item>
<item name="materialColorError">@color/system_error_dark</item>
+
+ <item name="customColorWidgetBackground">@color/system_widget_background_dark</item>
+ <item name="customColorClockHour">@color/system_clock_hour_dark</item>
+ <item name="customColorClockMinute">@color/system_clock_minute_dark</item>
+ <item name="customColorClockSecond">@color/system_clock_second_dark</item>
+ <item name="customColorThemeApp">@color/system_theme_app_dark</item>
+ <item name="customColorOnThemeApp">@color/system_on_theme_app_dark</item>
+ <item name="customColorThemeAppRing">@color/system_theme_app_ring_dark</item>
+ <item name="customColorOnThemeAppRing">@color/system_on_theme_app_ring_dark</item>
+ <item name="customColorBrandA">@color/system_brand_a_dark</item>
+ <item name="customColorBrandB">@color/system_brand_b_dark</item>
+ <item name="customColorBrandC">@color/system_brand_c_dark</item>
+ <item name="customColorBrandD">@color/system_brand_d_dark</item>
+ <item name="customColorUnderSurface">@color/system_under_surface_dark</item>
+ <item name="customColorShadeActive">@color/system_shade_active_dark</item>
+ <item name="customColorOnShadeActive">@color/system_on_shade_active_dark</item>
+ <item name="customColorOnShadeActiveVariant">@color/system_on_shade_active_variant_dark</item>
+ <item name="customColorShadeInactive">@color/system_shade_inactive_dark</item>
+ <item name="customColorOnShadeInactive">@color/system_on_shade_inactive_dark</item>
+ <item name="customColorOnShadeInactiveVariant">@color/system_on_shade_inactive_variant_dark</item>
+ <item name="customColorShadeDisabled">@color/system_shade_disabled_dark</item>
+ <item name="customColorOverviewBackground">@color/system_overview_background_dark</item>
</style>
<style name="Theme.DeviceDefault.Light.Dialog.Alert.UserSwitchingDialog" parent="Theme.DeviceDefault.NoActionBar.Fullscreen">
@@ -4988,6 +6066,28 @@
<item name="materialColorSecondary">@color/system_secondary_light</item>
<item name="materialColorTertiary">@color/system_tertiary_light</item>
<item name="materialColorError">@color/system_error_light</item>
+
+ <item name="customColorWidgetBackground">@color/system_widget_background_light</item>
+ <item name="customColorClockHour">@color/system_clock_hour_light</item>
+ <item name="customColorClockMinute">@color/system_clock_minute_light</item>
+ <item name="customColorClockSecond">@color/system_clock_second_light</item>
+ <item name="customColorThemeApp">@color/system_theme_app_light</item>
+ <item name="customColorOnThemeApp">@color/system_on_theme_app_light</item>
+ <item name="customColorThemeAppRing">@color/system_theme_app_ring_light</item>
+ <item name="customColorOnThemeAppRing">@color/system_on_theme_app_ring_light</item>
+ <item name="customColorBrandA">@color/system_brand_a_light</item>
+ <item name="customColorBrandB">@color/system_brand_b_light</item>
+ <item name="customColorBrandC">@color/system_brand_c_light</item>
+ <item name="customColorBrandD">@color/system_brand_d_light</item>
+ <item name="customColorUnderSurface">@color/system_under_surface_light</item>
+ <item name="customColorShadeActive">@color/system_shade_active_light</item>
+ <item name="customColorOnShadeActive">@color/system_on_shade_active_light</item>
+ <item name="customColorOnShadeActiveVariant">@color/system_on_shade_active_variant_light</item>
+ <item name="customColorShadeInactive">@color/system_shade_inactive_light</item>
+ <item name="customColorOnShadeInactive">@color/system_on_shade_inactive_light</item>
+ <item name="customColorOnShadeInactiveVariant">@color/system_on_shade_inactive_variant_light</item>
+ <item name="customColorShadeDisabled">@color/system_shade_disabled_light</item>
+ <item name="customColorOverviewBackground">@color/system_overview_background_light</item>
</style>
<style name="Theme.DeviceDefault.Notification" parent="@style/Theme.Material.Notification">
@@ -5052,6 +6152,28 @@
<item name="materialColorSecondary">@color/system_secondary_dark</item>
<item name="materialColorTertiary">@color/system_tertiary_dark</item>
<item name="materialColorError">@color/system_error_dark</item>
+
+ <item name="customColorWidgetBackground">@color/system_widget_background_dark</item>
+ <item name="customColorClockHour">@color/system_clock_hour_dark</item>
+ <item name="customColorClockMinute">@color/system_clock_minute_dark</item>
+ <item name="customColorClockSecond">@color/system_clock_second_dark</item>
+ <item name="customColorThemeApp">@color/system_theme_app_dark</item>
+ <item name="customColorOnThemeApp">@color/system_on_theme_app_dark</item>
+ <item name="customColorThemeAppRing">@color/system_theme_app_ring_dark</item>
+ <item name="customColorOnThemeAppRing">@color/system_on_theme_app_ring_dark</item>
+ <item name="customColorBrandA">@color/system_brand_a_dark</item>
+ <item name="customColorBrandB">@color/system_brand_b_dark</item>
+ <item name="customColorBrandC">@color/system_brand_c_dark</item>
+ <item name="customColorBrandD">@color/system_brand_d_dark</item>
+ <item name="customColorUnderSurface">@color/system_under_surface_dark</item>
+ <item name="customColorShadeActive">@color/system_shade_active_dark</item>
+ <item name="customColorOnShadeActive">@color/system_on_shade_active_dark</item>
+ <item name="customColorOnShadeActiveVariant">@color/system_on_shade_active_variant_dark</item>
+ <item name="customColorShadeInactive">@color/system_shade_inactive_dark</item>
+ <item name="customColorOnShadeInactive">@color/system_on_shade_inactive_dark</item>
+ <item name="customColorOnShadeInactiveVariant">@color/system_on_shade_inactive_variant_dark</item>
+ <item name="customColorShadeDisabled">@color/system_shade_disabled_dark</item>
+ <item name="customColorOverviewBackground">@color/system_overview_background_dark</item>
</style>
<style name="Theme.DeviceDefault.AutofillHalfScreenDialogList" parent="Theme.DeviceDefault.DayNight">
<item name="colorListDivider">@color/list_divider_opacity_device_default_light</item>
diff --git a/core/tests/coretests/Android.bp b/core/tests/coretests/Android.bp
index 436ba15..eb3c84a 100644
--- a/core/tests/coretests/Android.bp
+++ b/core/tests/coretests/Android.bp
@@ -260,6 +260,7 @@
"src/com/android/internal/os/**/*.java",
"src/com/android/internal/util/**/*.java",
"src/com/android/internal/power/EnergyConsumerStatsTest.java",
+ "src/com/android/internal/ravenwood/**/*.java",
// Pull in R.java from FrameworksCoreTests-resonly, not from FrameworksCoreTests,
// to avoid having a dependency to FrameworksCoreTests.
diff --git a/core/tests/coretests/src/android/app/servertransaction/ClientTransactionListenerControllerTest.java b/core/tests/coretests/src/android/app/servertransaction/ClientTransactionListenerControllerTest.java
index b6f4429..ee1d1e1 100644
--- a/core/tests/coretests/src/android/app/servertransaction/ClientTransactionListenerControllerTest.java
+++ b/core/tests/coretests/src/android/app/servertransaction/ClientTransactionListenerControllerTest.java
@@ -29,6 +29,7 @@
import static org.mockito.Mockito.clearInvocations;
import static org.mockito.Mockito.doNothing;
import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.doThrow;
import static org.mockito.Mockito.inOrder;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never;
@@ -65,6 +66,7 @@
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
+import java.util.concurrent.RejectedExecutionException;
import java.util.function.BiConsumer;
/**
@@ -221,4 +223,17 @@
123 /* newDisplayId */, true /* shouldReportConfigChange*/);
inOrder.verify(mController).onContextConfigurationPostChanged(context);
}
+
+ @Test
+ public void testDisplayListenerHandlerClosed() {
+ doReturn(123).when(mActivity).getDisplayId();
+ doThrow(new RejectedExecutionException()).when(mController).onDisplayChanged(123);
+
+ mController.onContextConfigurationPreChanged(mActivity);
+ mConfiguration.windowConfiguration.setMaxBounds(new Rect(0, 0, 100, 200));
+ mController.onContextConfigurationPostChanged(mActivity);
+
+ // No crash
+ verify(mController).onDisplayChanged(123);
+ }
}
diff --git a/core/tests/coretests/src/android/net/OWNERS b/core/tests/coretests/src/android/net/OWNERS
index a779c00..beb77dc 100644
--- a/core/tests/coretests/src/android/net/OWNERS
+++ b/core/tests/coretests/src/android/net/OWNERS
@@ -1,4 +1,5 @@
include /services/core/java/com/android/server/net/OWNERS
-per-file SSL*,Uri*,Url* = prb@google.com,oth@google.com,narayan@google.com,ngeoffray@google.com
+per-file SSL*,Url* = prb@google.com,oth@google.com,narayan@google.com,ngeoffray@google.com
per-file SntpClient* = file:/services/core/java/com/android/server/timedetector/OWNERS
+per-file Uri* = varunshah@google.com
diff --git a/core/tests/coretests/src/android/net/UriTest.java b/core/tests/coretests/src/android/net/UriTest.java
index 2a4ca79..57cb158 100644
--- a/core/tests/coretests/src/android/net/UriTest.java
+++ b/core/tests/coretests/src/android/net/UriTest.java
@@ -18,6 +18,7 @@
import android.content.ContentUris;
import android.os.Parcel;
+import android.platform.test.annotations.AsbSecurityTest;
import androidx.test.filters.SmallTest;
@@ -86,6 +87,16 @@
assertNull(u.getHost());
}
+ @AsbSecurityTest(cveBugId = 261721900)
+ @SmallTest
+ public void testSchemeSanitization() {
+ Uri uri = new Uri.Builder()
+ .scheme("http://https://evil.com:/te:st/")
+ .authority("google.com").path("one/way").build();
+ assertEquals("httphttpsevil.com:/te:st/", uri.getScheme());
+ assertEquals("httphttpsevil.com:/te:st/://google.com/one/way", uri.toString());
+ }
+
@SmallTest
public void testStringUri() {
assertEquals("bob lee",
diff --git a/core/tests/coretests/src/android/tracing/perfetto/DataSourceTest.java b/core/tests/coretests/src/android/tracing/perfetto/DataSourceTest.java
index 6ae3d65..df9a89e 100644
--- a/core/tests/coretests/src/android/tracing/perfetto/DataSourceTest.java
+++ b/core/tests/coretests/src/android/tracing/perfetto/DataSourceTest.java
@@ -674,8 +674,6 @@
protoOutputStream.write(SINGLE_INT, singleIntValue);
protoOutputStream.end(payloadToken);
protoOutputStream.end(forTestingToken);
-
- ctx.flush();
}),
(args) -> {}
);
diff --git a/core/tests/coretests/src/android/view/ImeBackAnimationControllerTest.java b/core/tests/coretests/src/android/view/ImeBackAnimationControllerTest.java
index 57bbb1c..5917cc1 100644
--- a/core/tests/coretests/src/android/view/ImeBackAnimationControllerTest.java
+++ b/core/tests/coretests/src/android/view/ImeBackAnimationControllerTest.java
@@ -37,6 +37,7 @@
import android.content.Context;
import android.graphics.Insets;
import android.platform.test.annotations.Presubmit;
+import android.util.SparseArray;
import android.view.animation.BackGestureInterpolator;
import android.view.animation.Interpolator;
import android.view.inputmethod.InputMethodManager;
@@ -54,6 +55,8 @@
import org.mockito.Mockito;
import org.mockito.MockitoAnnotations;
+import java.lang.reflect.Field;
+
/**
* Tests for {@link ImeBackAnimationController}.
*
@@ -104,6 +107,13 @@
when(mInsetsController.getHost()).thenReturn(mViewRootInsetsControllerHost);
when(mViewRootInsetsControllerHost.getInputMethodManager()).thenReturn(
inputMethodManager);
+ try {
+ Field field = InsetsController.class.getDeclaredField("mSourceConsumers");
+ field.setAccessible(true);
+ field.set(mInsetsController, new SparseArray<InsetsSourceConsumer>());
+ } catch (NoSuchFieldException | IllegalAccessException e) {
+ throw new RuntimeException("Unable to set mSourceConsumers", e);
+ }
});
InstrumentationRegistry.getInstrumentation().waitForIdleSync();
}
diff --git a/core/tests/coretests/src/android/view/ViewFrameRateTest.java b/core/tests/coretests/src/android/view/ViewFrameRateTest.java
index bc0ae9f..0b1b40c 100644
--- a/core/tests/coretests/src/android/view/ViewFrameRateTest.java
+++ b/core/tests/coretests/src/android/view/ViewFrameRateTest.java
@@ -623,28 +623,6 @@
assertEquals(FRAME_RATE_CATEGORY_HIGH_HINT, mViewRoot.getLastPreferredFrameRateCategory());
}
- @Test
- @RequiresFlagsEnabled({FLAG_TOOLKIT_SET_FRAME_RATE_READ_ONLY,
- FLAG_TOOLKIT_FRAME_RATE_VIEW_ENABLING_READ_ONLY
- })
- public void idleDetected() throws Throwable {
- waitForFrameRateCategoryToSettle();
- mActivityRule.runOnUiThread(() -> {
- mMovingView.setRequestedFrameRate(View.REQUESTED_FRAME_RATE_CATEGORY_HIGH);
- mMovingView.setFrameContentVelocity(Float.MAX_VALUE);
- mMovingView.invalidate();
- runAfterDraw(() -> assertEquals(FRAME_RATE_CATEGORY_HIGH,
- mViewRoot.getLastPreferredFrameRateCategory()));
- });
- waitForAfterDraw();
-
- // Wait for idle timeout
- Thread.sleep(500);
- assertEquals(0f, mViewRoot.getLastPreferredFrameRate());
- assertEquals(FRAME_RATE_CATEGORY_NO_PREFERENCE,
- mViewRoot.getLastPreferredFrameRateCategory());
- }
-
private void runAfterDraw(@NonNull Runnable runnable) {
Handler handler = new Handler(Looper.getMainLooper());
mAfterDrawLatch = new CountDownLatch(1);
diff --git a/core/tests/coretests/src/com/android/internal/content/res/OverlayConfigParserTest.java b/core/tests/coretests/src/com/android/internal/content/res/OverlayConfigParserTest.java
new file mode 100644
index 0000000..4eccbe5
--- /dev/null
+++ b/core/tests/coretests/src/com/android/internal/content/res/OverlayConfigParserTest.java
@@ -0,0 +1,119 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.internal.content.res;
+
+import static com.android.internal.content.om.OverlayConfigParser.SysPropWrapper;
+
+import static org.junit.Assert.assertEquals;
+
+import android.platform.test.annotations.Presubmit;
+
+import androidx.test.runner.AndroidJUnit4;
+
+import com.android.internal.content.om.OverlayConfigParser;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+@Presubmit
+@RunWith(AndroidJUnit4.class)
+public class OverlayConfigParserTest {
+ @Test(expected = IllegalStateException.class)
+ public void testMergePropNotRoProp() {
+ SysPropWrapper sysProp = p -> {
+ return "dummy_value";
+ };
+ OverlayConfigParser.expandProperty("${persist.value}/path", sysProp);
+ }
+
+ @Test(expected = IllegalStateException.class)
+ public void testMergePropMissingEndBracket() {
+ SysPropWrapper sysProp = p -> {
+ return "dummy_value";
+ };
+ OverlayConfigParser.expandProperty("${ro.value/path", sysProp);
+ }
+
+ @Test(expected = IllegalStateException.class)
+ public void testMergeOnlyPropStart() {
+ SysPropWrapper sysProp = p -> {
+ return "dummy_value";
+ };
+ OverlayConfigParser.expandProperty("path/${", sysProp);
+ }
+
+ @Test(expected = IllegalStateException.class)
+ public void testMergePropInProp() {
+ SysPropWrapper sysProp = p -> {
+ return "dummy_value";
+ };
+ OverlayConfigParser.expandProperty("path/${${ro.value}}", sysProp);
+ }
+
+ /**
+ * The path is only allowed to contain one property.
+ */
+ @Test(expected = IllegalStateException.class)
+ public void testMergePropMultipleProps() {
+ SysPropWrapper sysProp = p -> {
+ return "dummy_value";
+ };
+ OverlayConfigParser.expandProperty("${ro.value}/path${ro.value2}/path", sysProp);
+ }
+
+ @Test
+ public void testMergePropOneProp() {
+ final SysPropWrapper sysProp = p -> {
+ if ("ro.value".equals(p)) {
+ return "dummy_value";
+ } else {
+ return "invalid";
+ }
+ };
+
+ // Property in the beginnig of the string
+ String result = OverlayConfigParser.expandProperty("${ro.value}/path",
+ sysProp);
+ assertEquals("dummy_value/path", result);
+
+ // Property in the middle of the string
+ result = OverlayConfigParser.expandProperty("path/${ro.value}/file",
+ sysProp);
+ assertEquals("path/dummy_value/file", result);
+
+ // Property at the of the string
+ result = OverlayConfigParser.expandProperty("path/${ro.value}",
+ sysProp);
+ assertEquals("path/dummy_value", result);
+
+ // Property is the entire string
+ result = OverlayConfigParser.expandProperty("${ro.value}",
+ sysProp);
+ assertEquals("dummy_value", result);
+ }
+
+ @Test
+ public void testMergePropNoProp() {
+ final SysPropWrapper sysProp = p -> {
+ return "dummy_value";
+ };
+
+ final String path = "no_props/path";
+ String result = OverlayConfigParser.expandProperty(path, sysProp);
+ assertEquals(path, result);
+ }
+}
diff --git a/core/tests/coretests/src/com/android/internal/os/PowerStatsTest.java b/core/tests/coretests/src/com/android/internal/os/PowerStatsTest.java
index baab3b2..4846ed27 100644
--- a/core/tests/coretests/src/com/android/internal/os/PowerStatsTest.java
+++ b/core/tests/coretests/src/com/android/internal/os/PowerStatsTest.java
@@ -22,6 +22,7 @@
import android.os.Parcel;
import android.os.PersistableBundle;
import android.platform.test.ravenwood.RavenwoodRule;
+import android.util.IndentingPrintWriter;
import android.util.SparseArray;
import android.util.Xml;
@@ -31,6 +32,8 @@
import com.android.modules.utils.TypedXmlPullParser;
import com.android.modules.utils.TypedXmlSerializer;
+import com.google.common.truth.StringSubject;
+
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
@@ -38,6 +41,7 @@
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
+import java.io.StringWriter;
import java.nio.charset.StandardCharsets;
@RunWith(AndroidJUnit4.class)
@@ -56,6 +60,9 @@
extras.putBoolean("hasPowerMonitor", true);
SparseArray<String> stateLabels = new SparseArray<>();
stateLabels.put(0x0F, "idle");
+ extras.putString(PowerStats.Descriptor.EXTRA_DEVICE_STATS_FORMAT, "device:0[3]");
+ extras.putString(PowerStats.Descriptor.EXTRA_STATE_STATS_FORMAT, "state:0");
+ extras.putString(PowerStats.Descriptor.EXTRA_UID_STATS_FORMAT, "a:0 b:1");
mDescriptor = new PowerStats.Descriptor(BatteryConsumer.POWER_COMPONENT_CPU, 3, stateLabels,
1, 2, extras);
mRegistry.register(mDescriptor);
@@ -191,4 +198,68 @@
newParcel.setDataPosition(0);
return newParcel;
}
+
+ @Test
+ public void formatForBatteryHistory() {
+ PowerStats stats = new PowerStats(mDescriptor);
+ stats.durationMs = 1234;
+ stats.stats[0] = 10;
+ stats.stats[1] = 20;
+ stats.stats[2] = 30;
+ stats.stateStats.put(0x0F, new long[]{16});
+ stats.stateStats.put(0xF0, new long[]{17});
+ stats.uidStats.put(42, new long[]{40, 50});
+ stats.uidStats.put(99, new long[]{60, 70});
+
+ assertThat(stats.formatForBatteryHistory(" #"))
+ .isEqualTo("duration=1234 cpu="
+ + "device: [10, 20, 30]"
+ + " (idle) state: 16"
+ + " (cpu-f0) state: 17"
+ + " #42: a: 40 b: 50"
+ + " #99: a: 60 b: 70");
+ }
+
+ @Test
+ public void dump() {
+ PowerStats stats = new PowerStats(mDescriptor);
+ stats.durationMs = 1234;
+ stats.stats[0] = 10;
+ stats.stats[1] = 20;
+ stats.stats[2] = 30;
+ stats.stateStats.put(0x0F, new long[]{16});
+ stats.stateStats.put(0xF0, new long[]{17});
+ stats.uidStats.put(42, new long[]{40, 50});
+ stats.uidStats.put(99, new long[]{60, 70});
+
+ StringWriter sw = new StringWriter();
+ IndentingPrintWriter pw = new IndentingPrintWriter(sw);
+ stats.dump(pw);
+ pw.flush();
+ String dump = sw.toString();
+
+ assertThat(dump).contains("duration=1234");
+ assertThat(dump).contains("device: [10, 20, 30]");
+ assertThat(dump).contains("(idle) state: 16");
+ assertThat(dump).contains("(cpu-f0) state: 17");
+ assertThat(dump).contains("UID 42: a: 40 b: 50");
+ assertThat(dump).contains("UID 99: a: 60 b: 70");
+ }
+
+ @Test
+ public void formatter() {
+ assertThatFormatted(new long[]{12, 34, 56}, "a:0 b:1[2]")
+ .isEqualTo("a: 12 b: [34, 56]");
+ assertThatFormatted(new long[]{12, 0, 0}, "a:0? b:1[2]?")
+ .isEqualTo("a: 12");
+ assertThatFormatted(new long[]{0, 34, 56}, "a:0? b:1[2]?")
+ .isEqualTo("b: [34, 56]");
+ assertThatFormatted(new long[]{3141592, 2000000, 1414213}, "pi:0p sqrt:1[2]p")
+ .isEqualTo("pi: 3.14 sqrt: [2.00, 1.41]");
+ }
+
+ private static StringSubject assertThatFormatted(long[] stats, String format) {
+ return assertThat(new PowerStats.PowerStatsFormatter(format)
+ .format(stats));
+ }
}
diff --git a/core/tests/coretests/src/com/android/internal/ravenwood/RavenwoodEnvironmentTest.java b/core/tests/coretests/src/com/android/internal/ravenwood/RavenwoodEnvironmentTest.java
new file mode 100644
index 0000000..d1ef61b
--- /dev/null
+++ b/core/tests/coretests/src/com/android/internal/ravenwood/RavenwoodEnvironmentTest.java
@@ -0,0 +1,38 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.internal.ravenwood;
+
+import static junit.framework.TestCase.assertEquals;
+
+import android.platform.test.ravenwood.RavenwoodRule;
+
+import androidx.test.runner.AndroidJUnit4;
+
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+@RunWith(AndroidJUnit4.class)
+public class RavenwoodEnvironmentTest {
+ @Rule
+ public final RavenwoodRule mRavenwood = new RavenwoodRule();
+
+ @Test
+ public void testIsRunningOnRavenwood() {
+ assertEquals(RavenwoodRule.isUnderRavenwood(),
+ RavenwoodEnvironment.getInstance().isRunningOnRavenwood());
+ }
+}
diff --git a/data/etc/core.protolog.pb b/data/etc/core.protolog.pb
index 000f6ef..b41a607 100644
--- a/data/etc/core.protolog.pb
+++ b/data/etc/core.protolog.pb
Binary files differ
diff --git a/data/etc/services.core.protolog.json b/data/etc/services.core.protolog.json
index 01deb49..b93cd46 100644
--- a/data/etc/services.core.protolog.json
+++ b/data/etc/services.core.protolog.json
@@ -583,6 +583,12 @@
"group": "WM_DEBUG_STATES",
"at": "com\/android\/server\/wm\/ActivityRecord.java"
},
+ "7211222997110112110": {
+ "message": "Refreshing activity for freeform camera compatibility treatment, activityRecord=%s",
+ "level": "VERBOSE",
+ "group": "WM_DEBUG_STATES",
+ "at": "com\/android\/server\/wm\/ActivityRefresher.java"
+ },
"1665699123574159131": {
"message": "Starting activity when config will change = %b",
"level": "VERBOSE",
@@ -1771,12 +1777,6 @@
"group": "WM_DEBUG_ORIENTATION",
"at": "com\/android\/server\/wm\/DisplayRotationCompatPolicy.java"
},
- "-7756685416834187936": {
- "message": "Refreshing activity for camera compatibility treatment, activityRecord=%s",
- "level": "VERBOSE",
- "group": "WM_DEBUG_STATES",
- "at": "com\/android\/server\/wm\/DisplayRotationCompatPolicy.java"
- },
"-5176775281239247368": {
"message": "Reverting orientation after camera compat force rotation",
"level": "VERBOSE",
diff --git a/data/fonts/font_fallback_cjkvf.xml b/data/fonts/font_fallback_cjkvf.xml
index ac1b064..a4ee825 100644
--- a/data/fonts/font_fallback_cjkvf.xml
+++ b/data/fonts/font_fallback_cjkvf.xml
@@ -768,7 +768,7 @@
<font weight="400" style="normal">NotoSansSymbols-Regular-Subsetted.ttf</font>
</family>
<family lang="zh-Hans">
- <font weight="400" style="normal" index="2" postScriptName="NotoSansCJKjp-Thin"
+ <font weight="400" style="normal" index="2" postScriptName="NotoSansCJKJP-Regular"
supportedAxes="wght">
NotoSansCJK-Regular.ttc
<!-- The default instance of NotoSansCJK-Regular.ttc is wght=100, so specify wght=400
@@ -780,7 +780,7 @@
</font>
</family>
<family lang="zh-Hant,zh-Bopo">
- <font weight="400" style="normal" index="3" postScriptName="NotoSansCJKjp-Thin"
+ <font weight="400" style="normal" index="3" postScriptName="NotoSansCJKJP-Regular"
supportedAxes="wght">
NotoSansCJK-Regular.ttc
<!-- The default instance of NotoSansCJK-Regular.ttc is wght=100, so specify wght=400
@@ -792,7 +792,7 @@
</font>
</family>
<family lang="ja">
- <font weight="400" style="normal" index="0" postScriptName="NotoSansCJKjp-Thin"
+ <font weight="400" style="normal" index="0" postScriptName="NotoSansCJKJP-Regular"
supportedAxes="wght">
NotoSansCJK-Regular.ttc
<!-- The default instance of NotoSansCJK-Regular.ttc is wght=100, so specify wght=400
@@ -810,7 +810,7 @@
</font>
</family>
<family lang="ko">
- <font weight="400" style="normal" index="1" postScriptName="NotoSansCJKjp-Thin"
+ <font weight="400" style="normal" index="1" postScriptName="NotoSansCJKJP-Regular"
supportedAxes="wght">
NotoSansCJK-Regular.ttc
<!-- The default instance of NotoSansCJK-Regular.ttc is wght=100, so specify wght=400
diff --git a/data/fonts/fonts_cjkvf.xml b/data/fonts/fonts_cjkvf.xml
index 9545ae7..8cbc300 100644
--- a/data/fonts/fonts_cjkvf.xml
+++ b/data/fonts/fonts_cjkvf.xml
@@ -1409,39 +1409,39 @@
<font weight="400" style="normal">NotoSansSymbols-Regular-Subsetted.ttf</font>
</family>
<family lang="zh-Hans">
- <font weight="100" style="normal" index="2" postScriptName="NotoSansCJKjp-Thin">
+ <font weight="100" style="normal" index="2" postScriptName="NotoSansCJKJP-Regular">
NotoSansCJK-Regular.ttc
<axis tag="wght" stylevalue="100"/>
</font>
- <font weight="200" style="normal" index="2" postScriptName="NotoSansCJKjp-Thin">
+ <font weight="200" style="normal" index="2" postScriptName="NotoSansCJKJP-Regular">
NotoSansCJK-Regular.ttc
<axis tag="wght" stylevalue="200"/>
</font>
- <font weight="300" style="normal" index="2" postScriptName="NotoSansCJKjp-Thin">
+ <font weight="300" style="normal" index="2" postScriptName="NotoSansCJKJP-Regular">
NotoSansCJK-Regular.ttc
<axis tag="wght" stylevalue="300"/>
</font>
- <font weight="400" style="normal" index="2" postScriptName="NotoSansCJKjp-Thin">
+ <font weight="400" style="normal" index="2" postScriptName="NotoSansCJKJP-Regular">
NotoSansCJK-Regular.ttc
<axis tag="wght" stylevalue="400"/>
</font>
- <font weight="500" style="normal" index="2" postScriptName="NotoSansCJKjp-Thin">
+ <font weight="500" style="normal" index="2" postScriptName="NotoSansCJKJP-Regular">
NotoSansCJK-Regular.ttc
<axis tag="wght" stylevalue="500"/>
</font>
- <font weight="600" style="normal" index="2" postScriptName="NotoSansCJKjp-Thin">
+ <font weight="600" style="normal" index="2" postScriptName="NotoSansCJKJP-Regular">
NotoSansCJK-Regular.ttc
<axis tag="wght" stylevalue="600"/>
</font>
- <font weight="700" style="normal" index="2" postScriptName="NotoSansCJKjp-Thin">
+ <font weight="700" style="normal" index="2" postScriptName="NotoSansCJKJP-Regular">
NotoSansCJK-Regular.ttc
<axis tag="wght" stylevalue="700"/>
</font>
- <font weight="800" style="normal" index="2" postScriptName="NotoSansCJKjp-Thin">
+ <font weight="800" style="normal" index="2" postScriptName="NotoSansCJKJP-Regular">
NotoSansCJK-Regular.ttc
<axis tag="wght" stylevalue="800"/>
</font>
- <font weight="900" style="normal" index="2" postScriptName="NotoSansCJKjp-Thin">
+ <font weight="900" style="normal" index="2" postScriptName="NotoSansCJKJP-Regular">
NotoSansCJK-Regular.ttc
<axis tag="wght" stylevalue="900"/>
</font>
@@ -1450,39 +1450,39 @@
</font>
</family>
<family lang="zh-Hant,zh-Bopo">
- <font weight="100" style="normal" index="3" postScriptName="NotoSansCJKjp-Thin">
+ <font weight="100" style="normal" index="3" postScriptName="NotoSansCJKJP-Regular">
NotoSansCJK-Regular.ttc
<axis tag="wght" stylevalue="100"/>
</font>
- <font weight="200" style="normal" index="3" postScriptName="NotoSansCJKjp-Thin">
+ <font weight="200" style="normal" index="3" postScriptName="NotoSansCJKJP-Regular">
NotoSansCJK-Regular.ttc
<axis tag="wght" stylevalue="200"/>
</font>
- <font weight="300" style="normal" index="3" postScriptName="NotoSansCJKjp-Thin">
+ <font weight="300" style="normal" index="3" postScriptName="NotoSansCJKJP-Regular">
NotoSansCJK-Regular.ttc
<axis tag="wght" stylevalue="300"/>
</font>
- <font weight="400" style="normal" index="3" postScriptName="NotoSansCJKjp-Thin">
+ <font weight="400" style="normal" index="3" postScriptName="NotoSansCJKJP-Regular">
NotoSansCJK-Regular.ttc
<axis tag="wght" stylevalue="400"/>
</font>
- <font weight="500" style="normal" index="3" postScriptName="NotoSansCJKjp-Thin">
+ <font weight="500" style="normal" index="3" postScriptName="NotoSansCJKJP-Regular">
NotoSansCJK-Regular.ttc
<axis tag="wght" stylevalue="500"/>
</font>
- <font weight="600" style="normal" index="3" postScriptName="NotoSansCJKjp-Thin">
+ <font weight="600" style="normal" index="3" postScriptName="NotoSansCJKJP-Regular">
NotoSansCJK-Regular.ttc
<axis tag="wght" stylevalue="600"/>
</font>
- <font weight="700" style="normal" index="3" postScriptName="NotoSansCJKjp-Thin">
+ <font weight="700" style="normal" index="3" postScriptName="NotoSansCJKJP-Regular">
NotoSansCJK-Regular.ttc
<axis tag="wght" stylevalue="700"/>
</font>
- <font weight="800" style="normal" index="3" postScriptName="NotoSansCJKjp-Thin">
+ <font weight="800" style="normal" index="3" postScriptName="NotoSansCJKJP-Regular">
NotoSansCJK-Regular.ttc
<axis tag="wght" stylevalue="800"/>
</font>
- <font weight="900" style="normal" index="3" postScriptName="NotoSansCJKjp-Thin">
+ <font weight="900" style="normal" index="3" postScriptName="NotoSansCJKJP-Regular">
NotoSansCJK-Regular.ttc
<axis tag="wght" stylevalue="900"/>
</font>
@@ -1491,39 +1491,39 @@
</font>
</family>
<family lang="ja">
- <font weight="100" style="normal" index="0" postScriptName="NotoSansCJKjp-Thin">
+ <font weight="100" style="normal" index="0" postScriptName="NotoSansCJKJP-Regular">
NotoSansCJK-Regular.ttc
<axis tag="wght" stylevalue="100"/>
</font>
- <font weight="200" style="normal" index="0" postScriptName="NotoSansCJKjp-Thin">
+ <font weight="200" style="normal" index="0" postScriptName="NotoSansCJKJP-Regular">
NotoSansCJK-Regular.ttc
<axis tag="wght" stylevalue="200"/>
</font>
- <font weight="300" style="normal" index="0" postScriptName="NotoSansCJKjp-Thin">
+ <font weight="300" style="normal" index="0" postScriptName="NotoSansCJKJP-Regular">
NotoSansCJK-Regular.ttc
<axis tag="wght" stylevalue="300"/>
</font>
- <font weight="400" style="normal" index="0" postScriptName="NotoSansCJKjp-Thin">
+ <font weight="400" style="normal" index="0" postScriptName="NotoSansCJKJP-Regular">
NotoSansCJK-Regular.ttc
<axis tag="wght" stylevalue="400"/>
</font>
- <font weight="500" style="normal" index="0" postScriptName="NotoSansCJKjp-Thin">
+ <font weight="500" style="normal" index="0" postScriptName="NotoSansCJKJP-Regular">
NotoSansCJK-Regular.ttc
<axis tag="wght" stylevalue="500"/>
</font>
- <font weight="600" style="normal" index="0" postScriptName="NotoSansCJKjp-Thin">
+ <font weight="600" style="normal" index="0" postScriptName="NotoSansCJKJP-Regular">
NotoSansCJK-Regular.ttc
<axis tag="wght" stylevalue="600"/>
</font>
- <font weight="700" style="normal" index="0" postScriptName="NotoSansCJKjp-Thin">
+ <font weight="700" style="normal" index="0" postScriptName="NotoSansCJKJP-Regular">
NotoSansCJK-Regular.ttc
<axis tag="wght" stylevalue="700"/>
</font>
- <font weight="800" style="normal" index="0" postScriptName="NotoSansCJKjp-Thin">
+ <font weight="800" style="normal" index="0" postScriptName="NotoSansCJKJP-Regular">
NotoSansCJK-Regular.ttc
<axis tag="wght" stylevalue="800"/>
</font>
- <font weight="900" style="normal" index="0" postScriptName="NotoSansCJKjp-Thin">
+ <font weight="900" style="normal" index="0" postScriptName="NotoSansCJKJP-Regular">
NotoSansCJK-Regular.ttc
<axis tag="wght" stylevalue="900"/>
</font>
@@ -1542,39 +1542,39 @@
</font>
</family>
<family lang="ko">
- <font weight="100" style="normal" index="1" postScriptName="NotoSansCJKjp-Thin">
+ <font weight="100" style="normal" index="1" postScriptName="NotoSansCJKJP-Regular">
NotoSansCJK-Regular.ttc
<axis tag="wght" stylevalue="100"/>
</font>
- <font weight="200" style="normal" index="1" postScriptName="NotoSansCJKjp-Thin">
+ <font weight="200" style="normal" index="1" postScriptName="NotoSansCJKJP-Regular">
NotoSansCJK-Regular.ttc
<axis tag="wght" stylevalue="200"/>
</font>
- <font weight="300" style="normal" index="1" postScriptName="NotoSansCJKjp-Thin">
+ <font weight="300" style="normal" index="1" postScriptName="NotoSansCJKJP-Regular">
NotoSansCJK-Regular.ttc
<axis tag="wght" stylevalue="300"/>
</font>
- <font weight="400" style="normal" index="1" postScriptName="NotoSansCJKjp-Thin">
+ <font weight="400" style="normal" index="1" postScriptName="NotoSansCJKJP-Regular">
NotoSansCJK-Regular.ttc
<axis tag="wght" stylevalue="400"/>
</font>
- <font weight="500" style="normal" index="1" postScriptName="NotoSansCJKjp-Thin">
+ <font weight="500" style="normal" index="1" postScriptName="NotoSansCJKJP-Regular">
NotoSansCJK-Regular.ttc
<axis tag="wght" stylevalue="500"/>
</font>
- <font weight="600" style="normal" index="1" postScriptName="NotoSansCJKjp-Thin">
+ <font weight="600" style="normal" index="1" postScriptName="NotoSansCJKJP-Regular">
NotoSansCJK-Regular.ttc
<axis tag="wght" stylevalue="600"/>
</font>
- <font weight="700" style="normal" index="1" postScriptName="NotoSansCJKjp-Thin">
+ <font weight="700" style="normal" index="1" postScriptName="NotoSansCJKJP-Regular">
NotoSansCJK-Regular.ttc
<axis tag="wght" stylevalue="700"/>
</font>
- <font weight="800" style="normal" index="1" postScriptName="NotoSansCJKjp-Thin">
+ <font weight="800" style="normal" index="1" postScriptName="NotoSansCJKJP-Regular">
NotoSansCJK-Regular.ttc
<axis tag="wght" stylevalue="800"/>
</font>
- <font weight="900" style="normal" index="1" postScriptName="NotoSansCJKjp-Thin">
+ <font weight="900" style="normal" index="1" postScriptName="NotoSansCJKJP-Regular">
NotoSansCJK-Regular.ttc
<axis tag="wght" stylevalue="900"/>
</font>
diff --git a/data/keyboards/Generic.kl b/data/keyboards/Generic.kl
index e8b4104..f8d3bff 100644
--- a/data/keyboards/Generic.kl
+++ b/data/keyboards/Generic.kl
@@ -438,9 +438,15 @@
key usage 0x0c0079 KEYBOARD_BACKLIGHT_UP FALLBACK_USAGE_MAPPING
key usage 0x0c007A KEYBOARD_BACKLIGHT_DOWN FALLBACK_USAGE_MAPPING
key usage 0x0c007C KEYBOARD_BACKLIGHT_TOGGLE FALLBACK_USAGE_MAPPING
+key usage 0x0c00D9 EMOJI_PICKER FALLBACK_USAGE_MAPPING
key usage 0x0c0173 MEDIA_AUDIO_TRACK FALLBACK_USAGE_MAPPING
key usage 0x0c019C PROFILE_SWITCH FALLBACK_USAGE_MAPPING
+key usage 0x0c019F SETTINGS FALLBACK_USAGE_MAPPING
key usage 0x0c01A2 ALL_APPS FALLBACK_USAGE_MAPPING
+key usage 0x0c0227 REFRESH FALLBACK_USAGE_MAPPING
+key usage 0x0c029D LANGUAGE_SWITCH FALLBACK_USAGE_MAPPING
+key usage 0x0c029F RECENT_APPS FALLBACK_USAGE_MAPPING
+key usage 0x0c02A2 ALL_APPS FALLBACK_USAGE_MAPPING
key usage 0x0d0044 STYLUS_BUTTON_PRIMARY FALLBACK_USAGE_MAPPING
key usage 0x0d005a STYLUS_BUTTON_SECONDARY FALLBACK_USAGE_MAPPING
diff --git a/graphics/java/android/graphics/BitmapFactory.java b/graphics/java/android/graphics/BitmapFactory.java
index d915b74..1c20141 100644
--- a/graphics/java/android/graphics/BitmapFactory.java
+++ b/graphics/java/android/graphics/BitmapFactory.java
@@ -143,7 +143,7 @@
* the decoder will try to pick the best matching config based on the
* system's screen depth, and characteristics of the original image such
* as if it has per-pixel alpha (requiring a config that also does).
- *
+ *
* Image are loaded with the {@link Bitmap.Config#ARGB_8888} config by
* default.
*/
@@ -183,7 +183,7 @@
/**
* If true (which is the default), the resulting bitmap will have its
- * color channels pre-multipled by the alpha channel.
+ * color channels pre-multiplied by the alpha channel.
*
* <p>This should NOT be set to false for images to be directly drawn by
* the view system or through a {@link Canvas}. The view system and
@@ -221,9 +221,9 @@
* if {@link #inScaled} is set (which it is by default} and this
* density does not match {@link #inTargetDensity}, then the bitmap
* will be scaled to the target density before being returned.
- *
+ *
* <p>If this is 0,
- * {@link BitmapFactory#decodeResource(Resources, int)},
+ * {@link BitmapFactory#decodeResource(Resources, int)},
* {@link BitmapFactory#decodeResource(Resources, int, android.graphics.BitmapFactory.Options)},
* and {@link BitmapFactory#decodeResourceStream}
* will fill in the density associated with the resource. The other
@@ -242,29 +242,29 @@
* This is used in conjunction with {@link #inDensity} and
* {@link #inScaled} to determine if and how to scale the bitmap before
* returning it.
- *
+ *
* <p>If this is 0,
- * {@link BitmapFactory#decodeResource(Resources, int)},
+ * {@link BitmapFactory#decodeResource(Resources, int)},
* {@link BitmapFactory#decodeResource(Resources, int, android.graphics.BitmapFactory.Options)},
* and {@link BitmapFactory#decodeResourceStream}
* will fill in the density associated the Resources object's
* DisplayMetrics. The other
* functions will leave it as-is and no scaling for density will be
* performed.
- *
+ *
* @see #inDensity
* @see #inScreenDensity
* @see #inScaled
* @see android.util.DisplayMetrics#densityDpi
*/
public int inTargetDensity;
-
+
/**
* The pixel density of the actual screen that is being used. This is
* purely for applications running in density compatibility code, where
* {@link #inTargetDensity} is actually the density the application
* sees rather than the real screen density.
- *
+ *
* <p>By setting this, you
* allow the loading code to avoid scaling a bitmap that is currently
* in the screen density up/down to the compatibility density. Instead,
@@ -274,18 +274,18 @@
* Bitmap.getScaledWidth} and {@link Bitmap#getScaledHeight
* Bitmap.getScaledHeight} to account for any different between the
* bitmap's density and the target's density.
- *
+ *
* <p>This is never set automatically for the caller by
* {@link BitmapFactory} itself. It must be explicitly set, since the
* caller must deal with the resulting bitmap in a density-aware way.
- *
+ *
* @see #inDensity
* @see #inTargetDensity
* @see #inScaled
* @see android.util.DisplayMetrics#densityDpi
*/
public int inScreenDensity;
-
+
/**
* When this flag is set, if {@link #inDensity} and
* {@link #inTargetDensity} are not 0, the
@@ -345,7 +345,7 @@
* ignored.
*
* In {@link android.os.Build.VERSION_CODES#KITKAT} and below, this
- * field works in conjuction with inPurgeable. If inPurgeable is false,
+ * field works in conjunction with inPurgeable. If inPurgeable is false,
* then this field is ignored. If inPurgeable is true, then this field
* determines whether the bitmap can share a reference to the input
* data (inputstream, array, etc.) or if it must make a deep copy.
@@ -583,11 +583,11 @@
opts.inDensity = density;
}
}
-
+
if (opts.inTargetDensity == 0 && res != null) {
opts.inTargetDensity = res.getDisplayMetrics().densityDpi;
}
-
+
return decodeStream(is, pad, opts);
}
@@ -611,8 +611,8 @@
public static Bitmap decodeResource(Resources res, int id, Options opts) {
validate(opts);
Bitmap bm = null;
- InputStream is = null;
-
+ InputStream is = null;
+
try {
final TypedValue value = new TypedValue();
is = res.openRawResource(id, value);
diff --git a/graphics/java/android/graphics/Canvas.java b/graphics/java/android/graphics/Canvas.java
index e03a1da..0b3e545 100644
--- a/graphics/java/android/graphics/Canvas.java
+++ b/graphics/java/android/graphics/Canvas.java
@@ -55,7 +55,7 @@
* Canvas and Drawables</a> developer guide.</p></div>
*/
public class Canvas extends BaseCanvas {
- private static int sCompatiblityVersion = 0;
+ private static int sCompatibilityVersion = 0;
private static boolean sCompatibilityRestore = false;
private static boolean sCompatibilitySetBitmap = false;
@@ -74,7 +74,7 @@
// Maximum bitmap size as defined in Skia's native code
// (see SkCanvas.cpp, SkDraw.cpp)
- private static final int MAXMIMUM_BITMAP_SIZE = 32766;
+ private static final int MAXIMUM_BITMAP_SIZE = 32766;
// Use a Holder to allow static initialization of Canvas in the boot image.
private static class NoImagePreloadHolder {
@@ -331,7 +331,7 @@
* @see #getMaximumBitmapHeight()
*/
public int getMaximumBitmapWidth() {
- return MAXMIMUM_BITMAP_SIZE;
+ return MAXIMUM_BITMAP_SIZE;
}
/**
@@ -342,7 +342,7 @@
* @see #getMaximumBitmapWidth()
*/
public int getMaximumBitmapHeight() {
- return MAXMIMUM_BITMAP_SIZE;
+ return MAXIMUM_BITMAP_SIZE;
}
// the SAVE_FLAG constants must match their native equivalents
@@ -423,7 +423,7 @@
public static final int ALL_SAVE_FLAG = 0x1F;
private static void checkValidSaveFlags(int saveFlags) {
- if (sCompatiblityVersion >= Build.VERSION_CODES.P
+ if (sCompatibilityVersion >= Build.VERSION_CODES.P
&& saveFlags != ALL_SAVE_FLAG) {
throw new IllegalArgumentException(
"Invalid Layer Save Flag - only ALL_SAVE_FLAGS is allowed");
@@ -845,7 +845,7 @@
}
private static void checkValidClipOp(@NonNull Region.Op op) {
- if (sCompatiblityVersion >= Build.VERSION_CODES.P
+ if (sCompatibilityVersion >= Build.VERSION_CODES.P
&& op != Region.Op.INTERSECT && op != Region.Op.DIFFERENCE) {
throw new IllegalArgumentException(
"Invalid Region.Op - only INTERSECT and DIFFERENCE are allowed");
@@ -1435,7 +1435,7 @@
}
/*package*/ static void setCompatibilityVersion(int apiLevel) {
- sCompatiblityVersion = apiLevel;
+ sCompatibilityVersion = apiLevel;
sCompatibilityRestore = apiLevel < Build.VERSION_CODES.M;
sCompatibilitySetBitmap = apiLevel < Build.VERSION_CODES.O;
nSetCompatibilityVersion(apiLevel);
diff --git a/graphics/java/android/graphics/Color.java b/graphics/java/android/graphics/Color.java
index 0f2f879..c1edafc 100644
--- a/graphics/java/android/graphics/Color.java
+++ b/graphics/java/android/graphics/Color.java
@@ -289,6 +289,9 @@
*/
@AnyThread
@SuppressAutoDoc
+@android.ravenwood.annotation.RavenwoodKeepWholeClass
+@android.ravenwood.annotation.RavenwoodClassLoadHook(
+ android.ravenwood.annotation.RavenwoodClassLoadHook.LIBANDROID_LOADING_HOOK)
public class Color {
@ColorInt public static final int BLACK = 0xFF000000;
@ColorInt public static final int DKGRAY = 0xFF444444;
diff --git a/graphics/java/android/graphics/ColorSpace.java b/graphics/java/android/graphics/ColorSpace.java
index a2319a5..4bc3ece 100644
--- a/graphics/java/android/graphics/ColorSpace.java
+++ b/graphics/java/android/graphics/ColorSpace.java
@@ -135,6 +135,9 @@
@AnyThread
@SuppressWarnings("StaticInitializerReferencesSubClass")
@SuppressAutoDoc
+@android.ravenwood.annotation.RavenwoodKeepWholeClass
+@android.ravenwood.annotation.RavenwoodClassLoadHook(
+ android.ravenwood.annotation.RavenwoodClassLoadHook.LIBANDROID_LOADING_HOOK)
public abstract class ColorSpace {
/**
* Standard CIE 1931 2° illuminant A, encoded in xyY.
@@ -2490,9 +2493,16 @@
return mNativePtr;
}
- private static native long nativeGetNativeFinalizer();
- private static native long nativeCreate(float a, float b, float c, float d,
- float e, float f, float g, float[] xyz);
+ /**
+ * These methods can't be put in the Rgb class directly, because ColorSpace's
+ * static initializer instantiates Rgb, whose constructor needs them, which is a variation
+ * of b/337329128.
+ */
+ static class Native {
+ static native long nativeGetNativeFinalizer();
+ static native long nativeCreate(float a, float b, float c, float d,
+ float e, float f, float g, float[] xyz);
+ }
private static DoubleUnaryOperator generateOETF(TransferParameters function) {
if (function.isHLGish()) {
@@ -2959,7 +2969,7 @@
// This mimics the old code that was in native.
float[] nativeTransform = adaptToIlluminantD50(mWhitePoint, mTransform);
- mNativePtr = nativeCreate((float) mTransferParameters.a,
+ mNativePtr = Native.nativeCreate((float) mTransferParameters.a,
(float) mTransferParameters.b,
(float) mTransferParameters.c,
(float) mTransferParameters.d,
@@ -2975,7 +2985,7 @@
private static class NoImagePreloadHolder {
public static final NativeAllocationRegistry sRegistry = new NativeAllocationRegistry(
- ColorSpace.Rgb.class.getClassLoader(), nativeGetNativeFinalizer(), 0);
+ ColorSpace.Rgb.class.getClassLoader(), Native.nativeGetNativeFinalizer(), 0);
}
/**
diff --git a/graphics/java/android/graphics/ComposePathEffect.java b/graphics/java/android/graphics/ComposePathEffect.java
index 3fc9eb5..7d59ece 100644
--- a/graphics/java/android/graphics/ComposePathEffect.java
+++ b/graphics/java/android/graphics/ComposePathEffect.java
@@ -20,13 +20,13 @@
/**
* Construct a PathEffect whose effect is to apply first the inner effect
- * and the the outer pathEffect (e.g. outer(inner(path))).
+ * and the outer pathEffect (e.g. outer(inner(path))).
*/
public ComposePathEffect(PathEffect outerpe, PathEffect innerpe) {
native_instance = nativeCreate(outerpe.native_instance,
innerpe.native_instance);
}
-
+
private static native long nativeCreate(long nativeOuterpe,
long nativeInnerpe);
}
diff --git a/graphics/java/android/graphics/FontFamily.java b/graphics/java/android/graphics/FontFamily.java
index c86b744..88f0e8e 100644
--- a/graphics/java/android/graphics/FontFamily.java
+++ b/graphics/java/android/graphics/FontFamily.java
@@ -219,7 +219,7 @@
@CriticalNative
private static native long nGetFamilyReleaseFunc();
- // By passing -1 to weigth argument, the weight value is resolved by OS/2 table in the font.
+ // By passing -1 to weight argument, the weight value is resolved by OS/2 table in the font.
// By passing -1 to italic argument, the italic value is resolved by OS/2 table in the font.
private static native boolean nAddFont(long builderPtr, ByteBuffer font, int ttcIndex,
int weight, int isItalic);
diff --git a/graphics/java/android/graphics/FontListParser.java b/graphics/java/android/graphics/FontListParser.java
index 17c2dd9..13c4a94 100644
--- a/graphics/java/android/graphics/FontListParser.java
+++ b/graphics/java/android/graphics/FontListParser.java
@@ -127,7 +127,7 @@
parser.setInput(is, null);
parser.nextTag();
return readFamilies(parser, systemFontDir, oemCustomization, updatableFontMap,
- lastModifiedDate, configVersion, false /* filter out the non-exising files */);
+ lastModifiedDate, configVersion, false /* filter out the non-existing files */);
}
}
@@ -254,7 +254,7 @@
* @param parser An XML parser.
* @param fontDir a font directory name.
* @param updatableFontMap a updated font file map.
- * @param allowNonExistingFile true to allow font file that doesn't exists
+ * @param allowNonExistingFile true to allow font file that doesn't exist.
* @return a FontFamily instance. null if no font files are available in this FontFamily.
*/
public static @Nullable FontConfig.FontFamily readFamily(XmlPullParser parser, String fontDir,
diff --git a/graphics/java/android/graphics/FrameInfo.java b/graphics/java/android/graphics/FrameInfo.java
index b3615ff..8f12828 100644
--- a/graphics/java/android/graphics/FrameInfo.java
+++ b/graphics/java/android/graphics/FrameInfo.java
@@ -24,7 +24,7 @@
/**
* Class that contains all the timing information for the current frame. This
* is used in conjunction with the hardware renderer to provide
- * continous-monitoring jank events
+ * continuous-monitoring jank events
*
* All times in nanoseconds from CLOCK_MONOTONIC/System.nanoTime()
*
diff --git a/graphics/java/android/graphics/Interpolator.java b/graphics/java/android/graphics/Interpolator.java
index 994fb2d..28f296d 100644
--- a/graphics/java/android/graphics/Interpolator.java
+++ b/graphics/java/android/graphics/Interpolator.java
@@ -28,13 +28,13 @@
mFrameCount = 2;
native_instance = nativeConstructor(valueCount, 2);
}
-
+
public Interpolator(int valueCount, int frameCount) {
mValueCount = valueCount;
mFrameCount = frameCount;
native_instance = nativeConstructor(valueCount, frameCount);
}
-
+
/**
* Reset the Interpolator to have the specified number of values and an
* implicit keyFrame count of 2 (just a start and end). After this call the
@@ -43,7 +43,7 @@
public void reset(int valueCount) {
reset(valueCount, 2);
}
-
+
/**
* Reset the Interpolator to have the specified number of values and
* keyFrames. After this call the values for each keyFrame must be assigned
@@ -54,20 +54,20 @@
mFrameCount = frameCount;
nativeReset(native_instance, valueCount, frameCount);
}
-
+
public final int getKeyFrameCount() {
return mFrameCount;
}
-
+
public final int getValueCount() {
return mValueCount;
}
-
+
/**
* Assign the keyFrame (specified by index) a time value and an array of key
- * values (with an implicity blend array of [0, 0, 1, 1] giving linear
+ * values (with an implicitly blend array of [0, 0, 1, 1] giving linear
* transition to the next set of key values).
- *
+ *
* @param index The index of the key frame to assign
* @param msec The time (in mililiseconds) for this key frame. Based on the
* SystemClock.uptimeMillis() clock
@@ -80,7 +80,7 @@
/**
* Assign the keyFrame (specified by index) a time value and an array of key
* values and blend array.
- *
+ *
* @param index The index of the key frame to assign
* @param msec The time (in mililiseconds) for this key frame. Based on the
* SystemClock.uptimeMillis() clock
@@ -99,7 +99,7 @@
}
nativeSetKeyFrame(native_instance, index, msec, values, blend);
}
-
+
/**
* Set a repeat count (which may be fractional) for the interpolator, and
* whether the interpolator should mirror its repeats. The default settings
@@ -110,7 +110,7 @@
nativeSetRepeatMirror(native_instance, repeatCount, mirror);
}
}
-
+
public enum Result {
NORMAL,
FREEZE_START,
@@ -130,7 +130,7 @@
* return whether the specified time was within the range of key times
* (NORMAL), was before the first key time (FREEZE_START) or after the last
* key time (FREEZE_END). In any event, computed values are always returned.
- *
+ *
* @param msec The time (in milliseconds) used to sample into the
* Interpolator. Based on the SystemClock.uptimeMillis() clock
* @param values Where to write the computed values (may be NULL).
@@ -146,13 +146,13 @@
default: return Result.FREEZE_END;
}
}
-
+
@Override
protected void finalize() throws Throwable {
nativeDestructor(native_instance);
native_instance = 0; // Other finalizers can still call us.
}
-
+
private int mValueCount;
private int mFrameCount;
private long native_instance;
diff --git a/graphics/java/android/graphics/LightingColorFilter.java b/graphics/java/android/graphics/LightingColorFilter.java
index 0aa6f12..fe73a1a 100644
--- a/graphics/java/android/graphics/LightingColorFilter.java
+++ b/graphics/java/android/graphics/LightingColorFilter.java
@@ -16,8 +16,8 @@
// This file was generated from the C++ include file: SkColorFilter.h
// Any changes made to this file will be discarded by the build.
-// To change this file, either edit the include, or device/tools/gluemaker/main.cpp,
-// or one of the auxilary file specifications in device/tools/gluemaker.
+// To change this file, either edit the include, or device/tools/gluemaker/main.cpp,
+// or one of the auxiliary file specifications in device/tools/gluemaker.
package android.graphics;
diff --git a/graphics/java/android/graphics/LinearGradient.java b/graphics/java/android/graphics/LinearGradient.java
index 56d912b..0879371 100644
--- a/graphics/java/android/graphics/LinearGradient.java
+++ b/graphics/java/android/graphics/LinearGradient.java
@@ -63,7 +63,7 @@
* @param colors The sRGB colors to be distributed along the gradient line
* @param positions May be null. The relative positions [0..1] of
* each corresponding color in the colors array. If this is null,
- * the the colors are distributed evenly along the gradient line.
+ * the colors are distributed evenly along the gradient line.
* @param tile The Shader tiling mode
*/
public LinearGradient(float x0, float y0, float x1, float y1, @NonNull @ColorInt int[] colors,
@@ -82,7 +82,7 @@
* @param colors The colors to be distributed along the gradient line
* @param positions May be null. The relative positions [0..1] of
* each corresponding color in the colors array. If this is null,
- * the the colors are distributed evenly along the gradient line.
+ * the colors are distributed evenly along the gradient line.
* @param tile The Shader tiling mode
*
* @throws IllegalArgumentException if there are less than two colors, the colors do
diff --git a/graphics/java/android/graphics/Matrix.java b/graphics/java/android/graphics/Matrix.java
index fbb690c..748e9dd 100644
--- a/graphics/java/android/graphics/Matrix.java
+++ b/graphics/java/android/graphics/Matrix.java
@@ -232,7 +232,7 @@
private static class NoImagePreloadHolder {
public static final NativeAllocationRegistry sRegistry =
NativeAllocationRegistry.createMalloced(
- Matrix.class.getClassLoader(), nGetNativeFinalizerWrapper());
+ Matrix.class.getClassLoader(), ExtraNatives.nGetNativeFinalizer());
}
private final long native_instance;
@@ -241,7 +241,7 @@
* Create an identity matrix
*/
public Matrix() {
- native_instance = nCreateWrapper(0);
+ native_instance = ExtraNatives.nCreate(0);
NoImagePreloadHolder.sRegistry.registerNativeAllocation(this, native_instance);
}
@@ -251,7 +251,7 @@
* @param src The matrix to copy into this matrix
*/
public Matrix(Matrix src) {
- native_instance = nCreateWrapper(src != null ? src.native_instance : 0);
+ native_instance = ExtraNatives.nCreate(src != null ? src.native_instance : 0);
NoImagePreloadHolder.sRegistry.registerNativeAllocation(this, native_instance);
}
@@ -562,7 +562,7 @@
/**
* Set the matrix to the scale and translate values that map the source rectangle to the
- * destination rectangle, returning true if the the result can be represented.
+ * destination rectangle, returning true if the result can be represented.
*
* @param src the source rectangle to map from.
* @param dst the destination rectangle to map to.
@@ -849,40 +849,6 @@
return native_instance;
}
- /**
- * Wrapper method we use to switch to ExtraNatives.nCreate(src) only on Ravenwood.
- *
- * @see ExtraNatives
- */
- @android.ravenwood.annotation.RavenwoodReplace
- private static long nCreateWrapper(long src) {
- return nCreate(src);
- }
-
- private static long nCreateWrapper$ravenwood(long src) {
- return ExtraNatives.nCreate(src);
- }
-
- /**
- * Wrapper method we use to switch to ExtraNatives.nGetNativeFinalizer(src) only on Ravenwood.
- *
- * @see ExtraNatives
- */
- @android.ravenwood.annotation.RavenwoodReplace
- private static long nGetNativeFinalizerWrapper() {
- return nGetNativeFinalizer();
- }
-
- private static long nGetNativeFinalizerWrapper$ravenwood() {
- return ExtraNatives.nGetNativeFinalizer();
- }
-
- // ------------------ Regular JNI ------------------------
-
- private static native long nCreate(long nSrc_or_zero);
- private static native long nGetNativeFinalizer();
-
-
// ------------------ Fast JNI ------------------------
@FastNative
@@ -982,14 +948,6 @@
* There are two methods that are called by the static initializers (either directly or
* indirectly) in this class, namely nCreate() and nGetNativeFinalizer(). On Ravenwood
* these methods can't be on the Matrix class itself, so we use a nested class to host them.
- *
- * We still keep the original nCreate() method and call it on non-ravenwood environment,
- * in order to avoid problems in downstream (such as Android Studio).
- *
- * @see #nCreateWrapper(long)
- * @see #nGetNativeFinalizerWrapper()
- *
- * TODO(b/337110712) Clean it up somehow. (remove the original nCreate() and unify the code?)
*/
private static class ExtraNatives {
static native long nCreate(long nSrc_or_zero);
diff --git a/graphics/java/android/graphics/Mesh.java b/graphics/java/android/graphics/Mesh.java
index a4bce9e..6be8332 100644
--- a/graphics/java/android/graphics/Mesh.java
+++ b/graphics/java/android/graphics/Mesh.java
@@ -271,7 +271,7 @@
* not have a uniform with that name or if the uniform is declared with a type other than int
* or int[1] then an IllegalArgumentException is thrown.
*
- * @param uniformName name matching the int uniform delcared in the shader program.
+ * @param uniformName name matching the int uniform declared in the shader program.
* @param value value corresponding to the int uniform with the given name.
*/
public void setIntUniform(@NonNull String uniformName, int value) {
@@ -283,7 +283,7 @@
* not have a uniform with that name or if the uniform is declared with a type other than ivec2
* or int[2] then an IllegalArgumentException is thrown.
*
- * @param uniformName name matching the int uniform delcared in the shader program.
+ * @param uniformName name matching the int uniform declared in the shader program.
* @param value1 first value corresponding to the int uniform with the given name.
* @param value2 second value corresponding to the int uniform with the given name.
*/
@@ -296,7 +296,7 @@
* not have a uniform with that name or if the uniform is declared with a type other than ivec3
* or int[3] then an IllegalArgumentException is thrown.
*
- * @param uniformName name matching the int uniform delcared in the shader program.
+ * @param uniformName name matching the int uniform declared in the shader program.
* @param value1 first value corresponding to the int uniform with the given name.
* @param value2 second value corresponding to the int uniform with the given name.
* @param value3 third value corresponding to the int uniform with the given name.
@@ -310,7 +310,7 @@
* not have a uniform with that name or if the uniform is declared with a type other than ivec4
* or int[4] then an IllegalArgumentException is thrown.
*
- * @param uniformName name matching the int uniform delcared in the shader program.
+ * @param uniformName name matching the int uniform declared in the shader program.
* @param value1 first value corresponding to the int uniform with the given name.
* @param value2 second value corresponding to the int uniform with the given name.
* @param value3 third value corresponding to the int uniform with the given name.
@@ -327,7 +327,7 @@
* int (for N=1), ivecN, or int[N], where N is the length of the values param, then an
* IllegalArgumentException is thrown.
*
- * @param uniformName name matching the int uniform delcared in the shader program.
+ * @param uniformName name matching the int uniform declared in the shader program.
* @param values int values corresponding to the vec4 int uniform with the given name.
*/
public void setIntUniform(@NonNull String uniformName, @NonNull int[] values) {
diff --git a/graphics/java/android/graphics/NinePatch.java b/graphics/java/android/graphics/NinePatch.java
index af20957..382269f 100644
--- a/graphics/java/android/graphics/NinePatch.java
+++ b/graphics/java/android/graphics/NinePatch.java
@@ -22,12 +22,12 @@
* The NinePatch class permits drawing a bitmap in nine or more sections.
* Essentially, it allows the creation of custom graphics that will scale the
* way that you define, when content added within the image exceeds the normal
- * bounds of the graphic. For a thorough explanation of a NinePatch image,
- * read the discussion in the
+ * bounds of the graphic. For a thorough explanation of a NinePatch image,
+ * read the discussion in the
* <a href="{@docRoot}guide/topics/graphics/2d-graphics.html#nine-patch">2D
* Graphics</a> document.
* <p>
- * The <a href="{@docRoot}guide/developing/tools/draw9patch.html">Draw 9-Patch</a>
+ * The <a href="{@docRoot}guide/developing/tools/draw9patch.html">Draw 9-Patch</a>
* tool offers an extremely handy way to create your NinePatch images,
* using a WYSIWYG graphics editor.
* </p>
@@ -104,7 +104,7 @@
this(bitmap, chunk, null);
}
- /**
+ /**
* Create a drawable projection from a bitmap to nine patches.
*
* @param bitmap The bitmap describing the patches.
@@ -122,7 +122,7 @@
protected void finalize() throws Throwable {
try {
if (mNativeChunk != 0) {
- // only attempt to destroy correctly initilized chunks
+ // only attempt to destroy correctly initialized chunks
nativeFinalize(mNativeChunk);
mNativeChunk = 0;
}
@@ -169,8 +169,8 @@
public Bitmap getBitmap() {
return mBitmap;
}
-
- /**
+
+ /**
* Draws the NinePatch. This method will use the paint returned by {@link #getPaint()}.
*
* @param canvas A container for the current matrix and clip used to draw the NinePatch.
@@ -180,7 +180,7 @@
canvas.drawPatch(this, location, mPaint);
}
- /**
+ /**
* Draws the NinePatch. This method will use the paint returned by {@link #getPaint()}.
*
* @param canvas A container for the current matrix and clip used to draw the NinePatch.
@@ -190,7 +190,7 @@
canvas.drawPatch(this, location, mPaint);
}
- /**
+ /**
* Draws the NinePatch. This method will ignore the paint returned
* by {@link #getPaint()} and use the specified paint instead.
*
diff --git a/graphics/java/android/graphics/PathMeasure.java b/graphics/java/android/graphics/PathMeasure.java
index 5500c52..2c6cfa5 100644
--- a/graphics/java/android/graphics/PathMeasure.java
+++ b/graphics/java/android/graphics/PathMeasure.java
@@ -25,14 +25,14 @@
* setPath.
*
* Note that once a path is associated with the measure object, it is
- * undefined if the path is subsequently modified and the the measure object
+ * undefined if the path is subsequently modified and the measure object
* is used. If the path is modified, you must call setPath with the path.
*/
public PathMeasure() {
mPath = null;
native_instance = native_create(0, false);
}
-
+
/**
* Create a PathMeasure object associated with the specified path object
* (already created and specified). The measure object can now return the
@@ -40,7 +40,7 @@
* path.
*
* Note that once a path is associated with the measure object, it is
- * undefined if the path is subsequently modified and the the measure object
+ * undefined if the path is subsequently modified and the measure object
* is used. If the path is modified, you must call setPath with the path.
*
* @param path The path that will be measured by this object
@@ -121,7 +121,7 @@
* such as <code>dst.rLineTo(0, 0)</code>.</p>
*/
public boolean getSegment(float startD, float stopD, Path dst, boolean startWithMoveTo) {
- // Skia used to enforce this as part of it's API, but has since relaxed that restriction
+ // Skia used to enforce this as part of its API, but has since relaxed that restriction
// so to maintain consistency in our API we enforce the preconditions here.
float length = getLength();
if (startD < 0) {
diff --git a/graphics/java/android/graphics/Rasterizer.java b/graphics/java/android/graphics/Rasterizer.java
index 29d82fa..5750954 100644
--- a/graphics/java/android/graphics/Rasterizer.java
+++ b/graphics/java/android/graphics/Rasterizer.java
@@ -16,8 +16,8 @@
// This file was generated from the C++ include file: SkRasterizer.h
// Any changes made to this file will be discarded by the build.
-// To change this file, either edit the include, or device/tools/gluemaker/main.cpp,
-// or one of the auxilary file specifications in device/tools/gluemaker.
+// To change this file, either edit the include, or device/tools/gluemaker/main.cpp,
+// or one of the auxiliary file specifications in device/tools/gluemaker.
package android.graphics;
diff --git a/graphics/java/android/graphics/Rect.java b/graphics/java/android/graphics/Rect.java
index 411a10b..5211e3a 100644
--- a/graphics/java/android/graphics/Rect.java
+++ b/graphics/java/android/graphics/Rect.java
@@ -165,7 +165,7 @@
public String toShortString() {
return toShortString(new StringBuilder(32));
}
-
+
/**
* Return a string representation of the rectangle in a compact form.
* @hide
@@ -184,7 +184,7 @@
*
* <p>You can later recover the Rect from this string through
* {@link #unflattenFromString(String)}.
- *
+ *
* @return Returns a new String of the form "left top right bottom"
*/
@NonNull
@@ -314,7 +314,7 @@
public final int height() {
return bottom - top;
}
-
+
/**
* @return the horizontal center of the rectangle. If the computed value
* is fractional, this method returns the largest integer that is
@@ -323,7 +323,7 @@
public final int centerX() {
return (left + right) >> 1;
}
-
+
/**
* @return the vertical center of the rectangle. If the computed value
* is fractional, this method returns the largest integer that is
@@ -332,14 +332,14 @@
public final int centerY() {
return (top + bottom) >> 1;
}
-
+
/**
* @return the exact horizontal center of the rectangle as a float.
*/
public final float exactCenterX() {
return (left + right) * 0.5f;
}
-
+
/**
* @return the exact vertical center of the rectangle as a float.
*/
@@ -493,7 +493,7 @@
* @param top The top of the rectangle being tested for containment
* @param right The right side of the rectangle being tested for containment
* @param bottom The bottom of the rectangle being tested for containment
- * @return true iff the the 4 specified sides of a rectangle are inside or
+ * @return true iff the 4 specified sides of a rectangle are inside or
* equal to this rectangle
*/
public boolean contains(int left, int top, int right, int bottom) {
@@ -548,7 +548,7 @@
}
return false;
}
-
+
/**
* If the specified rectangle intersects this rectangle, return true and set
* this rectangle to that intersection, otherwise return false and do not
@@ -670,7 +670,7 @@
public void union(@NonNull Rect r) {
union(r.left, r.top, r.right, r.bottom);
}
-
+
/**
* Update this Rect to enclose itself and the [x,y] coordinate. There is no
* check to see that this rectangle is non-empty.
diff --git a/graphics/java/android/graphics/RectF.java b/graphics/java/android/graphics/RectF.java
index ff50a0c..5b9b764 100644
--- a/graphics/java/android/graphics/RectF.java
+++ b/graphics/java/android/graphics/RectF.java
@@ -38,7 +38,7 @@
public float top;
public float right;
public float bottom;
-
+
/**
* Create a new empty RectF. All coordinates are initialized to 0.
*/
@@ -78,7 +78,7 @@
bottom = r.bottom;
}
}
-
+
public RectF(@Nullable Rect r) {
if (r == null) {
left = top = right = bottom = 0.0f;
@@ -121,7 +121,7 @@
public String toShortString() {
return toShortString(new StringBuilder(32));
}
-
+
/**
* Return a string representation of the rectangle in a compact form.
* @hide
@@ -134,7 +134,7 @@
sb.append(','); sb.append(bottom); sb.append(']');
return sb.toString();
}
-
+
/**
* Print short representation to given writer.
* @hide
@@ -183,14 +183,14 @@
public final float centerY() {
return (top + bottom) * 0.5f;
}
-
+
/**
* Set the rectangle to (0,0,0,0)
*/
public void setEmpty() {
left = right = top = bottom = 0;
}
-
+
/**
* Set the rectangle's coordinates to the specified values. Note: no range
* checking is performed, so it is up to the caller to ensure that
@@ -220,7 +220,7 @@
this.right = src.right;
this.bottom = src.bottom;
}
-
+
/**
* Copy the coordinates from src into this rectangle.
*
@@ -261,7 +261,7 @@
left = newLeft;
top = newTop;
}
-
+
/**
* Inset the rectangle by (dx,dy). If dx is positive, then the sides are
* moved inwards, making the rectangle narrower. If dx is negative, then the
@@ -293,7 +293,7 @@
return left < right && top < bottom // check for empty first
&& x >= left && x < right && y >= top && y < bottom;
}
-
+
/**
* Returns true iff the 4 specified sides of a rectangle are inside or equal
* to this rectangle. i.e. is this rectangle a superset of the specified
@@ -303,7 +303,7 @@
* @param top The top of the rectangle being tested for containment
* @param right The right side of the rectangle being tested for containment
* @param bottom The bottom of the rectangle being tested for containment
- * @return true iff the the 4 specified sides of a rectangle are inside or
+ * @return true iff the 4 specified sides of a rectangle are inside or
* equal to this rectangle
*/
public boolean contains(float left, float top, float right, float bottom) {
@@ -313,7 +313,7 @@
&& this.left <= left && this.top <= top
&& this.right >= right && this.bottom >= bottom;
}
-
+
/**
* Returns true iff the specified rectangle r is inside or equal to this
* rectangle. An empty rectangle never contains another rectangle.
@@ -329,7 +329,7 @@
&& left <= r.left && top <= r.top
&& right >= r.right && bottom >= r.bottom;
}
-
+
/**
* If the rectangle specified by left,top,right,bottom intersects this
* rectangle, return true and set this rectangle to that intersection,
@@ -367,7 +367,7 @@
}
return false;
}
-
+
/**
* If the specified rectangle intersects this rectangle, return true and set
* this rectangle to that intersection, otherwise return false and do not
@@ -382,7 +382,7 @@
public boolean intersect(@NonNull RectF r) {
return intersect(r.left, r.top, r.right, r.bottom);
}
-
+
/**
* If rectangles a and b intersect, return true and set this rectangle to
* that intersection, otherwise return false and do not change this
@@ -406,7 +406,7 @@
}
return false;
}
-
+
/**
* Returns true if this rectangle intersects the specified rectangle.
* In no event is this rectangle modified. No check is performed to see
@@ -426,7 +426,7 @@
return this.left < right && left < this.right
&& this.top < bottom && top < this.bottom;
}
-
+
/**
* Returns true iff the two specified rectangles intersect. In no event are
* either of the rectangles modified. To record the intersection,
@@ -441,7 +441,7 @@
return a.left < b.right && b.left < a.right
&& a.top < b.bottom && b.top < a.bottom;
}
-
+
/**
* Set the dst integer Rect by rounding this rectangle's coordinates
* to their nearest integer values.
@@ -489,7 +489,7 @@
}
}
}
-
+
/**
* Update this Rect to enclose itself and the specified rectangle. If the
* specified rectangle is empty, nothing is done. If this rectangle is empty
@@ -500,7 +500,7 @@
public void union(@NonNull RectF r) {
union(r.left, r.top, r.right, r.bottom);
}
-
+
/**
* Update this Rect to enclose itself and the [x,y] coordinate. There is no
* check to see that this rectangle is non-empty.
@@ -520,7 +520,7 @@
bottom = y;
}
}
-
+
/**
* Swap top/bottom or left/right if there are flipped (i.e. left > right
* and/or top > bottom). This can be called if
@@ -548,7 +548,7 @@
public int describeContents() {
return 0;
}
-
+
/**
* Write this rectangle to the specified parcel. To restore a rectangle from
* a parcel, use readFromParcel()
@@ -561,7 +561,7 @@
out.writeFloat(right);
out.writeFloat(bottom);
}
-
+
public static final @android.annotation.NonNull Parcelable.Creator<RectF> CREATOR = new Parcelable.Creator<RectF>() {
/**
* Return a new rectangle from the data in the specified parcel.
@@ -572,7 +572,7 @@
r.readFromParcel(in);
return r;
}
-
+
/**
* Return an array of rectangles of the specified size.
*/
@@ -581,7 +581,7 @@
return new RectF[size];
}
};
-
+
/**
* Set the rectangle's coordinates from the data stored in the specified
* parcel. To write a rectangle to a parcel, call writeToParcel().
diff --git a/graphics/java/android/graphics/RenderNode.java b/graphics/java/android/graphics/RenderNode.java
index 2732569..0650b78 100644
--- a/graphics/java/android/graphics/RenderNode.java
+++ b/graphics/java/android/graphics/RenderNode.java
@@ -765,12 +765,12 @@
* Default value is false. See
* {@link #setProjectBackwards(boolean)} for a description of what this entails.
*
- * @param shouldRecieve True if this RenderNode is a projection receiver, false otherwise.
+ * @param shouldReceive True if this RenderNode is a projection receiver, false otherwise.
* Default is false.
* @return True if the value changed, false if the new value was the same as the previous value.
*/
- public boolean setProjectionReceiver(boolean shouldRecieve) {
- return nSetProjectionReceiver(mNativeRenderNode, shouldRecieve);
+ public boolean setProjectionReceiver(boolean shouldReceive) {
+ return nSetProjectionReceiver(mNativeRenderNode, shouldReceive);
}
/**
@@ -1799,7 +1799,7 @@
private static native boolean nSetProjectBackwards(long renderNode, boolean shouldProject);
@CriticalNative
- private static native boolean nSetProjectionReceiver(long renderNode, boolean shouldRecieve);
+ private static native boolean nSetProjectionReceiver(long renderNode, boolean shouldReceive);
@CriticalNative
private static native boolean nSetOutlineRoundRect(long renderNode, int left, int top,
diff --git a/graphics/java/android/graphics/SurfaceTexture.java b/graphics/java/android/graphics/SurfaceTexture.java
index 50b167e..3256f31 100644
--- a/graphics/java/android/graphics/SurfaceTexture.java
+++ b/graphics/java/android/graphics/SurfaceTexture.java
@@ -318,7 +318,7 @@
}
/**
- * Releases the the texture content. This is needed in single buffered mode to allow the image
+ * Releases the texture content. This is needed in single buffered mode to allow the image
* content producer to take ownership of the image buffer.
* <p>
* For more information see {@link #SurfaceTexture(int, boolean)}.
@@ -431,7 +431,7 @@
* error.
* <p>
* Note that while calling this method causes all the buffers to be freed
- * from the perspective of the the SurfaceTexture, if there are additional
+ * from the perspective of the SurfaceTexture, if there are additional
* references on the buffers (e.g. if a buffer is referenced by a client or
* by OpenGL ES as a texture) then those buffer will remain allocated.
* <p>
diff --git a/graphics/java/android/graphics/Typeface.java b/graphics/java/android/graphics/Typeface.java
index 4c4e8fa..fd78816 100644
--- a/graphics/java/android/graphics/Typeface.java
+++ b/graphics/java/android/graphics/Typeface.java
@@ -600,7 +600,7 @@
* {@link #setWeight} and {@link #setItalic}.
*
* If {@link #setWeight} is not called, the fallback family keeps the default weight.
- * Similary, if {@link #setItalic} is not called, the fallback family keeps the default
+ * Similarly, if {@link #setItalic} is not called, the fallback family keeps the default
* italic information. For example, calling {@code builder.setFallback("sans-serif-light")}
* is equivalent to calling {@code builder.setFallback("sans-serif").setWeight(300)} in
* terms of fallback. The default weight and italic information are overridden by calling
@@ -794,7 +794,7 @@
/**
* Returns the maximum capacity of custom fallback families.
*
- * This includes the the first font family passed to the constructor.
+ * This includes the first font family passed to the constructor.
* It is guaranteed that the value will be greater than or equal to 64.
*
* @return the maximum number of font families for the custom fallback
@@ -816,7 +816,7 @@
/**
* Sets a system fallback by name.
*
- * You can specify generic font familiy names or OEM specific family names. If the system
+ * You can specify generic font family names or OEM specific family names. If the system
* don't have a specified fallback, the default fallback is used instead.
* For more information about generic font families, see <a
* href="https://www.w3.org/TR/css-fonts-4/#generic-font-families">CSS specification</a>
diff --git a/graphics/java/android/graphics/Xfermode.java b/graphics/java/android/graphics/Xfermode.java
index 81769e2..6bb22a1 100644
--- a/graphics/java/android/graphics/Xfermode.java
+++ b/graphics/java/android/graphics/Xfermode.java
@@ -16,8 +16,8 @@
// This file was generated from the C++ include file: SkXfermode.h
// Any changes made to this file will be discarded by the build.
-// To change this file, either edit the include, or device/tools/gluemaker/main.cpp,
-// or one of the auxilary file specifications in device/tools/gluemaker.
+// To change this file, either edit the include, or device/tools/gluemaker/main.cpp,
+// or one of the auxiliary file specifications in device/tools/gluemaker.
package android.graphics;
@@ -28,7 +28,7 @@
* Xfermode is the base class for objects that are called to implement custom
* "transfer-modes" in the drawing pipeline. The static function Create(Modes)
* can be called to return an instance of any of the predefined subclasses as
- * specified in the Modes enum. When an Xfermode is assigned to an Paint, then
+ * specified in the Modes enum. When an Xfermode is assigned to a Paint, then
* objects drawn with that paint have the xfermode applied.
*/
public class Xfermode {
diff --git a/graphics/java/android/graphics/YuvImage.java b/graphics/java/android/graphics/YuvImage.java
index ce35b55..b0c7f20 100644
--- a/graphics/java/android/graphics/YuvImage.java
+++ b/graphics/java/android/graphics/YuvImage.java
@@ -63,7 +63,7 @@
private int mWidth;
/**
- * The height of the the image.
+ * The height of the image.
*/
private int mHeight;
diff --git a/graphics/java/android/graphics/drawable/AdaptiveIconDrawable.java b/graphics/java/android/graphics/drawable/AdaptiveIconDrawable.java
index 688425a..7ee7d6b 100644
--- a/graphics/java/android/graphics/drawable/AdaptiveIconDrawable.java
+++ b/graphics/java/android/graphics/drawable/AdaptiveIconDrawable.java
@@ -98,7 +98,7 @@
* extra content to reveal within the clip path when performing affine transformations on the
* layers.
*
- * Each layers will reserve 25% of it's width and height.
+ * Each layers will reserve 25% of its width and height.
*
* As a result, the view port of the layers is smaller than their intrinsic width and height.
*/
diff --git a/graphics/java/android/graphics/drawable/Drawable.java b/graphics/java/android/graphics/drawable/Drawable.java
index 4972e92..7f2feac 100644
--- a/graphics/java/android/graphics/drawable/Drawable.java
+++ b/graphics/java/android/graphics/drawable/Drawable.java
@@ -839,7 +839,7 @@
}
/**
- * Describes the current state, as a union of primitve states, such as
+ * Describes the current state, as a union of primitive states, such as
* {@link android.R.attr#state_focused},
* {@link android.R.attr#state_selected}, etc.
* Some drawables may modify their imagery based on the selected state.
diff --git a/graphics/java/android/graphics/drawable/GradientDrawable.java b/graphics/java/android/graphics/drawable/GradientDrawable.java
index 166a795..29d033e 100644
--- a/graphics/java/android/graphics/drawable/GradientDrawable.java
+++ b/graphics/java/android/graphics/drawable/GradientDrawable.java
@@ -936,7 +936,7 @@
}
/**
- * Retrn the inner radius of the ring
+ * Return the inner radius of the ring
*
* @see #setInnerRadius(int)
* @attr ref android.R.styleable#GradientDrawable_innerRadius
diff --git a/graphics/java/android/graphics/drawable/shapes/PathShape.java b/graphics/java/android/graphics/drawable/shapes/PathShape.java
index 393fdee..299f6d5 100644
--- a/graphics/java/android/graphics/drawable/shapes/PathShape.java
+++ b/graphics/java/android/graphics/drawable/shapes/PathShape.java
@@ -93,7 +93,7 @@
&& Float.compare(pathShape.mStdHeight, mStdHeight) == 0
&& Float.compare(pathShape.mScaleX, mScaleX) == 0
&& Float.compare(pathShape.mScaleY, mScaleY) == 0
- // Path does not have equals implementation but incase it gains one, use it here
+ // Path does not have equals implementation but in case it gains one, use it here
&& Objects.equals(mPath, pathShape.mPath);
}
diff --git a/graphics/java/android/graphics/fonts/Font.java b/graphics/java/android/graphics/fonts/Font.java
index 318aadd..2893177 100644
--- a/graphics/java/android/graphics/fonts/Font.java
+++ b/graphics/java/android/graphics/fonts/Font.java
@@ -789,7 +789,7 @@
return false;
}
- // ByteBuffer#equals compares all bytes which is not performant for e.g HashMap. Since
+ // ByteBuffer#equals compares all bytes which is not performant for e.g. HashMap. Since
// underlying native font object holds buffer address, check if this buffer points exactly
// the same address as a shortcut of equality. For being compatible with of API30 or before,
// check buffer position even if the buffer points the same address.
diff --git a/graphics/java/android/graphics/fonts/FontFamily.java b/graphics/java/android/graphics/fonts/FontFamily.java
index bd13276..5a7b0bb 100644
--- a/graphics/java/android/graphics/fonts/FontFamily.java
+++ b/graphics/java/android/graphics/fonts/FontFamily.java
@@ -25,6 +25,7 @@
import android.annotation.IntRange;
import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.annotation.SuppressLint;
import android.text.FontConfig;
import android.util.SparseIntArray;
@@ -151,6 +152,7 @@
* @return A variable font family. null if a variable font cannot be built from the given
* fonts.
*/
+ @SuppressLint("BuilderSetStyle")
@FlaggedApi(FLAG_NEW_FONTS_FALLBACK_XML)
public @Nullable FontFamily buildVariableFamily() {
int variableFamilyType = analyzeAndResolveVariableType(mFonts);
diff --git a/graphics/java/android/graphics/fonts/FontFileUtil.java b/graphics/java/android/graphics/fonts/FontFileUtil.java
index ff38282..abcafb6 100644
--- a/graphics/java/android/graphics/fonts/FontFileUtil.java
+++ b/graphics/java/android/graphics/fonts/FontFileUtil.java
@@ -34,7 +34,7 @@
*/
public class FontFileUtil {
- private FontFileUtil() {} // Do not instanciate
+ private FontFileUtil() {} // Do not instantiate
/**
* Unpack the weight value from packed integer.
@@ -87,7 +87,7 @@
}
if (weight != -1 && italic != -1) {
- // Both weight/italic style are specifeid by variation settings.
+ // Both weight/italic style are specified by variation settings.
// No need to look into OS/2 table.
// TODO: Good to look HVAR table to check if this font supports wght/ital axes.
return pack(weight, italic == 1);
diff --git a/graphics/java/android/graphics/fonts/SystemFonts.java b/graphics/java/android/graphics/fonts/SystemFonts.java
index a90961e..f727f5b 100644
--- a/graphics/java/android/graphics/fonts/SystemFonts.java
+++ b/graphics/java/android/graphics/fonts/SystemFonts.java
@@ -216,7 +216,7 @@
} else if (defaultFamily != null) {
familyListSet.familyList.add(defaultFamily);
} else {
- // There is no valid for for default fallback. Ignore.
+ // There is no valid for default fallback. Ignore.
}
}
}
diff --git a/graphics/java/android/graphics/text/LineBreakConfig.java b/graphics/java/android/graphics/text/LineBreakConfig.java
index 7d55928..5a1086c 100644
--- a/graphics/java/android/graphics/text/LineBreakConfig.java
+++ b/graphics/java/android/graphics/text/LineBreakConfig.java
@@ -23,6 +23,7 @@
import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.annotation.SuppressLint;
import android.app.ActivityThread;
import android.os.Build;
import android.os.LocaleList;
@@ -314,6 +315,7 @@
* @param config an override line break config
* @return This {@code Builder}.
*/
+ @SuppressLint("BuilderSetStyle")
@FlaggedApi(FLAG_NO_BREAK_NO_HYPHENATION_SPAN)
public @NonNull Builder merge(@NonNull LineBreakConfig config) {
if (config.mLineBreakStyle != LINE_BREAK_STYLE_UNSPECIFIED) {
diff --git a/graphics/java/android/graphics/text/LineBreaker.java b/graphics/java/android/graphics/text/LineBreaker.java
index 0c6d4bd..d8cf21e 100644
--- a/graphics/java/android/graphics/text/LineBreaker.java
+++ b/graphics/java/android/graphics/text/LineBreaker.java
@@ -376,8 +376,8 @@
* @see LineBreaker#computeLineBreaks
*/
public static class Result {
- // Following two contstant must be synced with minikin's line breaker.
- // TODO(nona): Remove these constatns by introducing native methods.
+ // Following two constants must be synced with minikin's line breaker.
+ // TODO(nona): Remove these constants by introducing native methods.
private static final int TAB_MASK = 0x20000000;
private static final int HYPHEN_MASK = 0xFF;
private static final int START_HYPHEN_MASK = 0x18; // 0b11000
diff --git a/graphics/java/android/graphics/text/PositionedGlyphs.java b/graphics/java/android/graphics/text/PositionedGlyphs.java
index f8328b1..671eb6e 100644
--- a/graphics/java/android/graphics/text/PositionedGlyphs.java
+++ b/graphics/java/android/graphics/text/PositionedGlyphs.java
@@ -139,7 +139,7 @@
* Returns the glyph ID used for drawing the glyph at the given index.
*
* @param index the glyph index
- * @return An glyph ID of the font.
+ * @return A glyph ID of the font.
*/
@IntRange(from = 0)
public int getGlyphId(@IntRange(from = 0) int index) {
diff --git a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/DividerPresenter.java b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/DividerPresenter.java
index e93b0bf..29936cc 100644
--- a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/DividerPresenter.java
+++ b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/DividerPresenter.java
@@ -33,7 +33,9 @@
import static androidx.window.extensions.embedding.SplitPresenter.CONTAINER_POSITION_RIGHT;
import static androidx.window.extensions.embedding.SplitPresenter.CONTAINER_POSITION_TOP;
-import android.annotation.DimenRes;
+import android.animation.Animator;
+import android.animation.AnimatorListenerAdapter;
+import android.animation.ValueAnimator;
import android.annotation.Nullable;
import android.app.Activity;
import android.app.ActivityThread;
@@ -53,9 +55,11 @@
import android.view.MotionEvent;
import android.view.SurfaceControl;
import android.view.SurfaceControlViewHost;
+import android.view.VelocityTracker;
import android.view.View;
import android.view.WindowManager;
import android.view.WindowlessWindowManager;
+import android.view.animation.PathInterpolator;
import android.widget.FrameLayout;
import android.widget.ImageButton;
import android.window.InputTransferToken;
@@ -97,6 +101,16 @@
@VisibleForTesting
static final int DEFAULT_DIVIDER_WIDTH_DP = 24;
+ @VisibleForTesting
+ static final PathInterpolator FLING_ANIMATION_INTERPOLATOR =
+ new PathInterpolator(0.4f, 0f, 0.2f, 1f);
+ @VisibleForTesting
+ static final int FLING_ANIMATION_DURATION = 250;
+ @VisibleForTesting
+ static final int MIN_DISMISS_VELOCITY_DP_PER_SECOND = 600;
+ @VisibleForTesting
+ static final int MIN_FLING_VELOCITY_DP_PER_SECOND = 400;
+
private final int mTaskId;
@NonNull
@@ -109,6 +123,14 @@
private final Executor mCallbackExecutor;
/**
+ * The VelocityTracker of the divider, used to track the dragging velocity. This field is
+ * {@code null} until dragging starts.
+ */
+ @GuardedBy("mLock")
+ @Nullable
+ VelocityTracker mVelocityTracker;
+
+ /**
* The {@link Properties} of the divider. This field is {@code null} when no divider should be
* drawn, e.g. when the split doesn't have {@link DividerAttributes} or when the decor surface
* is not available.
@@ -256,8 +278,10 @@
static Color getContainerBackgroundColor(
@NonNull TaskFragmentContainer container, @NonNull Color defaultColor) {
final Activity activity = container.getTopNonFinishingActivity();
- if (activity == null || !activity.isResumed()) {
- // This can happen when the top activity in the container is from a different process.
+ if (activity == null) {
+ // This can happen when the activities in the container are from a different process.
+ // TODO(b/340984203) Report whether the top activity is in the same process. Use default
+ // color if not.
return defaultColor;
}
@@ -368,13 +392,11 @@
applicationContext.getResources().getDisplayMetrics());
}
- private static int getDimensionDp(@DimenRes int resId) {
- final Context context = ActivityThread.currentActivityThread().getApplication();
- final int px = context.getResources().getDimensionPixelSize(resId);
- return (int) TypedValue.convertPixelsToDimension(
- COMPLEX_UNIT_DIP,
- px,
- context.getResources().getDisplayMetrics());
+ private static float getDisplayDensity() {
+ // TODO(b/329193115) support divider on secondary display
+ final Context applicationContext =
+ ActivityThread.currentActivityThread().getApplication();
+ return applicationContext.getResources().getDisplayMetrics().density;
}
/**
@@ -485,24 +507,27 @@
@Override
public boolean onTouch(@NonNull View view, @NonNull MotionEvent event) {
synchronized (mLock) {
- final Rect taskBounds = mProperties.mConfiguration.windowConfiguration.getBounds();
- mDividerPosition = calculateDividerPosition(
- event, taskBounds, mRenderer.mDividerWidthPx, mProperties.mDividerAttributes,
- mProperties.mIsVerticalSplit, calculateMinPosition(), calculateMaxPosition());
- mRenderer.setDividerPosition(mDividerPosition);
- switch (event.getAction()) {
- case MotionEvent.ACTION_DOWN:
- onStartDragging();
- break;
- case MotionEvent.ACTION_UP:
- case MotionEvent.ACTION_CANCEL:
- onFinishDragging();
- break;
- case MotionEvent.ACTION_MOVE:
- onDrag();
- break;
- default:
- break;
+ if (mProperties != null && mRenderer != null) {
+ final Rect taskBounds = mProperties.mConfiguration.windowConfiguration.getBounds();
+ mDividerPosition = calculateDividerPosition(
+ event, taskBounds, mRenderer.mDividerWidthPx,
+ mProperties.mDividerAttributes, mProperties.mIsVerticalSplit,
+ calculateMinPosition(), calculateMaxPosition());
+ mRenderer.setDividerPosition(mDividerPosition);
+ switch (event.getAction()) {
+ case MotionEvent.ACTION_DOWN:
+ onStartDragging(event);
+ break;
+ case MotionEvent.ACTION_UP:
+ case MotionEvent.ACTION_CANCEL:
+ onFinishDragging(event);
+ break;
+ case MotionEvent.ACTION_MOVE:
+ onDrag(event);
+ break;
+ default:
+ break;
+ }
}
}
@@ -512,11 +537,17 @@
}
@GuardedBy("mLock")
- private void onStartDragging() {
+ private void onStartDragging(@NonNull MotionEvent event) {
+ mVelocityTracker = VelocityTracker.obtain();
+ mVelocityTracker.addMovement(event);
+
mRenderer.mIsDragging = true;
mRenderer.mDragHandle.setPressed(mRenderer.mIsDragging);
+ mRenderer.updateSurface();
+
+ // Veil visibility change should be applied together with the surface boost transaction in
+ // the wct.
final SurfaceControl.Transaction t = new SurfaceControl.Transaction();
- mRenderer.updateSurface(t);
mRenderer.showVeils(t);
// Callbacks must be executed on the executor to release mLock and prevent deadlocks.
@@ -531,19 +562,84 @@
}
@GuardedBy("mLock")
- private void onDrag() {
- final SurfaceControl.Transaction t = new SurfaceControl.Transaction();
- mRenderer.updateSurface(t);
- t.apply();
+ private void onDrag(@NonNull MotionEvent event) {
+ if (mVelocityTracker != null) {
+ mVelocityTracker.addMovement(event);
+ }
+ mRenderer.updateSurface();
}
@GuardedBy("mLock")
- private void onFinishDragging() {
- mDividerPosition = adjustDividerPositionForSnapPoints(mDividerPosition);
- mRenderer.setDividerPosition(mDividerPosition);
+ private void onFinishDragging(@NonNull MotionEvent event) {
+ float velocity = 0.0f;
+ if (mVelocityTracker != null) {
+ mVelocityTracker.addMovement(event);
+ mVelocityTracker.computeCurrentVelocity(1000 /* units */);
+ velocity = mProperties.mIsVerticalSplit
+ ? mVelocityTracker.getXVelocity()
+ : mVelocityTracker.getYVelocity();
+ mVelocityTracker.recycle();
+ }
+ final int prevDividerPosition = mDividerPosition;
+ mDividerPosition = dividerPositionForSnapPoints(mDividerPosition, velocity);
+ if (mDividerPosition != prevDividerPosition) {
+ ValueAnimator animator = getFlingAnimator(prevDividerPosition, mDividerPosition);
+ animator.start();
+ } else {
+ onDraggingEnd();
+ }
+ }
+
+ @GuardedBy("mLock")
+ @NonNull
+ @VisibleForTesting
+ ValueAnimator getFlingAnimator(int prevDividerPosition, int snappedDividerPosition) {
+ final ValueAnimator animator =
+ getValueAnimator(prevDividerPosition, snappedDividerPosition);
+ animator.addUpdateListener(animation -> {
+ synchronized (mLock) {
+ updateDividerPosition((int) animation.getAnimatedValue());
+ }
+ });
+ animator.addListener(new AnimatorListenerAdapter() {
+ @Override
+ public void onAnimationEnd(Animator animation) {
+ synchronized (mLock) {
+ onDraggingEnd();
+ }
+ }
+
+ @Override
+ public void onAnimationCancel(Animator animation) {
+ synchronized (mLock) {
+ onDraggingEnd();
+ }
+ }
+ });
+ return animator;
+ }
+
+ @VisibleForTesting
+ static ValueAnimator getValueAnimator(int prevDividerPosition, int snappedDividerPosition) {
+ ValueAnimator animator = ValueAnimator
+ .ofInt(prevDividerPosition, snappedDividerPosition)
+ .setDuration(FLING_ANIMATION_DURATION);
+ animator.setInterpolator(FLING_ANIMATION_INTERPOLATOR);
+ return animator;
+ }
+
+ @GuardedBy("mLock")
+ private void updateDividerPosition(int position) {
+ mRenderer.setDividerPosition(position);
+ mRenderer.updateSurface();
+ }
+
+ @GuardedBy("mLock")
+ private void onDraggingEnd() {
+ // Veil visibility change should be applied together with the surface boost transaction in
+ // the wct.
final SurfaceControl.Transaction t = new SurfaceControl.Transaction();
- mRenderer.updateSurface(t);
mRenderer.hideVeils(t);
// Callbacks must be executed on the executor to release mLock and prevent deadlocks.
@@ -565,38 +661,78 @@
/**
* Returns the divider position adjusted for the min max ratio and fullscreen expansion.
- *
- * If the dragging position is above the {@link DividerAttributes#getPrimaryMaxRatio()} or below
- * {@link DividerAttributes#getPrimaryMinRatio()} and
- * {@link DividerAttributes#isDraggingToFullscreenAllowed} is {@code true}, the system will
- * choose a snap algorithm to adjust the ending position to either fully expand one container or
- * move the divider back to the specified min/max ratio.
- *
- * TODO(b/327067596) implement snap algorithm
- *
* The adjusted divider position is in the range of [minPosition, maxPosition] for a split, 0
* for expanded right (bottom) container, or task width (height) minus the divider width for
* expanded left (top) container.
*/
@GuardedBy("mLock")
- private int adjustDividerPositionForSnapPoints(int dividerPosition) {
+ private int dividerPositionForSnapPoints(int dividerPosition, float velocity) {
final Rect taskBounds = mProperties.mConfiguration.windowConfiguration.getBounds();
final int minPosition = calculateMinPosition();
final int maxPosition = calculateMaxPosition();
final int fullyExpandedPosition = mProperties.mIsVerticalSplit
? taskBounds.right - mRenderer.mDividerWidthPx
: taskBounds.bottom - mRenderer.mDividerWidthPx;
+
if (isDraggingToFullscreenAllowed(mProperties.mDividerAttributes)) {
- if (dividerPosition < minPosition) {
- return 0;
- }
- if (dividerPosition > maxPosition) {
- return fullyExpandedPosition;
- }
+ final float displayDensity = getDisplayDensity();
+ return dividerPositionWithDraggingToFullscreenAllowed(
+ dividerPosition,
+ minPosition,
+ maxPosition,
+ fullyExpandedPosition,
+ velocity,
+ displayDensity);
}
return Math.clamp(dividerPosition, minPosition, maxPosition);
}
+ /**
+ * Returns the divider position given a set of position options. A snap algorithm is used to
+ * adjust the ending position to either fully expand one container or move the divider back to
+ * the specified min/max ratio depending on the dragging velocity.
+ */
+ @VisibleForTesting
+ static int dividerPositionWithDraggingToFullscreenAllowed(int dividerPosition, int minPosition,
+ int maxPosition, int fullyExpandedPosition, float velocity, float displayDensity) {
+ final float minDismissVelocityPxPerSecond =
+ MIN_DISMISS_VELOCITY_DP_PER_SECOND * displayDensity;
+ final float minFlingVelocityPxPerSecond =
+ MIN_FLING_VELOCITY_DP_PER_SECOND * displayDensity;
+ if (dividerPosition < minPosition && velocity < -minDismissVelocityPxPerSecond) {
+ return 0;
+ }
+ if (dividerPosition > maxPosition && velocity > minDismissVelocityPxPerSecond) {
+ return fullyExpandedPosition;
+ }
+ if (Math.abs(velocity) < minFlingVelocityPxPerSecond) {
+ if (dividerPosition >= minPosition && dividerPosition <= maxPosition) {
+ return dividerPosition;
+ }
+ int[] possiblePositions = {0, minPosition, maxPosition, fullyExpandedPosition};
+ return snap(dividerPosition, possiblePositions);
+ }
+ if (velocity < 0) {
+ return 0;
+ } else {
+ return fullyExpandedPosition;
+ }
+ }
+
+ /** Calculates the snapped divider position based on the possible positions and distance. */
+ private static int snap(int dividerPosition, int[] possiblePositions) {
+ int snappedPosition = dividerPosition;
+ float minDistance = Float.MAX_VALUE;
+ for (int position : possiblePositions) {
+ float distance = Math.abs(dividerPosition - position);
+ if (distance < minDistance) {
+ snappedPosition = position;
+ minDistance = distance;
+ }
+ }
+ return snappedPosition;
+ }
+
private static void setDecorSurfaceBoosted(
@NonNull WindowContainerTransaction wct,
@Nullable IBinder decorSurfaceOwner,
@@ -994,6 +1130,22 @@
* Updates the positions and crops of the divider surface and veil surfaces. This method
* should be called when {@link #mProperties} is changed or while dragging to update the
* position of the divider surface and the veil surfaces.
+ *
+ * This method applies the changes in a stand-alone surface transaction immediately.
+ */
+ private void updateSurface() {
+ final SurfaceControl.Transaction t = new SurfaceControl.Transaction();
+ updateSurface(t);
+ t.apply();
+ }
+
+ /**
+ * Updates the positions and crops of the divider surface and veil surfaces. This method
+ * should be called when {@link #mProperties} is changed or while dragging to update the
+ * position of the divider surface and the veil surfaces.
+ *
+ * This method applies the changes in the provided surface transaction and can be synced
+ * with other changes.
*/
private void updateSurface(@NonNull SurfaceControl.Transaction t) {
final Rect taskBounds = mProperties.mConfiguration.windowConfiguration.getBounds();
diff --git a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/JetpackTaskFragmentOrganizer.java b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/JetpackTaskFragmentOrganizer.java
index a23a4741..f9a6caf 100644
--- a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/JetpackTaskFragmentOrganizer.java
+++ b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/JetpackTaskFragmentOrganizer.java
@@ -21,6 +21,7 @@
import static android.window.TaskFragmentOperation.OP_TYPE_SET_ANIMATION_PARAMS;
import static android.window.TaskFragmentOperation.OP_TYPE_SET_DIM_ON_TASK;
import static android.window.TaskFragmentOperation.OP_TYPE_SET_ISOLATED_NAVIGATION;
+import static android.window.TaskFragmentOperation.OP_TYPE_SET_PINNED;
import static androidx.window.extensions.embedding.SplitContainer.getFinishPrimaryWithSecondaryBehavior;
import static androidx.window.extensions.embedding.SplitContainer.getFinishSecondaryWithPrimaryBehavior;
@@ -358,6 +359,13 @@
wct.addTaskFragmentOperation(fragmentToken, operation);
}
+ void setTaskFragmentPinned(@NonNull WindowContainerTransaction wct,
+ @NonNull IBinder fragmentToken, boolean pinned) {
+ final TaskFragmentOperation operation = new TaskFragmentOperation.Builder(
+ OP_TYPE_SET_PINNED).setBooleanValue(pinned).build();
+ wct.addTaskFragmentOperation(fragmentToken, operation);
+ }
+
void setTaskFragmentDimOnTask(@NonNull WindowContainerTransaction wct,
@NonNull IBinder fragmentToken, boolean dimOnTask) {
final TaskFragmentOperation operation = new TaskFragmentOperation.Builder(
diff --git a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitController.java b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitController.java
index 5b0e6b9..b764b6e 100644
--- a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitController.java
+++ b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitController.java
@@ -50,6 +50,7 @@
import static androidx.window.extensions.embedding.SplitPresenter.getMinDimensions;
import static androidx.window.extensions.embedding.SplitPresenter.sanitizeBounds;
import static androidx.window.extensions.embedding.SplitPresenter.shouldShowSplit;
+import static androidx.window.extensions.embedding.TaskFragmentContainer.OverlayContainerRestoreParams;
import android.annotation.CallbackExecutor;
import android.app.Activity;
@@ -133,6 +134,13 @@
private final List<EmbeddingRule> mSplitRules = new ArrayList<>();
/**
+ * Stores the token of the associated Activity that maps to the
+ * {@link OverlayContainerRestoreParams} of the most recent created overlay container.
+ */
+ @GuardedBy("mLock")
+ final ArrayMap<IBinder, OverlayContainerRestoreParams> mOverlayRestoreParams = new ArrayMap<>();
+
+ /**
* A developer-defined {@link SplitAttributes} calculator to compute the current
* {@link SplitAttributes} with the current device and window states.
* It is registered via {@link #setSplitAttributesCalculator(Function)}
@@ -350,8 +358,7 @@
// Resets the isolated navigation and updates the container.
final TransactionRecord transactionRecord = mTransactionManager.startNewTransaction();
final WindowContainerTransaction wct = transactionRecord.getTransaction();
- mPresenter.setTaskFragmentIsolatedNavigation(wct, containerToUnpin,
- false /* isolated */);
+ mPresenter.setTaskFragmentPinned(wct, containerToUnpin, false /* pinned */);
updateContainer(wct, containerToUnpin);
transactionRecord.apply(false /* shouldApplyIndependently */);
updateCallbackIfNecessary();
@@ -687,11 +694,20 @@
exception);
break;
case TYPE_ACTIVITY_REPARENTED_TO_TASK:
+ final IBinder candidateAssociatedActToken, lastOverlayToken;
+ if (Flags.fixPipRestoreToOverlay()) {
+ candidateAssociatedActToken = change.getOtherActivityToken();
+ lastOverlayToken = change.getTaskFragmentToken();
+ } else {
+ candidateAssociatedActToken = lastOverlayToken = null;
+ }
onActivityReparentedToTask(
wct,
taskId,
change.getActivityIntent(),
- change.getActivityToken());
+ change.getActivityToken(),
+ candidateAssociatedActToken,
+ lastOverlayToken);
break;
default:
throw new IllegalArgumentException(
@@ -918,11 +934,28 @@
* different process, the server will generate a temporary token that
* the organizer can use to reparent the activity through
* {@link WindowContainerTransaction} if needed.
+ * @param candidateAssociatedActToken The token of the candidate associated-activity.
+ * @param lastOverlayToken The last parent overlay container token.
*/
@VisibleForTesting
@GuardedBy("mLock")
void onActivityReparentedToTask(@NonNull WindowContainerTransaction wct,
- int taskId, @NonNull Intent activityIntent, @NonNull IBinder activityToken) {
+ int taskId, @NonNull Intent activityIntent, @NonNull IBinder activityToken,
+ @Nullable IBinder candidateAssociatedActToken, @Nullable IBinder lastOverlayToken) {
+ // Reparent the activity to an overlay container if needed.
+ final OverlayContainerRestoreParams params = getOverlayContainerRestoreParams(
+ candidateAssociatedActToken, lastOverlayToken);
+ if (params != null) {
+ final Activity associatedActivity = getActivity(candidateAssociatedActToken);
+ final TaskFragmentContainer targetContainer = createOrUpdateOverlayTaskFragmentIfNeeded(
+ wct, params.mOptions, params.mIntent, associatedActivity);
+ if (targetContainer != null) {
+ wct.reparentActivityToTaskFragment(targetContainer.getTaskFragmentToken(),
+ activityToken);
+ return;
+ }
+ }
+
// If the activity belongs to the current app process, we treat it as a new activity
// launch.
final Activity activity = getActivity(activityToken);
@@ -967,6 +1000,43 @@
}
/**
+ * Returns the {@link OverlayContainerRestoreParams} that stored last time the {@code
+ * associatedActivityToken} associated with and only if data matches the {@code overlayToken}.
+ * Otherwise, return {@code null}.
+ */
+ @VisibleForTesting
+ @GuardedBy("mLock")
+ @Nullable
+ OverlayContainerRestoreParams getOverlayContainerRestoreParams(
+ @Nullable IBinder associatedActivityToken, @Nullable IBinder overlayToken) {
+ if (!Flags.fixPipRestoreToOverlay()) {
+ return null;
+ }
+
+ if (associatedActivityToken == null || overlayToken == null) {
+ return null;
+ }
+
+ final TaskFragmentContainer.OverlayContainerRestoreParams params =
+ mOverlayRestoreParams.get(associatedActivityToken);
+ if (params == null) {
+ return null;
+ }
+
+ if (params.mOverlayToken != overlayToken) {
+ // Not the same overlay container, no need to restore.
+ return null;
+ }
+
+ final Activity associatedActivity = getActivity(associatedActivityToken);
+ if (associatedActivity == null || associatedActivity.isFinishing()) {
+ return null;
+ }
+
+ return params;
+ }
+
+ /**
* Called when the {@link WindowContainerTransaction} created with
* {@link WindowContainerTransaction#setErrorCallbackToken(IBinder)} failed on the server side.
*
@@ -1078,8 +1148,7 @@
return true;
}
- // Skip resolving if the activity is on an isolated navigated TaskFragmentContainer.
- if (container != null && container.isIsolatedNavigationEnabled()) {
+ if (container != null && container.shouldSkipActivityResolving()) {
return true;
}
@@ -1435,6 +1504,8 @@
for (int i = mTaskContainers.size() - 1; i >= 0; i--) {
mTaskContainers.valueAt(i).onFinishingActivityPaused(wct, activityToken);
}
+
+ mOverlayRestoreParams.remove(activity.getActivityToken());
updateCallbackIfNecessary();
}
@@ -1452,6 +1523,8 @@
for (int i = mTaskContainers.size() - 1; i >= 0; i--) {
mTaskContainers.valueAt(i).onActivityDestroyed(wct, activityToken);
}
+
+ mOverlayRestoreParams.remove(activity.getActivityToken());
// We didn't trigger the callback if there were any pending appeared activities, so check
// again after the pending is removed.
updateCallbackIfNecessary();
@@ -1535,8 +1608,7 @@
final TaskFragmentContainer taskFragmentContainer = getContainerWithActivity(
launchingActivity);
if (taskFragmentContainer != null
- && taskFragmentContainer.isIsolatedNavigationEnabled()) {
- // Skip resolving if started from an isolated navigated TaskFragmentContainer.
+ && taskFragmentContainer.shouldSkipActivityResolving()) {
return null;
}
if (isAssociatedWithOverlay(launchingActivity)) {
diff --git a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitPresenter.java b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitPresenter.java
index 0e4fb30..2704813 100644
--- a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitPresenter.java
+++ b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitPresenter.java
@@ -401,18 +401,26 @@
return;
}
- setTaskFragmentIsolatedNavigation(wct, secondaryContainer, !isStacked /* isolatedNav */);
+ setTaskFragmentPinned(wct, secondaryContainer, !isStacked /* pinned */);
if (isStacked && !splitPinRule.isSticky()) {
secondaryContainer.getTaskContainer().removeSplitPinContainer();
}
}
/**
- * Sets whether to enable isolated navigation for this {@link TaskFragmentContainer}
+ * Sets whether to enable isolated navigation for this {@link TaskFragmentContainer}.
+ * <p>
+ * If a container enables isolated navigation, activities can't be launched to this container
+ * unless explicitly requested to be launched to.
+ *
+ * @see TaskFragmentContainer#isOverlayWithActivityAssociation()
*/
void setTaskFragmentIsolatedNavigation(@NonNull WindowContainerTransaction wct,
@NonNull TaskFragmentContainer container,
boolean isolatedNavigationEnabled) {
+ if (!Flags.activityEmbeddingOverlayPresentationFlag() && container.isOverlay()) {
+ return;
+ }
if (container.isIsolatedNavigationEnabled() == isolatedNavigationEnabled) {
return;
}
@@ -422,6 +430,28 @@
}
/**
+ * Sets whether to pin this {@link TaskFragmentContainer}.
+ * <p>
+ * If a container is pinned, it won't be chosen as the launch target unless it's the launching
+ * container.
+ *
+ * @see TaskFragmentContainer#isAlwaysOnTopOverlay()
+ * @see TaskContainer#getSplitPinContainer()
+ */
+ void setTaskFragmentPinned(@NonNull WindowContainerTransaction wct,
+ @NonNull TaskFragmentContainer container,
+ boolean pinned) {
+ if (!Flags.activityEmbeddingOverlayPresentationFlag() && container.isOverlay()) {
+ return;
+ }
+ if (container.isPinned() == pinned) {
+ return;
+ }
+ container.setPinned(pinned);
+ setTaskFragmentPinned(wct, container.getTaskFragmentToken(), pinned);
+ }
+
+ /**
* Resizes the task fragment if it was already registered. Skips the operation if the container
* creation has not been reported from the server yet.
*/
@@ -586,6 +616,11 @@
super.setCompanionTaskFragment(wct, primary, secondary);
}
+ /**
+ * Applies the {@code attributes} to a standalone {@code container}.
+ *
+ * @param minDimensions the minimum dimension of the container.
+ */
void applyActivityStackAttributes(
@NonNull WindowContainerTransaction wct,
@NonNull TaskFragmentContainer container,
@@ -594,16 +629,17 @@
final Rect relativeBounds = sanitizeBounds(attributes.getRelativeBounds(), minDimensions,
container);
final boolean isFillParent = relativeBounds.isEmpty();
- // Note that we only set isolated navigation for overlay container without activity
- // association. Activity will be launched to an expanded container on top of the overlay
- // if the overlay is associated with an activity. Thus, an overlay with activity association
- // will never be isolated navigated.
- final boolean isIsolatedNavigated = container.isAlwaysOnTopOverlay() && !isFillParent;
final boolean dimOnTask = !isFillParent
- && attributes.getWindowAttributes().getDimAreaBehavior() == DIM_AREA_ON_TASK
- && Flags.fullscreenDimFlag();
+ && Flags.fullscreenDimFlag()
+ && attributes.getWindowAttributes().getDimAreaBehavior() == DIM_AREA_ON_TASK;
final IBinder fragmentToken = container.getTaskFragmentToken();
+ if (container.isAlwaysOnTopOverlay()) {
+ setTaskFragmentPinned(wct, container, !isFillParent);
+ } else if (container.isOverlayWithActivityAssociation()) {
+ setTaskFragmentIsolatedNavigation(wct, container, !isFillParent);
+ }
+
// TODO(b/243518738): Update to resizeTaskFragment after we migrate WCT#setRelativeBounds
// and WCT#setWindowingMode to take fragmentToken.
resizeTaskFragmentIfRegistered(wct, container, relativeBounds);
@@ -612,7 +648,6 @@
updateTaskFragmentWindowingModeIfRegistered(wct, container, windowingMode);
// Always use default animation for standalone ActivityStack.
updateAnimationParams(wct, fragmentToken, TaskFragmentAnimationParams.DEFAULT);
- setTaskFragmentIsolatedNavigation(wct, container, isIsolatedNavigated);
setTaskFragmentDimOnTask(wct, fragmentToken, dimOnTask);
}
diff --git a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/TaskFragmentContainer.java b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/TaskFragmentContainer.java
index c952dfe..d0b6a01 100644
--- a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/TaskFragmentContainer.java
+++ b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/TaskFragmentContainer.java
@@ -36,6 +36,7 @@
import androidx.annotation.Nullable;
import com.android.internal.annotations.VisibleForTesting;
+import com.android.window.flags.Flags;
import java.util.ArrayList;
import java.util.Collections;
@@ -184,6 +185,11 @@
private boolean mIsIsolatedNavigationEnabled;
/**
+ * Whether this TaskFragment is pinned.
+ */
+ private boolean mIsPinned;
+
+ /**
* Whether to apply dimming on the parent Task that was requested last.
*/
private boolean mLastDimOnTask;
@@ -269,6 +275,15 @@
addPendingAppearedActivity(pendingAppearedActivity);
}
mPendingAppearedIntent = pendingAppearedIntent;
+
+ // Save the information necessary for restoring the overlay when needed.
+ if (Flags.fixPipRestoreToOverlay() && overlayTag != null && pendingAppearedIntent != null
+ && associatedActivity != null && !associatedActivity.isFinishing()) {
+ final IBinder associatedActivityToken = associatedActivity.getActivityToken();
+ final OverlayContainerRestoreParams params = new OverlayContainerRestoreParams(mToken,
+ launchOptions, pendingAppearedIntent);
+ mController.mOverlayRestoreParams.put(associatedActivityToken, params);
+ }
}
/**
@@ -893,6 +908,34 @@
mIsIsolatedNavigationEnabled = isolatedNavigationEnabled;
}
+ /**
+ * Returns whether this container is pinned.
+ *
+ * @see android.window.TaskFragmentOperation#OP_TYPE_SET_PINNED
+ */
+ boolean isPinned() {
+ return mIsPinned;
+ }
+
+ /**
+ * Sets whether to pin this container or not.
+ *
+ * @see #isPinned()
+ */
+ void setPinned(boolean pinned) {
+ mIsPinned = pinned;
+ }
+
+ /**
+ * Indicates to skip activity resolving if the activity is from this container.
+ *
+ * @see #isIsolatedNavigationEnabled()
+ * @see #isPinned()
+ */
+ boolean shouldSkipActivityResolving() {
+ return isIsolatedNavigationEnabled() || isPinned();
+ }
+
/** Sets whether to apply dim on the parent Task. */
void setLastDimOnTask(boolean lastDimOnTask) {
mLastDimOnTask = lastDimOnTask;
@@ -1072,4 +1115,25 @@
}
return sb.append("]").toString();
}
+
+ static class OverlayContainerRestoreParams {
+ /** The token of the overlay container */
+ @NonNull
+ final IBinder mOverlayToken;
+
+ /** The launch options to create this container. */
+ @NonNull
+ final Bundle mOptions;
+
+ /** The Intent that used to be started in the overlay container. */
+ @NonNull
+ final Intent mIntent;
+
+ OverlayContainerRestoreParams(@NonNull IBinder overlayToken, @NonNull Bundle options,
+ @NonNull Intent intent) {
+ mOverlayToken = overlayToken;
+ mOptions = options;
+ mIntent = intent;
+ }
+ }
}
diff --git a/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/DividerPresenterTest.java b/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/DividerPresenterTest.java
index ad913c9..746607c 100644
--- a/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/DividerPresenterTest.java
+++ b/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/DividerPresenterTest.java
@@ -19,6 +19,10 @@
import static android.window.TaskFragmentOperation.OP_TYPE_CREATE_OR_MOVE_TASK_FRAGMENT_DECOR_SURFACE;
import static android.window.TaskFragmentOperation.OP_TYPE_REMOVE_TASK_FRAGMENT_DECOR_SURFACE;
+import static androidx.window.extensions.embedding.DividerPresenter.FLING_ANIMATION_DURATION;
+import static androidx.window.extensions.embedding.DividerPresenter.FLING_ANIMATION_INTERPOLATOR;
+import static androidx.window.extensions.embedding.DividerPresenter.MIN_DISMISS_VELOCITY_DP_PER_SECOND;
+import static androidx.window.extensions.embedding.DividerPresenter.MIN_FLING_VELOCITY_DP_PER_SECOND;
import static androidx.window.extensions.embedding.DividerPresenter.getBoundsOffsetForDivider;
import static androidx.window.extensions.embedding.DividerPresenter.getInitialDividerPosition;
import static androidx.window.extensions.embedding.SplitPresenter.CONTAINER_POSITION_BOTTOM;
@@ -35,6 +39,7 @@
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
+import android.animation.ValueAnimator;
import android.app.Activity;
import android.content.res.Configuration;
import android.graphics.Color;
@@ -631,19 +636,111 @@
assertEquals(defaultColor,
DividerPresenter.getContainerBackgroundColor(container, defaultColor));
- // When the top non-finishing activity is not resumed, the default color should be returned.
+ // When the top non-finishing activity is non-null, its background color should be returned.
when(container.getTopNonFinishingActivity()).thenReturn(activity);
- when(activity.isResumed()).thenReturn(false);
- assertEquals(defaultColor,
- DividerPresenter.getContainerBackgroundColor(container, defaultColor));
-
- // When the top non-finishing activity is resumed, its background color should be returned.
- when(container.getTopNonFinishingActivity()).thenReturn(activity);
- when(activity.isResumed()).thenReturn(true);
assertEquals(activityBackgroundColor,
DividerPresenter.getContainerBackgroundColor(container, defaultColor));
}
+ @Test
+ public void testGetValueAnimator() {
+ ValueAnimator animator =
+ DividerPresenter.getValueAnimator(
+ 375 /* prevDividerPosition */,
+ 500 /* snappedDividerPosition */);
+
+ assertEquals(animator.getDuration(), FLING_ANIMATION_DURATION);
+ assertEquals(animator.getInterpolator(), FLING_ANIMATION_INTERPOLATOR);
+ }
+
+ @Test
+ public void testDividerPositionWithDraggingToFullscreenAllowed() {
+ final float displayDensity = 600F;
+ final float dismissVelocity = MIN_DISMISS_VELOCITY_DP_PER_SECOND * displayDensity + 10f;
+ final float nonFlingVelocity = MIN_FLING_VELOCITY_DP_PER_SECOND * displayDensity - 10f;
+ final float flingVelocity = MIN_FLING_VELOCITY_DP_PER_SECOND * displayDensity + 10f;
+
+ // Divider position is less than minPosition and the velocity is enough to be dismissed
+ assertEquals(
+ 0, // Closed position
+ DividerPresenter.dividerPositionWithDraggingToFullscreenAllowed(
+ 10 /* dividerPosition */,
+ 30 /* minPosition */,
+ 900 /* maxPosition */,
+ 1200 /* fullyExpandedPosition */,
+ -dismissVelocity,
+ displayDensity));
+
+ // Divider position is greater than maxPosition and the velocity is enough to be dismissed
+ assertEquals(
+ 1200, // Fully expanded position
+ DividerPresenter.dividerPositionWithDraggingToFullscreenAllowed(
+ 1000 /* dividerPosition */,
+ 30 /* minPosition */,
+ 900 /* maxPosition */,
+ 1200 /* fullyExpandedPosition */,
+ dismissVelocity,
+ displayDensity));
+
+ // Divider position is returned when the velocity is not fast enough for fling and is in
+ // between minPosition and maxPosition
+ assertEquals(
+ 500, // dividerPosition is not snapped
+ DividerPresenter.dividerPositionWithDraggingToFullscreenAllowed(
+ 500 /* dividerPosition */,
+ 30 /* minPosition */,
+ 900 /* maxPosition */,
+ 1200 /* fullyExpandedPosition */,
+ nonFlingVelocity,
+ displayDensity));
+
+ // Divider position is snapped when the velocity is not fast enough for fling and larger
+ // than maxPosition
+ assertEquals(
+ 900, // Closest position is maxPosition
+ DividerPresenter.dividerPositionWithDraggingToFullscreenAllowed(
+ 950 /* dividerPosition */,
+ 30 /* minPosition */,
+ 900 /* maxPosition */,
+ 1200 /* fullyExpandedPosition */,
+ nonFlingVelocity,
+ displayDensity));
+
+ // Divider position is snapped when the velocity is not fast enough for fling and smaller
+ // than minPosition
+ assertEquals(
+ 30, // Closest position is minPosition
+ DividerPresenter.dividerPositionWithDraggingToFullscreenAllowed(
+ 20 /* dividerPosition */,
+ 30 /* minPosition */,
+ 900 /* maxPosition */,
+ 1200 /* fullyExpandedPosition */,
+ nonFlingVelocity,
+ displayDensity));
+
+ // Divider position is greater than minPosition and the velocity is enough for fling
+ assertEquals(
+ 0, // Closed position
+ DividerPresenter.dividerPositionWithDraggingToFullscreenAllowed(
+ 50 /* dividerPosition */,
+ 30 /* minPosition */,
+ 900 /* maxPosition */,
+ 1200 /* fullyExpandedPosition */,
+ -flingVelocity,
+ displayDensity));
+
+ // Divider position is less than maxPosition and the velocity is enough for fling
+ assertEquals(
+ 1200, // Fully expanded position
+ DividerPresenter.dividerPositionWithDraggingToFullscreenAllowed(
+ 800 /* dividerPosition */,
+ 30 /* minPosition */,
+ 900 /* maxPosition */,
+ 1200 /* fullyExpandedPosition */,
+ flingVelocity,
+ displayDensity));
+ }
+
private TaskFragmentContainer createMockTaskFragmentContainer(
@NonNull IBinder token, @NonNull Rect bounds) {
final TaskFragmentContainer container = mock(TaskFragmentContainer.class);
diff --git a/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/OverlayPresentationTest.java b/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/OverlayPresentationTest.java
index 049a9e2..f322257 100644
--- a/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/OverlayPresentationTest.java
+++ b/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/OverlayPresentationTest.java
@@ -188,6 +188,32 @@
}
@Test
+ public void testSetIsolatedNavigation_overlayFeatureDisabled_earlyReturn() {
+ mSetFlagRule.disableFlags(Flags.FLAG_ACTIVITY_EMBEDDING_OVERLAY_PRESENTATION_FLAG);
+
+ final TaskFragmentContainer container = createTestOverlayContainer(TASK_ID, "test");
+
+ mSplitPresenter.setTaskFragmentIsolatedNavigation(mTransaction, container,
+ !container.isIsolatedNavigationEnabled());
+
+ verify(mSplitPresenter, never()).setTaskFragmentIsolatedNavigation(any(),
+ any(IBinder.class), anyBoolean());
+ }
+
+ @Test
+ public void testSetPinned_overlayFeatureDisabled_earlyReturn() {
+ mSetFlagRule.disableFlags(Flags.FLAG_ACTIVITY_EMBEDDING_OVERLAY_PRESENTATION_FLAG);
+
+ final TaskFragmentContainer container = createTestOverlayContainer(TASK_ID, "test");
+
+ mSplitPresenter.setTaskFragmentPinned(mTransaction, container,
+ !container.isPinned());
+
+ verify(mSplitPresenter, never()).setTaskFragmentPinned(any(), any(IBinder.class),
+ anyBoolean());
+ }
+
+ @Test
public void testGetAllNonFinishingOverlayContainers() {
assertThat(mSplitController.getAllNonFinishingOverlayContainers()).isEmpty();
@@ -608,8 +634,11 @@
WINDOWING_MODE_UNDEFINED);
verify(mSplitPresenter).updateAnimationParams(mTransaction, token,
TaskFragmentAnimationParams.DEFAULT);
- verify(mSplitPresenter).setTaskFragmentIsolatedNavigation(mTransaction, container, false);
verify(mSplitPresenter).setTaskFragmentDimOnTask(mTransaction, token, false);
+ verify(mSplitPresenter, never()).setTaskFragmentPinned(any(),
+ any(TaskFragmentContainer.class), anyBoolean());
+ verify(mSplitPresenter, never()).setTaskFragmentIsolatedNavigation(any(),
+ any(TaskFragmentContainer.class), anyBoolean());
}
@Test
@@ -630,9 +659,9 @@
WINDOWING_MODE_MULTI_WINDOW);
verify(mSplitPresenter).updateAnimationParams(mTransaction, token,
TaskFragmentAnimationParams.DEFAULT);
- // Set isolated navigation to false if the overlay container is associated with
- // the launching activity.
- verify(mSplitPresenter).setTaskFragmentIsolatedNavigation(mTransaction, container, false);
+ verify(mSplitPresenter).setTaskFragmentIsolatedNavigation(mTransaction, container, true);
+ verify(mSplitPresenter, never()).setTaskFragmentPinned(any(),
+ any(TaskFragmentContainer.class), anyBoolean());
verify(mSplitPresenter).setTaskFragmentDimOnTask(mTransaction, token, true);
}
@@ -655,10 +684,9 @@
container, WINDOWING_MODE_MULTI_WINDOW);
verify(mSplitPresenter).updateAnimationParams(mTransaction, token,
TaskFragmentAnimationParams.DEFAULT);
- // Set isolated navigation to false if the overlay container is associated with
- // the launching activity.
- verify(mSplitPresenter).setTaskFragmentIsolatedNavigation(mTransaction,
- container, true);
+ verify(mSplitPresenter, never()).setTaskFragmentIsolatedNavigation(any(),
+ any(TaskFragmentContainer.class), anyBoolean());
+ verify(mSplitPresenter).setTaskFragmentPinned(mTransaction, container, true);
verify(mSplitPresenter).setTaskFragmentDimOnTask(mTransaction, token, true);
}
@@ -678,6 +706,8 @@
verify(mSplitPresenter).updateAnimationParams(mTransaction, token,
TaskFragmentAnimationParams.DEFAULT);
verify(mSplitPresenter).setTaskFragmentIsolatedNavigation(mTransaction, container, false);
+ verify(mSplitPresenter, never()).setTaskFragmentPinned(any(),
+ any(TaskFragmentContainer.class), anyBoolean());
verify(mSplitPresenter).setTaskFragmentDimOnTask(mTransaction, token, false);
}
@@ -806,6 +836,30 @@
any());
}
+ @Test
+ public void testOnActivityReparentedToTask_overlayRestoration() {
+ mSetFlagRule.enableFlags(Flags.FLAG_FIX_PIP_RESTORE_TO_OVERLAY);
+
+ // Prepares and mock the data necessary for the test.
+ final IBinder activityToken = mActivity.getActivityToken();
+ final Intent intent = new Intent();
+ final IBinder fillTaskActivityToken = new Binder();
+ final IBinder lastOverlayToken = new Binder();
+ final TaskFragmentContainer overlayContainer = mSplitController.newContainer(intent,
+ mActivity, TASK_ID);
+ final TaskFragmentContainer.OverlayContainerRestoreParams params = mock(
+ TaskFragmentContainer.OverlayContainerRestoreParams.class);
+ doReturn(params).when(mSplitController).getOverlayContainerRestoreParams(any(), any());
+ doReturn(overlayContainer).when(mSplitController).createOrUpdateOverlayTaskFragmentIfNeeded(
+ any(), any(), any(), any());
+
+ // Verify the activity should be reparented to the overlay container.
+ mSplitController.onActivityReparentedToTask(mTransaction, TASK_ID, intent, activityToken,
+ fillTaskActivityToken, lastOverlayToken);
+ verify(mTransaction).reparentActivityToTaskFragment(
+ eq(overlayContainer.getTaskFragmentToken()), eq(activityToken));
+ }
+
/**
* A simplified version of {@link SplitController#createOrUpdateOverlayTaskFragmentIfNeeded}
*/
diff --git a/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/SplitControllerTest.java b/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/SplitControllerTest.java
index 7d86ec2..35353db 100644
--- a/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/SplitControllerTest.java
+++ b/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/SplitControllerTest.java
@@ -397,7 +397,8 @@
@Test
public void testOnActivityReparentedToTask_sameProcess() {
mSplitController.onActivityReparentedToTask(mTransaction, TASK_ID, new Intent(),
- mActivity.getActivityToken());
+ mActivity.getActivityToken(), null /* fillTaskActivityToken */,
+ null /* lastOverlayToken */);
// Treated as on activity created, but allow to split as primary.
verify(mSplitController).resolveActivityToContainer(mTransaction,
@@ -413,7 +414,8 @@
final IBinder activityToken = new Binder();
final Intent intent = new Intent();
- mSplitController.onActivityReparentedToTask(mTransaction, TASK_ID, intent, activityToken);
+ mSplitController.onActivityReparentedToTask(mTransaction, TASK_ID, intent, activityToken,
+ null /* fillTaskActivityToken */, null /* lastOverlayToken */);
// Treated as starting new intent
verify(mSplitController, never()).resolveActivityToContainer(any(), any(), anyBoolean());
@@ -1210,7 +1212,7 @@
mSplitController.onTransactionReady(transaction);
verify(mSplitController).onActivityReparentedToTask(any(), eq(TASK_ID), eq(intent),
- eq(activityToken));
+ eq(activityToken), any(), any());
verify(mSplitPresenter).onTransactionHandled(eq(transaction.getTransactionToken()), any(),
anyInt(), anyBoolean());
}
diff --git a/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/SplitPresenterTest.java b/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/SplitPresenterTest.java
index c677484..3fbce9ec 100644
--- a/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/SplitPresenterTest.java
+++ b/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/SplitPresenterTest.java
@@ -20,6 +20,7 @@
import static android.app.WindowConfiguration.WINDOWING_MODE_MULTI_WINDOW;
import static android.view.Display.DEFAULT_DISPLAY;
import static android.window.TaskFragmentOperation.OP_TYPE_SET_ANIMATION_PARAMS;
+import static android.window.TaskFragmentOperation.OP_TYPE_SET_PINNED;
import static androidx.window.extensions.embedding.EmbeddingTestUtils.DEFAULT_FINISH_PRIMARY_WITH_SECONDARY;
import static androidx.window.extensions.embedding.EmbeddingTestUtils.DEFAULT_FINISH_SECONDARY_WITH_PRIMARY;
@@ -285,6 +286,28 @@
}
@Test
+ public void testSetTaskFragmentPinned() {
+ final TaskFragmentContainer container = mController.newContainer(mActivity, TASK_ID);
+
+ // Verify the default.
+ assertFalse(container.isPinned());
+
+ mPresenter.setTaskFragmentPinned(mTransaction, container, true);
+
+ final TaskFragmentOperation expectedOperation = new TaskFragmentOperation.Builder(
+ OP_TYPE_SET_PINNED).setBooleanValue(true).build();
+ verify(mTransaction).addTaskFragmentOperation(container.getTaskFragmentToken(),
+ expectedOperation);
+ assertTrue(container.isPinned());
+
+ // No request to set the same animation params.
+ clearInvocations(mTransaction);
+ mPresenter.setTaskFragmentPinned(mTransaction, container, true);
+
+ verify(mTransaction, never()).addTaskFragmentOperation(any(), any());
+ }
+
+ @Test
public void testGetMinDimensionsForIntent() {
final Intent intent = new Intent(ApplicationProvider.getApplicationContext(),
MinimumDimensionActivity.class);
diff --git a/libs/WindowManager/Shell/aconfig/multitasking.aconfig b/libs/WindowManager/Shell/aconfig/multitasking.aconfig
index fe68123..66d4879 100644
--- a/libs/WindowManager/Shell/aconfig/multitasking.aconfig
+++ b/libs/WindowManager/Shell/aconfig/multitasking.aconfig
@@ -71,3 +71,17 @@
description: "Hides the bubble overflow if there aren't any overflowed bubbles"
bug: "334175587"
}
+
+flag {
+ name: "enable_retrievable_bubbles"
+ namespace: "multitasking"
+ description: "Allow opening bubbles overflow UI without bubbles being visible"
+ bug: "340337839"
+}
+
+flag {
+ name: "enable_bubble_stashing"
+ namespace: "multitasking"
+ description: "Allow the floating bubble stack to stash on the edge of the screen"
+ bug: "341361249"
+}
diff --git a/libs/WindowManager/Shell/multivalentTests/src/com/android/wm/shell/bubbles/BubblePositionerTest.kt b/libs/WindowManager/Shell/multivalentTests/src/com/android/wm/shell/bubbles/BubblePositionerTest.kt
index 8487e379..9e1440d 100644
--- a/libs/WindowManager/Shell/multivalentTests/src/com/android/wm/shell/bubbles/BubblePositionerTest.kt
+++ b/libs/WindowManager/Shell/multivalentTests/src/com/android/wm/shell/bubbles/BubblePositionerTest.kt
@@ -218,11 +218,10 @@
insets = Insets.of(10, 20, 5, 15),
windowBounds = Rect(0, 0, 1800, 2600)
)
- val bubbleBarBounds = Rect(1700, 2500, 1780, 2600)
positioner.setShowingInBubbleBar(true)
positioner.update(deviceConfig)
- positioner.bubbleBarBounds = bubbleBarBounds
+ positioner.bubbleBarTopOnScreen = 2500
val spaceBetweenTopInsetAndBubbleBarInLandscape = 1680
val expandedViewVerticalSpacing =
@@ -246,10 +245,9 @@
insets = Insets.of(10, 20, 5, 15),
windowBounds = Rect(0, 0, screenWidth, 2600)
)
- val bubbleBarBounds = Rect(100, 2500, 280, 2550)
positioner.setShowingInBubbleBar(true)
positioner.update(deviceConfig)
- positioner.bubbleBarBounds = bubbleBarBounds
+ positioner.bubbleBarTopOnScreen = 2500
val spaceBetweenTopInsetAndBubbleBarInLandscape = 180
val expandedViewSpacing =
@@ -597,16 +595,19 @@
private fun testGetBubbleBarExpandedViewBounds(onLeft: Boolean, isOverflow: Boolean) {
positioner.setShowingInBubbleBar(true)
+ val windowBounds = Rect(0, 0, 2000, 2600)
+ val insets = Insets.of(10, 20, 5, 15)
val deviceConfig =
defaultDeviceConfig.copy(
isLargeScreen = true,
isLandscape = true,
- insets = Insets.of(10, 20, 5, 15),
- windowBounds = Rect(0, 0, 2000, 2600)
+ insets = insets,
+ windowBounds = windowBounds
)
positioner.update(deviceConfig)
- positioner.bubbleBarBounds = getBubbleBarBounds(onLeft, deviceConfig)
+ val bubbleBarHeight = 100
+ positioner.bubbleBarTopOnScreen = windowBounds.bottom - insets.bottom - bubbleBarHeight
val expandedViewPadding =
context.resources.getDimensionPixelSize(R.dimen.bubble_expanded_view_padding)
@@ -624,7 +625,7 @@
left = right - positioner.getExpandedViewWidthForBubbleBar(isOverflow)
}
// Above the bubble bar
- val bottom = positioner.bubbleBarBounds.top - expandedViewPadding
+ val bottom = positioner.bubbleBarTopOnScreen - expandedViewPadding
// Calculate right and top based on size
val top = bottom - positioner.getExpandedViewHeightForBubbleBar(isOverflow)
val expectedBounds = Rect(left, top, right, bottom)
@@ -666,21 +667,4 @@
positioner.getAllowableStackPositionRegion(1 /* bubbleCount */)
return allowableStackRegion.top + allowableStackRegion.height() * offsetPercent
}
-
- private fun getBubbleBarBounds(onLeft: Boolean, deviceConfig: DeviceConfig): Rect {
- val width = 200
- val height = 100
- val bottom = deviceConfig.windowBounds.bottom - deviceConfig.insets.bottom
- val top = bottom - height
- val left: Int
- val right: Int
- if (onLeft) {
- left = deviceConfig.insets.left
- right = left + width
- } else {
- right = deviceConfig.windowBounds.right - deviceConfig.insets.right
- left = right - width
- }
- return Rect(left, top, right, bottom)
- }
}
diff --git a/libs/WindowManager/Shell/multivalentTests/src/com/android/wm/shell/bubbles/bar/BubbleExpandedViewPinControllerTest.kt b/libs/WindowManager/Shell/multivalentTests/src/com/android/wm/shell/bubbles/bar/BubbleExpandedViewPinControllerTest.kt
index 0764141..12d1927 100644
--- a/libs/WindowManager/Shell/multivalentTests/src/com/android/wm/shell/bubbles/bar/BubbleExpandedViewPinControllerTest.kt
+++ b/libs/WindowManager/Shell/multivalentTests/src/com/android/wm/shell/bubbles/bar/BubbleExpandedViewPinControllerTest.kt
@@ -53,7 +53,6 @@
const val SCREEN_WIDTH = 2000
const val SCREEN_HEIGHT = 1000
- const val BUBBLE_BAR_WIDTH = 100
const val BUBBLE_BAR_HEIGHT = 50
}
@@ -84,14 +83,8 @@
insets = Insets.of(10, 20, 30, 40)
)
positioner.update(deviceConfig)
- positioner.bubbleBarBounds =
- Rect(
- SCREEN_WIDTH - deviceConfig.insets.right - BUBBLE_BAR_WIDTH,
- SCREEN_HEIGHT - deviceConfig.insets.bottom - BUBBLE_BAR_HEIGHT,
- SCREEN_WIDTH - deviceConfig.insets.right,
- SCREEN_HEIGHT - deviceConfig.insets.bottom
- )
-
+ positioner.bubbleBarTopOnScreen =
+ SCREEN_HEIGHT - deviceConfig.insets.bottom - BUBBLE_BAR_HEIGHT
controller = BubbleExpandedViewPinController(context, container, positioner)
testListener = TestLocationChangeListener()
controller.setListener(testListener)
@@ -247,9 +240,10 @@
private val dropTargetView: View?
get() = container.findViewById(R.id.bubble_bar_drop_target)
- private fun getExpectedDropTargetBounds(onLeft: Boolean): Rect = Rect().also {
- positioner.getBubbleBarExpandedViewBounds(onLeft, false /* isOveflowExpanded */, it)
- }
+ private fun getExpectedDropTargetBounds(onLeft: Boolean): Rect =
+ Rect().also {
+ positioner.getBubbleBarExpandedViewBounds(onLeft, false /* isOveflowExpanded */, it)
+ }
private fun runOnMainSync(runnable: Runnable) {
InstrumentationRegistry.getInstrumentation().runOnMainSync(runnable)
diff --git a/libs/WindowManager/Shell/res/values/dimen.xml b/libs/WindowManager/Shell/res/values/dimen.xml
index 8d24c16..aa4fb44 100644
--- a/libs/WindowManager/Shell/res/values/dimen.xml
+++ b/libs/WindowManager/Shell/res/values/dimen.xml
@@ -247,13 +247,11 @@
<!-- Padding for the bubble popup view contents. -->
<dimen name="bubble_popup_padding">24dp</dimen>
<!-- The size of the caption bar inset at the top of bubble bar expanded view. -->
- <dimen name="bubble_bar_expanded_view_caption_height">32dp</dimen>
+ <dimen name="bubble_bar_expanded_view_caption_height">36dp</dimen>
<!-- The width of the caption bar at the top of bubble bar expanded view. -->
- <dimen name="bubble_bar_expanded_view_caption_width">128dp</dimen>
- <!-- The height of the dots shown for the caption menu in the bubble bar expanded view.. -->
- <dimen name="bubble_bar_expanded_view_caption_dot_size">4dp</dimen>
- <!-- The spacing between the dots for the caption menu in the bubble bar expanded view.. -->
- <dimen name="bubble_bar_expanded_view_caption_dot_spacing">4dp</dimen>
+ <dimen name="bubble_bar_expanded_view_caption_width">80dp</dimen>
+ <!-- The height of the handle shown for the caption menu in the bubble bar expanded view. -->
+ <dimen name="bubble_bar_expanded_view_handle_height">4dp</dimen>
<!-- Width of the expanded bubble bar view shown when the bubble is expanded. -->
<dimen name="bubble_bar_expanded_view_width">412dp</dimen>
<!-- Minimum width of the bubble bar manage menu. -->
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeStatus.java b/libs/WindowManager/Shell/shared/src/com/android/wm/shell/shared/DesktopModeStatus.java
similarity index 86%
rename from libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeStatus.java
rename to libs/WindowManager/Shell/shared/src/com/android/wm/shell/shared/DesktopModeStatus.java
index b41454d..8d8655a 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeStatus.java
+++ b/libs/WindowManager/Shell/shared/src/com/android/wm/shell/shared/DesktopModeStatus.java
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.wm.shell.desktopmode;
+package com.android.wm.shell.shared;
import android.annotation.NonNull;
import android.content.Context;
@@ -41,15 +41,6 @@
public static final boolean IS_DISPLAY_CHANGE_ENABLED = SystemProperties.getBoolean(
"persist.wm.debug.desktop_change_display", false);
-
- /**
- * Flag to indicate that desktop stashing is enabled.
- * When enabled, swiping home from desktop stashes the open apps. Next app that launches,
- * will be added to the desktop.
- */
- private static final boolean IS_STASHING_ENABLED = SystemProperties.getBoolean(
- "persist.wm.debug.desktop_stashing", false);
-
/**
* Flag to indicate whether to apply shadows to windows in desktop mode.
*/
@@ -76,6 +67,16 @@
private static final boolean ENFORCE_DEVICE_RESTRICTIONS = SystemProperties.getBoolean(
"persist.wm.debug.desktop_mode_enforce_device_restrictions", true);
+ /** Override density for tasks when they're inside the desktop. */
+ public static final int DESKTOP_DENSITY_OVERRIDE =
+ SystemProperties.getInt("persist.wm.debug.desktop_mode_density", 284);
+
+ /** The minimum override density allowed for tasks inside the desktop. */
+ private static final int DESKTOP_DENSITY_MIN = 100;
+
+ /** The maximum override density allowed for tasks inside the desktop. */
+ private static final int DESKTOP_DENSITY_MAX = 1000;
+
/**
* Default value for {@code MAX_TASK_LIMIT}.
*/
@@ -109,14 +110,6 @@
}
/**
- * Return {@code true} if desktop task stashing is enabled when going home.
- * Allows users to use home screen to add tasks to desktop.
- */
- public static boolean isStashingEnabled() {
- return IS_STASHING_ENABLED;
- }
-
- /**
* Return whether to use window shadows.
*
* @param isFocusedWindow whether the window to apply shadows to is focused
@@ -144,7 +137,7 @@
/**
* Return the maximum limit on the number of Tasks to show in Desktop Mode at any one time.
*/
- static int getMaxTaskLimit() {
+ public static int getMaxTaskLimit() {
return MAX_TASK_LIMIT;
}
@@ -162,4 +155,12 @@
public static boolean canEnterDesktopMode(@NonNull Context context) {
return (!enforceDeviceRestrictions() || isDesktopModeSupported(context)) && isEnabled();
}
+
+ /**
+ * Return {@code true} if the override desktop density is set.
+ */
+ public static boolean isDesktopDensityOverrideSet() {
+ return DESKTOP_DENSITY_OVERRIDE >= DESKTOP_DENSITY_MIN
+ && DESKTOP_DENSITY_OVERRIDE <= DESKTOP_DENSITY_MAX;
+ }
}
diff --git a/libs/WindowManager/Shell/shared/src/com/android/wm/shell/shared/TransitionUtil.java b/libs/WindowManager/Shell/shared/src/com/android/wm/shell/shared/TransitionUtil.java
index dcd4062..785e30d 100644
--- a/libs/WindowManager/Shell/shared/src/com/android/wm/shell/shared/TransitionUtil.java
+++ b/libs/WindowManager/Shell/shared/src/com/android/wm/shell/shared/TransitionUtil.java
@@ -69,8 +69,12 @@
/** Returns {@code true} if the transition is opening or closing mode. */
public static boolean isOpenOrCloseMode(@TransitionInfo.TransitionMode int mode) {
- return mode == TRANSIT_OPEN || mode == TRANSIT_CLOSE
- || mode == TRANSIT_TO_FRONT || mode == TRANSIT_TO_BACK;
+ return isOpeningMode(mode) || mode == TRANSIT_CLOSE || mode == TRANSIT_TO_BACK;
+ }
+
+ /** Returns {@code true} if the transition is opening mode. */
+ public static boolean isOpeningMode(@TransitionInfo.TransitionMode int mode) {
+ return mode == TRANSIT_OPEN || mode == TRANSIT_TO_FRONT;
}
/** Returns {@code true} if the transition has a display change. */
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/back/BackAnimationController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/back/BackAnimationController.java
index 163a896..5600664 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/back/BackAnimationController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/back/BackAnimationController.java
@@ -646,7 +646,7 @@
private void tryDispatchOnBackCancelled(IOnBackInvokedCallback callback) {
if (!mOnBackStartDispatched) {
- Log.e(TAG, "Skipping dispatching onBackCancelled. Start was never dispatched.");
+ Log.d(TAG, "Skipping dispatching onBackCancelled. Start was never dispatched.");
return;
}
if (callback == null) {
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/back/CrossActivityBackAnimation.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/back/CrossActivityBackAnimation.kt
index 037b1ec..c988c2f 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/back/CrossActivityBackAnimation.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/back/CrossActivityBackAnimation.kt
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2022 The Android Open Source Project
+ * Copyright (C) 2024 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -13,6 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
+
package com.android.wm.shell.back
import android.animation.Animator
@@ -34,6 +35,7 @@
import android.view.SurfaceControl
import android.view.animation.DecelerateInterpolator
import android.view.animation.Interpolator
+import android.view.animation.Transformation
import android.window.BackEvent
import android.window.BackMotionEvent
import android.window.BackNavigationInfo
@@ -46,52 +48,45 @@
import com.android.wm.shell.RootTaskDisplayAreaOrganizer
import com.android.wm.shell.animation.Interpolators
import com.android.wm.shell.protolog.ShellProtoLogGroup
-import com.android.wm.shell.shared.annotations.ShellMainThread
-import javax.inject.Inject
import kotlin.math.abs
import kotlin.math.max
import kotlin.math.min
-/** Class that defines cross-activity animation. */
-@ShellMainThread
-class CrossActivityBackAnimation @Inject constructor(
+abstract class CrossActivityBackAnimation(
private val context: Context,
private val background: BackAnimationBackground,
- private val rootTaskDisplayAreaOrganizer: RootTaskDisplayAreaOrganizer
+ private val rootTaskDisplayAreaOrganizer: RootTaskDisplayAreaOrganizer,
+ protected val transaction: SurfaceControl.Transaction,
+ private val choreographer: Choreographer
) : ShellBackAnimation() {
- private val startClosingRect = RectF()
- private val targetClosingRect = RectF()
- private val currentClosingRect = RectF()
+ protected val startClosingRect = RectF()
+ protected val targetClosingRect = RectF()
+ protected val currentClosingRect = RectF()
- private val startEnteringRect = RectF()
- private val targetEnteringRect = RectF()
- private val currentEnteringRect = RectF()
+ protected val startEnteringRect = RectF()
+ protected val targetEnteringRect = RectF()
+ protected val currentEnteringRect = RectF()
- private val backAnimRect = Rect()
+ protected val backAnimRect = Rect()
private val cropRect = Rect()
private var cornerRadius = ScreenDecorationsUtils.getWindowCornerRadius(context)
- private val backAnimationRunner = BackAnimationRunner(
- Callback(), Runner(), context, Cuj.CUJ_PREDICTIVE_BACK_CROSS_ACTIVITY
- )
+ private val backAnimationRunner =
+ BackAnimationRunner(Callback(), Runner(), context, Cuj.CUJ_PREDICTIVE_BACK_CROSS_ACTIVITY)
private val initialTouchPos = PointF()
private val transformMatrix = Matrix()
private val tmpFloat9 = FloatArray(9)
- private var enteringTarget: RemoteAnimationTarget? = null
- private var closingTarget: RemoteAnimationTarget? = null
- private val transaction = SurfaceControl.Transaction()
+ protected var enteringTarget: RemoteAnimationTarget? = null
+ protected var closingTarget: RemoteAnimationTarget? = null
private var triggerBack = false
private var finishCallback: IRemoteAnimationFinishedCallback? = null
private val progressAnimator = BackProgressAnimator()
private val displayBoundsMargin =
context.resources.getDimension(R.dimen.cross_task_back_vertical_margin)
- private val enteringStartOffset =
- context.resources.getDimension(R.dimen.cross_activity_back_entering_start_offset)
private val gestureInterpolator = Interpolators.BACK_GESTURE
- private val postCommitInterpolator = Interpolators.FAST_OUT_SLOW_IN
private val verticalMoveInterpolator: Interpolator = DecelerateInterpolator()
private var scrimLayer: SurfaceControl? = null
@@ -103,13 +98,42 @@
private var rightLetterboxLayer: SurfaceControl? = null
private var letterboxColor: Int = 0
+ /** Background color to be used during the animation, also see [getBackgroundColor] */
+ protected var customizedBackgroundColor = 0
+
+ /**
+ * Whether the entering target should be shifted vertically with the user gesture in pre-commit
+ */
+ abstract val allowEnteringYShift: Boolean
+
+ /**
+ * Subclasses must set the [startEnteringRect] and [targetEnteringRect] to define the movement
+ * of the enteringTarget during pre-commit phase.
+ */
+ abstract fun preparePreCommitEnteringRectMovement()
+
+ /**
+ * Returns a base transformation to apply to the entering target during pre-commit. The system
+ * will apply the default animation on top of it.
+ */
+ protected open fun getPreCommitEnteringBaseTransformation(progress: Float): Transformation? =
+ null
+
override fun onConfigurationChanged(newConfiguration: Configuration) {
cornerRadius = ScreenDecorationsUtils.getWindowCornerRadius(context)
}
override fun getRunner() = backAnimationRunner
- private fun startBackAnimation(backMotionEvent: BackMotionEvent) {
+ private fun getBackgroundColor(): Int =
+ when {
+ customizedBackgroundColor != 0 -> customizedBackgroundColor
+ isLetterboxed -> letterboxColor
+ enteringTarget != null -> enteringTarget!!.taskInfo.taskDescription!!.backgroundColor
+ else -> 0
+ }
+
+ protected open fun startBackAnimation(backMotionEvent: BackMotionEvent) {
if (enteringTarget == null || closingTarget == null) {
ProtoLog.d(
ShellProtoLogGroup.WM_SHELL_BACK_PREVIEW,
@@ -122,8 +146,8 @@
transaction.setAnimationTransaction()
isLetterboxed = closingTarget!!.taskInfo.appCompatTaskInfo.topActivityBoundsLetterboxed
- enteringHasSameLetterbox = isLetterboxed &&
- closingTarget!!.localBounds.equals(enteringTarget!!.localBounds)
+ enteringHasSameLetterbox =
+ isLetterboxed && closingTarget!!.localBounds.equals(enteringTarget!!.localBounds)
if (isLetterboxed && !enteringHasSameLetterbox) {
// Play animation with letterboxes, if closing and entering target have mismatching
@@ -143,32 +167,27 @@
targetClosingRect.scaleCentered(MAX_SCALE)
if (backMotionEvent.swipeEdge != BackEvent.EDGE_RIGHT) {
targetClosingRect.offset(
- startClosingRect.right - targetClosingRect.right - displayBoundsMargin, 0f
+ startClosingRect.right - targetClosingRect.right - displayBoundsMargin,
+ 0f
)
}
- // the entering target starts 96dp to the left of the screen edge...
- startEnteringRect.set(startClosingRect)
- startEnteringRect.offset(-enteringStartOffset, 0f)
+ preparePreCommitEnteringRectMovement()
- // ...and gets scaled in sync with the closing target
- targetEnteringRect.set(startEnteringRect)
- targetEnteringRect.scaleCentered(MAX_SCALE)
-
- // Draw background with task background color (or letterbox color).
- val backgroundColor = if (isLetterboxed) {
- letterboxColor
- } else {
- enteringTarget!!.taskInfo.taskDescription!!.backgroundColor
- }
background.ensureBackground(
- closingTarget!!.windowConfiguration.bounds, backgroundColor, transaction
+ closingTarget!!.windowConfiguration.bounds,
+ getBackgroundColor(),
+ transaction
)
ensureScrimLayer()
if (isLetterboxed && enteringHasSameLetterbox) {
// crop left and right letterboxes
- cropRect.set(closingTarget!!.localBounds.left, 0, closingTarget!!.localBounds.right,
- closingTarget!!.windowConfiguration.bounds.height())
+ cropRect.set(
+ closingTarget!!.localBounds.left,
+ 0,
+ closingTarget!!.localBounds.right,
+ closingTarget!!.windowConfiguration.bounds.height()
+ )
// and add fake letterbox square surfaces instead
ensureLetterboxes()
} else {
@@ -185,8 +204,14 @@
currentClosingRect.offset(0f, yOffset)
applyTransform(closingTarget?.leash, currentClosingRect, 1f)
currentEnteringRect.setInterpolatedRectF(startEnteringRect, targetEnteringRect, progress)
- currentEnteringRect.offset(0f, yOffset)
- applyTransform(enteringTarget?.leash, currentEnteringRect, 1f)
+ if (allowEnteringYShift) currentEnteringRect.offset(0f, yOffset)
+ val enteringTransformation = getPreCommitEnteringBaseTransformation(progress)
+ applyTransform(
+ enteringTarget?.leash,
+ currentEnteringRect,
+ enteringTransformation?.alpha ?: 1f,
+ enteringTransformation
+ )
applyTransaction()
}
@@ -199,30 +224,25 @@
val deltaYRatio = min(screenHeight / 2f, abs(rawYDelta)) / (screenHeight / 2f)
val interpolatedYRatio: Float = verticalMoveInterpolator.getInterpolation(deltaYRatio)
// limit y-shift so surface never passes 8dp screen margin
- val deltaY = yDirection * interpolatedYRatio * max(
- 0f, (screenHeight - centeredRect.height()) / 2f - displayBoundsMargin
- )
+ val deltaY =
+ max(0f, (screenHeight - centeredRect.height()) / 2f - displayBoundsMargin) *
+ interpolatedYRatio *
+ yDirection
return deltaY
}
- private fun onGestureCommitted() {
- if (closingTarget?.leash == null || enteringTarget?.leash == null ||
- !enteringTarget!!.leash.isValid || !closingTarget!!.leash.isValid
+ protected open fun onGestureCommitted() {
+ if (
+ closingTarget?.leash == null ||
+ enteringTarget?.leash == null ||
+ !enteringTarget!!.leash.isValid ||
+ !closingTarget!!.leash.isValid
) {
finishAnimation()
return
}
- // We enter phase 2 of the animation, the starting coordinates for phase 2 are the current
- // coordinate of the gesture driven phase. Let's update the start and target rects and kick
- // off the animator
- startClosingRect.set(currentClosingRect)
- startEnteringRect.set(currentEnteringRect)
- targetEnteringRect.set(backAnimRect)
- targetClosingRect.set(backAnimRect)
- targetClosingRect.offset(currentClosingRect.left + enteringStartOffset, 0f)
-
- val valueAnimator = ValueAnimator.ofFloat(1f, 0f).setDuration(POST_ANIMATION_DURATION)
+ val valueAnimator = ValueAnimator.ofFloat(1f, 0f).setDuration(POST_COMMIT_DURATION)
valueAnimator.addUpdateListener { animation: ValueAnimator ->
val progress = animation.animatedFraction
onPostCommitProgress(progress)
@@ -230,27 +250,22 @@
background.resetStatusBarCustomization()
}
}
- valueAnimator.addListener(object : AnimatorListenerAdapter() {
- override fun onAnimationEnd(animation: Animator) {
- background.resetStatusBarCustomization()
- finishAnimation()
+ valueAnimator.addListener(
+ object : AnimatorListenerAdapter() {
+ override fun onAnimationEnd(animation: Animator) {
+ background.resetStatusBarCustomization()
+ finishAnimation()
+ }
}
- })
+ )
valueAnimator.start()
}
- private fun onPostCommitProgress(linearProgress: Float) {
- val closingAlpha = max(1f - linearProgress * 2, 0f)
- val progress = postCommitInterpolator.getInterpolation(linearProgress)
+ protected open fun onPostCommitProgress(linearProgress: Float) {
scrimLayer?.let { transaction.setAlpha(it, maxScrimAlpha * (1f - linearProgress)) }
- currentClosingRect.setInterpolatedRectF(startClosingRect, targetClosingRect, progress)
- applyTransform(closingTarget?.leash, currentClosingRect, closingAlpha)
- currentEnteringRect.setInterpolatedRectF(startEnteringRect, targetEnteringRect, progress)
- applyTransform(enteringTarget?.leash, currentEnteringRect, 1f)
- applyTransaction()
}
- private fun finishAnimation() {
+ protected open fun finishAnimation() {
enteringTarget?.let {
if (it.leash != null && it.leash.isValid) {
transaction.setCornerRadius(it.leash, 0f)
@@ -278,47 +293,56 @@
enteringHasSameLetterbox = false
}
- private fun applyTransform(leash: SurfaceControl?, rect: RectF, alpha: Float) {
+ protected fun applyTransform(
+ leash: SurfaceControl?,
+ rect: RectF,
+ alpha: Float,
+ baseTransformation: Transformation? = null
+ ) {
if (leash == null || !leash.isValid) return
val scale = rect.width() / backAnimRect.width()
- transformMatrix.reset()
- val scalePivotX = if (isLetterboxed && enteringHasSameLetterbox) {
- closingTarget!!.localBounds.left.toFloat()
- } else {
- 0f
- }
- transformMatrix.setScale(scale, scale, scalePivotX, 0f)
- transformMatrix.postTranslate(rect.left, rect.top)
- transaction.setAlpha(leash, alpha)
- .setMatrix(leash, transformMatrix, tmpFloat9)
+ val matrix = baseTransformation?.matrix ?: transformMatrix.apply { reset() }
+ val scalePivotX =
+ if (isLetterboxed && enteringHasSameLetterbox) {
+ closingTarget!!.localBounds.left.toFloat()
+ } else {
+ 0f
+ }
+ matrix.postScale(scale, scale, scalePivotX, 0f)
+ matrix.postTranslate(rect.left, rect.top)
+ transaction
+ .setAlpha(leash, keepMinimumAlpha(alpha))
+ .setMatrix(leash, matrix, tmpFloat9)
.setCrop(leash, cropRect)
.setCornerRadius(leash, cornerRadius)
}
- private fun applyTransaction() {
- transaction.setFrameTimelineVsync(Choreographer.getInstance().vsyncId)
+ protected fun applyTransaction() {
+ transaction.setFrameTimelineVsync(choreographer.vsyncId)
transaction.apply()
}
private fun ensureScrimLayer() {
if (scrimLayer != null) return
val isDarkTheme: Boolean = isDarkMode(context)
- val scrimBuilder = SurfaceControl.Builder()
- .setName("Cross-Activity back animation scrim")
- .setCallsite("CrossActivityBackAnimation")
- .setColorLayer()
- .setOpaque(false)
- .setHidden(false)
+ val scrimBuilder =
+ SurfaceControl.Builder()
+ .setName("Cross-Activity back animation scrim")
+ .setCallsite("CrossActivityBackAnimation")
+ .setColorLayer()
+ .setOpaque(false)
+ .setHidden(false)
rootTaskDisplayAreaOrganizer.attachToDisplayArea(Display.DEFAULT_DISPLAY, scrimBuilder)
scrimLayer = scrimBuilder.build()
val colorComponents = floatArrayOf(0f, 0f, 0f)
maxScrimAlpha = if (isDarkTheme) MAX_SCRIM_ALPHA_DARK else MAX_SCRIM_ALPHA_LIGHT
- val scrimCrop = if (isLetterboxed) {
- closingTarget!!.windowConfiguration.bounds
- } else {
- closingTarget!!.localBounds
- }
+ val scrimCrop =
+ if (isLetterboxed) {
+ closingTarget!!.windowConfiguration.bounds
+ } else {
+ closingTarget!!.localBounds
+ }
transaction
.setColor(scrimLayer, colorComponents)
.setAlpha(scrimLayer!!, maxScrimAlpha)
@@ -339,21 +363,34 @@
private fun ensureLetterboxes() {
closingTarget?.let { t ->
if (t.localBounds.left != 0 && leftLetterboxLayer == null) {
- val bounds = Rect(0, t.windowConfiguration.bounds.top, t.localBounds.left,
- t.windowConfiguration.bounds.bottom)
+ val bounds =
+ Rect(
+ 0,
+ t.windowConfiguration.bounds.top,
+ t.localBounds.left,
+ t.windowConfiguration.bounds.bottom
+ )
leftLetterboxLayer = ensureLetterbox(bounds)
}
- if (t.localBounds.right != t.windowConfiguration.bounds.right &&
- rightLetterboxLayer == null) {
- val bounds = Rect(t.localBounds.right, t.windowConfiguration.bounds.top,
- t.windowConfiguration.bounds.right, t.windowConfiguration.bounds.bottom)
+ if (
+ t.localBounds.right != t.windowConfiguration.bounds.right &&
+ rightLetterboxLayer == null
+ ) {
+ val bounds =
+ Rect(
+ t.localBounds.right,
+ t.windowConfiguration.bounds.top,
+ t.windowConfiguration.bounds.right,
+ t.windowConfiguration.bounds.bottom
+ )
rightLetterboxLayer = ensureLetterbox(bounds)
}
}
}
private fun ensureLetterbox(bounds: Rect): SurfaceControl {
- val letterboxBuilder = SurfaceControl.Builder()
+ val letterboxBuilder =
+ SurfaceControl.Builder()
.setName("Cross-Activity back animation letterbox")
.setCallsite("CrossActivityBackAnimation")
.setColorLayer()
@@ -362,13 +399,17 @@
rootTaskDisplayAreaOrganizer.attachToDisplayArea(Display.DEFAULT_DISPLAY, letterboxBuilder)
val layer = letterboxBuilder.build()
- val colorComponents = floatArrayOf(Color.red(letterboxColor) / 255f,
- Color.green(letterboxColor) / 255f, Color.blue(letterboxColor) / 255f)
+ val colorComponents =
+ floatArrayOf(
+ Color.red(letterboxColor) / 255f,
+ Color.green(letterboxColor) / 255f,
+ Color.blue(letterboxColor) / 255f
+ )
transaction
- .setColor(layer, colorComponents)
- .setCrop(layer, bounds)
- .setRelativeLayer(layer, closingTarget!!.leash, 1)
- .show(layer)
+ .setColor(layer, colorComponents)
+ .setCrop(layer, bounds)
+ .setRelativeLayer(layer, closingTarget!!.leash, 1)
+ .show(layer)
return layer
}
@@ -389,8 +430,8 @@
}
override fun prepareNextAnimation(
- animationInfo: BackNavigationInfo.CustomAnimationInfo?,
- letterboxColor: Int
+ animationInfo: BackNavigationInfo.CustomAnimationInfo?,
+ letterboxColor: Int
): Boolean {
this.letterboxColor = letterboxColor
return false
@@ -415,9 +456,7 @@
}
override fun onBackCancelled() {
- progressAnimator.onBackCancelled {
- finishAnimation()
- }
+ progressAnimator.onBackCancelled { finishAnimation() }
}
override fun onBackInvoked() {
@@ -435,7 +474,8 @@
finishedCallback: IRemoteAnimationFinishedCallback
) {
ProtoLog.d(
- ShellProtoLogGroup.WM_SHELL_BACK_PREVIEW, "Start back to activity animation."
+ ShellProtoLogGroup.WM_SHELL_BACK_PREVIEW,
+ "Start back to activity animation."
)
for (a in apps) {
when (a.mode) {
@@ -452,23 +492,25 @@
}
companion object {
- /** Max scale of the entering/closing window.*/
- private const val MAX_SCALE = 0.9f
-
- /** Duration of post animation after gesture committed. */
- private const val POST_ANIMATION_DURATION = 300L
-
+ /** Max scale of the closing window. */
+ internal const val MAX_SCALE = 0.9f
private const val MAX_SCRIM_ALPHA_DARK = 0.8f
private const val MAX_SCRIM_ALPHA_LIGHT = 0.2f
+ private const val POST_COMMIT_DURATION = 300L
}
}
+// The target will loose focus when alpha == 0, so keep a minimum value for it.
+private fun keepMinimumAlpha(transAlpha: Float): Float {
+ return max(transAlpha.toDouble(), 0.005).toFloat()
+}
+
private fun isDarkMode(context: Context): Boolean {
return context.resources.configuration.uiMode and Configuration.UI_MODE_NIGHT_MASK ==
- Configuration.UI_MODE_NIGHT_YES
+ Configuration.UI_MODE_NIGHT_YES
}
-private fun RectF.setInterpolatedRectF(start: RectF, target: RectF, progress: Float) {
+internal fun RectF.setInterpolatedRectF(start: RectF, target: RectF, progress: Float) {
require(!(progress < 0 || progress > 1)) { "Progress value must be between 0 and 1" }
left = start.left + (target.left - start.left) * progress
top = start.top + (target.top - start.top) * progress
@@ -476,7 +518,7 @@
bottom = start.bottom + (target.bottom - start.bottom) * progress
}
-private fun RectF.scaleCentered(
+internal fun RectF.scaleCentered(
scale: Float,
pivotX: Float = left + width() / 2,
pivotY: Float = top + height() / 2
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/back/CustomCrossActivityBackAnimation.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/back/CustomCrossActivityBackAnimation.kt
new file mode 100644
index 0000000..e6ec2b4
--- /dev/null
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/back/CustomCrossActivityBackAnimation.kt
@@ -0,0 +1,256 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.wm.shell.back
+
+import android.content.Context
+import android.graphics.Rect
+import android.graphics.RectF
+import android.util.MathUtils
+import android.view.Choreographer
+import android.view.SurfaceControl
+import android.view.animation.Animation
+import android.view.animation.Transformation
+import android.window.BackMotionEvent
+import android.window.BackNavigationInfo
+import com.android.internal.R
+import com.android.internal.policy.TransitionAnimation
+import com.android.internal.protolog.common.ProtoLog
+import com.android.wm.shell.RootTaskDisplayAreaOrganizer
+import com.android.wm.shell.protolog.ShellProtoLogGroup
+import com.android.wm.shell.shared.annotations.ShellMainThread
+import javax.inject.Inject
+
+/** Class that handles customized predictive cross activity back animations. */
+@ShellMainThread
+class CustomCrossActivityBackAnimation(
+ context: Context,
+ background: BackAnimationBackground,
+ rootTaskDisplayAreaOrganizer: RootTaskDisplayAreaOrganizer,
+ transaction: SurfaceControl.Transaction,
+ choreographer: Choreographer,
+ private val customAnimationLoader: CustomAnimationLoader
+) :
+ CrossActivityBackAnimation(
+ context,
+ background,
+ rootTaskDisplayAreaOrganizer,
+ transaction,
+ choreographer
+ ) {
+
+ private var enterAnimation: Animation? = null
+ private var closeAnimation: Animation? = null
+ private val transformation = Transformation()
+ private var gestureProgress = 0f
+
+ override val allowEnteringYShift = false
+
+ @Inject
+ constructor(
+ context: Context,
+ background: BackAnimationBackground,
+ rootTaskDisplayAreaOrganizer: RootTaskDisplayAreaOrganizer
+ ) : this(
+ context,
+ background,
+ rootTaskDisplayAreaOrganizer,
+ SurfaceControl.Transaction(),
+ Choreographer.getInstance(),
+ CustomAnimationLoader(
+ TransitionAnimation(context, false /* debug */, "CustomCrossActivityBackAnimation")
+ )
+ )
+
+ override fun preparePreCommitEnteringRectMovement() {
+ // No movement for the entering rect
+ startEnteringRect.set(startClosingRect)
+ targetEnteringRect.set(startClosingRect)
+ }
+
+ override fun getPreCommitEnteringBaseTransformation(progress: Float): Transformation {
+ gestureProgress = progress
+ transformation.clear()
+ enterAnimation!!.getTransformationAt(progress * PRE_COMMIT_MAX_PROGRESS, transformation)
+ return transformation
+ }
+
+ override fun startBackAnimation(backMotionEvent: BackMotionEvent) {
+ super.startBackAnimation(backMotionEvent)
+ if (
+ closeAnimation == null ||
+ enterAnimation == null ||
+ closingTarget == null ||
+ enteringTarget == null
+ ) {
+ ProtoLog.d(
+ ShellProtoLogGroup.WM_SHELL_BACK_PREVIEW,
+ "Enter animation or close animation is null."
+ )
+ return
+ }
+ initializeAnimation(closeAnimation!!, closingTarget!!.localBounds)
+ initializeAnimation(enterAnimation!!, enteringTarget!!.localBounds)
+ }
+
+ override fun onPostCommitProgress(linearProgress: Float) {
+ super.onPostCommitProgress(linearProgress)
+ if (closingTarget == null || enteringTarget == null) return
+
+ // TODO: Should we use the duration from the custom xml spec for the post-commit animation?
+ applyTransform(closingTarget!!.leash, currentClosingRect, linearProgress, closeAnimation!!)
+ val enteringProgress =
+ MathUtils.lerp(gestureProgress * PRE_COMMIT_MAX_PROGRESS, 1f, linearProgress)
+ applyTransform(
+ enteringTarget!!.leash,
+ currentEnteringRect,
+ enteringProgress,
+ enterAnimation!!
+ )
+ applyTransaction()
+ }
+
+ private fun applyTransform(
+ leash: SurfaceControl,
+ rect: RectF,
+ progress: Float,
+ animation: Animation
+ ) {
+ transformation.clear()
+ animation.getTransformationAt(progress, transformation)
+ applyTransform(leash, rect, transformation.alpha, transformation)
+ }
+
+ override fun finishAnimation() {
+ closeAnimation?.reset()
+ closeAnimation = null
+ enterAnimation?.reset()
+ enterAnimation = null
+ transformation.clear()
+ gestureProgress = 0f
+ super.finishAnimation()
+ }
+
+ /** Load customize animation before animation start. */
+ override fun prepareNextAnimation(
+ animationInfo: BackNavigationInfo.CustomAnimationInfo?,
+ letterboxColor: Int
+ ): Boolean {
+ super.prepareNextAnimation(animationInfo, letterboxColor)
+ if (animationInfo == null) return false
+ customAnimationLoader.loadAll(animationInfo)?.let { result ->
+ closeAnimation = result.closeAnimation
+ enterAnimation = result.enterAnimation
+ customizedBackgroundColor = result.backgroundColor
+ return true
+ }
+ return false
+ }
+
+ class AnimationLoadResult {
+ var closeAnimation: Animation? = null
+ var enterAnimation: Animation? = null
+ var backgroundColor = 0
+ }
+
+ companion object {
+ private const val PRE_COMMIT_MAX_PROGRESS = 0.2f
+ }
+}
+
+/** Helper class to load custom animation. */
+class CustomAnimationLoader(private val transitionAnimation: TransitionAnimation) {
+
+ /**
+ * Load both enter and exit animation for the close activity transition. Note that the result is
+ * only valid if the exit animation has set and loaded success. If the entering animation has
+ * not set(i.e. 0), here will load the default entering animation for it.
+ *
+ * @param animationInfo The information of customize animation, which can be set from
+ * [Activity.overrideActivityTransition] and/or [LayoutParams.windowAnimations]
+ */
+ fun loadAll(
+ animationInfo: BackNavigationInfo.CustomAnimationInfo
+ ): CustomCrossActivityBackAnimation.AnimationLoadResult? {
+ if (animationInfo.packageName.isEmpty()) return null
+ val close = loadAnimation(animationInfo, false) ?: return null
+ val open = loadAnimation(animationInfo, true)
+ val result = CustomCrossActivityBackAnimation.AnimationLoadResult()
+ result.closeAnimation = close
+ result.enterAnimation = open
+ result.backgroundColor = animationInfo.customBackground
+ return result
+ }
+
+ /**
+ * Load enter or exit animation from CustomAnimationInfo
+ *
+ * @param animationInfo The information for customize animation.
+ * @param enterAnimation true when load for enter animation, false for exit animation.
+ * @return Loaded animation.
+ */
+ fun loadAnimation(
+ animationInfo: BackNavigationInfo.CustomAnimationInfo,
+ enterAnimation: Boolean
+ ): Animation? {
+ var a: Animation? = null
+ // Activity#overrideActivityTransition has higher priority than windowAnimations
+ // Try to get animation from Activity#overrideActivityTransition
+ if (
+ enterAnimation && animationInfo.customEnterAnim != 0 ||
+ !enterAnimation && animationInfo.customExitAnim != 0
+ ) {
+ a =
+ transitionAnimation.loadAppTransitionAnimation(
+ animationInfo.packageName,
+ if (enterAnimation) animationInfo.customEnterAnim
+ else animationInfo.customExitAnim
+ )
+ } else if (animationInfo.windowAnimations != 0) {
+ // try to get animation from LayoutParams#windowAnimations
+ a =
+ transitionAnimation.loadAnimationAttr(
+ animationInfo.packageName,
+ animationInfo.windowAnimations,
+ if (enterAnimation) R.styleable.WindowAnimation_activityCloseEnterAnimation
+ else R.styleable.WindowAnimation_activityCloseExitAnimation,
+ false /* translucent */
+ )
+ }
+ // Only allow to load default animation for opening target.
+ if (a == null && enterAnimation) {
+ a = loadDefaultOpenAnimation()
+ }
+ if (a != null) {
+ ProtoLog.d(ShellProtoLogGroup.WM_SHELL_BACK_PREVIEW, "custom animation loaded %s", a)
+ } else {
+ ProtoLog.e(ShellProtoLogGroup.WM_SHELL_BACK_PREVIEW, "No custom animation loaded")
+ }
+ return a
+ }
+
+ private fun loadDefaultOpenAnimation(): Animation? {
+ return transitionAnimation.loadDefaultAnimationAttr(
+ R.styleable.WindowAnimation_activityCloseEnterAnimation,
+ false /* translucent */
+ )
+ }
+}
+
+private fun initializeAnimation(animation: Animation, bounds: Rect) {
+ val width = bounds.width()
+ val height = bounds.height()
+ animation.initialize(width, height, width, height)
+}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/back/CustomizeActivityAnimation.java b/libs/WindowManager/Shell/src/com/android/wm/shell/back/CustomizeActivityAnimation.java
deleted file mode 100644
index e27b40e..0000000
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/back/CustomizeActivityAnimation.java
+++ /dev/null
@@ -1,443 +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.wm.shell.back;
-
-import static android.view.RemoteAnimationTarget.MODE_CLOSING;
-import static android.view.RemoteAnimationTarget.MODE_OPENING;
-
-import static com.android.internal.jank.InteractionJankMonitor.CUJ_PREDICTIVE_BACK_CROSS_ACTIVITY;
-import static com.android.wm.shell.protolog.ShellProtoLogGroup.WM_SHELL_BACK_PREVIEW;
-
-import android.animation.Animator;
-import android.animation.AnimatorListenerAdapter;
-import android.animation.ValueAnimator;
-import android.annotation.NonNull;
-import android.annotation.Nullable;
-import android.app.Activity;
-import android.content.Context;
-import android.content.res.Configuration;
-import android.graphics.Color;
-import android.graphics.Rect;
-import android.os.RemoteException;
-import android.util.FloatProperty;
-import android.view.Choreographer;
-import android.view.IRemoteAnimationFinishedCallback;
-import android.view.IRemoteAnimationRunner;
-import android.view.RemoteAnimationTarget;
-import android.view.SurfaceControl;
-import android.view.WindowManager.LayoutParams;
-import android.view.animation.Animation;
-import android.view.animation.DecelerateInterpolator;
-import android.view.animation.Transformation;
-import android.window.BackEvent;
-import android.window.BackMotionEvent;
-import android.window.BackNavigationInfo;
-import android.window.BackProgressAnimator;
-import android.window.IOnBackInvokedCallback;
-
-import com.android.internal.R;
-import com.android.internal.dynamicanimation.animation.SpringAnimation;
-import com.android.internal.dynamicanimation.animation.SpringForce;
-import com.android.internal.policy.ScreenDecorationsUtils;
-import com.android.internal.policy.TransitionAnimation;
-import com.android.internal.protolog.common.ProtoLog;
-import com.android.wm.shell.shared.annotations.ShellMainThread;
-
-import javax.inject.Inject;
-
-/** Class that handle customized close activity transition animation. */
-@ShellMainThread
-public class CustomizeActivityAnimation extends ShellBackAnimation {
- private final BackProgressAnimator mProgressAnimator = new BackProgressAnimator();
- private final BackAnimationRunner mBackAnimationRunner;
- private float mCornerRadius;
- private final SurfaceControl.Transaction mTransaction;
- private final BackAnimationBackground mBackground;
- private RemoteAnimationTarget mEnteringTarget;
- private RemoteAnimationTarget mClosingTarget;
- private IRemoteAnimationFinishedCallback mFinishCallback;
- /** Duration of post animation after gesture committed. */
- private static final int POST_ANIMATION_DURATION = 250;
-
- private static final int SCALE_FACTOR = 1000;
- private final SpringAnimation mProgressSpring;
- private float mLatestProgress = 0.0f;
-
- private static final float TARGET_COMMIT_PROGRESS = 0.5f;
-
- private final float[] mTmpFloat9 = new float[9];
- private final DecelerateInterpolator mDecelerateInterpolator = new DecelerateInterpolator();
-
- final CustomAnimationLoader mCustomAnimationLoader;
- private Animation mEnterAnimation;
- private Animation mCloseAnimation;
- private int mNextBackgroundColor;
- final Transformation mTransformation = new Transformation();
-
- private final Choreographer mChoreographer;
- private final Context mContext;
-
- @Inject
- public CustomizeActivityAnimation(Context context, BackAnimationBackground background) {
- this(context, background, new SurfaceControl.Transaction(), null);
- }
-
- CustomizeActivityAnimation(Context context, BackAnimationBackground background,
- SurfaceControl.Transaction transaction, Choreographer choreographer) {
- mCornerRadius = ScreenDecorationsUtils.getWindowCornerRadius(context);
- mBackground = background;
- mBackAnimationRunner = new BackAnimationRunner(
- new Callback(), new Runner(), context, CUJ_PREDICTIVE_BACK_CROSS_ACTIVITY);
- mCustomAnimationLoader = new CustomAnimationLoader(context);
-
- mProgressSpring = new SpringAnimation(this, ENTER_PROGRESS_PROP);
- mProgressSpring.setSpring(new SpringForce()
- .setStiffness(SpringForce.STIFFNESS_MEDIUM)
- .setDampingRatio(SpringForce.DAMPING_RATIO_NO_BOUNCY));
- mTransaction = transaction == null ? new SurfaceControl.Transaction() : transaction;
- mChoreographer = choreographer != null ? choreographer : Choreographer.getInstance();
- mContext = context;
- }
-
- @Override
- public void onConfigurationChanged(Configuration newConfig) {
- mCornerRadius = ScreenDecorationsUtils.getWindowCornerRadius(mContext);
- }
-
- private float getLatestProgress() {
- return mLatestProgress * SCALE_FACTOR;
- }
- private void setLatestProgress(float value) {
- mLatestProgress = value / SCALE_FACTOR;
- applyTransformTransaction(mLatestProgress);
- }
-
- private static final FloatProperty<CustomizeActivityAnimation> ENTER_PROGRESS_PROP =
- new FloatProperty<>("enter") {
- @Override
- public void setValue(CustomizeActivityAnimation anim, float value) {
- anim.setLatestProgress(value);
- }
-
- @Override
- public Float get(CustomizeActivityAnimation object) {
- return object.getLatestProgress();
- }
- };
-
- // The target will lose focus when alpha == 0, so keep a minimum value for it.
- private static float keepMinimumAlpha(float transAlpha) {
- return Math.max(transAlpha, 0.005f);
- }
-
- private static void initializeAnimation(Animation animation, Rect bounds) {
- final int width = bounds.width();
- final int height = bounds.height();
- animation.initialize(width, height, width, height);
- }
-
- private void startBackAnimation() {
- if (mEnteringTarget == null || mClosingTarget == null
- || mCloseAnimation == null || mEnterAnimation == null) {
- ProtoLog.d(WM_SHELL_BACK_PREVIEW, "Entering target or closing target is null.");
- return;
- }
- initializeAnimation(mCloseAnimation, mClosingTarget.localBounds);
- initializeAnimation(mEnterAnimation, mEnteringTarget.localBounds);
-
- // Draw background with task background color.
- if (mEnteringTarget.taskInfo != null && mEnteringTarget.taskInfo.taskDescription != null) {
- mBackground.ensureBackground(mClosingTarget.windowConfiguration.getBounds(),
- mNextBackgroundColor == Color.TRANSPARENT
- ? mEnteringTarget.taskInfo.taskDescription.getBackgroundColor()
- : mNextBackgroundColor,
- mTransaction);
- }
- }
-
- private void applyTransformTransaction(float progress) {
- if (mClosingTarget == null || mEnteringTarget == null) {
- return;
- }
- applyTransform(mClosingTarget.leash, progress, mCloseAnimation);
- applyTransform(mEnteringTarget.leash, progress, mEnterAnimation);
- mTransaction.setFrameTimelineVsync(mChoreographer.getVsyncId());
- mTransaction.apply();
- }
-
- private void applyTransform(SurfaceControl leash, float progress, Animation animation) {
- mTransformation.clear();
- animation.getTransformationAt(progress, mTransformation);
- mTransaction.setMatrix(leash, mTransformation.getMatrix(), mTmpFloat9);
- mTransaction.setAlpha(leash, keepMinimumAlpha(mTransformation.getAlpha()));
- mTransaction.setCornerRadius(leash, mCornerRadius);
- }
-
- void finishAnimation() {
- if (mCloseAnimation != null) {
- mCloseAnimation.reset();
- mCloseAnimation = null;
- }
- if (mEnterAnimation != null) {
- mEnterAnimation.reset();
- mEnterAnimation = null;
- }
- if (mEnteringTarget != null) {
- mEnteringTarget.leash.release();
- mEnteringTarget = null;
- }
- if (mClosingTarget != null) {
- mClosingTarget.leash.release();
- mClosingTarget = null;
- }
- if (mBackground != null) {
- mBackground.removeBackground(mTransaction);
- }
- mTransaction.setFrameTimelineVsync(mChoreographer.getVsyncId());
- mTransaction.apply();
- mTransformation.clear();
- mLatestProgress = 0;
- mNextBackgroundColor = Color.TRANSPARENT;
- if (mFinishCallback != null) {
- try {
- mFinishCallback.onAnimationFinished();
- } catch (RemoteException e) {
- e.printStackTrace();
- }
- mFinishCallback = null;
- }
- mProgressSpring.animateToFinalPosition(0);
- mProgressSpring.skipToEnd();
- }
-
- void onGestureProgress(@NonNull BackEvent backEvent) {
- if (mEnteringTarget == null || mClosingTarget == null
- || mCloseAnimation == null || mEnterAnimation == null) {
- return;
- }
-
- final float progress = backEvent.getProgress();
-
- float springProgress = (progress > 0.1f
- ? mapLinear(progress, 0.1f, 1f, TARGET_COMMIT_PROGRESS, 1f)
- : mapLinear(progress, 0, 1f, 0f, TARGET_COMMIT_PROGRESS)) * SCALE_FACTOR;
-
- mProgressSpring.animateToFinalPosition(springProgress);
- }
-
- static float mapLinear(float x, float a1, float a2, float b1, float b2) {
- return b1 + (x - a1) * (b2 - b1) / (a2 - a1);
- }
-
- void onGestureCommitted() {
- if (mEnteringTarget == null || mClosingTarget == null
- || mCloseAnimation == null || mEnterAnimation == null) {
- finishAnimation();
- return;
- }
- mProgressSpring.cancel();
-
- // Enter phase 2 of the animation
- final ValueAnimator valueAnimator = ValueAnimator.ofFloat(mLatestProgress, 1f)
- .setDuration(POST_ANIMATION_DURATION);
- valueAnimator.setInterpolator(mDecelerateInterpolator);
- valueAnimator.addUpdateListener(animation -> {
- float progress = (float) animation.getAnimatedValue();
- applyTransformTransaction(progress);
- });
-
- valueAnimator.addListener(new AnimatorListenerAdapter() {
- @Override
- public void onAnimationEnd(Animator animation) {
- finishAnimation();
- }
- });
- valueAnimator.start();
- }
-
- /** Load customize animation before animation start. */
- @Override
- public boolean prepareNextAnimation(BackNavigationInfo.CustomAnimationInfo animationInfo,
- int letterboxColor) {
- if (animationInfo == null) {
- return false;
- }
- final AnimationLoadResult result = mCustomAnimationLoader.loadAll(animationInfo);
- if (result != null) {
- mCloseAnimation = result.mCloseAnimation;
- mEnterAnimation = result.mEnterAnimation;
- mNextBackgroundColor = result.mBackgroundColor;
- return true;
- }
- return false;
- }
-
- @Override
- public BackAnimationRunner getRunner() {
- return mBackAnimationRunner;
- }
-
- private final class Callback extends IOnBackInvokedCallback.Default {
- @Override
- public void onBackStarted(BackMotionEvent backEvent) {
- // in case we're still animating an onBackCancelled event, let's remove the finish-
- // callback from the progress animator to prevent calling finishAnimation() before
- // restarting a new animation
- mProgressAnimator.removeOnBackCancelledFinishCallback();
-
- mProgressAnimator.onBackStarted(backEvent,
- CustomizeActivityAnimation.this::onGestureProgress);
- }
-
- @Override
- public void onBackProgressed(@NonNull BackMotionEvent backEvent) {
- mProgressAnimator.onBackProgressed(backEvent);
- }
-
- @Override
- public void onBackCancelled() {
- mProgressAnimator.onBackCancelled(CustomizeActivityAnimation.this::finishAnimation);
- }
-
- @Override
- public void onBackInvoked() {
- mProgressAnimator.reset();
- onGestureCommitted();
- }
- }
-
- private final class Runner extends IRemoteAnimationRunner.Default {
- @Override
- public void onAnimationStart(
- int transit,
- RemoteAnimationTarget[] apps,
- RemoteAnimationTarget[] wallpapers,
- RemoteAnimationTarget[] nonApps,
- IRemoteAnimationFinishedCallback finishedCallback) {
- ProtoLog.d(WM_SHELL_BACK_PREVIEW, "Start back to customize animation.");
- for (RemoteAnimationTarget a : apps) {
- if (a.mode == MODE_CLOSING) {
- mClosingTarget = a;
- }
- if (a.mode == MODE_OPENING) {
- mEnteringTarget = a;
- }
- }
- if (mCloseAnimation == null || mEnterAnimation == null) {
- ProtoLog.d(WM_SHELL_BACK_PREVIEW,
- "No animation loaded, should choose cross-activity animation?");
- }
-
- startBackAnimation();
- mFinishCallback = finishedCallback;
- }
-
- @Override
- public void onAnimationCancelled() {
- finishAnimation();
- }
- }
-
-
- static final class AnimationLoadResult {
- Animation mCloseAnimation;
- Animation mEnterAnimation;
- int mBackgroundColor;
- }
-
- /**
- * Helper class to load custom animation.
- */
- static class CustomAnimationLoader {
- final TransitionAnimation mTransitionAnimation;
-
- CustomAnimationLoader(Context context) {
- mTransitionAnimation = new TransitionAnimation(
- context, false /* debug */, "CustomizeBackAnimation");
- }
-
- /**
- * Load both enter and exit animation for the close activity transition.
- * Note that the result is only valid if the exit animation has set and loaded success.
- * If the entering animation has not set(i.e. 0), here will load the default entering
- * animation for it.
- *
- * @param animationInfo The information of customize animation, which can be set from
- * {@link Activity#overrideActivityTransition} and/or
- * {@link LayoutParams#windowAnimations}
- */
- AnimationLoadResult loadAll(BackNavigationInfo.CustomAnimationInfo animationInfo) {
- if (animationInfo.getPackageName().isEmpty()) {
- return null;
- }
- final Animation close = loadAnimation(animationInfo, false);
- if (close == null) {
- return null;
- }
- final Animation open = loadAnimation(animationInfo, true);
- AnimationLoadResult result = new AnimationLoadResult();
- result.mCloseAnimation = close;
- result.mEnterAnimation = open;
- result.mBackgroundColor = animationInfo.getCustomBackground();
- return result;
- }
-
- /**
- * Load enter or exit animation from CustomAnimationInfo
- * @param animationInfo The information for customize animation.
- * @param enterAnimation true when load for enter animation, false for exit animation.
- * @return Loaded animation.
- */
- @Nullable
- Animation loadAnimation(BackNavigationInfo.CustomAnimationInfo animationInfo,
- boolean enterAnimation) {
- Animation a = null;
- // Activity#overrideActivityTransition has higher priority than windowAnimations
- // Try to get animation from Activity#overrideActivityTransition
- if ((enterAnimation && animationInfo.getCustomEnterAnim() != 0)
- || (!enterAnimation && animationInfo.getCustomExitAnim() != 0)) {
- a = mTransitionAnimation.loadAppTransitionAnimation(
- animationInfo.getPackageName(),
- enterAnimation ? animationInfo.getCustomEnterAnim()
- : animationInfo.getCustomExitAnim());
- } else if (animationInfo.getWindowAnimations() != 0) {
- // try to get animation from LayoutParams#windowAnimations
- a = mTransitionAnimation.loadAnimationAttr(animationInfo.getPackageName(),
- animationInfo.getWindowAnimations(), enterAnimation
- ? R.styleable.WindowAnimation_activityCloseEnterAnimation
- : R.styleable.WindowAnimation_activityCloseExitAnimation,
- false /* translucent */);
- }
- // Only allow to load default animation for opening target.
- if (a == null && enterAnimation) {
- a = loadDefaultOpenAnimation();
- }
- if (a != null) {
- ProtoLog.d(WM_SHELL_BACK_PREVIEW, "custom animation loaded %s", a);
- } else {
- ProtoLog.e(WM_SHELL_BACK_PREVIEW, "No custom animation loaded");
- }
- return a;
- }
-
- private Animation loadDefaultOpenAnimation() {
- return mTransitionAnimation.loadDefaultAnimationAttr(
- R.styleable.WindowAnimation_activityCloseEnterAnimation,
- false /* translucent */);
- }
- }
-}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/back/DefaultCrossActivityBackAnimation.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/back/DefaultCrossActivityBackAnimation.kt
new file mode 100644
index 0000000..f33c5b9
--- /dev/null
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/back/DefaultCrossActivityBackAnimation.kt
@@ -0,0 +1,81 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.wm.shell.back
+
+import android.content.Context
+import android.view.Choreographer
+import android.view.SurfaceControl
+import com.android.wm.shell.R
+import com.android.wm.shell.RootTaskDisplayAreaOrganizer
+import com.android.wm.shell.animation.Interpolators
+import com.android.wm.shell.shared.annotations.ShellMainThread
+import javax.inject.Inject
+import kotlin.math.max
+
+/** Class that defines cross-activity animation. */
+@ShellMainThread
+class DefaultCrossActivityBackAnimation
+@Inject
+constructor(
+ context: Context,
+ background: BackAnimationBackground,
+ rootTaskDisplayAreaOrganizer: RootTaskDisplayAreaOrganizer
+) :
+ CrossActivityBackAnimation(
+ context,
+ background,
+ rootTaskDisplayAreaOrganizer,
+ SurfaceControl.Transaction(),
+ Choreographer.getInstance()
+ ) {
+
+ private val postCommitInterpolator = Interpolators.FAST_OUT_SLOW_IN
+ private val enteringStartOffset =
+ context.resources.getDimension(R.dimen.cross_activity_back_entering_start_offset)
+ override val allowEnteringYShift = true
+
+ override fun preparePreCommitEnteringRectMovement() {
+ // the entering target starts 96dp to the left of the screen edge...
+ startEnteringRect.set(startClosingRect)
+ startEnteringRect.offset(-enteringStartOffset, 0f)
+ // ...and gets scaled in sync with the closing target
+ targetEnteringRect.set(startEnteringRect)
+ targetEnteringRect.scaleCentered(MAX_SCALE)
+ }
+
+ override fun onGestureCommitted() {
+ // We enter phase 2 of the animation, the starting coordinates for phase 2 are the current
+ // coordinate of the gesture driven phase. Let's update the start and target rects and kick
+ // off the animator in the superclass
+ startClosingRect.set(currentClosingRect)
+ startEnteringRect.set(currentEnteringRect)
+ targetEnteringRect.set(backAnimRect)
+ targetClosingRect.set(backAnimRect)
+ targetClosingRect.offset(currentClosingRect.left + enteringStartOffset, 0f)
+ super.onGestureCommitted()
+ }
+
+ override fun onPostCommitProgress(linearProgress: Float) {
+ super.onPostCommitProgress(linearProgress)
+ val closingAlpha = max(1f - linearProgress * 2, 0f)
+ val progress = postCommitInterpolator.getInterpolation(linearProgress)
+ currentClosingRect.setInterpolatedRectF(startClosingRect, targetClosingRect, progress)
+ applyTransform(closingTarget?.leash, currentClosingRect, closingAlpha)
+ currentEnteringRect.setInterpolatedRectF(startEnteringRect, targetEnteringRect, progress)
+ applyTransform(enteringTarget?.leash, currentEnteringRect, 1f)
+ applyTransaction()
+ }
+}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleController.java
index 9e6c5fb..42a4ab2 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleController.java
@@ -87,6 +87,7 @@
import com.android.internal.protolog.common.ProtoLog;
import com.android.internal.statusbar.IStatusBarService;
import com.android.launcher3.icons.BubbleIconFactory;
+import com.android.wm.shell.Flags;
import com.android.wm.shell.R;
import com.android.wm.shell.ShellTaskOrganizer;
import com.android.wm.shell.WindowManagerShellWrapper;
@@ -1170,7 +1171,9 @@
* @param bubbleKey key of the bubble being dragged
*/
public void startBubbleDrag(String bubbleKey) {
- onBubbleDrag(bubbleKey, true /* isBeingDragged */);
+ if (mBubbleData.getSelectedBubble() != null) {
+ mBubbleBarViewCallback.expansionChanged(/* isExpanded = */ false);
+ }
if (mBubbleStateListener != null) {
boolean overflow = BubbleOverflow.KEY.equals(bubbleKey);
Rect rect = new Rect();
@@ -1183,23 +1186,31 @@
}
/**
- * A bubble is no longer being dragged in Launcher. As was released in given location.
+ * A bubble is no longer being dragged in Launcher. And was released in given location.
* Will be called only when bubble bar is expanded.
*
- * @param bubbleKey key of the bubble being dragged
- * @param location location where bubble was released
+ * @param location location where bubble was released
+ * @param topOnScreen top coordinate of the bubble bar on the screen after release
*/
- public void stopBubbleDrag(String bubbleKey, BubbleBarLocation location) {
+ public void stopBubbleDrag(BubbleBarLocation location, int topOnScreen) {
mBubblePositioner.setBubbleBarLocation(location);
- onBubbleDrag(bubbleKey, false /* isBeingDragged */);
+ mBubblePositioner.setBubbleBarTopOnScreen(topOnScreen);
+ if (mBubbleData.getSelectedBubble() != null) {
+ mBubbleBarViewCallback.expansionChanged(/* isExpanded = */ true);
+ }
}
- private void onBubbleDrag(String bubbleKey, boolean isBeingDragged) {
- // TODO(b/330585402): collapse stack if any bubble is dragged
- if (mBubbleData.getSelectedBubble() != null
- && mBubbleData.getSelectedBubble().getKey().equals(bubbleKey)) {
- // Should collapse/expand only if equals to selected bubble.
- mBubbleBarViewCallback.expansionChanged(/* isExpanded = */ !isBeingDragged);
+ /**
+ * A bubble was dragged and is released in dismiss target in Launcher.
+ *
+ * @param bubbleKey key of the bubble being dragged to dismiss target
+ */
+ public void dragBubbleToDismiss(String bubbleKey) {
+ String selectedBubbleKey = mBubbleData.getSelectedBubbleKey();
+ removeBubble(bubbleKey, Bubbles.DISMISS_USER_GESTURE);
+ if (selectedBubbleKey != null && !selectedBubbleKey.equals(bubbleKey)) {
+ // We did not remove the selected bubble. Expand it again
+ mBubbleBarViewCallback.expansionChanged(/* isExpanded = */ true);
}
}
@@ -1239,8 +1250,8 @@
* <p>This is used by external callers (launcher).
*/
@VisibleForTesting
- public void expandStackAndSelectBubbleFromLauncher(String key, Rect bubbleBarBounds) {
- mBubblePositioner.setBubbleBarBounds(bubbleBarBounds);
+ public void expandStackAndSelectBubbleFromLauncher(String key, int topOnScreen) {
+ mBubblePositioner.setBubbleBarTopOnScreen(topOnScreen);
if (BubbleOverflow.KEY.equals(key)) {
mBubbleData.setSelectedBubbleFromLauncher(mBubbleData.getOverflow());
@@ -1858,7 +1869,11 @@
@Override
public void bubbleOverflowChanged(boolean hasBubbles) {
- // TODO (b/334175587): tell stack view to hide / show the overflow
+ if (Flags.enableOptionalBubbleOverflow()) {
+ if (mStackView != null) {
+ mStackView.showOverflow(hasBubbles);
+ }
+ }
}
};
@@ -2351,16 +2366,9 @@
}
@Override
- public void showBubble(String key, Rect bubbleBarBounds) {
+ public void showBubble(String key, int topOnScreen) {
mMainExecutor.execute(
- () -> mController.expandStackAndSelectBubbleFromLauncher(
- key, bubbleBarBounds));
- }
-
- @Override
- public void removeBubble(String key) {
- mMainExecutor.execute(
- () -> mController.removeBubble(key, Bubbles.DISMISS_USER_GESTURE));
+ () -> mController.expandStackAndSelectBubbleFromLauncher(key, topOnScreen));
}
@Override
@@ -2379,8 +2387,13 @@
}
@Override
- public void stopBubbleDrag(String bubbleKey, BubbleBarLocation location) {
- mMainExecutor.execute(() -> mController.stopBubbleDrag(bubbleKey, location));
+ public void stopBubbleDrag(BubbleBarLocation location, int topOnScreen) {
+ mMainExecutor.execute(() -> mController.stopBubbleDrag(location, topOnScreen));
+ }
+
+ @Override
+ public void dragBubbleToDismiss(String key) {
+ mMainExecutor.execute(() -> mController.dragBubbleToDismiss(key));
}
@Override
@@ -2396,8 +2409,11 @@
}
@Override
- public void setBubbleBarBounds(Rect bubbleBarBounds) {
- mMainExecutor.execute(() -> mBubblePositioner.setBubbleBarBounds(bubbleBarBounds));
+ public void updateBubbleBarTopOnScreen(int topOnScreen) {
+ mMainExecutor.execute(() -> {
+ mBubblePositioner.setBubbleBarTopOnScreen(topOnScreen);
+ if (mLayerView != null) mLayerView.updateExpandedView();
+ });
}
}
@@ -2709,6 +2725,15 @@
() -> BubbleController.this.onSensitiveNotificationProtectionStateChanged(
sensitiveNotificationProtectionActive));
}
+
+ @Override
+ public boolean canShowBubbleNotification() {
+ // in bubble bar mode, when the IME is visible we can't animate new bubbles.
+ if (BubbleController.this.isShowingAsBubbleBar()) {
+ return !BubbleController.this.mBubblePositioner.getIsImeVisible();
+ }
+ return true;
+ }
}
/**
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleData.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleData.java
index ea30af5..26483c8 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleData.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleData.java
@@ -327,6 +327,14 @@
return mSelectedBubble;
}
+ /**
+ * Returns the key of the selected bubble, or null if no bubble is selected.
+ */
+ @Nullable
+ public String getSelectedBubbleKey() {
+ return mSelectedBubble != null ? mSelectedBubble.getKey() : null;
+ }
+
public BubbleOverflow getOverflow() {
return mOverflow;
}
@@ -1228,9 +1236,7 @@
public void dump(PrintWriter pw) {
pw.println("BubbleData state:");
pw.print(" selected: ");
- pw.println(mSelectedBubble != null
- ? mSelectedBubble.getKey()
- : "null");
+ pw.println(getSelectedBubbleKey());
pw.print(" expanded: ");
pw.println(mExpanded);
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleOverflowContainerView.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleOverflowContainerView.java
index 633b01b..18e04d1 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleOverflowContainerView.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleOverflowContainerView.java
@@ -44,6 +44,7 @@
import com.android.internal.protolog.common.ProtoLog;
import com.android.internal.util.ContrastColorUtil;
+import com.android.wm.shell.Flags;
import com.android.wm.shell.R;
import java.util.ArrayList;
@@ -195,7 +196,9 @@
}
void updateEmptyStateVisibility() {
- mEmptyState.setVisibility(mOverflowBubbles.isEmpty() ? View.VISIBLE : View.GONE);
+ boolean showEmptyState = mOverflowBubbles.isEmpty()
+ && !Flags.enableOptionalBubbleOverflow();
+ mEmptyState.setVisibility(showEmptyState ? View.VISIBLE : View.GONE);
mRecyclerView.setVisibility(mOverflowBubbles.isEmpty() ? View.GONE : View.VISIBLE);
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubblePositioner.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubblePositioner.java
index c4bbe32..1e482ca 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubblePositioner.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubblePositioner.java
@@ -98,7 +98,7 @@
private boolean mShowingInBubbleBar;
private BubbleBarLocation mBubbleBarLocation = BubbleBarLocation.DEFAULT;
- private final Rect mBubbleBarBounds = new Rect();
+ private int mBubbleBarTopOnScreen;
public BubblePositioner(Context context, WindowManager windowManager) {
mContext = context;
@@ -324,6 +324,11 @@
return 0;
}
+ /** Returns whether the IME is visible. */
+ public boolean getIsImeVisible() {
+ return mImeVisible;
+ }
+
/** Sets whether the IME is visible. **/
public void setImeVisible(boolean visible, int height) {
mImeVisible = visible;
@@ -841,17 +846,17 @@
}
/**
- * Sets the position of the bubble bar in display coordinates.
+ * Set top coordinate of bubble bar on screen
*/
- public void setBubbleBarBounds(Rect bubbleBarBounds) {
- mBubbleBarBounds.set(bubbleBarBounds);
+ public void setBubbleBarTopOnScreen(int topOnScreen) {
+ mBubbleBarTopOnScreen = topOnScreen;
}
/**
- * Returns the display coordinates of the bubble bar.
+ * Returns the top coordinate of bubble bar on screen
*/
- public Rect getBubbleBarBounds() {
- return mBubbleBarBounds;
+ public int getBubbleBarTopOnScreen() {
+ return mBubbleBarTopOnScreen;
}
/**
@@ -903,7 +908,7 @@
/** The bottom position of the expanded view when showing above the bubble bar. */
public int getExpandedViewBottomForBubbleBar() {
- return mBubbleBarBounds.top - mExpandedViewPadding;
+ return mBubbleBarTopOnScreen - mExpandedViewPadding;
}
/**
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleStackView.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleStackView.java
index be88b34..9fabd42 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleStackView.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleStackView.java
@@ -80,6 +80,7 @@
import com.android.internal.policy.ScreenDecorationsUtils;
import com.android.internal.protolog.common.ProtoLog;
import com.android.internal.util.FrameworkStatsLog;
+import com.android.wm.shell.Flags;
import com.android.wm.shell.R;
import com.android.wm.shell.animation.Interpolators;
import com.android.wm.shell.bubbles.BubblesNavBarMotionEventHandler.MotionEventListener;
@@ -863,6 +864,7 @@
}
};
+ private boolean mShowingOverflow;
private BubbleOverflow mBubbleOverflow;
private StackEducationView mStackEduView;
private StackEducationView.Manager mStackEducationViewManager;
@@ -992,18 +994,12 @@
mBubbleOverflow = mBubbleData.getOverflow();
- resetOverflowView();
- mBubbleContainer.addView(mBubbleOverflow.getIconView(),
- mBubbleContainer.getChildCount() /* index */,
- new FrameLayout.LayoutParams(mPositioner.getBubbleSize(),
- mPositioner.getBubbleSize()));
- updateOverflow();
- mBubbleOverflow.getIconView().setOnClickListener((View v) -> {
- mBubbleData.setShowingOverflow(true);
- mBubbleData.setSelectedBubble(mBubbleOverflow);
- mBubbleData.setExpanded(true);
- });
-
+ if (Flags.enableOptionalBubbleOverflow()) {
+ showOverflow(mBubbleData.hasOverflowBubbles());
+ } else {
+ mShowingOverflow = true; // if the flags not on this is always true
+ setUpOverflow();
+ }
mScrim = new View(getContext());
mScrim.setImportantForAccessibility(View.IMPORTANT_FOR_ACCESSIBILITY_NO);
mScrim.setBackgroundDrawable(new ColorDrawable(
@@ -1220,6 +1216,19 @@
}
};
+ private void setUpOverflow() {
+ resetOverflowView();
+ mBubbleContainer.addView(mBubbleOverflow.getIconView(),
+ mBubbleContainer.getChildCount() /* index */,
+ new FrameLayout.LayoutParams(mBubbleSize, mBubbleSize));
+ updateOverflow();
+ mBubbleOverflow.getIconView().setOnClickListener((View v) -> {
+ mBubbleData.setShowingOverflow(true);
+ mBubbleData.setSelectedBubble(mBubbleOverflow);
+ mBubbleData.setExpanded(true);
+ });
+ }
+
private void setUpDismissView() {
if (mDismissView != null) {
removeView(mDismissView);
@@ -1458,24 +1467,56 @@
b.getExpandedView().updateFontSize();
}
}
- if (mBubbleOverflow != null && mBubbleOverflow.getExpandedView() != null) {
+ if (mShowingOverflow && mBubbleOverflow != null
+ && mBubbleOverflow.getExpandedView() != null) {
mBubbleOverflow.getExpandedView().updateFontSize();
}
}
void updateLocale() {
- if (mBubbleOverflow != null && mBubbleOverflow.getExpandedView() != null) {
+ if (mShowingOverflow && mBubbleOverflow != null
+ && mBubbleOverflow.getExpandedView() != null) {
mBubbleOverflow.getExpandedView().updateLocale();
}
}
private void updateOverflow() {
mBubbleOverflow.update();
- mBubbleContainer.reorderView(mBubbleOverflow.getIconView(),
- mBubbleContainer.getChildCount() - 1 /* index */);
+ if (mShowingOverflow) {
+ mBubbleContainer.reorderView(mBubbleOverflow.getIconView(),
+ mBubbleContainer.getChildCount() - 1 /* index */);
+ }
updateOverflowVisibility();
}
+ private void updateOverflowVisibility() {
+ mBubbleOverflow.setVisible(mShowingOverflow
+ && (mIsExpanded || mBubbleData.isShowingOverflow())
+ ? VISIBLE
+ : GONE);
+ }
+
+ private void updateOverflowDotVisibility(boolean expanding) {
+ if (mShowingOverflow && mBubbleOverflow.showDot()) {
+ mBubbleOverflow.getIconView().animateDotScale(expanding ? 1 : 0f, () -> {
+ mBubbleOverflow.setVisible(expanding ? VISIBLE : GONE);
+ });
+ }
+ }
+
+ /** Sets whether the overflow should be visible or not. */
+ public void showOverflow(boolean showOverflow) {
+ if (!Flags.enableOptionalBubbleOverflow()) return;
+ if (mShowingOverflow != showOverflow) {
+ mShowingOverflow = showOverflow;
+ if (showOverflow) {
+ setUpOverflow();
+ } else if (mBubbleOverflow != null) {
+ resetOverflowView();
+ }
+ }
+ }
+
/**
* Handle theme changes.
*/
@@ -1535,7 +1576,10 @@
b.getExpandedView().updateDimensions();
}
}
- mBubbleOverflow.getIconView().setLayoutParams(new LayoutParams(mBubbleSize, mBubbleSize));
+ if (mShowingOverflow) {
+ mBubbleOverflow.getIconView().setLayoutParams(
+ new LayoutParams(mBubbleSize, mBubbleSize));
+ }
mExpandedAnimationController.updateResources();
mStackAnimationController.updateResources();
mDismissView.updateResources();
@@ -1699,7 +1743,7 @@
bubble.getIconView().setContentDescription(getResources().getString(
R.string.bubble_content_description_single, titleStr, appName));
} else {
- final int moreCount = mBubbleContainer.getChildCount() - 1;
+ final int moreCount = getBubbleCount();
bubble.getIconView().setContentDescription(getResources().getString(
R.string.bubble_content_description_stack,
titleStr, appName, moreCount));
@@ -1752,7 +1796,8 @@
View bubbleOverflowIconView =
mBubbleOverflow != null ? mBubbleOverflow.getIconView() : null;
- if (bubbleOverflowIconView != null && !mBubbleData.getBubbles().isEmpty()) {
+ if (mShowingOverflow && bubbleOverflowIconView != null
+ && !mBubbleData.getBubbles().isEmpty()) {
Bubble lastBubble =
mBubbleData.getBubbles().get(mBubbleData.getBubbles().size() - 1);
View lastBubbleIconView = lastBubble.getIconView();
@@ -1928,20 +1973,6 @@
}
}
- private void updateOverflowVisibility() {
- mBubbleOverflow.setVisible((mIsExpanded || mBubbleData.isShowingOverflow())
- ? VISIBLE
- : GONE);
- }
-
- private void updateOverflowDotVisibility(boolean expanding) {
- if (mBubbleOverflow.showDot()) {
- mBubbleOverflow.getIconView().animateDotScale(expanding ? 1 : 0f, () -> {
- mBubbleOverflow.setVisible(expanding ? VISIBLE : GONE);
- });
- }
- }
-
// via BubbleData.Listener
void updateBubble(Bubble bubble) {
animateInFlyoutForBubble(bubble);
@@ -3428,8 +3459,9 @@
* @return the number of bubbles in the stack view.
*/
public int getBubbleCount() {
- // Subtract 1 for the overflow button that is always in the bubble container.
- return mBubbleContainer.getChildCount() - 1;
+ final int childCount = mBubbleContainer.getChildCount();
+ // Subtract 1 for the overflow button if it's showing.
+ return mShowingOverflow ? childCount - 1 : childCount;
}
/**
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/Bubbles.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/Bubbles.java
index 322088b..1d053f9 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/Bubbles.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/Bubbles.java
@@ -297,6 +297,15 @@
boolean sensitiveNotificationProtectionActive);
/**
+ * Determines whether Bubbles can show notifications.
+ *
+ * <p>Normally bubble notifications are shown by Bubbles, but in some cases the bubble
+ * notification is suppressed and should be shown by the Notifications pipeline as regular
+ * notifications.
+ */
+ boolean canShowBubbleNotification();
+
+ /**
* A listener to be notified of bubble state changes, used by launcher to render bubbles in
* its process.
*/
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/IBubbles.aidl b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/IBubbles.aidl
index 66f77fa..1db556c 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/IBubbles.aidl
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/IBubbles.aidl
@@ -31,9 +31,9 @@
oneway void unregisterBubbleListener(in IBubblesListener listener) = 2;
- oneway void showBubble(in String key, in Rect bubbleBarBounds) = 3;
+ oneway void showBubble(in String key, in int topOnScreen) = 3;
- oneway void removeBubble(in String key) = 4;
+ oneway void dragBubbleToDismiss(in String key) = 4;
oneway void removeAllBubbles() = 5;
@@ -45,7 +45,7 @@
oneway void setBubbleBarLocation(in BubbleBarLocation location) = 9;
- oneway void setBubbleBarBounds(in Rect bubbleBarBounds) = 10;
+ oneway void updateBubbleBarTopOnScreen(in int topOnScreen) = 10;
- oneway void stopBubbleDrag(in String key, in BubbleBarLocation location) = 11;
+ oneway void stopBubbleDrag(in BubbleBarLocation location, in int topOnScreen) = 11;
}
\ No newline at end of file
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleBarAnimationHelper.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleBarAnimationHelper.java
index 45ad631..8e58db1 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleBarAnimationHelper.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleBarAnimationHelper.java
@@ -237,12 +237,10 @@
private void setScaleFromBubbleBar(AnimatableScaleMatrix matrix, float scale) {
// Set the pivot point for the scale, so the view animates out from the bubble bar.
- Rect bubbleBarBounds = mPositioner.getBubbleBarBounds();
- matrix.setScale(
- scale,
- scale,
- bubbleBarBounds.centerX(),
- bubbleBarBounds.top);
+ Rect availableRect = mPositioner.getAvailableRect();
+ float pivotX = mPositioner.isBubbleBarOnLeft() ? availableRect.left : availableRect.right;
+ float pivotY = mPositioner.getBubbleBarTopOnScreen();
+ matrix.setScale(scale, scale, pivotX, pivotY);
}
/**
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleBarHandleView.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleBarHandleView.java
index 2b7a070..d54a6b0 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleBarHandleView.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleBarHandleView.java
@@ -37,15 +37,11 @@
*/
public class BubbleBarHandleView extends View {
private static final long COLOR_CHANGE_DURATION = 120;
-
- // The handle view is currently rendered as 3 evenly spaced dots.
- private int mDotSize;
- private int mDotSpacing;
// Path used to draw the dots
private final Path mPath = new Path();
- private @ColorInt int mHandleLightColor;
- private @ColorInt int mHandleDarkColor;
+ private final @ColorInt int mHandleLightColor;
+ private final @ColorInt int mHandleDarkColor;
private @Nullable ObjectAnimator mColorChangeAnim;
public BubbleBarHandleView(Context context) {
@@ -63,10 +59,8 @@
public BubbleBarHandleView(Context context, AttributeSet attrs, int defStyleAttr,
int defStyleRes) {
super(context, attrs, defStyleAttr, defStyleRes);
- mDotSize = getResources().getDimensionPixelSize(
- R.dimen.bubble_bar_expanded_view_caption_dot_size);
- mDotSpacing = getResources().getDimensionPixelSize(
- R.dimen.bubble_bar_expanded_view_caption_dot_spacing);
+ final int handleHeight = getResources().getDimensionPixelSize(
+ R.dimen.bubble_bar_expanded_view_handle_height);
mHandleLightColor = ContextCompat.getColor(getContext(),
R.color.bubble_bar_expanded_view_handle_light);
mHandleDarkColor = ContextCompat.getColor(getContext(),
@@ -76,27 +70,13 @@
setOutlineProvider(new ViewOutlineProvider() {
@Override
public void getOutline(View view, Outline outline) {
- final int handleCenterX = view.getWidth() / 2;
final int handleCenterY = view.getHeight() / 2;
- final int handleTotalWidth = mDotSize * 3 + mDotSpacing * 2;
- final int handleLeft = handleCenterX - handleTotalWidth / 2;
- final int handleTop = handleCenterY - mDotSize / 2;
- final int handleBottom = handleTop + mDotSize;
- RectF dot1 = new RectF(
- handleLeft, handleTop,
- handleLeft + mDotSize, handleBottom);
- RectF dot2 = new RectF(
- dot1.right + mDotSpacing, handleTop,
- dot1.right + mDotSpacing + mDotSize, handleBottom
- );
- RectF dot3 = new RectF(
- dot2.right + mDotSpacing, handleTop,
- dot2.right + mDotSpacing + mDotSize, handleBottom
- );
+ final int handleTop = handleCenterY - handleHeight / 2;
+ final int handleBottom = handleTop + handleHeight;
+ final int radius = handleHeight / 2;
+ RectF handle = new RectF(/* left = */ 0, handleTop, view.getWidth(), handleBottom);
mPath.reset();
- mPath.addOval(dot1, Path.Direction.CW);
- mPath.addOval(dot2, Path.Direction.CW);
- mPath.addOval(dot3, Path.Direction.CW);
+ mPath.addRoundRect(handle, radius, radius, Path.Direction.CW);
outline.setPath(mPath);
}
});
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleBarLayerView.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleBarLayerView.java
index a351cef..123cc7e 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleBarLayerView.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleBarLayerView.java
@@ -356,7 +356,7 @@
}
/** Updates the expanded view size and position. */
- private void updateExpandedView() {
+ public void updateExpandedView() {
if (mExpandedView == null || mExpandedBubble == null) return;
boolean isOverflowExpanded = mExpandedBubble.getKey().equals(BubbleOverflow.KEY);
mPositioner.getBubbleBarExpandedViewBounds(mPositioner.isBubbleBarOnLeft(),
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/common/pip/PipUtils.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/common/pip/PipUtils.kt
index dba0a98..579a794 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/common/pip/PipUtils.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/common/pip/PipUtils.kt
@@ -152,7 +152,8 @@
"org.chromium.arc", 0)
val isTv = AppGlobals.getPackageManager().hasSystemFeature(
PackageManager.FEATURE_LEANBACK, 0)
- isPip2ExperimentEnabled = SystemProperties.getBoolean("wm_shell.pip2", false) ||
+ isPip2ExperimentEnabled = SystemProperties.getBoolean(
+ "persist.wm_shell.pip2", false) ||
(Flags.enablePip2Implementation() && !isArc && !isTv)
}
return isPip2ExperimentEnabled as Boolean
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/DividerView.java b/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/DividerView.java
index 607a3b5..2234041 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/DividerView.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/DividerView.java
@@ -347,7 +347,7 @@
if (mMoving) {
final int position = mSplitLayout.getDividerPosition() + touchPos - mStartPos;
mLastDraggingPosition = position;
- mSplitLayout.updateDividerBounds(position);
+ mSplitLayout.updateDividerBounds(position, true /* shouldUseParallaxEffect */);
}
break;
case MotionEvent.ACTION_UP:
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitDecorManager.java b/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitDecorManager.java
index 30eb8b5d..de016d3 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitDecorManager.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitDecorManager.java
@@ -31,7 +31,6 @@
import android.app.ActivityManager;
import android.content.Context;
import android.content.res.Configuration;
-import android.graphics.Color;
import android.graphics.PixelFormat;
import android.graphics.Rect;
import android.graphics.drawable.Drawable;
@@ -57,7 +56,13 @@
import java.util.function.Consumer;
/**
- * Handles split decor like showing resizing hint for a specific split.
+ * Handles additional layers over a running task in a split pair, for example showing a veil with an
+ * app icon when the task is being resized (usually to hide weird layouts while the app is being
+ * stretched). One SplitDecorManager is initialized on each window.
+ * <br>
+ * Currently, we show a veil when:
+ * a) Task is resizing down from a fullscreen window.
+ * b) Task is being stretched past its original bounds.
*/
public class SplitDecorManager extends WindowlessWindowManager {
private static final String TAG = SplitDecorManager.class.getSimpleName();
@@ -78,7 +83,11 @@
private boolean mShown;
private boolean mIsResizing;
- private final Rect mOldBounds = new Rect();
+ /** The original bounds of the main task, captured at the beginning of a resize transition. */
+ private final Rect mOldMainBounds = new Rect();
+ /** The original bounds of the side task, captured at the beginning of a resize transition. */
+ private final Rect mOldSideBounds = new Rect();
+ /** The current bounds of the main task, mid-resize. */
private final Rect mResizingBounds = new Rect();
private final Rect mTempRect = new Rect();
private ValueAnimator mFadeAnimator;
@@ -184,29 +193,38 @@
mResizingIconView = null;
mIsResizing = false;
mShown = false;
- mOldBounds.setEmpty();
+ mOldMainBounds.setEmpty();
+ mOldSideBounds.setEmpty();
mResizingBounds.setEmpty();
}
/** Showing resizing hint. */
public void onResizing(ActivityManager.RunningTaskInfo resizingTask, Rect newBounds,
Rect sideBounds, SurfaceControl.Transaction t, int offsetX, int offsetY,
- boolean immediately) {
+ boolean immediately, float[] veilColor) {
if (mResizingIconView == null) {
return;
}
if (!mIsResizing) {
mIsResizing = true;
- mOldBounds.set(newBounds);
+ mOldMainBounds.set(newBounds);
+ mOldSideBounds.set(sideBounds);
}
mResizingBounds.set(newBounds);
mOffsetX = offsetX;
mOffsetY = offsetY;
- final boolean show =
- newBounds.width() > mOldBounds.width() || newBounds.height() > mOldBounds.height();
- final boolean update = show != mShown;
+ // Show a veil when:
+ // a) Task is resizing down from a fullscreen window.
+ // b) Task is being stretched past its original bounds.
+ final boolean isResizingDownFromFullscreen =
+ mOldSideBounds.width() <= 1 || mOldSideBounds.height() <= 1;
+ final boolean isStretchingPastOriginalBounds =
+ newBounds.width() > mOldMainBounds.width()
+ || newBounds.height() > mOldMainBounds.height();
+ final boolean showVeil = isResizingDownFromFullscreen || isStretchingPastOriginalBounds;
+ final boolean update = showVeil != mShown;
if (update && mFadeAnimator != null && mFadeAnimator.isRunning()) {
// If we need to animate and animator still running, cancel it before we ensure both
// background and icon surfaces are non null for next animation.
@@ -216,18 +234,18 @@
if (mBackgroundLeash == null) {
mBackgroundLeash = SurfaceUtils.makeColorLayer(mHostLeash,
RESIZING_BACKGROUND_SURFACE_NAME, mSurfaceSession);
- t.setColor(mBackgroundLeash, getResizingBackgroundColor(resizingTask))
+ t.setColor(mBackgroundLeash, veilColor)
.setLayer(mBackgroundLeash, Integer.MAX_VALUE - 1);
}
if (mGapBackgroundLeash == null && !immediately) {
final boolean isLandscape = newBounds.height() == sideBounds.height();
- final int left = isLandscape ? mOldBounds.width() : 0;
- final int top = isLandscape ? 0 : mOldBounds.height();
+ final int left = isLandscape ? mOldMainBounds.width() : 0;
+ final int top = isLandscape ? 0 : mOldMainBounds.height();
mGapBackgroundLeash = SurfaceUtils.makeColorLayer(mHostLeash,
GAP_BACKGROUND_SURFACE_NAME, mSurfaceSession);
// Fill up another side bounds area.
- t.setColor(mGapBackgroundLeash, getResizingBackgroundColor(resizingTask))
+ t.setColor(mGapBackgroundLeash, veilColor)
.setLayer(mGapBackgroundLeash, Integer.MAX_VALUE - 2)
.setPosition(mGapBackgroundLeash, left, top)
.setWindowCrop(mGapBackgroundLeash, sideBounds.width(), sideBounds.height());
@@ -251,12 +269,12 @@
if (update) {
if (immediately) {
- t.setVisibility(mBackgroundLeash, show);
- t.setVisibility(mIconLeash, show);
+ t.setVisibility(mBackgroundLeash, showVeil);
+ t.setVisibility(mIconLeash, showVeil);
} else {
- startFadeAnimation(show, false, null);
+ startFadeAnimation(showVeil, false, null);
}
- mShown = show;
+ mShown = showVeil;
}
}
@@ -309,7 +327,8 @@
mIsResizing = false;
mOffsetX = 0;
mOffsetY = 0;
- mOldBounds.setEmpty();
+ mOldMainBounds.setEmpty();
+ mOldSideBounds.setEmpty();
mResizingBounds.setEmpty();
if (mFadeAnimator != null && mFadeAnimator.isRunning()) {
if (!mShown) {
@@ -346,14 +365,14 @@
/** Screenshot host leash and attach on it if meet some conditions */
public void screenshotIfNeeded(SurfaceControl.Transaction t) {
- if (!mShown && mIsResizing && !mOldBounds.equals(mResizingBounds)) {
+ if (!mShown && mIsResizing && !mOldMainBounds.equals(mResizingBounds)) {
if (mScreenshotAnimator != null && mScreenshotAnimator.isRunning()) {
mScreenshotAnimator.cancel();
} else if (mScreenshot != null) {
t.remove(mScreenshot);
}
- mTempRect.set(mOldBounds);
+ mTempRect.set(mOldMainBounds);
mTempRect.offsetTo(0, 0);
mScreenshot = ScreenshotUtils.takeScreenshot(t, mHostLeash, mTempRect,
Integer.MAX_VALUE - 1);
@@ -364,7 +383,7 @@
public void setScreenshotIfNeeded(SurfaceControl screenshot, SurfaceControl.Transaction t) {
if (screenshot == null || !screenshot.isValid()) return;
- if (!mShown && mIsResizing && !mOldBounds.equals(mResizingBounds)) {
+ if (!mShown && mIsResizing && !mOldMainBounds.equals(mResizingBounds)) {
if (mScreenshotAnimator != null && mScreenshotAnimator.isRunning()) {
mScreenshotAnimator.cancel();
} else if (mScreenshot != null) {
@@ -465,9 +484,4 @@
mIcon = null;
}
}
-
- private static float[] getResizingBackgroundColor(ActivityManager.RunningTaskInfo taskInfo) {
- final int taskBgColor = taskInfo.taskDescription.getBackgroundColor();
- return Color.valueOf(taskBgColor == -1 ? Color.WHITE : taskBgColor).getComponents();
- }
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitLayout.java b/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitLayout.java
index 2ea32f4..8331654 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitLayout.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitLayout.java
@@ -496,10 +496,10 @@
* Updates bounds with the passing position. Usually used to update recording bounds while
* performing animation or dragging divider bar to resize the splits.
*/
- void updateDividerBounds(int position) {
+ void updateDividerBounds(int position, boolean shouldUseParallaxEffect) {
updateBounds(position);
mSplitLayoutHandler.onLayoutSizeChanging(this, mSurfaceEffectPolicy.mParallaxOffset.x,
- mSurfaceEffectPolicy.mParallaxOffset.y);
+ mSurfaceEffectPolicy.mParallaxOffset.y, shouldUseParallaxEffect);
}
void setDividerPosition(int position, boolean applyLayoutChange) {
@@ -647,7 +647,9 @@
.setDuration(duration);
mDividerFlingAnimator.setInterpolator(Interpolators.FAST_OUT_SLOW_IN);
mDividerFlingAnimator.addUpdateListener(
- animation -> updateDividerBounds((int) animation.getAnimatedValue()));
+ animation -> updateDividerBounds(
+ (int) animation.getAnimatedValue(), false /* shouldUseParallaxEffect */)
+ );
mDividerFlingAnimator.addListener(new AnimatorListenerAdapter() {
@Override
public void onAnimationEnd(Animator animation) {
@@ -897,7 +899,8 @@
* @see #applySurfaceChanges(SurfaceControl.Transaction, SurfaceControl, SurfaceControl,
* SurfaceControl, SurfaceControl, boolean)
*/
- void onLayoutSizeChanging(SplitLayout layout, int offsetX, int offsetY);
+ void onLayoutSizeChanging(SplitLayout layout, int offsetX, int offsetY,
+ boolean shouldUseParallaxEffect);
/**
* Calls when finish resizing the split bounds.
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitScreenUtils.java b/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitScreenUtils.java
index f9259e7..e8226051 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitScreenUtils.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitScreenUtils.java
@@ -16,8 +16,6 @@
package com.android.wm.shell.common.split;
-import static android.content.pm.LauncherApps.ShortcutQuery.FLAG_MATCH_ALL_KINDS_WITH_ALL_PINNED;
-
import static com.android.wm.shell.common.split.SplitScreenConstants.CONTROLLED_ACTIVITY_TYPES;
import static com.android.wm.shell.common.split.SplitScreenConstants.CONTROLLED_WINDOWING_MODES;
import static com.android.wm.shell.common.split.SplitScreenConstants.SPLIT_POSITION_BOTTOM_OR_RIGHT;
@@ -26,25 +24,18 @@
import android.app.ActivityManager;
import android.app.PendingIntent;
-import android.content.ComponentName;
import android.content.Intent;
-import android.content.pm.LauncherApps;
-import android.content.pm.ShortcutInfo;
import android.content.res.Configuration;
import android.content.res.Resources;
+import android.graphics.Color;
import android.graphics.Rect;
-import android.os.UserHandle;
-import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import com.android.internal.util.ArrayUtils;
import com.android.wm.shell.Flags;
import com.android.wm.shell.ShellTaskOrganizer;
-import java.util.Arrays;
-import java.util.List;
-
/** Helper utility class for split screen components to use. */
public class SplitScreenUtils {
/** Reverse the split position. */
@@ -137,4 +128,10 @@
return isLandscape;
}
}
+
+ /** Returns the specified background color that matches a RunningTaskInfo. */
+ public static Color getResizingBackgroundColor(ActivityManager.RunningTaskInfo taskInfo) {
+ final int taskBgColor = taskInfo.taskDescription.getBackgroundColor();
+ return Color.valueOf(taskBgColor == -1 ? Color.WHITE : taskBgColor);
+ }
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/CompatUIController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/CompatUIController.java
index 5c292f1..bfac24b 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/CompatUIController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/CompatUIController.java
@@ -188,6 +188,11 @@
*/
private boolean mHasShownUserAspectRatioSettingsButton = false;
+ /**
+ * This is true when the rechability education is displayed for the first time.
+ */
+ private boolean mIsFirstReachabilityEducationRunning;
+
public CompatUIController(@NonNull Context context,
@NonNull ShellInit shellInit,
@NonNull ShellController shellController,
@@ -252,9 +257,35 @@
removeLayouts(taskInfo.taskId);
return;
}
-
+ // We're showing the first reachability education so we ignore incoming TaskInfo
+ // until the education flow has completed or we double tap.
+ if (mIsFirstReachabilityEducationRunning) {
+ return;
+ }
+ if (taskInfo.appCompatTaskInfo.topActivityBoundsLetterboxed) {
+ if (taskInfo.appCompatTaskInfo.isLetterboxEducationEnabled) {
+ createOrUpdateLetterboxEduLayout(taskInfo, taskListener);
+ } else if (!taskInfo.appCompatTaskInfo.isFromLetterboxDoubleTap) {
+ // In this case the app is letterboxed and the letterbox education
+ // is disabled. In this case we need to understand if it's the first
+ // time we show the reachability education. When this is happening
+ // we need to ignore all the incoming TaskInfo until the education
+ // completes. If we come from a double tap we follow the normal flow.
+ final boolean topActivityPillarboxed =
+ taskInfo.appCompatTaskInfo.isTopActivityPillarboxed();
+ final boolean isFirstTimeHorizontalReachabilityEdu = topActivityPillarboxed
+ && !mCompatUIConfiguration.hasSeenHorizontalReachabilityEducation(taskInfo);
+ final boolean isFirstTimeVerticalReachabilityEdu = !topActivityPillarboxed
+ && !mCompatUIConfiguration.hasSeenVerticalReachabilityEducation(taskInfo);
+ if (isFirstTimeHorizontalReachabilityEdu || isFirstTimeVerticalReachabilityEdu) {
+ mIsFirstReachabilityEducationRunning = true;
+ mCompatUIConfiguration.setSeenLetterboxEducation(taskInfo.userId);
+ createOrUpdateReachabilityEduLayout(taskInfo, taskListener);
+ return;
+ }
+ }
+ }
createOrUpdateCompatLayout(taskInfo, taskListener);
- createOrUpdateLetterboxEduLayout(taskInfo, taskListener);
createOrUpdateRestartDialogLayout(taskInfo, taskListener);
if (mCompatUIConfiguration.getHasSeenLetterboxEducation(taskInfo.userId)) {
createOrUpdateReachabilityEduLayout(taskInfo, taskListener);
@@ -589,6 +620,7 @@
private void onInitialReachabilityEduDismissed(@NonNull TaskInfo taskInfo,
@NonNull ShellTaskOrganizer.TaskListener taskListener) {
// We need to update the UI otherwise it will not be shown until the user relaunches the app
+ mIsFirstReachabilityEducationRunning = false;
createOrUpdateUserAspectRatioSettingsLayout(taskInfo, taskListener);
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellBaseModule.java b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellBaseModule.java
index e729c7d..991fbaf 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellBaseModule.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellBaseModule.java
@@ -72,7 +72,6 @@
import com.android.wm.shell.compatui.CompatUIController;
import com.android.wm.shell.compatui.CompatUIShellCommandHandler;
import com.android.wm.shell.desktopmode.DesktopMode;
-import com.android.wm.shell.desktopmode.DesktopModeStatus;
import com.android.wm.shell.desktopmode.DesktopModeTaskRepository;
import com.android.wm.shell.desktopmode.DesktopTasksController;
import com.android.wm.shell.displayareahelper.DisplayAreaHelper;
@@ -88,6 +87,7 @@
import com.android.wm.shell.recents.RecentTasks;
import com.android.wm.shell.recents.RecentTasksController;
import com.android.wm.shell.recents.RecentsTransitionHandler;
+import com.android.wm.shell.shared.DesktopModeStatus;
import com.android.wm.shell.shared.ShellTransitions;
import com.android.wm.shell.shared.annotations.ShellAnimationThread;
import com.android.wm.shell.shared.annotations.ShellBackgroundThread;
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellModule.java b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellModule.java
index a1910c5..4e9e8f9 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellModule.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellModule.java
@@ -57,7 +57,6 @@
import com.android.wm.shell.dagger.pip.PipModule;
import com.android.wm.shell.desktopmode.DesktopModeEventLogger;
import com.android.wm.shell.desktopmode.DesktopModeLoggerTransitionObserver;
-import com.android.wm.shell.desktopmode.DesktopModeStatus;
import com.android.wm.shell.desktopmode.DesktopModeTaskRepository;
import com.android.wm.shell.desktopmode.DesktopTasksController;
import com.android.wm.shell.desktopmode.DesktopTasksLimiter;
@@ -77,6 +76,7 @@
import com.android.wm.shell.pip.PipTransitionController;
import com.android.wm.shell.recents.RecentTasksController;
import com.android.wm.shell.recents.RecentsTransitionHandler;
+import com.android.wm.shell.shared.DesktopModeStatus;
import com.android.wm.shell.shared.annotations.ShellAnimationThread;
import com.android.wm.shell.shared.annotations.ShellBackgroundThread;
import com.android.wm.shell.shared.annotations.ShellMainThread;
@@ -100,6 +100,7 @@
import com.android.wm.shell.unfold.qualifier.UnfoldTransition;
import com.android.wm.shell.windowdecor.CaptionWindowDecorViewModel;
import com.android.wm.shell.windowdecor.DesktopModeWindowDecorViewModel;
+import com.android.wm.shell.windowdecor.ResizeHandleSizeRepository;
import com.android.wm.shell.windowdecor.WindowDecorViewModel;
import dagger.Binds;
@@ -220,7 +221,8 @@
SyncTransactionQueue syncQueue,
Transitions transitions,
Optional<DesktopTasksController> desktopTasksController,
- RootTaskDisplayAreaOrganizer rootTaskDisplayAreaOrganizer) {
+ RootTaskDisplayAreaOrganizer rootTaskDisplayAreaOrganizer,
+ ResizeHandleSizeRepository resizeHandleSizeRepository) {
if (DesktopModeStatus.canEnterDesktopMode(context)) {
return new DesktopModeWindowDecorViewModel(
context,
@@ -237,7 +239,8 @@
syncQueue,
transitions,
desktopTasksController,
- rootTaskDisplayAreaOrganizer);
+ rootTaskDisplayAreaOrganizer,
+ resizeHandleSizeRepository);
}
return new CaptionWindowDecorViewModel(
context,
@@ -247,7 +250,8 @@
displayController,
rootTaskDisplayAreaOrganizer,
syncQueue,
- transitions);
+ transitions,
+ resizeHandleSizeRepository);
}
//
@@ -529,7 +533,8 @@
exitDesktopTransitionHandler, toggleResizeDesktopTaskTransitionHandler,
dragToDesktopTransitionHandler, desktopModeTaskRepository,
desktopModeLoggerTransitionObserver, launchAdjacentController,
- recentsTransitionHandler, multiInstanceHelper, mainExecutor, desktopTasksLimiter);
+ recentsTransitionHandler, multiInstanceHelper,
+ mainExecutor, desktopTasksLimiter);
}
@WMSingleton
@@ -622,6 +627,12 @@
return new DesktopModeEventLogger();
}
+ @WMSingleton
+ @Provides
+ static ResizeHandleSizeRepository provideResizeHandleSizeRepository() {
+ return new ResizeHandleSizeRepository();
+ }
+
//
// Drag and drop
//
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/back/ShellBackAnimationModule.java b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/back/ShellBackAnimationModule.java
index 795bc1a..d2895b1 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/back/ShellBackAnimationModule.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/back/ShellBackAnimationModule.java
@@ -16,9 +16,9 @@
package com.android.wm.shell.dagger.back;
-import com.android.wm.shell.back.CrossActivityBackAnimation;
import com.android.wm.shell.back.CrossTaskBackAnimation;
-import com.android.wm.shell.back.CustomizeActivityAnimation;
+import com.android.wm.shell.back.CustomCrossActivityBackAnimation;
+import com.android.wm.shell.back.DefaultCrossActivityBackAnimation;
import com.android.wm.shell.back.ShellBackAnimation;
import com.android.wm.shell.back.ShellBackAnimationRegistry;
@@ -47,7 +47,7 @@
@Binds
@ShellBackAnimation.CrossActivity
ShellBackAnimation bindCrossActivityShellBackAnimation(
- CrossActivityBackAnimation crossActivityBackAnimation);
+ DefaultCrossActivityBackAnimation defaultCrossActivityBackAnimation);
/** Default cross task back animation */
@Binds
@@ -59,5 +59,5 @@
@Binds
@ShellBackAnimation.CustomizeActivity
ShellBackAnimation provideCustomizeActivityShellBackAnimation(
- CustomizeActivityAnimation customizeActivityAnimation);
+ CustomCrossActivityBackAnimation customCrossActivityBackAnimation);
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/pip/Pip2Module.java b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/pip/Pip2Module.java
index 414a9d1..01364d1 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/pip/Pip2Module.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/pip/Pip2Module.java
@@ -133,6 +133,7 @@
PipBoundsAlgorithm pipBoundsAlgorithm,
@NonNull PipBoundsState pipBoundsState,
@NonNull PipTransitionState pipTransitionState,
+ @NonNull PipScheduler pipScheduler,
@NonNull SizeSpecSource sizeSpecSource,
PipMotionHelper pipMotionHelper,
FloatingContentCoordinator floatingContentCoordinator,
@@ -140,7 +141,7 @@
@ShellMainThread ShellExecutor mainExecutor,
Optional<PipPerfHintController> pipPerfHintControllerOptional) {
return new PipTouchHandler(context, shellInit, menuPhoneController, pipBoundsAlgorithm,
- pipBoundsState, pipTransitionState, sizeSpecSource, pipMotionHelper,
+ pipBoundsState, pipTransitionState, pipScheduler, sizeSpecSource, pipMotionHelper,
floatingContentCoordinator, pipUiEventLogger, mainExecutor,
pipPerfHintControllerOptional);
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeLoggerTransitionObserver.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeLoggerTransitionObserver.kt
index 9038aaa..0b7a3e8 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeLoggerTransitionObserver.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeLoggerTransitionObserver.kt
@@ -39,6 +39,7 @@
import com.android.wm.shell.desktopmode.DesktopModeEventLogger.Companion.ExitReason
import com.android.wm.shell.desktopmode.DesktopModeEventLogger.Companion.TaskUpdate
import com.android.wm.shell.protolog.ShellProtoLogGroup.WM_SHELL_DESKTOP_MODE
+import com.android.wm.shell.shared.DesktopModeStatus
import com.android.wm.shell.shared.TransitionUtil
import com.android.wm.shell.sysui.ShellInit
import com.android.wm.shell.transition.Transitions
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeTaskRepository.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeTaskRepository.kt
index 2d508b2..7e0234e 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeTaskRepository.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeTaskRepository.kt
@@ -48,7 +48,6 @@
val activeTasks: ArraySet<Int> = ArraySet(),
val visibleTasks: ArraySet<Int> = ArraySet(),
val minimizedTasks: ArraySet<Int> = ArraySet(),
- var stashed: Boolean = false
)
// Token of the current wallpaper activity, used to remove it when the last task is removed
@@ -95,10 +94,8 @@
visibleTasksListeners[visibleTasksListener] = executor
displayData.keyIterator().forEach { displayId ->
val visibleTasksCount = getVisibleTaskCount(displayId)
- val stashed = isStashed(displayId)
executor.execute {
visibleTasksListener.onTasksVisibilityChanged(displayId, visibleTasksCount)
- visibleTasksListener.onStashedChanged(displayId, stashed)
}
}
}
@@ -400,26 +397,6 @@
}
/**
- * Update stashed status on display with id [displayId]
- */
- fun setStashed(displayId: Int, stashed: Boolean) {
- val data = displayData.getOrCreate(displayId)
- val oldValue = data.stashed
- data.stashed = stashed
- if (oldValue != stashed) {
- KtProtoLog.d(
- WM_SHELL_DESKTOP_MODE,
- "DesktopTaskRepo: mark stashed=%b displayId=%d",
- stashed,
- displayId
- )
- visibleTasksListeners.forEach { (listener, executor) ->
- executor.execute { listener.onStashedChanged(displayId, stashed) }
- }
- }
- }
-
- /**
* Removes and returns the bounds saved before maximizing the given task.
*/
fun removeBoundsBeforeMaximize(taskId: Int): Rect? {
@@ -433,13 +410,6 @@
boundsBeforeMaximizeByTaskId.set(taskId, Rect(bounds))
}
- /**
- * Check if display with id [displayId] has desktop tasks stashed
- */
- fun isStashed(displayId: Int): Boolean {
- return displayData[displayId]?.stashed ?: false
- }
-
internal fun dump(pw: PrintWriter, prefix: String) {
val innerPrefix = "$prefix "
pw.println("${prefix}DesktopModeTaskRepository")
@@ -455,7 +425,6 @@
pw.println("${prefix}Display $displayId:")
pw.println("${innerPrefix}activeTasks=${data.activeTasks.toDumpString()}")
pw.println("${innerPrefix}visibleTasks=${data.visibleTasks.toDumpString()}")
- pw.println("${innerPrefix}stashed=${data.stashed}")
}
}
@@ -477,11 +446,6 @@
* Called when the desktop changes the number of visible freeform tasks.
*/
fun onTasksVisibilityChanged(displayId: Int, visibleTasksCount: Int) {}
-
- /**
- * Called when the desktop stashed status changes.
- */
- fun onStashedChanged(displayId: Int, stashed: Boolean) {}
}
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeUiEventLogger.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeUiEventLogger.kt
new file mode 100644
index 0000000..aa11a7d
--- /dev/null
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeUiEventLogger.kt
@@ -0,0 +1,100 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.wm.shell.desktopmode
+
+import android.util.Log
+import com.android.internal.logging.InstanceId
+import com.android.internal.logging.InstanceIdSequence
+import com.android.internal.logging.UiEvent
+import com.android.internal.logging.UiEventLogger
+import com.android.wm.shell.dagger.WMSingleton
+import javax.inject.Inject
+
+/**
+ * Log Aster UIEvents for desktop windowing mode.
+ */
+@WMSingleton
+class DesktopModeUiEventLogger @Inject constructor(
+ private val mUiEventLogger: UiEventLogger,
+ private val mInstanceIdSequence: InstanceIdSequence
+) {
+ /**
+ * Logs an event for a CUI, on a particular package.
+ *
+ * @param uid The user id associated with the package the user is interacting with
+ * @param packageName The name of the package the user is interacting with
+ * @param event The event type to generate
+ */
+ fun log(uid: Int, packageName: String, event: DesktopUiEventEnum) {
+ if (packageName.isEmpty() || uid < 0) {
+ Log.d(TAG, "Skip logging since package name is empty or bad uid")
+ return
+ }
+ mUiEventLogger.log(event, uid, packageName)
+ }
+
+ /**
+ * Retrieves a new instance id for a new interaction.
+ */
+ fun getNewInstanceId(): InstanceId = mInstanceIdSequence.newInstanceId()
+
+ /**
+ * Logs an event as part of a particular CUI, on a particular package.
+ *
+ * @param instanceId The id identifying an interaction, potentially taking place across multiple
+ * surfaces. There should be a new id generated for each distinct CUI.
+ * @param uid The user id associated with the package the user is interacting with
+ * @param packageName The name of the package the user is interacting with
+ * @param event The event type to generate
+ */
+ fun logWithInstanceId(
+ instanceId: InstanceId,
+ uid: Int,
+ packageName: String,
+ event: DesktopUiEventEnum
+ ) {
+ if (packageName.isEmpty() || uid < 0) {
+ Log.d(TAG, "Skip logging since package name is empty or bad uid")
+ return
+ }
+ mUiEventLogger.logWithInstanceId(event, uid, packageName, instanceId)
+ }
+
+ companion object {
+ /**
+ * Enums for logging desktop windowing mode UiEvents.
+ */
+ enum class DesktopUiEventEnum(private val mId: Int) : UiEventLogger.UiEventEnum {
+
+ @UiEvent(doc = "Resize the window in desktop windowing mode by dragging the edge")
+ DESKTOP_WINDOW_EDGE_DRAG_RESIZE(1721),
+
+ @UiEvent(doc = "Resize the window in desktop windowing mode by dragging the corner")
+ DESKTOP_WINDOW_CORNER_DRAG_RESIZE(1722),
+
+ @UiEvent(doc = "Tap on the window header maximize button in desktop windowing mode")
+ DESKTOP_WINDOW_MAXIMIZE_BUTTON_TAP(1723),
+
+ @UiEvent(doc = "Double tap on window header to maximize it in desktop windowing mode")
+ DESKTOP_WINDOW_HEADER_DOUBLE_TAP_TO_MAXIMIZE(1724);
+
+ override fun getId(): Int = mId
+ }
+
+ private const val TAG = "DesktopModeUiEventLogger"
+ }
+}
\ No newline at end of file
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopTasksController.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopTasksController.kt
index b0d5923..e5bf53a 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopTasksController.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopTasksController.kt
@@ -72,6 +72,9 @@
import com.android.wm.shell.protolog.ShellProtoLogGroup.WM_SHELL_DESKTOP_MODE
import com.android.wm.shell.recents.RecentsTransitionHandler
import com.android.wm.shell.recents.RecentsTransitionStateListener
+import com.android.wm.shell.shared.DesktopModeStatus
+import com.android.wm.shell.shared.DesktopModeStatus.DESKTOP_DENSITY_OVERRIDE
+import com.android.wm.shell.shared.DesktopModeStatus.isDesktopDensityOverrideSet
import com.android.wm.shell.shared.annotations.ExternalThread
import com.android.wm.shell.shared.annotations.ShellMainThread
import com.android.wm.shell.splitscreen.SplitScreenController
@@ -227,7 +230,7 @@
bringDesktopAppsToFront(displayId, wct)
if (Transitions.ENABLE_SHELL_TRANSITIONS) {
- // TODO(b/255649902): ensure remote transition is supplied once state is introduced
+ // TODO(b/309014605): ensure remote transition is supplied once state is introduced
val transitionType = if (remoteTransition == null) TRANSIT_NONE else TRANSIT_TO_FRONT
val handler = remoteTransition?.let {
OneShotRemoteHandler(transitions.mainExecutor, remoteTransition)
@@ -240,34 +243,6 @@
}
}
- /**
- * Stash desktop tasks on display with id [displayId].
- *
- * When desktop tasks are stashed, launcher home screen icons are fully visible. New apps
- * launched in this state will be added to the desktop. Existing desktop tasks will be brought
- * back to front during the launch.
- */
- fun stashDesktopApps(displayId: Int) {
- if (DesktopModeStatus.isStashingEnabled()) {
- KtProtoLog.v(WM_SHELL_DESKTOP_MODE, "DesktopTasksController: stashDesktopApps")
- desktopModeTaskRepository.setStashed(displayId, true)
- }
- }
-
- /**
- * Clear the stashed state for the given display
- */
- fun hideStashedDesktopApps(displayId: Int) {
- if (DesktopModeStatus.isStashingEnabled()) {
- KtProtoLog.v(
- WM_SHELL_DESKTOP_MODE,
- "DesktopTasksController: hideStashedApps displayId=%d",
- displayId
- )
- desktopModeTaskRepository.setStashed(displayId, false)
- }
- }
-
/** Get number of tasks that are marked as visible */
fun getVisibleTaskCount(displayId: Int): Int {
return desktopModeTaskRepository.getVisibleTaskCount(displayId)
@@ -871,8 +846,6 @@
val result = triggerTask?.let { task ->
when {
request.type == TRANSIT_TO_BACK -> handleBackNavigation(task)
- // If display has tasks stashed, handle as stashed launch
- task.isStashed -> handleStashedTaskLaunch(task, transition)
// Check if the task has a top transparent activity
shouldLaunchAsModal(task) -> handleTransparentTaskLaunch(task)
// Check if fullscreen task should be updated
@@ -911,12 +884,8 @@
.forEach { finishTransaction.setCornerRadius(it.leash, cornerRadius) }
}
- private val TaskInfo.isStashed: Boolean
- get() = desktopModeTaskRepository.isStashed(displayId)
-
- private fun shouldLaunchAsModal(task: TaskInfo): Boolean {
- return Flags.enableDesktopWindowingModalsPolicy() && isSingleTopActivityTranslucent(task)
- }
+ private fun shouldLaunchAsModal(task: TaskInfo) =
+ Flags.enableDesktopWindowingModalsPolicy() && isSingleTopActivityTranslucent(task)
private fun shouldRemoveWallpaper(request: TransitionRequestInfo): Boolean {
return Flags.enableDesktopWindowingWallpaperActivity() &&
@@ -939,18 +908,22 @@
task.taskId
)
return WindowContainerTransaction().also { wct ->
- addMoveToFullscreenChanges(wct, task)
+ bringDesktopAppsToFrontBeforeShowingNewTask(task.displayId, wct, task.taskId)
+ wct.reorder(task.token, true)
}
}
+ val wct = WindowContainerTransaction()
+ if (isDesktopDensityOverrideSet()) {
+ wct.setDensityDpi(task.token, DESKTOP_DENSITY_OVERRIDE)
+ }
// Desktop Mode is showing and we're launching a new Task - we might need to minimize
// a Task.
- val wct = WindowContainerTransaction()
val taskToMinimize = addAndGetMinimizeChangesIfNeeded(task.displayId, wct, task)
if (taskToMinimize != null) {
addPendingMinimizeTransition(transition, taskToMinimize)
return wct
}
- return null
+ return if (wct.isEmpty) null else wct
}
private fun handleFullscreenTaskLaunch(
@@ -976,24 +949,6 @@
return null
}
- private fun handleStashedTaskLaunch(
- task: RunningTaskInfo,
- transition: IBinder
- ): WindowContainerTransaction {
- KtProtoLog.d(
- WM_SHELL_DESKTOP_MODE,
- "DesktopTasksController: launch apps with stashed on transition taskId=%d",
- task.taskId
- )
- val wct = WindowContainerTransaction()
- val taskToMinimize =
- bringDesktopAppsToFrontBeforeShowingNewTask(task.displayId, wct, task.taskId)
- addMoveToDesktopChanges(wct, task)
- desktopModeTaskRepository.setStashed(task.displayId, false)
- addPendingMinimizeTransition(transition, taskToMinimize)
- return wct
- }
-
// Always launch transparent tasks in fullscreen.
private fun handleTransparentTaskLaunch(task: RunningTaskInfo): WindowContainerTransaction? {
// Already fullscreen, no-op.
@@ -1036,7 +991,7 @@
wct.setWindowingMode(taskInfo.token, targetWindowingMode)
wct.reorder(taskInfo.token, true /* onTop */)
if (isDesktopDensityOverrideSet()) {
- wct.setDensityDpi(taskInfo.token, getDesktopDensityDpi())
+ wct.setDensityDpi(taskInfo.token, DESKTOP_DENSITY_OVERRIDE)
}
}
@@ -1140,10 +1095,6 @@
return context.resources.displayMetrics.densityDpi
}
- private fun getDesktopDensityDpi(): Int {
- return DESKTOP_DENSITY_OVERRIDE
- }
-
/** Creates a new instance of the external interface to pass to another process. */
private fun createExternalInterface(): ExternalInterfaceBinder {
return IDesktopModeImpl(this)
@@ -1426,16 +1377,6 @@
l -> l.onTasksVisibilityChanged(displayId, visibleTasksCount)
}
}
-
- override fun onStashedChanged(displayId: Int, stashed: Boolean) {
- KtProtoLog.v(
- WM_SHELL_DESKTOP_MODE,
- "IDesktopModeImpl: onStashedChanged display=%d stashed=%b",
- displayId,
- stashed
- )
- remoteListener.call { l -> l.onStashedChanged(displayId, stashed) }
- }
}
init {
@@ -1467,20 +1408,6 @@
) { c -> c.showDesktopApps(displayId, remoteTransition) }
}
- override fun stashDesktopApps(displayId: Int) {
- ExecutorUtils.executeRemoteCallWithTaskPermission(
- controller,
- "stashDesktopApps"
- ) { c -> c.stashDesktopApps(displayId) }
- }
-
- override fun hideStashedDesktopApps(displayId: Int) {
- ExecutorUtils.executeRemoteCallWithTaskPermission(
- controller,
- "hideStashedDesktopApps"
- ) { c -> c.hideStashedDesktopApps(displayId) }
- }
-
override fun showDesktopApp(taskId: Int) {
ExecutorUtils.executeRemoteCallWithTaskPermission(
controller,
@@ -1488,6 +1415,20 @@
) { c -> c.moveTaskToFront(taskId) }
}
+ override fun stashDesktopApps(displayId: Int) {
+ KtProtoLog.w(
+ WM_SHELL_DESKTOP_MODE,
+ "IDesktopModeImpl: stashDesktopApps is deprecated"
+ )
+ }
+
+ override fun hideStashedDesktopApps(displayId: Int) {
+ KtProtoLog.w(
+ WM_SHELL_DESKTOP_MODE,
+ "IDesktopModeImpl: hideStashedDesktopApps is deprecated"
+ )
+ }
+
override fun getVisibleTaskCount(displayId: Int): Int {
val result = IntArray(1)
ExecutorUtils.executeRemoteCallWithTaskPermission(
@@ -1527,21 +1468,9 @@
}
companion object {
- private val DESKTOP_DENSITY_OVERRIDE =
- SystemProperties.getInt("persist.wm.debug.desktop_mode_density", 284)
- private val DESKTOP_DENSITY_ALLOWED_RANGE = (100..1000)
-
@JvmField
val DESKTOP_MODE_INITIAL_BOUNDS_SCALE = SystemProperties
.getInt("persist.wm.debug.desktop_mode_initial_bounds_scale", 75) / 100f
-
- /**
- * Check if desktop density override is enabled
- */
- @JvmStatic
- fun isDesktopDensityOverrideSet(): Boolean {
- return DESKTOP_DENSITY_OVERRIDE in DESKTOP_DENSITY_ALLOWED_RANGE
- }
}
/** The positions on a screen that a task can snap to. */
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopTasksLimiter.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopTasksLimiter.kt
index 3404d37..0f88384 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopTasksLimiter.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopTasksLimiter.kt
@@ -25,6 +25,7 @@
import androidx.annotation.VisibleForTesting
import com.android.wm.shell.ShellTaskOrganizer
import com.android.wm.shell.protolog.ShellProtoLogGroup
+import com.android.wm.shell.shared.DesktopModeStatus
import com.android.wm.shell.transition.Transitions
import com.android.wm.shell.transition.Transitions.TransitionObserver
import com.android.wm.shell.util.KtProtoLog
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopTasksTransitionObserver.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopTasksTransitionObserver.kt
index 451e09c..dae75f9 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopTasksTransitionObserver.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopTasksTransitionObserver.kt
@@ -23,6 +23,7 @@
import android.window.TransitionInfo
import com.android.window.flags.Flags.enableDesktopWindowingWallpaperActivity
import com.android.wm.shell.protolog.ShellProtoLogGroup.WM_SHELL_DESKTOP_MODE
+import com.android.wm.shell.shared.DesktopModeStatus
import com.android.wm.shell.sysui.ShellInit
import com.android.wm.shell.transition.Transitions
import com.android.wm.shell.util.KtProtoLog
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/IDesktopMode.aidl b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/IDesktopMode.aidl
index fa43522..c36f8de 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/IDesktopMode.aidl
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/IDesktopMode.aidl
@@ -28,10 +28,10 @@
/** Show apps on the desktop on the given display */
void showDesktopApps(int displayId, in RemoteTransition remoteTransition);
- /** Stash apps on the desktop to allow launching another app from home screen */
+ /** @deprecated use {@link #showDesktopApps} instead. */
void stashDesktopApps(int displayId);
- /** Hide apps that may be stashed */
+ /** @deprecated this is no longer supported. */
void hideStashedDesktopApps(int displayId);
/** Bring task with the given id to front */
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/IDesktopTaskListener.aidl b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/IDesktopTaskListener.aidl
index 8ed87f2..8ebdfdc 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/IDesktopTaskListener.aidl
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/IDesktopTaskListener.aidl
@@ -25,6 +25,6 @@
/** Desktop tasks visibility has changed. Visible if at least 1 task is visible. */
oneway void onTasksVisibilityChanged(int displayId, int visibleTasksCount);
- /** Desktop task stashed status has changed. */
+ /** @deprecated this is no longer supported. */
oneway void onStashedChanged(int displayId, boolean stashed);
}
\ No newline at end of file
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/draganddrop/DragAndDropPolicy.java b/libs/WindowManager/Shell/src/com/android/wm/shell/draganddrop/DragAndDropPolicy.java
index 6a7d297..a42ca19 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/draganddrop/DragAndDropPolicy.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/draganddrop/DragAndDropPolicy.java
@@ -16,10 +16,9 @@
package com.android.wm.shell.draganddrop;
+import static android.app.ActivityOptions.MODE_BACKGROUND_ACTIVITY_START_ALLOWED;
import static android.app.ActivityOptions.MODE_BACKGROUND_ACTIVITY_START_DENIED;
import static android.app.ActivityTaskManager.INVALID_TASK_ID;
-import static android.app.ComponentOptions.KEY_PENDING_INTENT_BACKGROUND_ACTIVITY_ALLOWED;
-import static android.app.ComponentOptions.KEY_PENDING_INTENT_BACKGROUND_ACTIVITY_ALLOWED_BY_PERMISSION;
import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD;
import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
import static android.content.ClipDescription.EXTRA_ACTIVITY_OPTIONS;
@@ -47,7 +46,6 @@
import android.app.ActivityTaskManager;
import android.app.PendingIntent;
import android.content.ActivityNotFoundException;
-import android.content.ClipData;
import android.content.ClipDescription;
import android.content.Context;
import android.content.Intent;
@@ -265,13 +263,14 @@
final boolean isShortcut = description.hasMimeType(MIMETYPE_APPLICATION_SHORTCUT);
final ActivityOptions baseActivityOpts = ActivityOptions.makeBasic();
baseActivityOpts.setDisallowEnterPictureInPictureWhileLaunching(true);
+ // Put BAL flags to avoid activity start aborted.
+ baseActivityOpts.setPendingIntentBackgroundActivityStartMode(
+ MODE_BACKGROUND_ACTIVITY_START_ALLOWED);
+ baseActivityOpts.setPendingIntentBackgroundActivityLaunchAllowedByPermission(true);
final Bundle opts = baseActivityOpts.toBundle();
if (session.appData.hasExtra(EXTRA_ACTIVITY_OPTIONS)) {
opts.putAll(session.appData.getBundleExtra(EXTRA_ACTIVITY_OPTIONS));
}
- // Put BAL flags to avoid activity start aborted.
- opts.putBoolean(KEY_PENDING_INTENT_BACKGROUND_ACTIVITY_ALLOWED, true);
- opts.putBoolean(KEY_PENDING_INTENT_BACKGROUND_ACTIVITY_ALLOWED_BY_PERMISSION, true);
final UserHandle user = session.appData.getParcelableExtra(EXTRA_USER);
if (isTask) {
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/draganddrop/DragLayout.java b/libs/WindowManager/Shell/src/com/android/wm/shell/draganddrop/DragLayout.java
index 59d6969..4bb10df 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/draganddrop/DragLayout.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/draganddrop/DragLayout.java
@@ -22,11 +22,11 @@
import static android.content.pm.ActivityInfo.CONFIG_UI_MODE;
import static android.content.res.Configuration.ORIENTATION_LANDSCAPE;
import static android.view.ViewGroup.LayoutParams.MATCH_PARENT;
-import static android.view.ViewTreeObserver.InternalInsetsInfo.TOUCHABLE_INSETS_FRAME;
import static android.view.ViewTreeObserver.InternalInsetsInfo.TOUCHABLE_INSETS_REGION;
import static com.android.wm.shell.common.split.SplitScreenConstants.SPLIT_POSITION_BOTTOM_OR_RIGHT;
import static com.android.wm.shell.common.split.SplitScreenConstants.SPLIT_POSITION_TOP_OR_LEFT;
+import static com.android.wm.shell.common.split.SplitScreenUtils.getResizingBackgroundColor;
import static com.android.wm.shell.draganddrop.DragAndDropPolicy.Target.TYPE_SPLIT_BOTTOM;
import static com.android.wm.shell.draganddrop.DragAndDropPolicy.Target.TYPE_SPLIT_LEFT;
import static com.android.wm.shell.draganddrop.DragAndDropPolicy.Target.TYPE_SPLIT_RIGHT;
@@ -41,7 +41,6 @@
import android.content.Context;
import android.content.res.Configuration;
import android.content.res.Resources;
-import android.graphics.Color;
import android.graphics.Insets;
import android.graphics.Rect;
import android.graphics.Region;
@@ -278,7 +277,7 @@
final int activityType = taskInfo1.getActivityType();
if (activityType == ACTIVITY_TYPE_STANDARD) {
Drawable icon1 = mIconProvider.getIcon(taskInfo1.topActivityInfo);
- int bgColor1 = getResizingBackgroundColor(taskInfo1);
+ int bgColor1 = getResizingBackgroundColor(taskInfo1).toArgb();
mDropZoneView1.setAppInfo(bgColor1, icon1);
mDropZoneView2.setAppInfo(bgColor1, icon1);
updateDropZoneSizes(null, null); // passing null splits the views evenly
@@ -298,10 +297,10 @@
mSplitScreenController.getTaskInfo(SPLIT_POSITION_BOTTOM_OR_RIGHT);
if (topOrLeftTask != null && bottomOrRightTask != null) {
Drawable topOrLeftIcon = mIconProvider.getIcon(topOrLeftTask.topActivityInfo);
- int topOrLeftColor = getResizingBackgroundColor(topOrLeftTask);
+ int topOrLeftColor = getResizingBackgroundColor(topOrLeftTask).toArgb();
Drawable bottomOrRightIcon = mIconProvider.getIcon(
bottomOrRightTask.topActivityInfo);
- int bottomOrRightColor = getResizingBackgroundColor(bottomOrRightTask);
+ int bottomOrRightColor = getResizingBackgroundColor(bottomOrRightTask).toArgb();
mDropZoneView1.setAppInfo(topOrLeftColor, topOrLeftIcon);
mDropZoneView2.setAppInfo(bottomOrRightColor, bottomOrRightIcon);
}
@@ -556,11 +555,6 @@
}
}
- private static int getResizingBackgroundColor(ActivityManager.RunningTaskInfo taskInfo) {
- final int taskBgColor = taskInfo.taskDescription.getBackgroundColor();
- return Color.valueOf(taskBgColor == -1 ? Color.WHITE : taskBgColor).toArgb();
- }
-
/**
* Dumps information about this drag layout.
*/
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/freeform/FreeformTaskListener.java b/libs/WindowManager/Shell/src/com/android/wm/shell/freeform/FreeformTaskListener.java
index a414a55..e0e2e706 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/freeform/FreeformTaskListener.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/freeform/FreeformTaskListener.java
@@ -27,9 +27,9 @@
import com.android.internal.protolog.common.ProtoLog;
import com.android.wm.shell.ShellTaskOrganizer;
-import com.android.wm.shell.desktopmode.DesktopModeStatus;
import com.android.wm.shell.desktopmode.DesktopModeTaskRepository;
import com.android.wm.shell.protolog.ShellProtoLogGroup;
+import com.android.wm.shell.shared.DesktopModeStatus;
import com.android.wm.shell.sysui.ShellInit;
import com.android.wm.shell.transition.Transitions;
import com.android.wm.shell.windowdecor.WindowDecorViewModel;
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/keyguard/KeyguardTransitionHandler.java b/libs/WindowManager/Shell/src/com/android/wm/shell/keyguard/KeyguardTransitionHandler.java
index 9eaf7e4..c79eef7e 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/keyguard/KeyguardTransitionHandler.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/keyguard/KeyguardTransitionHandler.java
@@ -22,6 +22,7 @@
import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
import static android.service.dreams.Flags.dismissDreamOnKeyguardDismiss;
import static android.view.WindowManager.KEYGUARD_VISIBILITY_TRANSIT_FLAGS;
+import static android.view.WindowManager.TRANSIT_FLAG_KEYGUARD_APPEARING;
import static android.view.WindowManager.TRANSIT_FLAG_KEYGUARD_GOING_AWAY;
import static android.view.WindowManager.TRANSIT_FLAG_KEYGUARD_LOCKED;
import static android.view.WindowManager.TRANSIT_FLAG_KEYGUARD_OCCLUDING;
@@ -83,6 +84,7 @@
* @see KeyguardTransitions
*/
private IRemoteTransition mExitTransition = null;
+ private IRemoteTransition mAppearTransition = null;
private IRemoteTransition mOccludeTransition = null;
private IRemoteTransition mOccludeByDreamTransition = null;
private IRemoteTransition mUnoccludeTransition = null;
@@ -170,26 +172,28 @@
// Choose a transition applicable for the changes and keyguard state.
if ((info.getFlags() & TRANSIT_FLAG_KEYGUARD_GOING_AWAY) != 0) {
- return startAnimation(mExitTransition,
- "going-away",
+ return startAnimation(mExitTransition, "going-away",
transition, info, startTransaction, finishTransaction, finishCallback);
}
+ if ((info.getFlags() & TRANSIT_FLAG_KEYGUARD_APPEARING) != 0) {
+ return startAnimation(mAppearTransition, "appearing",
+ transition, info, startTransaction, finishTransaction, finishCallback);
+ }
+
+
// Occlude/unocclude animations are only played if the keyguard is locked.
if ((info.getFlags() & TRANSIT_FLAG_KEYGUARD_LOCKED) != 0) {
if ((info.getFlags() & TRANSIT_FLAG_KEYGUARD_OCCLUDING) != 0) {
if (hasOpeningDream(info)) {
- return startAnimation(mOccludeByDreamTransition,
- "occlude-by-dream",
+ return startAnimation(mOccludeByDreamTransition, "occlude-by-dream",
transition, info, startTransaction, finishTransaction, finishCallback);
} else {
- return startAnimation(mOccludeTransition,
- "occlude",
+ return startAnimation(mOccludeTransition, "occlude",
transition, info, startTransaction, finishTransaction, finishCallback);
}
} else if ((info.getFlags() & TRANSIT_FLAG_KEYGUARD_UNOCCLUDING) != 0) {
- return startAnimation(mUnoccludeTransition,
- "unocclude",
+ return startAnimation(mUnoccludeTransition, "unocclude",
transition, info, startTransaction, finishTransaction, finishCallback);
}
}
@@ -359,11 +363,13 @@
@Override
public void register(
IRemoteTransition exitTransition,
+ IRemoteTransition appearTransition,
IRemoteTransition occludeTransition,
IRemoteTransition occludeByDreamTransition,
IRemoteTransition unoccludeTransition) {
mMainExecutor.execute(() -> {
mExitTransition = exitTransition;
+ mAppearTransition = appearTransition;
mOccludeTransition = occludeTransition;
mOccludeByDreamTransition = occludeByDreamTransition;
mUnoccludeTransition = unoccludeTransition;
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/keyguard/KeyguardTransitions.java b/libs/WindowManager/Shell/src/com/android/wm/shell/keyguard/KeyguardTransitions.java
index 4215b2c..b7245b9 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/keyguard/KeyguardTransitions.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/keyguard/KeyguardTransitions.java
@@ -35,6 +35,7 @@
*/
default void register(
@NonNull IRemoteTransition unlockTransition,
+ @NonNull IRemoteTransition appearTransition,
@NonNull IRemoteTransition occludeTransition,
@NonNull IRemoteTransition occludeByDreamTransition,
@NonNull IRemoteTransition unoccludeTransition) {}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/Pip.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/Pip.java
index 7b1ef5c..a749019 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/Pip.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/Pip.java
@@ -39,7 +39,7 @@
* @param isSysUiStateValid Is SysUI state valid or not.
* @param flag Current SysUI state.
*/
- default void onSystemUiStateChanged(boolean isSysUiStateValid, int flag) {
+ default void onSystemUiStateChanged(boolean isSysUiStateValid, long flag) {
}
/**
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTaskOrganizer.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTaskOrganizer.java
index e885262..e1657f9 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTaskOrganizer.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTaskOrganizer.java
@@ -854,7 +854,8 @@
mPipUiEventLoggerLogger.log(uiEventEnum);
ProtoLog.d(ShellProtoLogGroup.WM_SHELL_PICTURE_IN_PICTURE,
- "onTaskAppeared: %s, state=%s", mTaskInfo.topActivity, mPipTransitionState);
+ "onTaskAppeared: %s, state=%s, taskId=%s", mTaskInfo.topActivity,
+ mPipTransitionState, mTaskInfo.taskId);
if (mPipTransitionState.getInSwipePipToHomeTransition()) {
if (!mWaitForFixedRotation) {
onEndOfSwipePipToHomeTransition();
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTransition.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTransition.java
index fdde3ee..2082756 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTransition.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTransition.java
@@ -824,12 +824,10 @@
@NonNull Transitions.TransitionFinishCallback finishCallback,
@NonNull TaskInfo taskInfo) {
startTransaction.apply();
- if (info.getChanges().isEmpty()) {
+ final TransitionInfo.Change pipChange = findCurrentPipTaskChange(info);
+ if (pipChange == null) {
ProtoLog.e(ShellProtoLogGroup.WM_SHELL_PICTURE_IN_PICTURE,
- "removePipImmediately is called with empty changes");
- } else {
- finishTransaction.setWindowCrop(info.getChanges().get(0).getLeash(),
- mPipDisplayLayoutState.getDisplayBounds());
+ "removePipImmediately is called without pip change");
}
mPipOrganizer.onExitPipFinished(taskInfo);
finishCallback.onTransitionFinished(null);
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipController.java
index 139cde2..85f9194 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipController.java
@@ -847,7 +847,7 @@
}
}
- private void onSystemUiStateChanged(boolean isValidState, int flag) {
+ private void onSystemUiStateChanged(boolean isValidState, long flag) {
mTouchHandler.onSystemUiStateChanged(isValidState);
}
@@ -1195,7 +1195,7 @@
}
@Override
- public void onSystemUiStateChanged(boolean isSysUiStateValid, int flag) {
+ public void onSystemUiStateChanged(boolean isSysUiStateValid, long flag) {
mMainExecutor.execute(() -> {
PipController.this.onSystemUiStateChanged(isSysUiStateValid, flag);
});
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipMotionHelper.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipMotionHelper.java
index a097a0f..be10151 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipMotionHelper.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipMotionHelper.java
@@ -58,7 +58,8 @@
* A helper to animate and manipulate the PiP.
*/
public class PipMotionHelper implements PipAppOpsListener.Callback,
- FloatingContentCoordinator.FloatingContent {
+ FloatingContentCoordinator.FloatingContent,
+ PipTransitionState.PipTransitionStateChangedListener {
private static final String TAG = "PipMotionHelper";
private static final String FLING_BOUNDS_CHANGE = "fling_bounds_change";
private static final boolean DEBUG = false;
@@ -181,7 +182,7 @@
}
};
mPipTransitionState = pipTransitionState;
- mPipTransitionState.addPipTransitionStateChangedListener(this::onPipTransitionStateChanged);
+ mPipTransitionState.addPipTransitionStateChangedListener(this);
}
void init() {
@@ -687,7 +688,8 @@
// setAnimatingToBounds(toBounds);
}
- private void onPipTransitionStateChanged(@PipTransitionState.TransitionState int oldState,
+ @Override
+ public void onPipTransitionStateChanged(@PipTransitionState.TransitionState int oldState,
@PipTransitionState.TransitionState int newState,
@Nullable Bundle extra) {
switch (newState) {
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipResizeGestureHandler.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipResizeGestureHandler.java
index 04cf350..b55a41d 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipResizeGestureHandler.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipResizeGestureHandler.java
@@ -24,6 +24,7 @@
import android.graphics.PointF;
import android.graphics.Rect;
import android.hardware.input.InputManager;
+import android.os.Bundle;
import android.os.Looper;
import android.view.BatchedInputEventReceiver;
import android.view.Choreographer;
@@ -32,6 +33,7 @@
import android.view.InputEventReceiver;
import android.view.InputMonitor;
import android.view.MotionEvent;
+import android.view.SurfaceControl;
import android.view.ViewConfiguration;
import androidx.annotation.VisibleForTesting;
@@ -51,16 +53,20 @@
* Helper on top of PipTouchHandler that handles inputs OUTSIDE of the PIP window, which is used to
* trigger dynamic resize.
*/
-public class PipResizeGestureHandler {
+public class PipResizeGestureHandler implements
+ PipTransitionState.PipTransitionStateChangedListener {
private static final String TAG = "PipResizeGestureHandler";
private static final int PINCH_RESIZE_SNAP_DURATION = 250;
private static final float PINCH_RESIZE_AUTO_MAX_RATIO = 0.9f;
+ private static final String RESIZE_BOUNDS_CHANGE = "resize_bounds_change";
private final Context mContext;
private final PipBoundsAlgorithm mPipBoundsAlgorithm;
private final PipBoundsState mPipBoundsState;
private final PipTouchState mPipTouchState;
+ private final PipScheduler mPipScheduler;
+ private final PipTransitionState mPipTransitionState;
private final PhonePipMenuController mPhonePipMenuController;
private final PipUiEventLogger mPipUiEventLogger;
private final PipPinchResizingAlgorithm mPinchResizingAlgorithm;
@@ -88,6 +94,7 @@
private boolean mIsSysUiStateValid;
private boolean mThresholdCrossed;
private boolean mOngoingPinchToResize = false;
+ private boolean mWaitingForBoundsChangeTransition = false;
private float mAngle = 0;
int mFirstIndex = -1;
int mSecondIndex = -1;
@@ -104,11 +111,17 @@
private int mCtrlType;
private int mOhmOffset;
- public PipResizeGestureHandler(Context context, PipBoundsAlgorithm pipBoundsAlgorithm,
- PipBoundsState pipBoundsState, PipTouchState pipTouchState,
+ public PipResizeGestureHandler(Context context,
+ PipBoundsAlgorithm pipBoundsAlgorithm,
+ PipBoundsState pipBoundsState,
+ PipTouchState pipTouchState,
+ PipScheduler pipScheduler,
+ PipTransitionState pipTransitionState,
Runnable updateMovementBoundsRunnable,
- PipUiEventLogger pipUiEventLogger, PhonePipMenuController menuActivityController,
- ShellExecutor mainExecutor, @Nullable PipPerfHintController pipPerfHintController) {
+ PipUiEventLogger pipUiEventLogger,
+ PhonePipMenuController menuActivityController,
+ ShellExecutor mainExecutor,
+ @Nullable PipPerfHintController pipPerfHintController) {
mContext = context;
mDisplayId = context.getDisplayId();
mMainExecutor = mainExecutor;
@@ -116,6 +129,11 @@
mPipBoundsAlgorithm = pipBoundsAlgorithm;
mPipBoundsState = pipBoundsState;
mPipTouchState = pipTouchState;
+ mPipScheduler = pipScheduler;
+
+ mPipTransitionState = pipTransitionState;
+ mPipTransitionState.addPipTransitionStateChangedListener(this);
+
mUpdateMovementBoundsRunnable = updateMovementBoundsRunnable;
mPhonePipMenuController = menuActivityController;
mPipUiEventLogger = pipUiEventLogger;
@@ -125,6 +143,7 @@
mUserResizeBounds.set(rect);
// mMotionHelper.synchronizePinnedStackBounds();
mUpdateMovementBoundsRunnable.run();
+ mPipBoundsState.setBounds(rect);
resetState();
};
}
@@ -202,7 +221,7 @@
@VisibleForTesting
void onInputEvent(InputEvent ev) {
if (!mEnablePinchResize) {
- // No need to handle anything if neither form of resizing is enabled.
+ // No need to handle anything if resizing isn't enabled.
return;
}
@@ -227,7 +246,7 @@
}
}
- if (mEnablePinchResize && mOngoingPinchToResize) {
+ if (mOngoingPinchToResize) {
onPinchResize(mv);
}
}
@@ -249,13 +268,11 @@
}
boolean willStartResizeGesture(MotionEvent ev) {
- if (isInValidSysUiState()) {
- if (ev.getActionMasked() == MotionEvent.ACTION_POINTER_DOWN) {
- if (mEnablePinchResize && ev.getPointerCount() == 2) {
- onPinchResize(ev);
- mOngoingPinchToResize = mAllowGesture;
- return mAllowGesture;
- }
+ if (ev.getActionMasked() == MotionEvent.ACTION_POINTER_DOWN) {
+ if (mEnablePinchResize && ev.getPointerCount() == 2) {
+ onPinchResize(ev);
+ mOngoingPinchToResize = mAllowGesture;
+ return mAllowGesture;
}
}
return false;
@@ -284,7 +301,6 @@
mSecondIndex = -1;
mAllowGesture = false;
finishResize();
- cleanUpHighPerfSessionMaybe();
}
if (ev.getPointerCount() != 2) {
@@ -347,10 +363,7 @@
mDownSecondPoint, mLastPoint, mLastSecondPoint, mMinSize, mMaxSize,
mDownBounds, mLastResizeBounds);
- /*
- mPipTaskOrganizer.scheduleUserResizePip(mDownBounds, mLastResizeBounds,
- mAngle, null);
- */
+ mPipScheduler.scheduleUserResizePip(mLastResizeBounds, mAngle);
mPipBoundsState.setHasUserResizedPip(true);
}
}
@@ -399,57 +412,43 @@
}
private void finishResize() {
- if (!mLastResizeBounds.isEmpty()) {
- // Pinch-to-resize needs to re-calculate snap fraction and animate to the snapped
- // position correctly. Drag-resize does not need to move, so just finalize resize.
- if (mOngoingPinchToResize) {
- final Rect startBounds = new Rect(mLastResizeBounds);
- // If user resize is pretty close to max size, just auto resize to max.
- if (mLastResizeBounds.width() >= PINCH_RESIZE_AUTO_MAX_RATIO * mMaxSize.x
- || mLastResizeBounds.height() >= PINCH_RESIZE_AUTO_MAX_RATIO * mMaxSize.y) {
- resizeRectAboutCenter(mLastResizeBounds, mMaxSize.x, mMaxSize.y);
- }
-
- // If user resize is smaller than min size, auto resize to min
- if (mLastResizeBounds.width() < mMinSize.x
- || mLastResizeBounds.height() < mMinSize.y) {
- resizeRectAboutCenter(mLastResizeBounds, mMinSize.x, mMinSize.y);
- }
-
- // get the current movement bounds
- final Rect movementBounds = mPipBoundsAlgorithm
- .getMovementBounds(mLastResizeBounds);
-
- // snap mLastResizeBounds to the correct edge based on movement bounds
- snapToMovementBoundsEdge(mLastResizeBounds, movementBounds);
-
- final float snapFraction = mPipBoundsAlgorithm.getSnapFraction(
- mLastResizeBounds, movementBounds);
- mPipBoundsAlgorithm.applySnapFraction(mLastResizeBounds, snapFraction);
-
- // disable any touch events beyond resizing too
- mPipTouchState.setAllowInputEvents(false);
-
- /*
- mPipTaskOrganizer.scheduleAnimateResizePip(startBounds, mLastResizeBounds,
- PINCH_RESIZE_SNAP_DURATION, mAngle, mUpdateResizeBoundsCallback, () -> {
- // enable touch events
- mPipTouchState.setAllowInputEvents(true);
- });
- */
- } else {
- /*
- mPipTaskOrganizer.scheduleFinishResizePip(mLastResizeBounds,
- TRANSITION_DIRECTION_USER_RESIZE,
- mUpdateResizeBoundsCallback);
- */
- }
- final float magnetRadiusPercent = (float) mLastResizeBounds.width() / mMinSize.x / 2.f;
- mPipUiEventLogger.log(
- PipUiEventLogger.PipUiEventEnum.PICTURE_IN_PICTURE_RESIZE);
- } else {
+ if (mLastResizeBounds.isEmpty()) {
resetState();
}
+ if (!mOngoingPinchToResize) {
+ return;
+ }
+ final Rect startBounds = new Rect(mLastResizeBounds);
+
+ // If user resize is pretty close to max size, just auto resize to max.
+ if (mLastResizeBounds.width() >= PINCH_RESIZE_AUTO_MAX_RATIO * mMaxSize.x
+ || mLastResizeBounds.height() >= PINCH_RESIZE_AUTO_MAX_RATIO * mMaxSize.y) {
+ resizeRectAboutCenter(mLastResizeBounds, mMaxSize.x, mMaxSize.y);
+ }
+
+ // If user resize is smaller than min size, auto resize to min
+ if (mLastResizeBounds.width() < mMinSize.x
+ || mLastResizeBounds.height() < mMinSize.y) {
+ resizeRectAboutCenter(mLastResizeBounds, mMinSize.x, mMinSize.y);
+ }
+
+ // get the current movement bounds
+ final Rect movementBounds = mPipBoundsAlgorithm
+ .getMovementBounds(mLastResizeBounds);
+
+ // snap mLastResizeBounds to the correct edge based on movement bounds
+ snapToMovementBoundsEdge(mLastResizeBounds, movementBounds);
+
+ final float snapFraction = mPipBoundsAlgorithm.getSnapFraction(
+ mLastResizeBounds, movementBounds);
+ mPipBoundsAlgorithm.applySnapFraction(mLastResizeBounds, snapFraction);
+
+ // Update the transition state to schedule a resize transition.
+ Bundle extra = new Bundle();
+ extra.putBoolean(RESIZE_BOUNDS_CHANGE, true);
+ mPipTransitionState.setState(PipTransitionState.SCHEDULED_BOUNDS_CHANGE, extra);
+
+ mPipUiEventLogger.log(PipUiEventLogger.PipUiEventEnum.PICTURE_IN_PICTURE_RESIZE);
}
private void resetState() {
@@ -509,6 +508,40 @@
rect.set(l, t, r, b);
}
+ @Override
+ public void onPipTransitionStateChanged(@PipTransitionState.TransitionState int oldState,
+ @PipTransitionState.TransitionState int newState, @Nullable Bundle extra) {
+ switch (newState) {
+ case PipTransitionState.SCHEDULED_BOUNDS_CHANGE:
+ if (!extra.getBoolean(RESIZE_BOUNDS_CHANGE)) break;
+ mWaitingForBoundsChangeTransition = true;
+ mPipScheduler.scheduleAnimateResizePip(mLastResizeBounds);
+ break;
+ case PipTransitionState.CHANGING_PIP_BOUNDS:
+ if (!mWaitingForBoundsChangeTransition) break;
+
+ // If bounds change transition was scheduled from this class, handle leash updates.
+ mWaitingForBoundsChangeTransition = false;
+
+ SurfaceControl.Transaction startTx = extra.getParcelable(
+ PipTransition.PIP_START_TX, SurfaceControl.Transaction.class);
+ Rect destinationBounds = extra.getParcelable(
+ PipTransition.PIP_DESTINATION_BOUNDS, Rect.class);
+ startTx.setPosition(mPipTransitionState.mPinnedTaskLeash,
+ destinationBounds.left, destinationBounds.top);
+ startTx.apply();
+
+ // All motion operations have actually finished, so make bounds cache updates.
+ cleanUpHighPerfSessionMaybe();
+
+ // Setting state to CHANGED_PIP_BOUNDS applies finishTx and notifies Core.
+ mPipTransitionState.setState(PipTransitionState.CHANGED_PIP_BOUNDS);
+
+ mUpdateResizeBoundsCallback.accept(destinationBounds);
+ break;
+ }
+ }
+
/**
* Dumps the {@link PipResizeGestureHandler} state.
*/
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipScheduler.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipScheduler.java
index c5b0de3..4947507 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipScheduler.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipScheduler.java
@@ -24,6 +24,7 @@
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
+import android.graphics.Matrix;
import android.graphics.Rect;
import android.view.SurfaceControl;
import android.window.WindowContainerTransaction;
@@ -165,6 +166,16 @@
* {@link WindowContainerTransaction}.
*/
public void scheduleUserResizePip(Rect toBounds) {
+ scheduleUserResizePip(toBounds, 0f /* degrees */);
+ }
+
+ /**
+ * Directly perform a scaled matrix transformation on the leash. This will not perform any
+ * {@link WindowContainerTransaction}.
+ *
+ * @param degrees the angle to rotate the bounds to.
+ */
+ public void scheduleUserResizePip(Rect toBounds, float degrees) {
if (toBounds.isEmpty()) {
ProtoLog.w(ShellProtoLogGroup.WM_SHELL_PICTURE_IN_PICTURE,
"%s: Attempted to user resize PIP to empty bounds, aborting.", TAG);
@@ -172,7 +183,16 @@
}
SurfaceControl leash = mPipTransitionState.mPinnedTaskLeash;
final SurfaceControl.Transaction tx = new SurfaceControl.Transaction();
- tx.setPosition(leash, toBounds.left, toBounds.top);
+
+ Matrix transformTensor = new Matrix();
+ final float[] mMatrixTmp = new float[9];
+ final float scale = (float) toBounds.width() / mPipBoundsState.getBounds().width();
+
+ transformTensor.setScale(scale, scale);
+ transformTensor.postTranslate(toBounds.left, toBounds.top);
+ transformTensor.postRotate(degrees, toBounds.centerX(), toBounds.centerY());
+
+ tx.setMatrix(leash, transformTensor, mMatrixTmp);
tx.apply();
}
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipTouchHandler.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipTouchHandler.java
index 9c6e3ea..319d199 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipTouchHandler.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipTouchHandler.java
@@ -73,7 +73,7 @@
* Manages all the touch handling for PIP on the Phone, including moving, dismissing and expanding
* the PIP.
*/
-public class PipTouchHandler {
+public class PipTouchHandler implements PipTransitionState.PipTransitionStateChangedListener {
private static final String TAG = "PipTouchHandler";
private static final float DEFAULT_STASH_VELOCITY_THRESHOLD = 18000.f;
@@ -84,6 +84,7 @@
private final PipBoundsAlgorithm mPipBoundsAlgorithm;
@NonNull private final PipBoundsState mPipBoundsState;
@NonNull private final PipTransitionState mPipTransitionState;
+ @NonNull private final PipScheduler mPipScheduler;
@NonNull private final SizeSpecSource mSizeSpecSource;
private final PipUiEventLogger mPipUiEventLogger;
private final PipDismissTargetHandler mPipDismissTargetHandler;
@@ -173,6 +174,7 @@
PipBoundsAlgorithm pipBoundsAlgorithm,
@NonNull PipBoundsState pipBoundsState,
@NonNull PipTransitionState pipTransitionState,
+ @NonNull PipScheduler pipScheduler,
@NonNull SizeSpecSource sizeSpecSource,
PipMotionHelper pipMotionHelper,
FloatingContentCoordinator floatingContentCoordinator,
@@ -188,6 +190,7 @@
mPipTransitionState = pipTransitionState;
mPipTransitionState.addPipTransitionStateChangedListener(this::onPipTransitionStateChanged);
+ mPipScheduler = pipScheduler;
mSizeSpecSource = sizeSpecSource;
mMenuController = menuController;
mPipUiEventLogger = pipUiEventLogger;
@@ -213,10 +216,10 @@
},
menuController::hideMenu,
mainExecutor);
- mPipResizeGestureHandler =
- new PipResizeGestureHandler(context, pipBoundsAlgorithm, pipBoundsState,
- mTouchState, this::updateMovementBounds, pipUiEventLogger,
- menuController, mainExecutor, mPipPerfHintController);
+ mPipResizeGestureHandler = new PipResizeGestureHandler(context, pipBoundsAlgorithm,
+ pipBoundsState, mTouchState, mPipScheduler, mPipTransitionState,
+ this::updateMovementBounds, pipUiEventLogger, menuController, mainExecutor,
+ mPipPerfHintController);
mPipBoundsState.addOnAspectRatioChangedCallback(this::updateMinMaxSize);
if (PipUtils.isPip2ExperimentEnabled()) {
@@ -1075,7 +1078,8 @@
mPipResizeGestureHandler.setOhmOffset(offset);
}
- private void onPipTransitionStateChanged(@PipTransitionState.TransitionState int oldState,
+ @Override
+ public void onPipTransitionStateChanged(@PipTransitionState.TransitionState int oldState,
@PipTransitionState.TransitionState int newState,
@Nullable Bundle extra) {
switch (newState) {
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/recents/RecentTasksController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/recents/RecentTasksController.java
index a8611d9..c53e7fe 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/recents/RecentTasksController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/recents/RecentTasksController.java
@@ -50,9 +50,9 @@
import com.android.wm.shell.common.SingleInstanceRemoteListener;
import com.android.wm.shell.common.TaskStackListenerCallback;
import com.android.wm.shell.common.TaskStackListenerImpl;
-import com.android.wm.shell.desktopmode.DesktopModeStatus;
import com.android.wm.shell.desktopmode.DesktopModeTaskRepository;
import com.android.wm.shell.protolog.ShellProtoLogGroup;
+import com.android.wm.shell.shared.DesktopModeStatus;
import com.android.wm.shell.shared.annotations.ExternalThread;
import com.android.wm.shell.shared.annotations.ShellMainThread;
import com.android.wm.shell.sysui.ShellCommandHandler;
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenTransitions.java b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenTransitions.java
index 6e5b767..0541a02 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenTransitions.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenTransitions.java
@@ -501,10 +501,11 @@
mAnimatingTransition = null;
mOnFinish.run();
- if (mFinishCallback != null) {
- mFinishCallback.onTransitionFinished(wct /* wct */);
- mFinishCallback = null;
- }
+ if (mFinishCallback != null) {
+ Transitions.TransitionFinishCallback currentFinishCallback = mFinishCallback;
+ mFinishCallback = null;
+ currentFinishCallback.onTransitionFinished(wct /* wct */);
+ }
}
private void startFadeAnimation(@NonNull SurfaceControl leash, boolean show) {
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java
index b10176d..9f8cb62 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java
@@ -16,10 +16,8 @@
package com.android.wm.shell.splitscreen;
-import static android.app.ActivityOptions.KEY_LAUNCH_ROOT_TASK_TOKEN;
+import static android.app.ActivityOptions.MODE_BACKGROUND_ACTIVITY_START_ALLOWED;
import static android.app.ActivityTaskManager.INVALID_TASK_ID;
-import static android.app.ComponentOptions.KEY_PENDING_INTENT_BACKGROUND_ACTIVITY_ALLOWED;
-import static android.app.ComponentOptions.KEY_PENDING_INTENT_BACKGROUND_ACTIVITY_ALLOWED_BY_PERMISSION;
import static android.app.WindowConfiguration.ACTIVITY_TYPE_HOME;
import static android.app.WindowConfiguration.ACTIVITY_TYPE_RECENTS;
import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
@@ -43,6 +41,7 @@
import static com.android.wm.shell.common.split.SplitScreenConstants.SPLIT_POSITION_TOP_OR_LEFT;
import static com.android.wm.shell.common.split.SplitScreenConstants.SPLIT_POSITION_UNDEFINED;
import static com.android.wm.shell.common.split.SplitScreenConstants.splitPositionToString;
+import static com.android.wm.shell.common.split.SplitScreenUtils.getResizingBackgroundColor;
import static com.android.wm.shell.common.split.SplitScreenUtils.reverseSplitPosition;
import static com.android.wm.shell.common.split.SplitScreenUtils.splitFailureMessage;
import static com.android.wm.shell.protolog.ShellProtoLogGroup.WM_SHELL_SPLIT_SCREEN;
@@ -67,6 +66,7 @@
import static com.android.wm.shell.splitscreen.SplitScreenController.EXIT_REASON_SCREEN_LOCKED_SHOW_ON_TOP;
import static com.android.wm.shell.splitscreen.SplitScreenController.EXIT_REASON_UNKNOWN;
import static com.android.wm.shell.splitscreen.SplitScreenController.exitReasonToString;
+import static com.android.wm.shell.transition.MixedTransitionHelper.getPipReplacingChange;
import static com.android.wm.shell.transition.Transitions.ENABLE_SHELL_TRANSITIONS;
import static com.android.wm.shell.transition.Transitions.TRANSIT_SPLIT_SCREEN_OPEN_TO_SIDE;
import static com.android.wm.shell.transition.Transitions.TRANSIT_SPLIT_SCREEN_PAIR_OPEN;
@@ -1885,13 +1885,15 @@
}
private void addActivityOptions(Bundle opts, @Nullable StageTaskListener launchTarget) {
+ ActivityOptions options = ActivityOptions.fromBundle(opts);
if (launchTarget != null) {
- opts.putParcelable(KEY_LAUNCH_ROOT_TASK_TOKEN, launchTarget.mRootTaskInfo.token);
+ options.setLaunchRootTask(launchTarget.mRootTaskInfo.token);
}
// Put BAL flags to avoid activity start aborted. Otherwise, flows like shortcut to split
// will be canceled.
- opts.putBoolean(KEY_PENDING_INTENT_BACKGROUND_ACTIVITY_ALLOWED, true);
- opts.putBoolean(KEY_PENDING_INTENT_BACKGROUND_ACTIVITY_ALLOWED_BY_PERMISSION, true);
+ options.setPendingIntentBackgroundActivityStartMode(MODE_BACKGROUND_ACTIVITY_START_ALLOWED);
+ options.setPendingIntentBackgroundActivityLaunchAllowedByPermission(true);
+ opts.putAll(options.toBundle());
}
void updateActivityOptions(Bundle opts, @SplitPosition int position) {
@@ -2386,14 +2388,20 @@
}
@Override
- public void onLayoutSizeChanging(SplitLayout layout, int offsetX, int offsetY) {
+ public void onLayoutSizeChanging(SplitLayout layout, int offsetX, int offsetY,
+ boolean shouldUseParallaxEffect) {
final SurfaceControl.Transaction t = mTransactionPool.acquire();
t.setFrameTimelineVsync(Choreographer.getInstance().getVsyncId());
- updateSurfaceBounds(layout, t, true /* applyResizingOffset */);
+ updateSurfaceBounds(layout, t, shouldUseParallaxEffect);
getMainStageBounds(mTempRect1);
getSideStageBounds(mTempRect2);
- mMainStage.onResizing(mTempRect1, mTempRect2, t, offsetX, offsetY, mShowDecorImmediately);
- mSideStage.onResizing(mTempRect2, mTempRect1, t, offsetX, offsetY, mShowDecorImmediately);
+ // TODO (b/307490004): "commonColor" below is a temporary fix to ensure the colors on both
+ // sides match. When b/307490004 is fixed, this code can be reverted.
+ float[] commonColor = getResizingBackgroundColor(mSideStage.mRootTaskInfo).getComponents();
+ mMainStage.onResizing(
+ mTempRect1, mTempRect2, t, offsetX, offsetY, mShowDecorImmediately, commonColor);
+ mSideStage.onResizing(
+ mTempRect2, mTempRect1, t, offsetX, offsetY, mShowDecorImmediately, commonColor);
t.apply();
mTransactionPool.release(t);
}
@@ -2836,7 +2844,7 @@
mSplitLayout.setFreezeDividerWindow(false);
final StageChangeRecord record = new StageChangeRecord();
final int transitType = info.getType();
- boolean hasEnteringPip = false;
+ TransitionInfo.Change pipChange = null;
for (int iC = 0; iC < info.getChanges().size(); ++iC) {
final TransitionInfo.Change change = info.getChanges().get(iC);
if (change.getMode() == TRANSIT_CHANGE
@@ -2847,7 +2855,7 @@
}
if (mMixedHandler.isEnteringPip(change, transitType)) {
- hasEnteringPip = true;
+ pipChange = change;
}
final ActivityManager.RunningTaskInfo taskInfo = change.getTaskInfo();
@@ -2899,9 +2907,19 @@
}
}
- if (hasEnteringPip) {
+ if (pipChange != null) {
+ TransitionInfo.Change pipReplacingChange = getPipReplacingChange(info, pipChange,
+ mMainStage.mRootTaskInfo.taskId, mSideStage.mRootTaskInfo.taskId,
+ getSplitItemStage(pipChange.getLastParent()));
+ if (pipReplacingChange != null) {
+ // Set an enter transition for when startAnimation gets called again
+ mSplitTransitions.setEnterTransition(transition, /*remoteTransition*/ null,
+ TRANSIT_SPLIT_SCREEN_OPEN_TO_SIDE, /*resizeAnim*/ false);
+ }
+
mMixedHandler.animatePendingEnterPipFromSplit(transition, info,
- startTransaction, finishTransaction, finishCallback);
+ startTransaction, finishTransaction, finishCallback,
+ pipReplacingChange != null);
notifySplitAnimationFinished();
return true;
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageTaskListener.java b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageTaskListener.java
index 1e305c5..0f3d6ca 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageTaskListener.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageTaskListener.java
@@ -177,9 +177,11 @@
@Override
@CallSuper
public void onTaskAppeared(ActivityManager.RunningTaskInfo taskInfo, SurfaceControl leash) {
- ProtoLog.d(WM_SHELL_SPLIT_SCREEN, "onTaskAppeared: task=%d taskParent=%d rootTask=%d",
+ ProtoLog.d(WM_SHELL_SPLIT_SCREEN, "onTaskAppeared: taskId=%d taskParent=%d rootTask=%d "
+ + "taskActivity=%s",
taskInfo.taskId, taskInfo.parentTaskId,
- mRootTaskInfo != null ? mRootTaskInfo.taskId : -1);
+ mRootTaskInfo != null ? mRootTaskInfo.taskId : -1,
+ taskInfo.baseActivity);
if (mRootTaskInfo == null) {
mRootLeash = leash;
mRootTaskInfo = taskInfo;
@@ -213,6 +215,8 @@
@Override
@CallSuper
public void onTaskInfoChanged(ActivityManager.RunningTaskInfo taskInfo) {
+ ProtoLog.d(WM_SHELL_SPLIT_SCREEN, "onTaskInfoChanged: taskId=%d taskAct=%s",
+ taskInfo.taskId, taskInfo.baseActivity);
mWindowDecorViewModel.ifPresent(viewModel -> viewModel.onTaskInfoChanged(taskInfo));
if (mRootTaskInfo.taskId == taskInfo.taskId) {
// Inflates split decor view only when the root task is visible.
@@ -310,10 +314,10 @@
}
void onResizing(Rect newBounds, Rect sideBounds, SurfaceControl.Transaction t, int offsetX,
- int offsetY, boolean immediately) {
+ int offsetY, boolean immediately, float[] veilColor) {
if (mSplitDecorManager != null && mRootTaskInfo != null) {
mSplitDecorManager.onResizing(mRootTaskInfo, newBounds, sideBounds, t, offsetX,
- offsetY, immediately);
+ offsetY, immediately, veilColor);
}
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/SplashscreenIconDrawableFactory.java b/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/SplashscreenIconDrawableFactory.java
index e419462..e07e1b4 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/SplashscreenIconDrawableFactory.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/SplashscreenIconDrawableFactory.java
@@ -45,6 +45,7 @@
import com.android.internal.R;
+import java.io.Closeable;
import java.util.function.LongConsumer;
/**
@@ -100,7 +101,7 @@
* Drawable pre-drawing the scaled icon in a separate thread to increase the speed of the
* final drawing.
*/
- private static class ImmobileIconDrawable extends Drawable {
+ private static class ImmobileIconDrawable extends Drawable implements Closeable {
private final Paint mPaint = new Paint(Paint.ANTI_ALIAS_FLAG | Paint.DITHER_FLAG
| Paint.FILTER_BITMAP_FLAG);
private final Matrix mMatrix = new Matrix();
@@ -154,6 +155,16 @@
public int getOpacity() {
return 1;
}
+
+ @Override
+ public void close() {
+ synchronized (mPaint) {
+ if (mIconBitmap != null) {
+ mIconBitmap.recycle();
+ mIconBitmap = null;
+ }
+ }
+ }
}
/**
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultMixedHandler.java b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultMixedHandler.java
index 968b27b..bcacecb 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultMixedHandler.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultMixedHandler.java
@@ -77,6 +77,7 @@
private ActivityEmbeddingController mActivityEmbeddingController;
abstract static class MixedTransition {
+ /** Entering Pip from split, breaks split. */
static final int TYPE_ENTER_PIP_FROM_SPLIT = 1;
/** Both the display and split-state (enter/exit) is changing */
@@ -103,6 +104,9 @@
/** Enter pip from one of the Activity Embedding windows. */
static final int TYPE_ENTER_PIP_FROM_ACTIVITY_EMBEDDING = 9;
+ /** Entering Pip from split, but replace the Pip stage instead of breaking split. */
+ static final int TYPE_ENTER_PIP_REPLACE_FROM_SPLIT = 10;
+
/** The default animation for this mixed transition. */
static final int ANIM_TYPE_DEFAULT = 0;
@@ -484,9 +488,11 @@
// TODO(b/287704263): Remove when split/mixed are reversed.
public boolean animatePendingEnterPipFromSplit(IBinder transition, TransitionInfo info,
SurfaceControl.Transaction startT, SurfaceControl.Transaction finishT,
- Transitions.TransitionFinishCallback finishCallback) {
- final MixedTransition mixed = createDefaultMixedTransition(
- MixedTransition.TYPE_ENTER_PIP_FROM_SPLIT, transition);
+ Transitions.TransitionFinishCallback finishCallback, boolean replacingPip) {
+ int type = replacingPip
+ ? MixedTransition.TYPE_ENTER_PIP_REPLACE_FROM_SPLIT
+ : MixedTransition.TYPE_ENTER_PIP_FROM_SPLIT;
+ final MixedTransition mixed = createDefaultMixedTransition(type, transition);
mActiveTransitions.add(mixed);
Transitions.TransitionFinishCallback callback = wct -> {
mActiveTransitions.remove(mixed);
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultMixedTransition.java b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultMixedTransition.java
index b028bd6..0ada749 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultMixedTransition.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultMixedTransition.java
@@ -76,7 +76,12 @@
info, startTransaction, finishTransaction, finishCallback);
case TYPE_ENTER_PIP_FROM_SPLIT ->
animateEnterPipFromSplit(this, info, startTransaction, finishTransaction,
- finishCallback, mPlayer, mMixedHandler, mPipHandler, mSplitHandler);
+ finishCallback, mPlayer, mMixedHandler, mPipHandler, mSplitHandler,
+ /*replacingPip*/ false);
+ case TYPE_ENTER_PIP_REPLACE_FROM_SPLIT ->
+ animateEnterPipFromSplit(this, info, startTransaction, finishTransaction,
+ finishCallback, mPlayer, mMixedHandler, mPipHandler, mSplitHandler,
+ /*replacingPip*/ true);
case TYPE_KEYGUARD ->
animateKeyguard(this, info, startTransaction, finishTransaction, finishCallback,
mKeyguardHandler, mPipHandler);
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/MixedTransitionHelper.java b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/MixedTransitionHelper.java
index ffc0b76..e8b01b5 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/MixedTransitionHelper.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/MixedTransitionHelper.java
@@ -23,11 +23,15 @@
import static com.android.wm.shell.common.split.SplitScreenConstants.FLAG_IS_DIVIDER_BAR;
import static com.android.wm.shell.common.split.SplitScreenConstants.SPLIT_POSITION_UNDEFINED;
import static com.android.wm.shell.pip.PipAnimationController.ANIM_TYPE_ALPHA;
+import static com.android.wm.shell.shared.TransitionUtil.isOpeningMode;
+import static com.android.wm.shell.splitscreen.SplitScreen.STAGE_TYPE_MAIN;
+import static com.android.wm.shell.splitscreen.SplitScreen.STAGE_TYPE_SIDE;
import static com.android.wm.shell.splitscreen.SplitScreen.STAGE_TYPE_UNDEFINED;
import static com.android.wm.shell.splitscreen.SplitScreenController.EXIT_REASON_CHILD_TASK_ENTER_PIP;
import static com.android.wm.shell.transition.DefaultMixedHandler.subCopy;
import android.annotation.NonNull;
+import android.annotation.Nullable;
import android.view.SurfaceControl;
import android.window.TransitionInfo;
@@ -45,7 +49,8 @@
@NonNull SurfaceControl.Transaction finishTransaction,
@NonNull Transitions.TransitionFinishCallback finishCallback,
@NonNull Transitions player, @NonNull MixedTransitionHandler mixedHandler,
- @NonNull PipTransitionController pipHandler, @NonNull StageCoordinator splitHandler) {
+ @NonNull PipTransitionController pipHandler, @NonNull StageCoordinator splitHandler,
+ boolean replacingPip) {
ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TRANSITIONS, " Animating a mixed transition for "
+ "entering PIP while Split-Screen is foreground.");
TransitionInfo.Change pipChange = null;
@@ -99,7 +104,7 @@
// we need a separate one to send over to launcher.
SurfaceControl.Transaction otherStartT = new SurfaceControl.Transaction();
@SplitScreen.StageType int topStageToKeep = STAGE_TYPE_UNDEFINED;
- if (splitHandler.isSplitScreenVisible()) {
+ if (splitHandler.isSplitScreenVisible() && !replacingPip) {
// The non-going home case, we could be pip-ing one of the split stages and keep
// showing the other
for (int i = info.getChanges().size() - 1; i >= 0; --i) {
@@ -115,11 +120,12 @@
break;
}
}
+
+ // Let split update internal state for dismiss.
+ splitHandler.prepareDismissAnimation(topStageToKeep,
+ EXIT_REASON_CHILD_TASK_ENTER_PIP, everythingElse, otherStartT,
+ finishTransaction);
}
- // Let split update internal state for dismiss.
- splitHandler.prepareDismissAnimation(topStageToKeep,
- EXIT_REASON_CHILD_TASK_ENTER_PIP, everythingElse, otherStartT,
- finishTransaction);
// We are trying to accommodate launcher's close animation which can't handle the
// divider-bar, so if split-handler is closing the divider-bar, just hide it and
@@ -152,6 +158,44 @@
return true;
}
+ /**
+ * Check to see if we're only closing split to enter pip or if we're replacing pip with
+ * another task. If we are replacing, this will return the change for the task we are replacing
+ * pip with
+ *
+ * @param info Any number of changes
+ * @param pipChange TransitionInfo.Change indicating the task that is being pipped
+ * @param splitMainStageRootId MainStage's rootTaskInfo's id
+ * @param splitSideStageRootId SideStage's rootTaskInfo's id
+ * @param lastPipSplitStage The last stage that {@param pipChange} was in
+ * @return The change from {@param info} that is replacing the {@param pipChange}, {@code null}
+ * otherwise
+ */
+ @Nullable
+ public static TransitionInfo.Change getPipReplacingChange(TransitionInfo info,
+ TransitionInfo.Change pipChange, int splitMainStageRootId, int splitSideStageRootId,
+ @SplitScreen.StageType int lastPipSplitStage) {
+ int lastPipParentTask = -1;
+ if (lastPipSplitStage == STAGE_TYPE_MAIN) {
+ lastPipParentTask = splitMainStageRootId;
+ } else if (lastPipSplitStage == STAGE_TYPE_SIDE) {
+ lastPipParentTask = splitSideStageRootId;
+ }
+
+ for (int i = info.getChanges().size() - 1; i >= 0; --i) {
+ TransitionInfo.Change change = info.getChanges().get(i);
+ if (change == pipChange || !isOpeningMode(change.getMode())) {
+ // Ignore the change/task that's going into Pip or not opening
+ continue;
+ }
+
+ if (change.getTaskInfo().parentTaskId == lastPipParentTask) {
+ return change;
+ }
+ }
+ return null;
+ }
+
private static boolean isHomeOpening(@NonNull TransitionInfo.Change change) {
return change.getTaskInfo() != null
&& change.getTaskInfo().getActivityType() == ACTIVITY_TYPE_HOME;
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/RecentsMixedTransition.java b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/RecentsMixedTransition.java
index d6e64cf..9fc6702 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/RecentsMixedTransition.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/RecentsMixedTransition.java
@@ -142,7 +142,8 @@
&& mSplitHandler.getSplitItemPosition(change.getLastParent())
!= SPLIT_POSITION_UNDEFINED) {
return animateEnterPipFromSplit(this, info, startTransaction, finishTransaction,
- finishCallback, mPlayer, mMixedHandler, mPipHandler, mSplitHandler);
+ finishCallback, mPlayer, mMixedHandler, mPipHandler, mSplitHandler,
+ /*replacingPip*/ false);
}
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/Transitions.java b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/Transitions.java
index 4d3c763..6224543 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/Transitions.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/Transitions.java
@@ -31,7 +31,6 @@
import static android.window.TransitionInfo.FLAG_BACK_GESTURE_ANIMATED;
import static android.window.TransitionInfo.FLAG_IS_BEHIND_STARTING_WINDOW;
import static android.window.TransitionInfo.FLAG_IS_OCCLUDED;
-import static android.window.TransitionInfo.FLAG_MOVED_TO_TOP;
import static android.window.TransitionInfo.FLAG_NO_ANIMATION;
import static android.window.TransitionInfo.FLAG_STARTING_WINDOW_TRANSFER_RECIPIENT;
@@ -567,15 +566,15 @@
final int mode = change.getMode();
// Put all the OPEN/SHOW on top
if (mode == TRANSIT_OPEN || mode == TRANSIT_TO_FRONT) {
- if (isOpening
- // This is for when an activity launches while a different transition is
- // collecting.
- || change.hasFlags(FLAG_MOVED_TO_TOP)) {
+ if (isOpening) {
// put on top
return zSplitLine + numChanges - i;
- } else {
+ } else if (isClosing) {
// put on bottom
return zSplitLine - i;
+ } else {
+ // maintain relative ordering (put all changes in the animating layer)
+ return zSplitLine + numChanges - i;
}
} else if (mode == TRANSIT_CLOSE || mode == TRANSIT_TO_BACK) {
if (isOpening) {
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/tracing/PerfettoTransitionTracer.java b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/tracing/PerfettoTransitionTracer.java
index 1897560..6adbe4f 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/tracing/PerfettoTransitionTracer.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/tracing/PerfettoTransitionTracer.java
@@ -49,8 +49,12 @@
public PerfettoTransitionTracer() {
Producer.init(InitArguments.DEFAULTS);
- mDataSource.register(
- new DataSourceParams(PERFETTO_DS_BUFFER_EXHAUSTED_POLICY_STALL_AND_ABORT));
+ DataSourceParams params =
+ new DataSourceParams.Builder()
+ .setBufferExhaustedPolicy(
+ PERFETTO_DS_BUFFER_EXHAUSTED_POLICY_STALL_AND_ABORT)
+ .build();
+ mDataSource.register(params);
}
/**
@@ -214,8 +218,6 @@
}
os.end(mappingsToken);
-
- ctx.flush();
});
}
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/CaptionWindowDecorViewModel.java b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/CaptionWindowDecorViewModel.java
index e85cb64..bfa163c 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/CaptionWindowDecorViewModel.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/CaptionWindowDecorViewModel.java
@@ -63,6 +63,7 @@
private final RootTaskDisplayAreaOrganizer mRootTaskDisplayAreaOrganizer;
private final SyncTransactionQueue mSyncQueue;
private final Transitions mTransitions;
+ private final ResizeHandleSizeRepository mResizeHandleSizeRepository;
private TaskOperations mTaskOperations;
private final SparseArray<CaptionWindowDecoration> mWindowDecorByTaskId = new SparseArray<>();
@@ -75,7 +76,8 @@
DisplayController displayController,
RootTaskDisplayAreaOrganizer rootTaskDisplayAreaOrganizer,
SyncTransactionQueue syncQueue,
- Transitions transitions) {
+ Transitions transitions,
+ ResizeHandleSizeRepository resizeHandleSizeRepository) {
mContext = context;
mMainHandler = mainHandler;
mMainChoreographer = mainChoreographer;
@@ -84,6 +86,7 @@
mRootTaskDisplayAreaOrganizer = rootTaskDisplayAreaOrganizer;
mSyncQueue = syncQueue;
mTransitions = transitions;
+ mResizeHandleSizeRepository = resizeHandleSizeRepository;
if (!Transitions.ENABLE_SHELL_TRANSITIONS) {
mTaskOperations = new TaskOperations(null, mContext, mSyncQueue);
}
@@ -231,7 +234,8 @@
taskSurface,
mMainHandler,
mMainChoreographer,
- mSyncQueue);
+ mSyncQueue,
+ mResizeHandleSizeRepository);
mWindowDecorByTaskId.put(taskInfo.taskId, windowDecoration);
final FluidResizeTaskPositioner taskPositioner =
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/CaptionWindowDecoration.java b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/CaptionWindowDecoration.java
index 6671391..8a49a73 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/CaptionWindowDecoration.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/CaptionWindowDecoration.java
@@ -16,11 +16,11 @@
package com.android.wm.shell.windowdecor;
-import static com.android.wm.shell.windowdecor.DragResizeWindowGeometry.getFineResizeCornerSize;
-import static com.android.wm.shell.windowdecor.DragResizeWindowGeometry.getLargeResizeCornerSize;
-import static com.android.wm.shell.windowdecor.DragResizeWindowGeometry.getResizeEdgeHandleSize;
+import static com.android.wm.shell.windowdecor.ResizeHandleSizeRepository.getFineResizeCornerPixels;
+import static com.android.wm.shell.windowdecor.ResizeHandleSizeRepository.getLargeResizeCornerPixels;
import android.annotation.NonNull;
+import android.annotation.Nullable;
import android.app.ActivityManager.RunningTaskInfo;
import android.app.WindowConfiguration;
import android.app.WindowConfiguration.WindowingMode;
@@ -45,6 +45,8 @@
import com.android.wm.shell.common.DisplayLayout;
import com.android.wm.shell.common.SyncTransactionQueue;
+import java.util.function.Consumer;
+
/**
* Defines visuals and behaviors of a window decoration of a caption bar and shadows. It works with
* {@link CaptionWindowDecorViewModel}. The caption bar contains a back button, minimize button,
@@ -58,12 +60,28 @@
private View.OnClickListener mOnCaptionButtonClickListener;
private View.OnTouchListener mOnCaptionTouchListener;
private DragPositioningCallback mDragPositioningCallback;
+ // Listener for handling drag resize events. Will be null if the task cannot be resized.
+ @Nullable
private DragResizeInputListener mDragResizeListener;
private DragDetector mDragDetector;
private RelayoutParams mRelayoutParams = new RelayoutParams();
private final RelayoutResult<WindowDecorLinearLayout> mResult =
new RelayoutResult<>();
+ private final ResizeHandleSizeRepository mResizeHandleSizeRepository;
+ private final Consumer<ResizeHandleSizeRepository> mResizeHandleSizeChangedFunction =
+ (ResizeHandleSizeRepository sizeRepository) -> {
+ if (mDragResizeListener == null) {
+ return;
+ }
+ final Resources res = mResult.mRootView.getResources();
+ mDragResizeListener.setGeometry(
+ new DragResizeWindowGeometry(0 /* taskCornerRadius */,
+ new Size(mResult.mWidth, mResult.mHeight),
+ sizeRepository.getResizeEdgeHandlePixels(res),
+ getFineResizeCornerPixels(res), getLargeResizeCornerPixels(res)),
+ mDragDetector.getTouchSlop());
+ };
CaptionWindowDecoration(
Context context,
@@ -73,13 +91,15 @@
SurfaceControl taskSurface,
Handler handler,
Choreographer choreographer,
- SyncTransactionQueue syncQueue) {
- super(context, displayController, taskOrganizer, taskInfo, taskSurface,
- taskInfo.getConfiguration());
+ SyncTransactionQueue syncQueue,
+ ResizeHandleSizeRepository resizeHandleSizeRepository) {
+ super(context, displayController, taskOrganizer, taskInfo, taskSurface);
mHandler = handler;
mChoreographer = choreographer;
mSyncQueue = syncQueue;
+ mResizeHandleSizeRepository = resizeHandleSizeRepository;
+ mResizeHandleSizeRepository.registerSizeChangeFunction(mResizeHandleSizeChangedFunction);
}
void setCaptionListeners(
@@ -238,10 +258,7 @@
.getScaledTouchSlop();
mDragDetector.setTouchSlop(touchSlop);
- final Resources res = mResult.mRootView.getResources();
- mDragResizeListener.setGeometry(new DragResizeWindowGeometry(0 /* taskCornerRadius */,
- new Size(mResult.mWidth, mResult.mHeight), getResizeEdgeHandleSize(res),
- getFineResizeCornerSize(res), getLargeResizeCornerSize(res)), touchSlop);
+ mResizeHandleSizeChangedFunction.accept(mResizeHandleSizeRepository);
}
/**
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModel.java b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModel.java
index dfdb58a..10ab13a 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModel.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModel.java
@@ -84,12 +84,12 @@
import com.android.wm.shell.common.ShellExecutor;
import com.android.wm.shell.common.SyncTransactionQueue;
import com.android.wm.shell.common.split.SplitScreenConstants.SplitPosition;
-import com.android.wm.shell.desktopmode.DesktopModeStatus;
import com.android.wm.shell.desktopmode.DesktopModeVisualIndicator;
import com.android.wm.shell.desktopmode.DesktopTasksController;
import com.android.wm.shell.desktopmode.DesktopTasksController.SnapPosition;
import com.android.wm.shell.desktopmode.DesktopWallpaperActivity;
import com.android.wm.shell.freeform.FreeformTaskTransitionStarter;
+import com.android.wm.shell.shared.DesktopModeStatus;
import com.android.wm.shell.splitscreen.SplitScreen;
import com.android.wm.shell.splitscreen.SplitScreen.StageType;
import com.android.wm.shell.splitscreen.SplitScreenController;
@@ -149,6 +149,7 @@
new DesktopModeKeyguardChangeListener();
private final RootTaskDisplayAreaOrganizer mRootTaskDisplayAreaOrganizer;
private final DisplayInsetsController mDisplayInsetsController;
+ private final ResizeHandleSizeRepository mResizeHandleSizeRepository;
private final Region mExclusionRegion = Region.obtain();
private boolean mInImmersiveMode;
@@ -181,7 +182,8 @@
SyncTransactionQueue syncQueue,
Transitions transitions,
Optional<DesktopTasksController> desktopTasksController,
- RootTaskDisplayAreaOrganizer rootTaskDisplayAreaOrganizer
+ RootTaskDisplayAreaOrganizer rootTaskDisplayAreaOrganizer,
+ ResizeHandleSizeRepository resizeHandleSizeRepository
) {
this(
context,
@@ -202,7 +204,8 @@
new InputMonitorFactory(),
SurfaceControl.Transaction::new,
rootTaskDisplayAreaOrganizer,
- new SparseArray<>());
+ new SparseArray<>(),
+ resizeHandleSizeRepository);
}
@VisibleForTesting
@@ -225,7 +228,8 @@
InputMonitorFactory inputMonitorFactory,
Supplier<SurfaceControl.Transaction> transactionFactory,
RootTaskDisplayAreaOrganizer rootTaskDisplayAreaOrganizer,
- SparseArray<DesktopModeWindowDecoration> windowDecorByTaskId) {
+ SparseArray<DesktopModeWindowDecoration> windowDecorByTaskId,
+ ResizeHandleSizeRepository resizeHandleSizeRepository) {
mContext = context;
mMainExecutor = shellExecutor;
mMainHandler = mainHandler;
@@ -246,6 +250,7 @@
mRootTaskDisplayAreaOrganizer = rootTaskDisplayAreaOrganizer;
mInputManager = mContext.getSystemService(InputManager.class);
mWindowDecorByTaskId = windowDecorByTaskId;
+ mResizeHandleSizeRepository = resizeHandleSizeRepository;
shellInit.addInitCallback(this::onInit, this);
}
@@ -1060,7 +1065,8 @@
mMainHandler,
mMainChoreographer,
mSyncQueue,
- mRootTaskDisplayAreaOrganizer);
+ mRootTaskDisplayAreaOrganizer,
+ mResizeHandleSizeRepository);
mWindowDecorByTaskId.put(taskInfo.taskId, windowDecoration);
final DragPositioningCallback dragPositioningCallback;
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecoration.java b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecoration.java
index 4c347ad..9c92791 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecoration.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecoration.java
@@ -24,11 +24,11 @@
import static android.view.MotionEvent.ACTION_UP;
import static com.android.launcher3.icons.BaseIconFactory.MODE_DEFAULT;
-import static com.android.wm.shell.windowdecor.DragResizeWindowGeometry.getFineResizeCornerSize;
-import static com.android.wm.shell.windowdecor.DragResizeWindowGeometry.getLargeResizeCornerSize;
-import static com.android.wm.shell.windowdecor.DragResizeWindowGeometry.getResizeEdgeHandleSize;
+import static com.android.wm.shell.windowdecor.ResizeHandleSizeRepository.getFineResizeCornerPixels;
+import static com.android.wm.shell.windowdecor.ResizeHandleSizeRepository.getLargeResizeCornerPixels;
import android.annotation.NonNull;
+import android.annotation.Nullable;
import android.app.ActivityManager;
import android.app.WindowConfiguration.WindowingMode;
import android.content.Context;
@@ -60,14 +60,14 @@
import com.android.internal.policy.ScreenDecorationsUtils;
import com.android.launcher3.icons.BaseIconFactory;
import com.android.launcher3.icons.IconProvider;
+import com.android.window.flags.Flags;
import com.android.wm.shell.R;
import com.android.wm.shell.RootTaskDisplayAreaOrganizer;
import com.android.wm.shell.ShellTaskOrganizer;
import com.android.wm.shell.common.DisplayController;
import com.android.wm.shell.common.DisplayLayout;
import com.android.wm.shell.common.SyncTransactionQueue;
-import com.android.wm.shell.desktopmode.DesktopModeStatus;
-import com.android.wm.shell.desktopmode.DesktopTasksController;
+import com.android.wm.shell.shared.DesktopModeStatus;
import com.android.wm.shell.windowdecor.extension.TaskInfoKt;
import com.android.wm.shell.windowdecor.viewholder.DesktopModeAppControlsWindowDecorationViewHolder;
import com.android.wm.shell.windowdecor.viewholder.DesktopModeFocusedWindowDecorationViewHolder;
@@ -75,6 +75,7 @@
import kotlin.Unit;
+import java.util.function.Function;
import java.util.function.Supplier;
/**
@@ -96,6 +97,8 @@
private View.OnLongClickListener mOnCaptionLongClickListener;
private View.OnGenericMotionListener mOnCaptionGenericMotionListener;
private DragPositioningCallback mDragPositioningCallback;
+ // Listener for handling drag resize events. Will be null if the task cannot be resized.
+ @Nullable
private DragResizeInputListener mDragResizeListener;
private DragDetector mDragDetector;
@@ -118,22 +121,35 @@
private final RootTaskDisplayAreaOrganizer mRootTaskDisplayAreaOrganizer;
+ private final ResizeHandleSizeRepository mResizeHandleSizeRepository;
+ private final Function<ResizeHandleSizeRepository, Boolean> mResizeHandleSizeChangedFunction =
+ (ResizeHandleSizeRepository sizeRepository) -> {
+ final Resources res = mResult.mRootView.getResources();
+ return mDragResizeListener == null || mDragResizeListener.setGeometry(
+ new DragResizeWindowGeometry(mRelayoutParams.mCornerRadius,
+ new Size(mResult.mWidth, mResult.mHeight),
+ sizeRepository.getResizeEdgeHandlePixels(res),
+ getFineResizeCornerPixels(res),
+ getLargeResizeCornerPixels(res)),
+ mDragDetector.getTouchSlop());
+ };
+
DesktopModeWindowDecoration(
Context context,
DisplayController displayController,
ShellTaskOrganizer taskOrganizer,
ActivityManager.RunningTaskInfo taskInfo,
SurfaceControl taskSurface,
- Configuration windowDecorConfig,
Handler handler,
Choreographer choreographer,
SyncTransactionQueue syncQueue,
- RootTaskDisplayAreaOrganizer rootTaskDisplayAreaOrganizer) {
- this (context, displayController, taskOrganizer, taskInfo, taskSurface, windowDecorConfig,
+ RootTaskDisplayAreaOrganizer rootTaskDisplayAreaOrganizer,
+ ResizeHandleSizeRepository resizeHandleSizeRepository) {
+ this (context, displayController, taskOrganizer, taskInfo, taskSurface,
handler, choreographer, syncQueue, rootTaskDisplayAreaOrganizer,
- SurfaceControl.Builder::new, SurfaceControl.Transaction::new,
- WindowContainerTransaction::new, SurfaceControl::new,
- new SurfaceControlViewHostFactory() {});
+ resizeHandleSizeRepository, SurfaceControl.Builder::new,
+ SurfaceControl.Transaction::new, WindowContainerTransaction::new,
+ SurfaceControl::new, new SurfaceControlViewHostFactory() {});
}
DesktopModeWindowDecoration(
@@ -142,17 +158,17 @@
ShellTaskOrganizer taskOrganizer,
ActivityManager.RunningTaskInfo taskInfo,
SurfaceControl taskSurface,
- Configuration windowDecorConfig,
Handler handler,
Choreographer choreographer,
SyncTransactionQueue syncQueue,
RootTaskDisplayAreaOrganizer rootTaskDisplayAreaOrganizer,
+ ResizeHandleSizeRepository resizeHandleSizeRepository,
Supplier<SurfaceControl.Builder> surfaceControlBuilderSupplier,
Supplier<SurfaceControl.Transaction> surfaceControlTransactionSupplier,
Supplier<WindowContainerTransaction> windowContainerTransactionSupplier,
Supplier<SurfaceControl> surfaceControlSupplier,
SurfaceControlViewHostFactory surfaceControlViewHostFactory) {
- super(context, displayController, taskOrganizer, taskInfo, taskSurface, windowDecorConfig,
+ super(context, displayController, taskOrganizer, taskInfo, taskSurface,
surfaceControlBuilderSupplier, surfaceControlTransactionSupplier,
windowContainerTransactionSupplier, surfaceControlSupplier,
surfaceControlViewHostFactory);
@@ -160,6 +176,9 @@
mChoreographer = choreographer;
mSyncQueue = syncQueue;
mRootTaskDisplayAreaOrganizer = rootTaskDisplayAreaOrganizer;
+ mResizeHandleSizeRepository = resizeHandleSizeRepository;
+ mResizeHandleSizeRepository.registerSizeChangeFunction(
+ mResizeHandleSizeChangedFunction::apply);
}
void setCaptionListeners(
@@ -305,11 +324,7 @@
// If either task geometry or position have changed, update this task's
// exclusion region listener
- final Resources res = mResult.mRootView.getResources();
- if (mDragResizeListener.setGeometry(
- new DragResizeWindowGeometry(mRelayoutParams.mCornerRadius,
- new Size(mResult.mWidth, mResult.mHeight), getResizeEdgeHandleSize(res),
- getFineResizeCornerSize(res), getLargeResizeCornerSize(res)), touchSlop)
+ if (mResizeHandleSizeChangedFunction.apply(mResizeHandleSizeRepository)
|| !mTaskInfo.positionInParent.equals(mPositionInParent)) {
updateExclusionRegion();
}
@@ -332,13 +347,16 @@
boolean applyStartTransactionOnDraw,
boolean shouldSetTaskPositionAndCrop) {
final int captionLayoutId = getDesktopModeWindowDecorLayoutId(taskInfo.getWindowingMode());
+ final boolean isAppHeader =
+ captionLayoutId == R.layout.desktop_mode_app_controls_window_decor;
+ final boolean isAppHandle = captionLayoutId == R.layout.desktop_mode_focused_window_decor;
relayoutParams.reset();
relayoutParams.mRunningTaskInfo = taskInfo;
relayoutParams.mLayoutResId = captionLayoutId;
relayoutParams.mCaptionHeightId = getCaptionHeightIdStatic(taskInfo.getWindowingMode());
relayoutParams.mCaptionWidthId = getCaptionWidthId(relayoutParams.mLayoutResId);
- if (captionLayoutId == R.layout.desktop_mode_app_controls_window_decor) {
+ if (isAppHeader) {
if (TaskInfoKt.isTransparentCaptionBarAppearance(taskInfo)) {
// If the app is requesting to customize the caption bar, allow input to fall
// through to the windows below so that the app can respond to input events on
@@ -359,7 +377,7 @@
controlsElement.mWidthResId = R.dimen.desktop_mode_customizable_caption_margin_end;
controlsElement.mAlignment = RelayoutParams.OccludingCaptionElement.Alignment.END;
relayoutParams.mOccludingCaptionElements.add(controlsElement);
- } else if (captionLayoutId == R.layout.desktop_mode_focused_window_decor) {
+ } else if (isAppHandle) {
// The focused decor (fullscreen/split) does not need to handle input because input in
// the App Handle is handled by the InputMonitor in DesktopModeWindowDecorViewModel.
relayoutParams.mInputFeatures
@@ -372,19 +390,25 @@
}
relayoutParams.mApplyStartTransactionOnDraw = applyStartTransactionOnDraw;
relayoutParams.mSetTaskPositionAndCrop = shouldSetTaskPositionAndCrop;
- // The configuration used to lay out the window decoration. The system context's config is
- // used when the task density has been overridden to a custom density so that the resources
- // and views of the decoration aren't affected and match the rest of the System UI, if not
- // then just use the task's configuration. A copy is made instead of using the original
- // reference so that the configuration isn't mutated on config changes and diff checks can
- // be made in WindowDecoration#relayout using the pre/post-relayout configuration.
- // See b/301119301.
+
+ // The configuration used to layout the window decoration. A copy is made instead of using
+ // the original reference so that the configuration isn't mutated on config changes and
+ // diff checks can be made in WindowDecoration#relayout using the pre/post-relayout
+ // configuration. See b/301119301.
// TODO(b/301119301): consider moving the config data needed for diffs to relayout params
// instead of using a whole Configuration as a parameter.
final Configuration windowDecorConfig = new Configuration();
- windowDecorConfig.setTo(DesktopTasksController.isDesktopDensityOverrideSet()
- ? context.getResources().getConfiguration() // Use system context.
- : taskInfo.configuration); // Use task configuration.
+ if (Flags.enableAppHeaderWithTaskDensity() && isAppHeader) {
+ // Should match the density of the task. The task may have had its density overridden
+ // to be different that SysUI's.
+ windowDecorConfig.setTo(taskInfo.configuration);
+ } else if (DesktopModeStatus.isDesktopDensityOverrideSet()) {
+ // The task has had its density overridden, but keep using the system's density to
+ // layout the header.
+ windowDecorConfig.setTo(context.getResources().getConfiguration());
+ } else {
+ windowDecorConfig.setTo(taskInfo.configuration);
+ }
relayoutParams.mWindowDecorConfig = windowDecorConfig;
if (DesktopModeStatus.useRoundedCorners()) {
@@ -936,22 +960,19 @@
Handler handler,
Choreographer choreographer,
SyncTransactionQueue syncQueue,
- RootTaskDisplayAreaOrganizer rootTaskDisplayAreaOrganizer) {
- final Configuration windowDecorConfig =
- DesktopTasksController.isDesktopDensityOverrideSet()
- ? context.getResources().getConfiguration() // Use system context
- : taskInfo.configuration; // Use task configuration
+ RootTaskDisplayAreaOrganizer rootTaskDisplayAreaOrganizer,
+ ResizeHandleSizeRepository resizeHandleSizeRepository) {
return new DesktopModeWindowDecoration(
context,
displayController,
taskOrganizer,
taskInfo,
taskSurface,
- windowDecorConfig,
handler,
choreographer,
syncQueue,
- rootTaskDisplayAreaOrganizer);
+ rootTaskDisplayAreaOrganizer,
+ resizeHandleSizeRepository);
}
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DragDetector.java b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DragDetector.java
index da26898..a3b0a71 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DragDetector.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DragDetector.java
@@ -119,6 +119,10 @@
mTouchSlop = touchSlop;
}
+ int getTouchSlop() {
+ return mTouchSlop;
+ }
+
private void resetState() {
mIsDragEvent = false;
mInputDownPoint.set(0, 0);
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DragResizeWindowGeometry.java b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DragResizeWindowGeometry.java
index 4f513f0..80d60d4 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DragResizeWindowGeometry.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DragResizeWindowGeometry.java
@@ -26,7 +26,6 @@
import static com.android.wm.shell.windowdecor.DragPositioningCallback.CTRL_TYPE_UNDEFINED;
import android.annotation.NonNull;
-import android.content.res.Resources;
import android.graphics.Point;
import android.graphics.Rect;
import android.graphics.Region;
@@ -36,8 +35,6 @@
import androidx.annotation.Nullable;
import androidx.annotation.VisibleForTesting;
-import com.android.wm.shell.R;
-
import java.util.Objects;
/**
@@ -63,6 +60,10 @@
// Extra-large edge bounds for logging to help debug when an edge resize is ignored.
private final @Nullable TaskEdges mDebugTaskEdges;
+ /**
+ * Constructs an instance representing the drag resize touch input regions, where all sizes
+ * are represented in pixels.
+ */
DragResizeWindowGeometry(int taskCornerRadius, @NonNull Size taskSize,
int resizeHandleThickness, int fineCornerSize, int largeCornerSize) {
mTaskCornerRadius = taskCornerRadius;
@@ -82,31 +83,6 @@
}
/**
- * Returns the resource value to use for the resize handle on the edge of the window.
- */
- static int getResizeEdgeHandleSize(@NonNull Resources res) {
- return enableWindowingEdgeDragResize()
- ? res.getDimensionPixelSize(R.dimen.desktop_mode_edge_handle)
- : res.getDimensionPixelSize(R.dimen.freeform_resize_handle);
- }
-
- /**
- * Returns the resource value to use for course input, such as touch, that benefits from a large
- * square on each of the window's corners.
- */
- static int getLargeResizeCornerSize(@NonNull Resources res) {
- return res.getDimensionPixelSize(R.dimen.desktop_mode_corner_resize_large);
- }
-
- /**
- * Returns the resource value to use for fine input, such as stylus, that can use a smaller
- * square on each of the window's corners.
- */
- static int getFineResizeCornerSize(@NonNull Resources res) {
- return res.getDimensionPixelSize(R.dimen.freeform_resize_corner);
- }
-
- /**
* Returns the size of the task this geometry is calculated for.
*/
@NonNull Size getTaskSize() {
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/ResizeHandleSizeRepository.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/ResizeHandleSizeRepository.kt
new file mode 100644
index 0000000..be7a301
--- /dev/null
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/ResizeHandleSizeRepository.kt
@@ -0,0 +1,133 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.wm.shell.windowdecor
+
+import android.content.res.Resources
+import com.android.window.flags.Flags.enableWindowingEdgeDragResize
+import com.android.wm.shell.R
+import com.android.wm.shell.protolog.ShellProtoLogGroup.WM_SHELL_DESKTOP_MODE
+import com.android.wm.shell.util.KtProtoLog
+import java.util.function.Consumer
+
+/** Repository for desktop mode drag resize handle sizes. */
+class ResizeHandleSizeRepository {
+ private val TAG = "ResizeHandleSizeRepository"
+ private var edgeResizeHandleSizePixels: Int? = null
+ private var sizeChangeFunctions: MutableList<Consumer<ResizeHandleSizeRepository>> =
+ mutableListOf()
+
+ /**
+ * Resets the window edge resize handle size if necessary.
+ */
+ fun resetResizeEdgeHandlePixels() {
+ if (enableWindowingEdgeDragResize() && edgeResizeHandleSizePixels != null) {
+ edgeResizeHandleSizePixels = null
+ applyToAll()
+ }
+ }
+
+ /**
+ * Sets the window edge resize handle to the given size in pixels.
+ */
+ fun setResizeEdgeHandlePixels(sizePixels: Int) {
+ if (enableWindowingEdgeDragResize()) {
+ KtProtoLog.d(WM_SHELL_DESKTOP_MODE, "$TAG: Set edge handle size to $sizePixels")
+ if (edgeResizeHandleSizePixels != null && edgeResizeHandleSizePixels == sizePixels) {
+ // Skip updating since override is the same size
+ return
+ }
+ edgeResizeHandleSizePixels = sizePixels
+ applyToAll()
+ } else {
+ KtProtoLog.d(
+ WM_SHELL_DESKTOP_MODE,
+ "$TAG: Can't set edge handle size to $sizePixels since " +
+ "enable_windowing_edge_drag_resize disabled"
+ )
+ }
+ }
+
+ /**
+ * Returns the resource value, in pixels, to use for the resize handle on the edge of the
+ * window.
+ */
+ fun getResizeEdgeHandlePixels(res: Resources): Int {
+ try {
+ return if (enableWindowingEdgeDragResize()) {
+ val resPixelSize = res.getDimensionPixelSize(R.dimen.desktop_mode_edge_handle)
+ val size = edgeResizeHandleSizePixels ?: resPixelSize
+ KtProtoLog.d(
+ WM_SHELL_DESKTOP_MODE,
+ "$TAG: Get edge handle size of $size from (vs base value $resPixelSize)"
+ )
+ size
+ } else {
+ KtProtoLog.d(
+ WM_SHELL_DESKTOP_MODE,
+ "$TAG: Get edge handle size from freeform since flag is disabled"
+ )
+ res.getDimensionPixelSize(R.dimen.freeform_resize_handle)
+ }
+ } catch (e: Resources.NotFoundException) {
+ KtProtoLog.e(WM_SHELL_DESKTOP_MODE, "$TAG: Unable to get edge handle size", e)
+ return 0
+ }
+ }
+
+ /** Register function to run when the resize handle size changes. */
+ fun registerSizeChangeFunction(function: Consumer<ResizeHandleSizeRepository>) {
+ sizeChangeFunctions.add(function)
+ }
+
+ private fun applyToAll() {
+ for (f in sizeChangeFunctions) {
+ f.accept(this)
+ }
+ }
+
+ companion object {
+ private val TAG = "ResizeHandleSizeRepositoryCompanion"
+
+ /**
+ * Returns the resource value in pixels to use for course input, such as touch, that
+ * benefits from a large square on each of the window's corners.
+ */
+ @JvmStatic
+ fun getLargeResizeCornerPixels(res: Resources): Int {
+ return try {
+ res.getDimensionPixelSize(R.dimen.desktop_mode_corner_resize_large)
+ } catch (e: Resources.NotFoundException) {
+ KtProtoLog.e(WM_SHELL_DESKTOP_MODE, "$TAG: Unable to get large corner size", e)
+ 0
+ }
+ }
+
+ /**
+ * Returns the resource value, in pixels, to use for fine input, such as stylus, that can
+ * use a smaller square on each of the window's corners.
+ */
+ @JvmStatic
+ fun getFineResizeCornerPixels(res: Resources): Int {
+ return try {
+ res.getDimensionPixelSize(R.dimen.freeform_resize_corner)
+ } catch (e: Resources.NotFoundException) {
+ KtProtoLog.e(WM_SHELL_DESKTOP_MODE, "$TAG: Unable to get fine corner size", e)
+ 0
+ }
+ }
+ }
+}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/WindowDecoration.java b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/WindowDecoration.java
index de6c035..2cbe472 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/WindowDecoration.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/WindowDecoration.java
@@ -18,6 +18,7 @@
import static android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM;
import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
+import static android.content.res.Configuration.DENSITY_DPI_UNDEFINED;
import static android.view.WindowInsets.Type.captionBar;
import static android.view.WindowInsets.Type.mandatorySystemGestures;
import static android.view.WindowInsets.Type.statusBars;
@@ -52,7 +53,7 @@
import com.android.wm.shell.ShellTaskOrganizer;
import com.android.wm.shell.common.DisplayController;
-import com.android.wm.shell.desktopmode.DesktopModeStatus;
+import com.android.wm.shell.shared.DesktopModeStatus;
import com.android.wm.shell.windowdecor.WindowDecoration.RelayoutParams.OccludingCaptionElement;
import java.util.ArrayList;
@@ -145,9 +146,8 @@
DisplayController displayController,
ShellTaskOrganizer taskOrganizer,
RunningTaskInfo taskInfo,
- SurfaceControl taskSurface,
- Configuration windowDecorConfig) {
- this(context, displayController, taskOrganizer, taskInfo, taskSurface, windowDecorConfig,
+ SurfaceControl taskSurface) {
+ this(context, displayController, taskOrganizer, taskInfo, taskSurface,
SurfaceControl.Builder::new, SurfaceControl.Transaction::new,
WindowContainerTransaction::new, SurfaceControl::new,
new SurfaceControlViewHostFactory() {});
@@ -159,7 +159,6 @@
ShellTaskOrganizer taskOrganizer,
RunningTaskInfo taskInfo,
@NonNull SurfaceControl taskSurface,
- Configuration windowDecorConfig,
Supplier<SurfaceControl.Builder> surfaceControlBuilderSupplier,
Supplier<SurfaceControl.Transaction> surfaceControlTransactionSupplier,
Supplier<WindowContainerTransaction> windowContainerTransactionSupplier,
@@ -176,8 +175,6 @@
mSurfaceControlViewHostFactory = surfaceControlViewHostFactory;
mDisplay = mDisplayController.getDisplay(mTaskInfo.displayId);
- mWindowDecorConfig = windowDecorConfig;
- mDecorWindowContext = mContext.createConfigurationContext(mWindowDecorConfig);
}
/**
@@ -220,8 +217,11 @@
outResult.mRootView = rootView;
rootView = null; // Clear it just in case we use it accidentally
- final int oldDensityDpi = mWindowDecorConfig.densityDpi;
- final int oldNightMode = mWindowDecorConfig.uiMode & Configuration.UI_MODE_NIGHT_MASK;
+ final int oldDensityDpi = mWindowDecorConfig != null
+ ? mWindowDecorConfig.densityDpi : DENSITY_DPI_UNDEFINED;
+ final int oldNightMode = mWindowDecorConfig != null
+ ? (mWindowDecorConfig.uiMode & Configuration.UI_MODE_NIGHT_MASK)
+ : Configuration.UI_MODE_NIGHT_UNDEFINED;
mWindowDecorConfig = params.mWindowDecorConfig != null ? params.mWindowDecorConfig
: mTaskInfo.getConfiguration();
final int newDensityDpi = mWindowDecorConfig.densityDpi;
@@ -230,7 +230,8 @@
|| mDisplay == null
|| mDisplay.getDisplayId() != mTaskInfo.displayId
|| oldLayoutResId != mLayoutResId
- || oldNightMode != newNightMode) {
+ || oldNightMode != newNightMode
+ || mDecorWindowContext == null) {
releaseViews(wct);
if (!obtainDisplayOrRegisterListener()) {
diff --git a/libs/WindowManager/Shell/tests/flicker/appcompat/AndroidTestTemplate.xml b/libs/WindowManager/Shell/tests/flicker/appcompat/AndroidTestTemplate.xml
index 4dd14f4..f69a90c 100644
--- a/libs/WindowManager/Shell/tests/flicker/appcompat/AndroidTestTemplate.xml
+++ b/libs/WindowManager/Shell/tests/flicker/appcompat/AndroidTestTemplate.xml
@@ -91,6 +91,7 @@
value="trace_config.textproto"
/>
<option name="instrumentation-arg" key="per_run" value="true"/>
+ <option name="instrumentation-arg" key="perfetto_persist_pid_track" value="true"/>
</test>
<!-- Needed for pulling the collected trace config on to the host -->
<metrics_collector class="com.android.tradefed.device.metric.FilePullerLogCollector">
diff --git a/libs/WindowManager/Shell/tests/flicker/bubble/AndroidTestTemplate.xml b/libs/WindowManager/Shell/tests/flicker/bubble/AndroidTestTemplate.xml
index 5c86a38..b76d065 100644
--- a/libs/WindowManager/Shell/tests/flicker/bubble/AndroidTestTemplate.xml
+++ b/libs/WindowManager/Shell/tests/flicker/bubble/AndroidTestTemplate.xml
@@ -91,6 +91,7 @@
value="trace_config.textproto"
/>
<option name="instrumentation-arg" key="per_run" value="true"/>
+ <option name="instrumentation-arg" key="perfetto_persist_pid_track" value="true"/>
</test>
<!-- Needed for pulling the collected trace config on to the host -->
<metrics_collector class="com.android.tradefed.device.metric.FilePullerLogCollector">
diff --git a/libs/WindowManager/Shell/tests/flicker/bubble/src/com/android/wm/shell/flicker/bubble/ChangeActiveActivityFromBubbleTest.kt b/libs/WindowManager/Shell/tests/flicker/bubble/src/com/android/wm/shell/flicker/bubble/ChangeActiveActivityFromBubbleTest.kt
index bc486c2..984abf8 100644
--- a/libs/WindowManager/Shell/tests/flicker/bubble/src/com/android/wm/shell/flicker/bubble/ChangeActiveActivityFromBubbleTest.kt
+++ b/libs/WindowManager/Shell/tests/flicker/bubble/src/com/android/wm/shell/flicker/bubble/ChangeActiveActivityFromBubbleTest.kt
@@ -32,7 +32,7 @@
/**
* Test launching a new activity from bubble.
*
- * To run this test: `atest WMShellFlickerTests:MultiBubblesScreen`
+ * To run this test: `atest WMShellFlickerTestsBubbles:ChangeActiveActivityFromBubbleTest`
*
* Actions:
* ```
diff --git a/libs/WindowManager/Shell/tests/flicker/bubble/src/com/android/wm/shell/flicker/bubble/DragToDismissBubbleScreenTest.kt b/libs/WindowManager/Shell/tests/flicker/bubble/src/com/android/wm/shell/flicker/bubble/DragToDismissBubbleScreenTest.kt
index 2a9b107..886b70c 100644
--- a/libs/WindowManager/Shell/tests/flicker/bubble/src/com/android/wm/shell/flicker/bubble/DragToDismissBubbleScreenTest.kt
+++ b/libs/WindowManager/Shell/tests/flicker/bubble/src/com/android/wm/shell/flicker/bubble/DragToDismissBubbleScreenTest.kt
@@ -35,7 +35,7 @@
/**
* Test launching a new activity from bubble.
*
- * To run this test: `atest WMShellFlickerTests:DismissBubbleScreen`
+ * To run this test: `atest WMShellFlickerTestsBubbles:DragToDismissBubbleScreenTest`
*
* Actions:
* ```
diff --git a/libs/WindowManager/Shell/tests/flicker/bubble/src/com/android/wm/shell/flicker/bubble/OpenActivityFromBubbleOnLocksreenTest.kt b/libs/WindowManager/Shell/tests/flicker/bubble/src/com/android/wm/shell/flicker/bubble/OpenActivityFromBubbleOnLocksreenTest.kt
index 9ef49c1..2ee53f4 100644
--- a/libs/WindowManager/Shell/tests/flicker/bubble/src/com/android/wm/shell/flicker/bubble/OpenActivityFromBubbleOnLocksreenTest.kt
+++ b/libs/WindowManager/Shell/tests/flicker/bubble/src/com/android/wm/shell/flicker/bubble/OpenActivityFromBubbleOnLocksreenTest.kt
@@ -38,7 +38,7 @@
/**
* Test launching a new activity from bubble.
*
- * To run this test: `atest WMShellFlickerTests:OpenActivityFromBubbleOnLocksreenTest`
+ * To run this test: `atest WMShellFlickerTestsBubbles:OpenActivityFromBubbleOnLocksreenTest`
*
* Actions:
* ```
diff --git a/libs/WindowManager/Shell/tests/flicker/bubble/src/com/android/wm/shell/flicker/bubble/OpenActivityFromBubbleTest.kt b/libs/WindowManager/Shell/tests/flicker/bubble/src/com/android/wm/shell/flicker/bubble/OpenActivityFromBubbleTest.kt
index ef7fbfb..463fe0e 100644
--- a/libs/WindowManager/Shell/tests/flicker/bubble/src/com/android/wm/shell/flicker/bubble/OpenActivityFromBubbleTest.kt
+++ b/libs/WindowManager/Shell/tests/flicker/bubble/src/com/android/wm/shell/flicker/bubble/OpenActivityFromBubbleTest.kt
@@ -29,7 +29,7 @@
/**
* Test launching a new activity from bubble.
*
- * To run this test: `atest WMShellFlickerTests:ExpandBubbleScreen`
+ * To run this test: `atest WMShellFlickerTestsBubbles:OpenActivityFromBubbleTest`
*
* Actions:
* ```
diff --git a/libs/WindowManager/Shell/tests/flicker/bubble/src/com/android/wm/shell/flicker/bubble/SendBubbleNotificationTest.kt b/libs/WindowManager/Shell/tests/flicker/bubble/src/com/android/wm/shell/flicker/bubble/SendBubbleNotificationTest.kt
index 87224b15..8df5056 100644
--- a/libs/WindowManager/Shell/tests/flicker/bubble/src/com/android/wm/shell/flicker/bubble/SendBubbleNotificationTest.kt
+++ b/libs/WindowManager/Shell/tests/flicker/bubble/src/com/android/wm/shell/flicker/bubble/SendBubbleNotificationTest.kt
@@ -29,7 +29,7 @@
/**
* Test creating a bubble notification
*
- * To run this test: `atest WMShellFlickerTests:LaunchBubbleScreen`
+ * To run this test: `atest WMShellFlickerTestsBubbles:SendBubbleNotificationTest`
*
* Actions:
* ```
diff --git a/libs/WindowManager/Shell/tests/flicker/pip/AndroidTestTemplate.xml b/libs/WindowManager/Shell/tests/flicker/pip/AndroidTestTemplate.xml
index aa70c09..041978c 100644
--- a/libs/WindowManager/Shell/tests/flicker/pip/AndroidTestTemplate.xml
+++ b/libs/WindowManager/Shell/tests/flicker/pip/AndroidTestTemplate.xml
@@ -91,6 +91,7 @@
value="trace_config.textproto"
/>
<option name="instrumentation-arg" key="per_run" value="true"/>
+ <option name="instrumentation-arg" key="perfetto_persist_pid_track" value="true"/>
</test>
<!-- Needed for pulling the collected trace config on to the host -->
<metrics_collector class="com.android.tradefed.device.metric.FilePullerLogCollector">
diff --git a/libs/WindowManager/Shell/tests/flicker/service/AndroidTestTemplate.xml b/libs/WindowManager/Shell/tests/flicker/service/AndroidTestTemplate.xml
index c7c804f..a66dfb4 100644
--- a/libs/WindowManager/Shell/tests/flicker/service/AndroidTestTemplate.xml
+++ b/libs/WindowManager/Shell/tests/flicker/service/AndroidTestTemplate.xml
@@ -91,6 +91,7 @@
value="trace_config.textproto"
/>
<option name="instrumentation-arg" key="per_run" value="true"/>
+ <option name="instrumentation-arg" key="perfetto_persist_pid_track" value="true"/>
</test>
<!-- Needed for pulling the collected trace config on to the host -->
<metrics_collector class="com.android.tradefed.device.metric.FilePullerLogCollector">
diff --git a/libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/desktopmode/flicker/DesktopModeFlickerScenarios.kt b/libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/desktopmode/flicker/DesktopModeFlickerScenarios.kt
index 17cace0..d485b82 100644
--- a/libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/desktopmode/flicker/DesktopModeFlickerScenarios.kt
+++ b/libs/WindowManager/Shell/tests/flicker/service/src/com/android/wm/shell/flicker/service/desktopmode/flicker/DesktopModeFlickerScenarios.kt
@@ -21,6 +21,7 @@
import android.tools.flicker.assertors.assertions.AppLayerIsVisibleAlways
import android.tools.flicker.assertors.assertions.AppLayerIsVisibleAtStart
import android.tools.flicker.assertors.assertions.AppWindowHasDesktopModeInitialBoundsAtTheEnd
+import android.tools.flicker.assertors.assertions.AppWindowIsVisibleAlways
import android.tools.flicker.assertors.assertions.AppWindowOnTopAtEnd
import android.tools.flicker.assertors.assertions.AppWindowOnTopAtStart
import android.tools.flicker.assertors.assertions.AppWindowRemainInsideDisplayBounds
@@ -133,9 +134,8 @@
}
),
assertions =
- AssertionTemplates.COMMON_ASSERTIONS +
listOf(
- AppLayerIsVisibleAlways(Components.DESKTOP_MODE_APP),
+ AppWindowIsVisibleAlways(Components.DESKTOP_MODE_APP),
AppWindowOnTopAtEnd(Components.DESKTOP_MODE_APP),
AppWindowRemainInsideDisplayBounds(Components.DESKTOP_MODE_APP),
).associateBy({ it }, { AssertionInvocationGroup.BLOCKING }),
diff --git a/libs/WindowManager/Shell/tests/flicker/splitscreen/AndroidTestTemplate.xml b/libs/WindowManager/Shell/tests/flicker/splitscreen/AndroidTestTemplate.xml
index 214bdfa..85715db 100644
--- a/libs/WindowManager/Shell/tests/flicker/splitscreen/AndroidTestTemplate.xml
+++ b/libs/WindowManager/Shell/tests/flicker/splitscreen/AndroidTestTemplate.xml
@@ -91,6 +91,7 @@
value="trace_config.textproto"
/>
<option name="instrumentation-arg" key="per_run" value="true"/>
+ <option name="instrumentation-arg" key="perfetto_persist_pid_track" value="true"/>
</test>
<!-- Needed for pulling the collected trace config on to the host -->
<metrics_collector class="com.android.tradefed.device.metric.FilePullerLogCollector">
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/back/BackAnimationControllerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/back/BackAnimationControllerTest.java
index f99b4b2..f6f3aa4 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/back/BackAnimationControllerTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/back/BackAnimationControllerTest.java
@@ -120,7 +120,7 @@
private TestableContentResolver mContentResolver;
private TestableLooper mTestableLooper;
- private CrossActivityBackAnimation mCrossActivityBackAnimation;
+ private DefaultCrossActivityBackAnimation mDefaultCrossActivityBackAnimation;
private CrossTaskBackAnimation mCrossTaskBackAnimation;
private ShellBackAnimationRegistry mShellBackAnimationRegistry;
@@ -135,13 +135,14 @@
ANIMATION_ENABLED);
mTestableLooper = TestableLooper.get(this);
mShellInit = spy(new ShellInit(mShellExecutor));
- mCrossActivityBackAnimation = new CrossActivityBackAnimation(mContext, mAnimationBackground,
- mRootTaskDisplayAreaOrganizer);
+ mDefaultCrossActivityBackAnimation = new DefaultCrossActivityBackAnimation(mContext,
+ mAnimationBackground, mRootTaskDisplayAreaOrganizer);
mCrossTaskBackAnimation = new CrossTaskBackAnimation(mContext, mAnimationBackground);
mShellBackAnimationRegistry =
- new ShellBackAnimationRegistry(mCrossActivityBackAnimation, mCrossTaskBackAnimation,
- /* dialogCloseAnimation= */ null,
- new CustomizeActivityAnimation(mContext, mAnimationBackground),
+ new ShellBackAnimationRegistry(mDefaultCrossActivityBackAnimation,
+ mCrossTaskBackAnimation, /* dialogCloseAnimation= */ null,
+ new CustomCrossActivityBackAnimation(mContext, mAnimationBackground,
+ mRootTaskDisplayAreaOrganizer),
/* defaultBackToHomeAnimation= */ null);
mController =
new BackAnimationController(
@@ -582,7 +583,7 @@
@Test
public void testBackToActivity() throws RemoteException {
verifySystemBackBehavior(BackNavigationInfo.TYPE_CROSS_ACTIVITY,
- mCrossActivityBackAnimation.getRunner());
+ mDefaultCrossActivityBackAnimation.getRunner());
}
@Test
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/back/CustomCrossActivityBackAnimationTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/back/CustomCrossActivityBackAnimationTest.kt
new file mode 100644
index 0000000..8bf0111
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/back/CustomCrossActivityBackAnimationTest.kt
@@ -0,0 +1,264 @@
+/*
+ * 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.f
+ */
+package com.android.wm.shell.back
+
+import android.app.ActivityManager
+import android.app.ActivityManager.RunningTaskInfo
+import android.app.AppCompatTaskInfo
+import android.app.WindowConfiguration
+import android.graphics.Color
+import android.graphics.Point
+import android.graphics.Rect
+import android.os.RemoteException
+import android.testing.AndroidTestingRunner
+import android.testing.TestableLooper
+import android.view.Choreographer
+import android.view.RemoteAnimationTarget
+import android.view.SurfaceControl
+import android.view.SurfaceControl.Transaction
+import android.view.animation.Animation
+import android.window.BackEvent
+import android.window.BackMotionEvent
+import android.window.BackNavigationInfo
+import androidx.test.filters.SmallTest
+import com.android.internal.policy.TransitionAnimation
+import com.android.wm.shell.RootTaskDisplayAreaOrganizer
+import com.android.wm.shell.ShellTestCase
+import java.util.concurrent.CountDownLatch
+import java.util.concurrent.TimeUnit
+import junit.framework.TestCase.assertEquals
+import org.junit.Assert
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.ArgumentMatchers.any
+import org.mockito.ArgumentMatchers.anyBoolean
+import org.mockito.ArgumentMatchers.anyFloat
+import org.mockito.ArgumentMatchers.anyInt
+import org.mockito.ArgumentMatchers.eq
+import org.mockito.Mock
+import org.mockito.Mockito.mock
+import org.mockito.Mockito.never
+import org.mockito.Mockito.times
+import org.mockito.kotlin.spy
+import org.mockito.kotlin.verify
+import org.mockito.kotlin.whenever
+
+@SmallTest
+@TestableLooper.RunWithLooper
+@RunWith(AndroidTestingRunner::class)
+class CustomCrossActivityBackAnimationTest : ShellTestCase() {
+ @Mock private lateinit var backAnimationBackground: BackAnimationBackground
+ @Mock private lateinit var mockCloseAnimation: Animation
+ @Mock private lateinit var mockOpenAnimation: Animation
+ @Mock private lateinit var rootTaskDisplayAreaOrganizer: RootTaskDisplayAreaOrganizer
+ @Mock private lateinit var transitionAnimation: TransitionAnimation
+ @Mock private lateinit var appCompatTaskInfo: AppCompatTaskInfo
+ @Mock private lateinit var transaction: Transaction
+
+ private lateinit var customCrossActivityBackAnimation: CustomCrossActivityBackAnimation
+ private lateinit var customAnimationLoader: CustomAnimationLoader
+
+ @Before
+ @Throws(Exception::class)
+ fun setUp() {
+ customAnimationLoader = CustomAnimationLoader(transitionAnimation)
+ customCrossActivityBackAnimation =
+ CustomCrossActivityBackAnimation(
+ context,
+ backAnimationBackground,
+ rootTaskDisplayAreaOrganizer,
+ transaction,
+ mock(Choreographer::class.java),
+ customAnimationLoader
+ )
+
+ whenever(transitionAnimation.loadAppTransitionAnimation(eq(PACKAGE_NAME), eq(OPEN_RES_ID)))
+ .thenReturn(mockOpenAnimation)
+ whenever(transitionAnimation.loadAppTransitionAnimation(eq(PACKAGE_NAME), eq(CLOSE_RES_ID)))
+ .thenReturn(mockCloseAnimation)
+ whenever(transaction.setColor(any(), any())).thenReturn(transaction)
+ whenever(transaction.setAlpha(any(), anyFloat())).thenReturn(transaction)
+ whenever(transaction.setCrop(any(), any())).thenReturn(transaction)
+ whenever(transaction.setRelativeLayer(any(), any(), anyInt())).thenReturn(transaction)
+ spy(customCrossActivityBackAnimation)
+ }
+
+ @Test
+ @Throws(InterruptedException::class)
+ fun receiveFinishAfterInvoke() {
+ val finishCalled = startCustomAnimation()
+ try {
+ customCrossActivityBackAnimation.getRunner().callback.onBackInvoked()
+ } catch (r: RemoteException) {
+ Assert.fail("onBackInvoked throw remote exception")
+ }
+ finishCalled.await(1, TimeUnit.SECONDS)
+ }
+
+ @Test
+ @Throws(InterruptedException::class)
+ fun receiveFinishAfterCancel() {
+ val finishCalled = startCustomAnimation()
+ try {
+ customCrossActivityBackAnimation.getRunner().callback.onBackCancelled()
+ } catch (r: RemoteException) {
+ Assert.fail("onBackCancelled throw remote exception")
+ }
+ finishCalled.await(1, TimeUnit.SECONDS)
+ }
+
+ @Test
+ @Throws(InterruptedException::class)
+ fun receiveFinishWithoutAnimationAfterInvoke() {
+ val finishCalled = startCustomAnimation(targets = arrayOf())
+ try {
+ customCrossActivityBackAnimation.getRunner().callback.onBackInvoked()
+ } catch (r: RemoteException) {
+ Assert.fail("onBackInvoked throw remote exception")
+ }
+ finishCalled.await(1, TimeUnit.SECONDS)
+ }
+
+ @Test
+ fun testLoadCustomAnimation() {
+ testLoadCustomAnimation(OPEN_RES_ID, CLOSE_RES_ID, 0)
+ }
+
+ @Test
+ fun testLoadCustomAnimationNoEnter() {
+ testLoadCustomAnimation(0, CLOSE_RES_ID, 0)
+ }
+
+ @Test
+ fun testLoadWindowAnimations() {
+ testLoadCustomAnimation(0, 0, 30)
+ }
+
+ @Test
+ fun testCustomAnimationHigherThanWindowAnimations() {
+ testLoadCustomAnimation(OPEN_RES_ID, CLOSE_RES_ID, 30)
+ }
+
+ private fun testLoadCustomAnimation(enterResId: Int, exitResId: Int, windowAnimations: Int) {
+ val builder =
+ BackNavigationInfo.Builder()
+ .setCustomAnimation(PACKAGE_NAME, enterResId, exitResId, Color.GREEN)
+ .setWindowAnimations(PACKAGE_NAME, windowAnimations)
+ val info = builder.build().customAnimationInfo!!
+ whenever(
+ transitionAnimation.loadAnimationAttr(
+ eq(PACKAGE_NAME),
+ eq(windowAnimations),
+ anyInt(),
+ anyBoolean()
+ )
+ )
+ .thenReturn(mockCloseAnimation)
+ whenever(transitionAnimation.loadDefaultAnimationAttr(anyInt(), anyBoolean()))
+ .thenReturn(mockOpenAnimation)
+ val result = customAnimationLoader.loadAll(info)!!
+ if (exitResId != 0) {
+ if (enterResId == 0) {
+ verify(transitionAnimation, never())
+ .loadAppTransitionAnimation(eq(PACKAGE_NAME), eq(enterResId))
+ verify(transitionAnimation).loadDefaultAnimationAttr(anyInt(), anyBoolean())
+ } else {
+ assertEquals(result.enterAnimation, mockOpenAnimation)
+ }
+ assertEquals(result.backgroundColor.toLong(), Color.GREEN.toLong())
+ assertEquals(result.closeAnimation, mockCloseAnimation)
+ verify(transitionAnimation, never())
+ .loadAnimationAttr(eq(PACKAGE_NAME), anyInt(), anyInt(), anyBoolean())
+ } else if (windowAnimations != 0) {
+ verify(transitionAnimation, times(2))
+ .loadAnimationAttr(eq(PACKAGE_NAME), anyInt(), anyInt(), anyBoolean())
+ Assert.assertEquals(result.closeAnimation, mockCloseAnimation)
+ }
+ }
+
+ private fun startCustomAnimation(
+ targets: Array<RemoteAnimationTarget> =
+ arrayOf(createAnimationTarget(false), createAnimationTarget(true))
+ ): CountDownLatch {
+ val backNavigationInfo =
+ BackNavigationInfo.Builder()
+ .setCustomAnimation(PACKAGE_NAME, OPEN_RES_ID, CLOSE_RES_ID, /*backgroundColor*/ 0)
+ .build()
+ customCrossActivityBackAnimation.prepareNextAnimation(
+ backNavigationInfo.customAnimationInfo,
+ 0
+ )
+ val finishCalled = CountDownLatch(1)
+ val finishCallback = Runnable { finishCalled.countDown() }
+ customCrossActivityBackAnimation
+ .getRunner()
+ .startAnimation(targets, null, null, finishCallback)
+ customCrossActivityBackAnimation.runner.callback.onBackStarted(backMotionEventFrom(0f, 0f))
+ if (targets.isNotEmpty()) {
+ verify(mockCloseAnimation)
+ .initialize(eq(BOUND_SIZE), eq(BOUND_SIZE), eq(BOUND_SIZE), eq(BOUND_SIZE))
+ verify(mockOpenAnimation)
+ .initialize(eq(BOUND_SIZE), eq(BOUND_SIZE), eq(BOUND_SIZE), eq(BOUND_SIZE))
+ }
+ return finishCalled
+ }
+
+ private fun backMotionEventFrom(touchX: Float, progress: Float) =
+ BackMotionEvent(
+ /* touchX = */ touchX,
+ /* touchY = */ 0f,
+ /* progress = */ progress,
+ /* velocityX = */ 0f,
+ /* velocityY = */ 0f,
+ /* triggerBack = */ false,
+ /* swipeEdge = */ BackEvent.EDGE_LEFT,
+ /* departingAnimationTarget = */ null
+ )
+
+ private fun createAnimationTarget(open: Boolean): RemoteAnimationTarget {
+ val topWindowLeash = SurfaceControl()
+ val taskInfo = RunningTaskInfo()
+ taskInfo.appCompatTaskInfo = appCompatTaskInfo
+ taskInfo.taskDescription = ActivityManager.TaskDescription()
+ return RemoteAnimationTarget(
+ 1,
+ if (open) RemoteAnimationTarget.MODE_OPENING else RemoteAnimationTarget.MODE_CLOSING,
+ topWindowLeash,
+ false,
+ Rect(),
+ Rect(),
+ -1,
+ Point(0, 0),
+ Rect(0, 0, BOUND_SIZE, BOUND_SIZE),
+ Rect(),
+ WindowConfiguration(),
+ true,
+ null,
+ null,
+ taskInfo,
+ false,
+ -1
+ )
+ }
+
+ companion object {
+ private const val BOUND_SIZE = 100
+ private const val OPEN_RES_ID = 1000
+ private const val CLOSE_RES_ID = 1001
+ private const val PACKAGE_NAME = "TestPackage"
+ }
+}
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/back/CustomizeActivityAnimationTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/back/CustomizeActivityAnimationTest.java
deleted file mode 100644
index 158d640..0000000
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/back/CustomizeActivityAnimationTest.java
+++ /dev/null
@@ -1,237 +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.wm.shell.back;
-
-import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn;
-import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn;
-import static com.android.dx.mockito.inline.extended.ExtendedMockito.times;
-
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.fail;
-import static org.mockito.ArgumentMatchers.any;
-import static org.mockito.ArgumentMatchers.anyBoolean;
-import static org.mockito.ArgumentMatchers.anyInt;
-import static org.mockito.ArgumentMatchers.eq;
-import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.never;
-import static org.mockito.Mockito.verify;
-
-import android.app.WindowConfiguration;
-import android.graphics.Color;
-import android.graphics.Point;
-import android.graphics.Rect;
-import android.os.RemoteException;
-import android.testing.AndroidTestingRunner;
-import android.testing.TestableLooper;
-import android.view.Choreographer;
-import android.view.RemoteAnimationTarget;
-import android.view.SurfaceControl;
-import android.view.animation.Animation;
-import android.window.BackNavigationInfo;
-
-import androidx.test.filters.SmallTest;
-
-import com.android.wm.shell.ShellTestCase;
-
-import org.junit.Before;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.mockito.Mock;
-
-import java.util.concurrent.CountDownLatch;
-import java.util.concurrent.TimeUnit;
-
-@SmallTest
-@TestableLooper.RunWithLooper
-@RunWith(AndroidTestingRunner.class)
-public class CustomizeActivityAnimationTest extends ShellTestCase {
- private static final int BOUND_SIZE = 100;
- @Mock
- private BackAnimationBackground mBackAnimationBackground;
- @Mock
- private Animation mMockCloseAnimation;
- @Mock
- private Animation mMockOpenAnimation;
-
- private CustomizeActivityAnimation mCustomizeActivityAnimation;
-
- @Before
- public void setUp() throws Exception {
- mCustomizeActivityAnimation = new CustomizeActivityAnimation(mContext,
- mBackAnimationBackground, mock(SurfaceControl.Transaction.class),
- mock(Choreographer.class));
- spyOn(mCustomizeActivityAnimation);
- spyOn(mCustomizeActivityAnimation.mCustomAnimationLoader.mTransitionAnimation);
- }
-
- RemoteAnimationTarget createAnimationTarget(boolean open) {
- SurfaceControl topWindowLeash = new SurfaceControl();
- return new RemoteAnimationTarget(1,
- open ? RemoteAnimationTarget.MODE_OPENING : RemoteAnimationTarget.MODE_CLOSING,
- topWindowLeash, false, new Rect(), new Rect(), -1,
- new Point(0, 0), new Rect(0, 0, BOUND_SIZE, BOUND_SIZE), new Rect(),
- new WindowConfiguration(), true, null, null, null, false, -1);
- }
-
- @Test
- public void receiveFinishAfterInvoke() throws InterruptedException {
- spyOn(mCustomizeActivityAnimation.mCustomAnimationLoader);
- doReturn(mMockCloseAnimation).when(mCustomizeActivityAnimation.mCustomAnimationLoader)
- .loadAnimation(any(), eq(false));
- doReturn(mMockOpenAnimation).when(mCustomizeActivityAnimation.mCustomAnimationLoader)
- .loadAnimation(any(), eq(true));
-
- mCustomizeActivityAnimation.prepareNextAnimation(
- new BackNavigationInfo.CustomAnimationInfo("TestPackage"), 0);
- final RemoteAnimationTarget close = createAnimationTarget(false);
- final RemoteAnimationTarget open = createAnimationTarget(true);
- // start animation with remote animation targets
- final CountDownLatch finishCalled = new CountDownLatch(1);
- final Runnable finishCallback = finishCalled::countDown;
- mCustomizeActivityAnimation
- .getRunner()
- .startAnimation(
- new RemoteAnimationTarget[] {close, open}, null, null, finishCallback);
- verify(mMockCloseAnimation).initialize(eq(BOUND_SIZE), eq(BOUND_SIZE),
- eq(BOUND_SIZE), eq(BOUND_SIZE));
- verify(mMockOpenAnimation).initialize(eq(BOUND_SIZE), eq(BOUND_SIZE),
- eq(BOUND_SIZE), eq(BOUND_SIZE));
-
- try {
- mCustomizeActivityAnimation.getRunner().getCallback().onBackInvoked();
- } catch (RemoteException r) {
- fail("onBackInvoked throw remote exception");
- }
- verify(mCustomizeActivityAnimation).onGestureCommitted();
- finishCalled.await(1, TimeUnit.SECONDS);
- }
-
- @Test
- public void receiveFinishAfterCancel() throws InterruptedException {
- spyOn(mCustomizeActivityAnimation.mCustomAnimationLoader);
- doReturn(mMockCloseAnimation).when(mCustomizeActivityAnimation.mCustomAnimationLoader)
- .loadAnimation(any(), eq(false));
- doReturn(mMockOpenAnimation).when(mCustomizeActivityAnimation.mCustomAnimationLoader)
- .loadAnimation(any(), eq(true));
-
- mCustomizeActivityAnimation.prepareNextAnimation(
- new BackNavigationInfo.CustomAnimationInfo("TestPackage"), 0);
- final RemoteAnimationTarget close = createAnimationTarget(false);
- final RemoteAnimationTarget open = createAnimationTarget(true);
- // start animation with remote animation targets
- final CountDownLatch finishCalled = new CountDownLatch(1);
- final Runnable finishCallback = finishCalled::countDown;
- mCustomizeActivityAnimation
- .getRunner()
- .startAnimation(
- new RemoteAnimationTarget[] {close, open}, null, null, finishCallback);
- verify(mMockCloseAnimation).initialize(eq(BOUND_SIZE), eq(BOUND_SIZE),
- eq(BOUND_SIZE), eq(BOUND_SIZE));
- verify(mMockOpenAnimation).initialize(eq(BOUND_SIZE), eq(BOUND_SIZE),
- eq(BOUND_SIZE), eq(BOUND_SIZE));
-
- try {
- mCustomizeActivityAnimation.getRunner().getCallback().onBackCancelled();
- } catch (RemoteException r) {
- fail("onBackCancelled throw remote exception");
- }
- finishCalled.await(1, TimeUnit.SECONDS);
- }
-
- @Test
- public void receiveFinishWithoutAnimationAfterInvoke() throws InterruptedException {
- mCustomizeActivityAnimation.prepareNextAnimation(
- new BackNavigationInfo.CustomAnimationInfo("TestPackage"), 0);
- // start animation without any remote animation targets
- final CountDownLatch finishCalled = new CountDownLatch(1);
- final Runnable finishCallback = finishCalled::countDown;
- mCustomizeActivityAnimation
- .getRunner()
- .startAnimation(new RemoteAnimationTarget[] {}, null, null, finishCallback);
-
- try {
- mCustomizeActivityAnimation.getRunner().getCallback().onBackInvoked();
- } catch (RemoteException r) {
- fail("onBackInvoked throw remote exception");
- }
- verify(mCustomizeActivityAnimation).onGestureCommitted();
- finishCalled.await(1, TimeUnit.SECONDS);
- }
-
- @Test
- public void testLoadCustomAnimation() {
- testLoadCustomAnimation(10, 20, 0);
- }
-
- @Test
- public void testLoadCustomAnimationNoEnter() {
- testLoadCustomAnimation(0, 10, 0);
- }
-
- @Test
- public void testLoadWindowAnimations() {
- testLoadCustomAnimation(0, 0, 30);
- }
-
- @Test
- public void testCustomAnimationHigherThanWindowAnimations() {
- testLoadCustomAnimation(10, 20, 30);
- }
-
- private void testLoadCustomAnimation(int enterResId, int exitResId, int windowAnimations) {
- final String testPackage = "TestPackage";
- BackNavigationInfo.Builder builder = new BackNavigationInfo.Builder()
- .setCustomAnimation(testPackage, enterResId, exitResId, Color.GREEN)
- .setWindowAnimations(testPackage, windowAnimations);
- final BackNavigationInfo.CustomAnimationInfo info = builder.build()
- .getCustomAnimationInfo();
-
- doReturn(mMockOpenAnimation).when(mCustomizeActivityAnimation.mCustomAnimationLoader
- .mTransitionAnimation)
- .loadAppTransitionAnimation(eq(testPackage), eq(enterResId));
- doReturn(mMockCloseAnimation).when(mCustomizeActivityAnimation.mCustomAnimationLoader
- .mTransitionAnimation)
- .loadAppTransitionAnimation(eq(testPackage), eq(exitResId));
- doReturn(mMockCloseAnimation).when(mCustomizeActivityAnimation.mCustomAnimationLoader
- .mTransitionAnimation)
- .loadAnimationAttr(eq(testPackage), eq(windowAnimations), anyInt(), anyBoolean());
- doReturn(mMockOpenAnimation).when(mCustomizeActivityAnimation.mCustomAnimationLoader
- .mTransitionAnimation).loadDefaultAnimationAttr(anyInt(), anyBoolean());
-
- CustomizeActivityAnimation.AnimationLoadResult result =
- mCustomizeActivityAnimation.mCustomAnimationLoader.loadAll(info);
-
- if (exitResId != 0) {
- if (enterResId == 0) {
- verify(mCustomizeActivityAnimation.mCustomAnimationLoader.mTransitionAnimation,
- never()).loadAppTransitionAnimation(eq(testPackage), eq(enterResId));
- verify(mCustomizeActivityAnimation.mCustomAnimationLoader.mTransitionAnimation)
- .loadDefaultAnimationAttr(anyInt(), anyBoolean());
- } else {
- assertEquals(result.mEnterAnimation, mMockOpenAnimation);
- }
- assertEquals(result.mBackgroundColor, Color.GREEN);
- assertEquals(result.mCloseAnimation, mMockCloseAnimation);
- verify(mCustomizeActivityAnimation.mCustomAnimationLoader.mTransitionAnimation, never())
- .loadAnimationAttr(eq(testPackage), anyInt(), anyInt(), anyBoolean());
- } else if (windowAnimations != 0) {
- verify(mCustomizeActivityAnimation.mCustomAnimationLoader.mTransitionAnimation,
- times(2)).loadAnimationAttr(eq(testPackage), anyInt(), anyInt(), anyBoolean());
- assertEquals(result.mCloseAnimation, mMockCloseAnimation);
- }
- }
-}
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/split/SplitLayoutTests.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/split/SplitLayoutTests.java
index 8de60b7..cfe8e07 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/split/SplitLayoutTests.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/split/SplitLayoutTests.java
@@ -26,6 +26,7 @@
import static com.google.common.truth.Truth.assertThat;
import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyBoolean;
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.never;
@@ -115,9 +116,9 @@
@Test
public void testUpdateDivideBounds() {
- mSplitLayout.updateDividerBounds(anyInt());
+ mSplitLayout.updateDividerBounds(anyInt(), anyBoolean());
verify(mSplitLayoutHandler).onLayoutSizeChanging(any(SplitLayout.class), anyInt(),
- anyInt());
+ anyInt(), anyBoolean());
}
@Test
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/compatui/CompatUIControllerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/compatui/CompatUIControllerTest.java
index afae653..9c00864 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/compatui/CompatUIControllerTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/compatui/CompatUIControllerTest.java
@@ -668,6 +668,18 @@
Assert.assertTrue(mController.hasShownUserAspectRatioSettingsButton());
}
+ @Test
+ public void testLetterboxEduLayout_notCreatedWhenLetterboxEducationIsDisabled() {
+ TaskInfo taskInfo = createTaskInfo(DISPLAY_ID, TASK_ID, /* hasSizeCompat= */ true,
+ CAMERA_COMPAT_CONTROL_HIDDEN);
+ taskInfo.appCompatTaskInfo.isLetterboxEducationEnabled = false;
+
+ mController.onCompatInfoChanged(taskInfo, mMockTaskListener);
+
+ verify(mController, never()).createLetterboxEduWindowManager(any(), eq(taskInfo),
+ eq(mMockTaskListener));
+ }
+
private static TaskInfo createTaskInfo(int displayId, int taskId, boolean hasSizeCompat,
@CameraCompatControlState int cameraCompatControlState) {
return createTaskInfo(displayId, taskId, hasSizeCompat, cameraCompatControlState,
@@ -694,6 +706,8 @@
taskInfo.isVisible = isVisible;
taskInfo.isFocused = isFocused;
taskInfo.isTopActivityTransparent = isTopActivityTransparent;
+ taskInfo.appCompatTaskInfo.isLetterboxEducationEnabled = true;
+ taskInfo.appCompatTaskInfo.topActivityBoundsLetterboxed = true;
return taskInfo;
}
}
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopModeLoggerTransitionObserverTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopModeLoggerTransitionObserverTest.kt
index 60a7dcd..2a2483d 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopModeLoggerTransitionObserverTest.kt
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopModeLoggerTransitionObserverTest.kt
@@ -41,6 +41,7 @@
import com.android.wm.shell.common.ShellExecutor
import com.android.wm.shell.desktopmode.DesktopModeEventLogger.Companion.EnterReason
import com.android.wm.shell.desktopmode.DesktopModeEventLogger.Companion.ExitReason
+import com.android.wm.shell.shared.DesktopModeStatus
import com.android.wm.shell.sysui.ShellInit
import com.android.wm.shell.transition.TransitionInfoBuilder
import com.android.wm.shell.transition.Transitions
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopModeTaskRepositoryTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopModeTaskRepositoryTest.kt
index dca7be1..8f59f30 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopModeTaskRepositoryTest.kt
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopModeTaskRepositoryTest.kt
@@ -182,18 +182,6 @@
}
@Test
- fun addListener_notifiesStashed() {
- repo.setStashed(DEFAULT_DISPLAY, true)
- val listener = TestVisibilityListener()
- val executor = TestShellExecutor()
- repo.addVisibleTasksListener(listener, executor)
- executor.flushAll()
-
- assertThat(listener.stashedOnDefaultDisplay).isTrue()
- assertThat(listener.stashedChangesOnDefaultDisplay).isEqualTo(1)
- }
-
- @Test
fun addListener_tasksOnDifferentDisplay_doesNotNotify() {
repo.updateVisibleFreeformTasks(SECOND_DISPLAY, taskId = 1, visible = true)
val listener = TestVisibilityListener()
@@ -400,65 +388,6 @@
}
@Test
- fun setStashed_stateIsUpdatedForTheDisplay() {
- repo.setStashed(DEFAULT_DISPLAY, true)
- assertThat(repo.isStashed(DEFAULT_DISPLAY)).isTrue()
- assertThat(repo.isStashed(SECOND_DISPLAY)).isFalse()
-
- repo.setStashed(DEFAULT_DISPLAY, false)
- assertThat(repo.isStashed(DEFAULT_DISPLAY)).isFalse()
- }
-
- @Test
- fun setStashed_notifyListener() {
- val listener = TestVisibilityListener()
- val executor = TestShellExecutor()
- repo.addVisibleTasksListener(listener, executor)
- repo.setStashed(DEFAULT_DISPLAY, true)
- executor.flushAll()
- assertThat(listener.stashedOnDefaultDisplay).isTrue()
- assertThat(listener.stashedChangesOnDefaultDisplay).isEqualTo(1)
-
- repo.setStashed(DEFAULT_DISPLAY, false)
- executor.flushAll()
- assertThat(listener.stashedOnDefaultDisplay).isFalse()
- assertThat(listener.stashedChangesOnDefaultDisplay).isEqualTo(2)
- }
-
- @Test
- fun setStashed_secondCallDoesNotNotify() {
- val listener = TestVisibilityListener()
- val executor = TestShellExecutor()
- repo.addVisibleTasksListener(listener, executor)
- repo.setStashed(DEFAULT_DISPLAY, true)
- repo.setStashed(DEFAULT_DISPLAY, true)
- executor.flushAll()
- assertThat(listener.stashedChangesOnDefaultDisplay).isEqualTo(1)
- }
-
- @Test
- fun setStashed_tracksPerDisplay() {
- val listener = TestVisibilityListener()
- val executor = TestShellExecutor()
- repo.addVisibleTasksListener(listener, executor)
-
- repo.setStashed(DEFAULT_DISPLAY, true)
- executor.flushAll()
- assertThat(listener.stashedOnDefaultDisplay).isTrue()
- assertThat(listener.stashedOnSecondaryDisplay).isFalse()
-
- repo.setStashed(SECOND_DISPLAY, true)
- executor.flushAll()
- assertThat(listener.stashedOnDefaultDisplay).isTrue()
- assertThat(listener.stashedOnSecondaryDisplay).isTrue()
-
- repo.setStashed(DEFAULT_DISPLAY, false)
- executor.flushAll()
- assertThat(listener.stashedOnDefaultDisplay).isFalse()
- assertThat(listener.stashedOnSecondaryDisplay).isTrue()
- }
-
- @Test
fun removeFreeformTask_removesTaskBoundsBeforeMaximize() {
val taskId = 1
repo.saveBoundsBeforeMaximize(taskId, Rect(0, 0, 200, 200))
@@ -598,12 +527,6 @@
var visibleChangesOnDefaultDisplay = 0
var visibleChangesOnSecondaryDisplay = 0
- var stashedOnDefaultDisplay = false
- var stashedOnSecondaryDisplay = false
-
- var stashedChangesOnDefaultDisplay = 0
- var stashedChangesOnSecondaryDisplay = 0
-
override fun onTasksVisibilityChanged(displayId: Int, visibleTasksCount: Int) {
when (displayId) {
DEFAULT_DISPLAY -> {
@@ -617,20 +540,6 @@
else -> fail("Visible task listener received unexpected display id: $displayId")
}
}
-
- override fun onStashedChanged(displayId: Int, stashed: Boolean) {
- when (displayId) {
- DEFAULT_DISPLAY -> {
- stashedOnDefaultDisplay = stashed
- stashedChangesOnDefaultDisplay++
- }
- SECOND_DISPLAY -> {
- stashedOnSecondaryDisplay = stashed
- stashedChangesOnDefaultDisplay++
- }
- else -> fail("Visible task listener received unexpected display id: $displayId")
- }
- }
}
companion object {
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopModeUiEventLoggerTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopModeUiEventLoggerTest.kt
new file mode 100644
index 0000000..285e5b6
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopModeUiEventLoggerTest.kt
@@ -0,0 +1,111 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.wm.shell.desktopmode
+
+
+import android.testing.AndroidTestingRunner
+import androidx.test.filters.SmallTest
+import com.android.internal.logging.InstanceId
+import com.android.internal.logging.InstanceIdSequence
+import com.android.internal.logging.testing.UiEventLoggerFake
+import com.android.wm.shell.ShellTestCase
+import com.android.wm.shell.desktopmode.DesktopModeUiEventLogger.Companion.DesktopUiEventEnum.DESKTOP_WINDOW_EDGE_DRAG_RESIZE
+import com.google.common.truth.Truth.assertThat
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+
+/**
+ * Test class for [DesktopModeUiEventLogger]
+ *
+ * Usage: atest WMShellUnitTests:DesktopModeUiEventLoggerTest
+ */
+@SmallTest
+@RunWith(AndroidTestingRunner::class)
+class DesktopModeUiEventLoggerTest : ShellTestCase() {
+ private lateinit var uiEventLoggerFake: UiEventLoggerFake
+ private lateinit var logger: DesktopModeUiEventLogger
+ private val instanceIdSequence = InstanceIdSequence(10)
+
+
+ @Before
+ fun setUp() {
+ uiEventLoggerFake = UiEventLoggerFake()
+ logger = DesktopModeUiEventLogger(uiEventLoggerFake, instanceIdSequence)
+ }
+
+ @Test
+ fun log_invalidUid_eventNotLogged() {
+ logger.log(-1, PACKAGE_NAME, DESKTOP_WINDOW_EDGE_DRAG_RESIZE)
+ assertThat(uiEventLoggerFake.numLogs()).isEqualTo(0)
+ }
+
+ @Test
+ fun log_emptyPackageName_eventNotLogged() {
+ logger.log(UID, "", DESKTOP_WINDOW_EDGE_DRAG_RESIZE)
+ assertThat(uiEventLoggerFake.numLogs()).isEqualTo(0)
+ }
+
+ @Test
+ fun log_eventLogged() {
+ val event =
+ DESKTOP_WINDOW_EDGE_DRAG_RESIZE
+ logger.log(UID, PACKAGE_NAME, event)
+ assertThat(uiEventLoggerFake.numLogs()).isEqualTo(1)
+ assertThat(uiEventLoggerFake.eventId(0)).isEqualTo(event.id)
+ assertThat(uiEventLoggerFake[0].instanceId).isNull()
+ assertThat(uiEventLoggerFake[0].uid).isEqualTo(UID)
+ assertThat(uiEventLoggerFake[0].packageName).isEqualTo(PACKAGE_NAME)
+ }
+
+ @Test
+ fun getNewInstanceId() {
+ val first = logger.getNewInstanceId()
+ assertThat(first).isNotEqualTo(logger.getNewInstanceId())
+ }
+
+ @Test
+ fun logWithInstanceId_invalidUid_eventNotLogged() {
+ logger.logWithInstanceId(INSTANCE_ID, -1, PACKAGE_NAME, DESKTOP_WINDOW_EDGE_DRAG_RESIZE)
+ assertThat(uiEventLoggerFake.numLogs()).isEqualTo(0)
+ }
+
+ @Test
+ fun logWithInstanceId_emptyPackageName_eventNotLogged() {
+ logger.logWithInstanceId(INSTANCE_ID, UID, "", DESKTOP_WINDOW_EDGE_DRAG_RESIZE)
+ assertThat(uiEventLoggerFake.numLogs()).isEqualTo(0)
+ }
+
+ @Test
+ fun logWithInstanceId_eventLogged() {
+ val event =
+ DESKTOP_WINDOW_EDGE_DRAG_RESIZE
+ logger.logWithInstanceId(INSTANCE_ID, UID, PACKAGE_NAME, event)
+ assertThat(uiEventLoggerFake.numLogs()).isEqualTo(1)
+ assertThat(uiEventLoggerFake.eventId(0)).isEqualTo(event.id)
+ assertThat(uiEventLoggerFake[0].instanceId).isEqualTo(INSTANCE_ID)
+ assertThat(uiEventLoggerFake[0].uid).isEqualTo(UID)
+ assertThat(uiEventLoggerFake[0].packageName).isEqualTo(PACKAGE_NAME)
+ }
+
+
+ companion object {
+ private val INSTANCE_ID = InstanceId.fakeInstanceId(0)
+ private const val UID = 10
+ private const val PACKAGE_NAME = "com.foo"
+ }
+}
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopTasksControllerTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopTasksControllerTest.kt
index 3f76c4f..d8d534b 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopTasksControllerTest.kt
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopTasksControllerTest.kt
@@ -25,6 +25,7 @@
import android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED
import android.content.Intent
import android.content.pm.ActivityInfo
+import android.content.pm.ActivityInfo.CONFIG_DENSITY
import android.content.pm.ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE
import android.content.pm.ActivityInfo.SCREEN_ORIENTATION_PORTRAIT
import android.content.pm.ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED
@@ -79,6 +80,7 @@
import com.android.wm.shell.draganddrop.DragAndDropController
import com.android.wm.shell.recents.RecentsTransitionHandler
import com.android.wm.shell.recents.RecentsTransitionStateListener
+import com.android.wm.shell.shared.DesktopModeStatus
import com.android.wm.shell.splitscreen.SplitScreenController
import com.android.wm.shell.sysui.ShellCommandHandler
import com.android.wm.shell.sysui.ShellController
@@ -114,6 +116,8 @@
import org.mockito.kotlin.capture
import org.mockito.quality.Strictness
import java.util.Optional
+import junit.framework.Assert.assertFalse
+import junit.framework.Assert.assertTrue
import org.mockito.Mockito.`when` as whenever
/**
@@ -1044,40 +1048,6 @@
}
@Test
- @DisableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_WALLPAPER_ACTIVITY)
- fun handleRequest_fullscreenTask_desktopStashed_returnWCTWithAllAppsBroughtToFront() {
- assumeTrue(ENABLE_SHELL_TRANSITIONS)
- whenever(DesktopModeStatus.isStashingEnabled()).thenReturn(true)
-
- val stashedFreeformTask = setUpFreeformTask(DEFAULT_DISPLAY)
- markTaskHidden(stashedFreeformTask)
-
- val fullscreenTask = createFullscreenTask(DEFAULT_DISPLAY)
-
- controller.stashDesktopApps(DEFAULT_DISPLAY)
-
- val result = controller.handleRequest(Binder(), createTransition(fullscreenTask))
- assertThat(result).isNotNull()
- result!!.assertReorderSequence(stashedFreeformTask, fullscreenTask)
- assertThat(result.changes[fullscreenTask.token.asBinder()]?.windowingMode)
- .isEqualTo(WINDOWING_MODE_FREEFORM)
-
- // Stashed state should be cleared
- assertThat(desktopModeTaskRepository.isStashed(DEFAULT_DISPLAY)).isFalse()
- }
-
- @Test
- fun handleRequest_freeformTask_freeformVisible_returnNull() {
- assumeTrue(ENABLE_SHELL_TRANSITIONS)
-
- val freeformTask1 = setUpFreeformTask()
- markTaskVisible(freeformTask1)
-
- val freeformTask2 = createFreeformTask()
- assertThat(controller.handleRequest(Binder(), createTransition(freeformTask2))).isNull()
- }
-
- @Test
fun handleRequest_freeformTask_freeformVisible_aboveTaskLimit_minimize() {
assumeTrue(ENABLE_SHELL_TRANSITIONS)
@@ -1094,7 +1064,7 @@
}
@Test
- fun handleRequest_freeformTask_freeformNotVisible_returnSwitchToFullscreenWCT() {
+ fun handleRequest_freeformTask_freeformNotVisible_reorderedToTop() {
assumeTrue(ENABLE_SHELL_TRANSITIONS)
val freeformTask1 = setUpFreeformTask()
@@ -1106,51 +1076,60 @@
Binder(),
createTransition(freeformTask2, type = TRANSIT_TO_FRONT)
)
- assertThat(result?.changes?.get(freeformTask2.token.asBinder())?.windowingMode)
- .isEqualTo(WINDOWING_MODE_UNDEFINED) // inherited FULLSCREEN
+
+ assertThat(result?.hierarchyOps?.size).isEqualTo(2)
+ result!!.assertReorderAt(1, freeformTask2, toTop = true)
}
@Test
- fun handleRequest_freeformTask_noOtherTasks_returnSwitchToFullscreenWCT() {
+ fun handleRequest_freeformTask_noOtherTasks_reorderedToTop() {
assumeTrue(ENABLE_SHELL_TRANSITIONS)
val task = createFreeformTask()
val result = controller.handleRequest(Binder(), createTransition(task))
- assertThat(result?.changes?.get(task.token.asBinder())?.windowingMode)
- .isEqualTo(WINDOWING_MODE_UNDEFINED) // inherited FULLSCREEN
+
+ assertThat(result?.hierarchyOps?.size).isEqualTo(1)
+ result!!.assertReorderAt(0, task, toTop = true)
}
@Test
- fun handleRequest_freeformTask_freeformOnOtherDisplayOnly_returnSwitchToFullscreenWCT() {
+ fun handleRequest_freeformTask_freeformOnOtherDisplayOnly_reorderedToTop() {
assumeTrue(ENABLE_SHELL_TRANSITIONS)
val taskDefaultDisplay = createFreeformTask(displayId = DEFAULT_DISPLAY)
- createFreeformTask(displayId = SECOND_DISPLAY)
+ val taskSecondDisplay = createFreeformTask(displayId = SECOND_DISPLAY)
val result = controller.handleRequest(Binder(), createTransition(taskDefaultDisplay))
- assertThat(result?.changes?.get(taskDefaultDisplay.token.asBinder())?.windowingMode)
- .isEqualTo(WINDOWING_MODE_UNDEFINED) // inherited FULLSCREEN
+ assertThat(result?.hierarchyOps?.size).isEqualTo(1)
+ result!!.assertReorderAt(0, taskDefaultDisplay, toTop = true)
}
@Test
- @DisableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_WALLPAPER_ACTIVITY)
- fun handleRequest_freeformTask_desktopStashed_returnWCTWithAllAppsBroughtToFront() {
+ fun handleRequest_freeformTask_alreadyInDesktop_noOverrideDensity_noConfigDensityChange() {
assumeTrue(ENABLE_SHELL_TRANSITIONS)
- whenever(DesktopModeStatus.isStashingEnabled()).thenReturn(true)
+ whenever(DesktopModeStatus.isDesktopDensityOverrideSet()).thenReturn(false)
- val stashedFreeformTask = setUpFreeformTask(DEFAULT_DISPLAY)
- markTaskHidden(stashedFreeformTask)
+ val freeformTask1 = setUpFreeformTask()
+ markTaskVisible(freeformTask1)
- val freeformTask = createFreeformTask(DEFAULT_DISPLAY)
+ val freeformTask2 = createFreeformTask()
+ val result = controller.handleRequest(freeformTask2.token.asBinder(),
+ createTransition(freeformTask2))
+ assertFalse(result.anyDensityConfigChange(freeformTask2.token))
+ }
- controller.stashDesktopApps(DEFAULT_DISPLAY)
+ @Test
+ fun handleRequest_freeformTask_alreadyInDesktop_overrideDensity_hasConfigDensityChange() {
+ assumeTrue(ENABLE_SHELL_TRANSITIONS)
+ whenever(DesktopModeStatus.isDesktopDensityOverrideSet()).thenReturn(true)
- val result = controller.handleRequest(Binder(), createTransition(freeformTask))
- assertThat(result).isNotNull()
- result?.assertReorderSequence(stashedFreeformTask, freeformTask)
+ val freeformTask1 = setUpFreeformTask()
+ markTaskVisible(freeformTask1)
- // Stashed state should be cleared
- assertThat(desktopModeTaskRepository.isStashed(DEFAULT_DISPLAY)).isFalse()
+ val freeformTask2 = createFreeformTask()
+ val result = controller.handleRequest(freeformTask2.token.asBinder(),
+ createTransition(freeformTask2))
+ assertTrue(result.anyDensityConfigChange(freeformTask2.token))
}
@Test
@@ -1269,29 +1248,6 @@
}
@Test
- fun stashDesktopApps_stateUpdates() {
- whenever(DesktopModeStatus.isStashingEnabled()).thenReturn(true)
-
- controller.stashDesktopApps(DEFAULT_DISPLAY)
-
- assertThat(desktopModeTaskRepository.isStashed(DEFAULT_DISPLAY)).isTrue()
- assertThat(desktopModeTaskRepository.isStashed(SECOND_DISPLAY)).isFalse()
- }
-
- @Test
- fun hideStashedDesktopApps_stateUpdates() {
- whenever(DesktopModeStatus.isStashingEnabled()).thenReturn(true)
-
- desktopModeTaskRepository.setStashed(DEFAULT_DISPLAY, true)
- desktopModeTaskRepository.setStashed(SECOND_DISPLAY, true)
- controller.hideStashedDesktopApps(DEFAULT_DISPLAY)
-
- assertThat(desktopModeTaskRepository.isStashed(DEFAULT_DISPLAY)).isFalse()
- // Check that second display is not affected
- assertThat(desktopModeTaskRepository.isStashed(SECOND_DISPLAY)).isTrue()
- }
-
- @Test
fun desktopTasksVisibilityChange_visible_setLaunchAdjacentDisabled() {
val task = setUpFreeformTask()
clearInvocations(launchAdjacentController)
@@ -1877,3 +1833,11 @@
assertThat(op.type).isEqualTo(HIERARCHY_OP_TYPE_PENDING_INTENT)
assertThat(op.pendingIntent?.intent?.component).isEqualTo(intent.component)
}
+
+private fun WindowContainerTransaction?.anyDensityConfigChange(
+ token: WindowContainerToken
+): Boolean {
+ return this?.changes?.any { change ->
+ change.key == token.asBinder() && ((change.value.configSetMask and CONFIG_DENSITY) != 0)
+ } ?: false
+}
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopTasksLimiterTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopTasksLimiterTest.kt
index 539d5b8..3c488ca 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopTasksLimiterTest.kt
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopTasksLimiterTest.kt
@@ -32,6 +32,7 @@
import com.android.wm.shell.ShellTaskOrganizer
import com.android.wm.shell.ShellTestCase
import com.android.wm.shell.desktopmode.DesktopTestHelpers.Companion.createFreeformTask
+import com.android.wm.shell.shared.DesktopModeStatus
import com.android.wm.shell.transition.TransitionInfoBuilder
import com.android.wm.shell.transition.Transitions
import com.android.wm.shell.util.StubTransaction
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/freeform/FreeformTaskListenerTests.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/freeform/FreeformTaskListenerTests.java
index 665077b..cd68c69 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/freeform/FreeformTaskListenerTests.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/freeform/FreeformTaskListenerTests.java
@@ -35,8 +35,8 @@
import com.android.wm.shell.ShellTaskOrganizer;
import com.android.wm.shell.ShellTestCase;
import com.android.wm.shell.TestRunningTaskInfoBuilder;
-import com.android.wm.shell.desktopmode.DesktopModeStatus;
import com.android.wm.shell.desktopmode.DesktopModeTaskRepository;
+import com.android.wm.shell.shared.DesktopModeStatus;
import com.android.wm.shell.sysui.ShellInit;
import com.android.wm.shell.windowdecor.WindowDecorViewModel;
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/recents/RecentTasksControllerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/recents/RecentTasksControllerTest.java
index 240324b..884cb6e 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/recents/RecentTasksControllerTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/recents/RecentTasksControllerTest.java
@@ -67,8 +67,8 @@
import com.android.wm.shell.TestShellExecutor;
import com.android.wm.shell.common.DisplayInsetsController;
import com.android.wm.shell.common.TaskStackListenerImpl;
-import com.android.wm.shell.desktopmode.DesktopModeStatus;
import com.android.wm.shell.desktopmode.DesktopModeTaskRepository;
+import com.android.wm.shell.shared.DesktopModeStatus;
import com.android.wm.shell.sysui.ShellCommandHandler;
import com.android.wm.shell.sysui.ShellController;
import com.android.wm.shell.sysui.ShellInit;
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/StageCoordinatorTests.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/StageCoordinatorTests.java
index d7c3835..d18fec2 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/StageCoordinatorTests.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/StageCoordinatorTests.java
@@ -16,10 +16,7 @@
package com.android.wm.shell.splitscreen;
-import static android.app.ActivityOptions.KEY_LAUNCH_ROOT_TASK_TOKEN;
import static android.app.ActivityTaskManager.INVALID_TASK_ID;
-import static android.app.ComponentOptions.KEY_PENDING_INTENT_BACKGROUND_ACTIVITY_ALLOWED;
-import static android.app.ComponentOptions.KEY_PENDING_INTENT_BACKGROUND_ACTIVITY_ALLOWED_BY_PERMISSION;
import static android.view.Display.DEFAULT_DISPLAY;
import static com.android.wm.shell.common.split.SplitScreenConstants.SPLIT_POSITION_BOTTOM_OR_RIGHT;
@@ -31,8 +28,9 @@
import static com.android.wm.shell.splitscreen.SplitScreenController.EXIT_REASON_RETURN_HOME;
import static com.android.wm.shell.transition.Transitions.TRANSIT_SPLIT_DISMISS;
+import static com.google.common.truth.Truth.assertThat;
+
import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertTrue;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.ArgumentMatchers.notNull;
@@ -45,6 +43,7 @@
import static org.mockito.Mockito.when;
import android.app.ActivityManager;
+import android.app.ActivityOptions;
import android.app.PendingIntent;
import android.content.res.Configuration;
import android.graphics.Rect;
@@ -54,7 +53,6 @@
import android.view.SurfaceControl;
import android.view.SurfaceSession;
import android.window.RemoteTransition;
-import android.window.WindowContainerToken;
import android.window.WindowContainerTransaction;
import androidx.test.annotation.UiThreadTest;
@@ -343,14 +341,14 @@
@Test
public void testAddActivityOptions_addsBackgroundActivitiesFlags() {
- Bundle options = mStageCoordinator.resolveStartStage(STAGE_TYPE_MAIN,
+ Bundle bundle = mStageCoordinator.resolveStartStage(STAGE_TYPE_MAIN,
SPLIT_POSITION_UNDEFINED, null /* options */, null /* wct */);
+ ActivityOptions options = ActivityOptions.fromBundle(bundle);
- assertEquals(options.getParcelable(KEY_LAUNCH_ROOT_TASK_TOKEN, WindowContainerToken.class),
- mMainStage.mRootTaskInfo.token);
- assertTrue(options.getBoolean(KEY_PENDING_INTENT_BACKGROUND_ACTIVITY_ALLOWED));
- assertTrue(options.getBoolean(
- KEY_PENDING_INTENT_BACKGROUND_ACTIVITY_ALLOWED_BY_PERMISSION));
+ assertThat(options.getLaunchRootTask()).isEqualTo(mMainStage.mRootTaskInfo.token);
+ assertThat(options.getPendingIntentBackgroundActivityStartMode())
+ .isEqualTo(ActivityOptions.MODE_BACKGROUND_ACTIVITY_START_ALLOWED);
+ assertThat(options.isPendingIntentBackgroundActivityLaunchAllowedByPermission()).isTrue();
}
@Test
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModelTests.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModelTests.kt
index 7d19f3c..282495d 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModelTests.kt
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModelTests.kt
@@ -60,8 +60,8 @@
import com.android.wm.shell.common.DisplayLayout
import com.android.wm.shell.common.ShellExecutor
import com.android.wm.shell.common.SyncTransactionQueue
-import com.android.wm.shell.desktopmode.DesktopModeStatus
import com.android.wm.shell.desktopmode.DesktopTasksController
+import com.android.wm.shell.shared.DesktopModeStatus
import com.android.wm.shell.sysui.KeyguardChangeListener
import com.android.wm.shell.sysui.ShellCommandHandler
import com.android.wm.shell.sysui.ShellController
@@ -121,6 +121,7 @@
@Mock private lateinit var mockShellController: ShellController
@Mock private lateinit var mockShellExecutor: ShellExecutor
@Mock private lateinit var mockRootTaskDisplayAreaOrganizer: RootTaskDisplayAreaOrganizer
+ @Mock private lateinit var mockResizeHandleSizeRepository: ResizeHandleSizeRepository
@Mock private lateinit var mockShellCommandHandler: ShellCommandHandler
@Mock private lateinit var mockWindowManager: IWindowManager
@@ -156,7 +157,8 @@
mockInputMonitorFactory,
transactionFactory,
mockRootTaskDisplayAreaOrganizer,
- windowDecorByTaskIdSpy
+ windowDecorByTaskIdSpy,
+ mockResizeHandleSizeRepository
)
whenever(mockDisplayController.getDisplayLayout(any())).thenReturn(mockDisplayLayout)
@@ -197,7 +199,8 @@
mockMainHandler,
mockMainChoreographer,
mockSyncQueue,
- mockRootTaskDisplayAreaOrganizer
+ mockRootTaskDisplayAreaOrganizer,
+ mockResizeHandleSizeRepository
)
verify(decoration).close()
}
@@ -221,7 +224,8 @@
mockMainHandler,
mockMainChoreographer,
mockSyncQueue,
- mockRootTaskDisplayAreaOrganizer
+ mockRootTaskDisplayAreaOrganizer,
+ mockResizeHandleSizeRepository
)
task.setWindowingMode(WINDOWING_MODE_FREEFORM)
@@ -236,7 +240,8 @@
mockMainHandler,
mockMainChoreographer,
mockSyncQueue,
- mockRootTaskDisplayAreaOrganizer
+ mockRootTaskDisplayAreaOrganizer,
+ mockResizeHandleSizeRepository
)
}
@@ -296,7 +301,7 @@
onTaskChanging(task)
verify(mockDesktopModeWindowDecorFactory, never())
- .create(any(), any(), any(), eq(task), any(), any(), any(), any(), any())
+ .create(any(), any(), any(), eq(task), any(), any(), any(), any(), any(), any())
}
@Test
@@ -309,7 +314,7 @@
onTaskOpening(task)
verify(mockDesktopModeWindowDecorFactory, never())
- .create(any(), any(), any(), eq(task), any(), any(), any(), any(), any())
+ .create(any(), any(), any(), eq(task), any(), any(), any(), any(), any(), any())
}
@Test
@@ -406,7 +411,7 @@
onTaskOpening(task)
verify(mockDesktopModeWindowDecorFactory, never())
- .create(any(), any(), any(), eq(task), any(), any(), any(), any(), any())
+ .create(any(), any(), any(), eq(task), any(), any(), any(), any(), any(), any())
} finally {
mockitoSession.finishMocking()
}
@@ -430,7 +435,7 @@
onTaskOpening(task)
verify(mockDesktopModeWindowDecorFactory)
- .create(any(), any(), any(), eq(task), any(), any(), any(), any(), any())
+ .create(any(), any(), any(), eq(task), any(), any(), any(), any(), any(), any())
} finally {
mockitoSession.finishMocking()
}
@@ -453,7 +458,7 @@
onTaskOpening(task)
verify(mockDesktopModeWindowDecorFactory)
- .create(any(), any(), any(), eq(task), any(), any(), any(), any(), any())
+ .create(any(), any(), any(), eq(task), any(), any(), any(), any(), any(), any())
} finally {
mockitoSession.finishMocking()
}
@@ -497,7 +502,7 @@
val decoration = mock(DesktopModeWindowDecoration::class.java)
whenever(
mockDesktopModeWindowDecorFactory.create(
- any(), any(), any(), eq(task), any(), any(), any(), any(), any())
+ any(), any(), any(), eq(task), any(), any(), any(), any(), any(), any())
).thenReturn(decoration)
decoration.mTaskInfo = task
whenever(decoration.isFocused).thenReturn(task.isFocused)
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorationTests.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorationTests.java
index 608f74b..e737861 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorationTests.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorationTests.java
@@ -19,6 +19,7 @@
import static android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM;
import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
import static android.app.WindowConfiguration.WINDOWING_MODE_MULTI_WINDOW;
+import static android.platform.test.flag.junit.SetFlagsRule.DefaultInitValueType.DEVICE_DEFAULT;
import static android.view.WindowInsetsController.APPEARANCE_TRANSPARENT_CAPTION_BAR_BACKGROUND;
import static com.google.common.truth.Truth.assertThat;
@@ -39,6 +40,9 @@
import android.content.res.TypedArray;
import android.os.Handler;
import android.os.SystemProperties;
+import android.platform.test.annotations.DisableFlags;
+import android.platform.test.annotations.EnableFlags;
+import android.platform.test.flag.junit.SetFlagsRule;
import android.testing.AndroidTestingRunner;
import android.testing.TestableContext;
import android.view.Choreographer;
@@ -51,6 +55,7 @@
import androidx.test.filters.SmallTest;
import com.android.internal.R;
+import com.android.window.flags.Flags;
import com.android.wm.shell.RootTaskDisplayAreaOrganizer;
import com.android.wm.shell.ShellTaskOrganizer;
import com.android.wm.shell.ShellTestCase;
@@ -61,6 +66,7 @@
import org.junit.Before;
import org.junit.BeforeClass;
+import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
@@ -83,6 +89,8 @@
private static final String USE_ROUNDED_CORNERS_SYSPROP_KEY =
"persist.wm.debug.desktop_use_rounded_corners";
+ @Rule public final SetFlagsRule mSetFlagsRule = new SetFlagsRule(DEVICE_DEFAULT);
+
@Mock
private DisplayController mMockDisplayController;
@Mock
@@ -107,6 +115,8 @@
private WindowDecoration.SurfaceControlViewHostFactory mMockSurfaceControlViewHostFactory;
@Mock
private TypedArray mMockRoundedCornersRadiusArray;
+ @Mock
+ private ResizeHandleSizeRepository mMockResizeHandleSizeRepository;
private final Configuration mConfiguration = new Configuration();
@@ -175,6 +185,48 @@
}
@Test
+ @EnableFlags(Flags.FLAG_ENABLE_APP_HEADER_WITH_TASK_DENSITY)
+ public void updateRelayoutParams_appHeader_usesTaskDensity() {
+ final int systemDensity = mTestableContext.getOrCreateTestableResources().getResources()
+ .getConfiguration().densityDpi;
+ final int customTaskDensity = systemDensity + 300;
+ final ActivityManager.RunningTaskInfo taskInfo = createTaskInfo(/* visible= */ true);
+ taskInfo.configuration.windowConfiguration.setWindowingMode(WINDOWING_MODE_FREEFORM);
+ taskInfo.configuration.densityDpi = customTaskDensity;
+ final RelayoutParams relayoutParams = new RelayoutParams();
+
+ DesktopModeWindowDecoration.updateRelayoutParams(
+ relayoutParams,
+ mTestableContext,
+ taskInfo,
+ /* applyStartTransactionOnDraw= */ true,
+ /* shouldSetTaskPositionAndCrop */ false);
+
+ assertThat(relayoutParams.mWindowDecorConfig.densityDpi).isEqualTo(customTaskDensity);
+ }
+
+ @Test
+ @DisableFlags(Flags.FLAG_ENABLE_APP_HEADER_WITH_TASK_DENSITY)
+ public void updateRelayoutParams_appHeader_usesSystemDensity() {
+ final int systemDensity = mTestableContext.getOrCreateTestableResources().getResources()
+ .getConfiguration().densityDpi;
+ final int customTaskDensity = systemDensity + 300;
+ final ActivityManager.RunningTaskInfo taskInfo = createTaskInfo(/* visible= */ true);
+ taskInfo.configuration.windowConfiguration.setWindowingMode(WINDOWING_MODE_FULLSCREEN);
+ taskInfo.configuration.densityDpi = customTaskDensity;
+ final RelayoutParams relayoutParams = new RelayoutParams();
+
+ DesktopModeWindowDecoration.updateRelayoutParams(
+ relayoutParams,
+ mTestableContext,
+ taskInfo,
+ /* applyStartTransactionOnDraw= */ true,
+ /* shouldSetTaskPositionAndCrop */ false);
+
+ assertThat(relayoutParams.mWindowDecorConfig.densityDpi).isEqualTo(systemDensity);
+ }
+
+ @Test
public void updateRelayoutParams_freeformAndTransparentAppearance_allowsInputFallthrough() {
final ActivityManager.RunningTaskInfo taskInfo = createTaskInfo(/* visible= */ true);
taskInfo.configuration.windowConfiguration.setWindowingMode(WINDOWING_MODE_FREEFORM);
@@ -294,10 +346,10 @@
private DesktopModeWindowDecoration createWindowDecoration(
ActivityManager.RunningTaskInfo taskInfo) {
return new DesktopModeWindowDecoration(mContext, mMockDisplayController,
- mMockShellTaskOrganizer, taskInfo, mMockSurfaceControl, mConfiguration,
+ mMockShellTaskOrganizer, taskInfo, mMockSurfaceControl,
mMockHandler, mMockChoreographer, mMockSyncQueue, mMockRootTaskDisplayAreaOrganizer,
- SurfaceControl.Builder::new, mMockTransactionSupplier,
- WindowContainerTransaction::new, SurfaceControl::new,
+ mMockResizeHandleSizeRepository, SurfaceControl.Builder::new,
+ mMockTransactionSupplier, WindowContainerTransaction::new, SurfaceControl::new,
mMockSurfaceControlViewHostFactory);
}
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/DragResizeWindowGeometryTests.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/DragResizeWindowGeometryTests.java
index 5464508..62fb1c4 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/DragResizeWindowGeometryTests.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/DragResizeWindowGeometryTests.java
@@ -27,10 +27,7 @@
import android.annotation.NonNull;
import android.graphics.Point;
import android.graphics.Region;
-import android.platform.test.annotations.RequiresFlagsDisabled;
-import android.platform.test.annotations.RequiresFlagsEnabled;
-import android.platform.test.flag.junit.CheckFlagsRule;
-import android.platform.test.flag.junit.DeviceFlagsValueProvider;
+import android.platform.test.flag.junit.SetFlagsRule;
import android.testing.AndroidTestingRunner;
import android.util.Size;
@@ -74,7 +71,7 @@
TASK_SIZE.getHeight() + EDGE_RESIZE_THICKNESS / 2);
@Rule
- public final CheckFlagsRule mCheckFlagsRule = DeviceFlagsValueProvider.createCheckFlagsRule();
+ public final SetFlagsRule mSetFlagsRule = new SetFlagsRule();
/**
* Check that both groups of objects satisfy equals/hashcode within each group, and that each
@@ -147,8 +144,8 @@
* capture all eligible input regardless of source (touch or cursor).
*/
@Test
- @RequiresFlagsEnabled(Flags.FLAG_ENABLE_WINDOWING_EDGE_DRAG_RESIZE)
public void testRegionUnion_edgeDragResizeEnabled_containsLargeCorners() {
+ mSetFlagsRule.enableFlags(Flags.FLAG_ENABLE_WINDOWING_EDGE_DRAG_RESIZE);
Region region = new Region();
GEOMETRY.union(region);
// Make sure we're choosing a point outside of any debug region buffer.
@@ -164,8 +161,8 @@
* size.
*/
@Test
- @RequiresFlagsDisabled(Flags.FLAG_ENABLE_WINDOWING_EDGE_DRAG_RESIZE)
public void testRegionUnion_edgeDragResizeDisabled_containsFineCorners() {
+ mSetFlagsRule.disableFlags(Flags.FLAG_ENABLE_WINDOWING_EDGE_DRAG_RESIZE);
Region region = new Region();
GEOMETRY.union(region);
final int cornerRadius = DragResizeWindowGeometry.DEBUG
@@ -176,16 +173,16 @@
}
@Test
- @RequiresFlagsEnabled(Flags.FLAG_ENABLE_WINDOWING_EDGE_DRAG_RESIZE)
public void testCalculateControlType_edgeDragResizeEnabled_edges() {
+ mSetFlagsRule.enableFlags(Flags.FLAG_ENABLE_WINDOWING_EDGE_DRAG_RESIZE);
// The input source (touch or cursor) shouldn't impact the edge resize size.
validateCtrlTypeForEdges(/* isTouch= */ false);
validateCtrlTypeForEdges(/* isTouch= */ true);
}
@Test
- @RequiresFlagsDisabled(Flags.FLAG_ENABLE_WINDOWING_EDGE_DRAG_RESIZE)
public void testCalculateControlType_edgeDragResizeDisabled_edges() {
+ mSetFlagsRule.disableFlags(Flags.FLAG_ENABLE_WINDOWING_EDGE_DRAG_RESIZE);
// Edge resizing is not supported when the flag is disabled.
validateCtrlTypeForEdges(/* isTouch= */ false);
validateCtrlTypeForEdges(/* isTouch= */ false);
@@ -203,8 +200,8 @@
}
@Test
- @RequiresFlagsEnabled(Flags.FLAG_ENABLE_WINDOWING_EDGE_DRAG_RESIZE)
public void testCalculateControlType_edgeDragResizeEnabled_corners() {
+ mSetFlagsRule.enableFlags(Flags.FLAG_ENABLE_WINDOWING_EDGE_DRAG_RESIZE);
final TestPoints fineTestPoints = new TestPoints(TASK_SIZE, FINE_CORNER_SIZE / 2);
final TestPoints largeCornerTestPoints = new TestPoints(TASK_SIZE, LARGE_CORNER_SIZE / 2);
@@ -226,8 +223,8 @@
}
@Test
- @RequiresFlagsDisabled(Flags.FLAG_ENABLE_WINDOWING_EDGE_DRAG_RESIZE)
public void testCalculateControlType_edgeDragResizeDisabled_corners() {
+ mSetFlagsRule.disableFlags(Flags.FLAG_ENABLE_WINDOWING_EDGE_DRAG_RESIZE);
final TestPoints fineTestPoints = new TestPoints(TASK_SIZE, FINE_CORNER_SIZE / 2);
final TestPoints largeCornerTestPoints = new TestPoints(TASK_SIZE, LARGE_CORNER_SIZE / 2);
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/ResizeHandleSizeRepositoryParameterizedTests.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/ResizeHandleSizeRepositoryParameterizedTests.kt
new file mode 100644
index 0000000..a9fddc6
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/ResizeHandleSizeRepositoryParameterizedTests.kt
@@ -0,0 +1,150 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.wm.shell.windowdecor
+
+import android.content.Context
+import android.platform.test.flag.junit.SetFlagsRule
+import androidx.test.core.app.ApplicationProvider
+import androidx.test.filters.SmallTest
+import com.android.window.flags.Flags
+import java.util.function.Consumer
+import org.junit.Before
+import org.junit.Rule
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.junit.runners.Parameterized
+import org.junit.runners.Parameterized.Parameter
+import org.junit.runners.Parameterized.Parameters
+import org.mockito.Mock
+import org.mockito.Mockito.mock
+import org.mockito.MockitoAnnotations
+import org.mockito.kotlin.any
+import org.mockito.kotlin.never
+import org.mockito.kotlin.verify
+
+/**
+ * Tests for {@link ResizeHandleSizeRepository}.
+ *
+ * Build/Install/Run: atest WMShellUnitTests:ResizeHandleSizeRepositoryParameterizedTests
+ */
+@SmallTest
+@RunWith(Parameterized::class)
+class ResizeHandleSizeRepositoryParameterizedTests {
+ private val resources = ApplicationProvider.getApplicationContext<Context>().resources
+ private val resizeHandleSizeRepository = ResizeHandleSizeRepository()
+ @Mock private lateinit var mockSizeChangeFunctionOne: Consumer<ResizeHandleSizeRepository>
+ @Mock private lateinit var mockSizeChangeFunctionTwo: Consumer<ResizeHandleSizeRepository>
+
+ @JvmField @Rule val setFlagsRule = SetFlagsRule()
+
+ @Parameter(0) lateinit var name: String
+ // The current ResizeHandleSizeRepository API under test.
+
+ @Parameter(1) lateinit var operation: (ResizeHandleSizeRepository) -> Unit
+
+ @Before
+ fun setOverrideBeforeResetResizeHandle() {
+ MockitoAnnotations.initMocks(this)
+ if (name != "reset") return
+ val originalEdgeHandle =
+ resizeHandleSizeRepository.getResizeEdgeHandlePixels(resources)
+ resizeHandleSizeRepository.setResizeEdgeHandlePixels(originalEdgeHandle + 2)
+ }
+
+ companion object {
+ @Parameters(name = "{index}: {0}")
+ @JvmStatic
+ fun data(): Iterable<Array<Any>> {
+ return listOf(
+ arrayOf(
+ "reset",
+ { sizeRepository: ResizeHandleSizeRepository ->
+ sizeRepository.resetResizeEdgeHandlePixels()
+ }
+ ),
+ arrayOf(
+ "set",
+ { sizeRepository: ResizeHandleSizeRepository ->
+ sizeRepository.setResizeEdgeHandlePixels(99)
+ }
+ )
+ )
+ }
+ }
+
+ // =================
+ // Validate that listeners are notified correctly for reset resize handle API.
+ // =================
+
+ @Test
+ fun testUpdateResizeHandleSize_flagDisabled() {
+ setFlagsRule.disableFlags(Flags.FLAG_ENABLE_WINDOWING_EDGE_DRAG_RESIZE)
+ registerSizeChangeFunctions()
+ operation.invoke(resizeHandleSizeRepository)
+ // Nothing is notified since flag is disabled.
+ verify(mockSizeChangeFunctionOne, never()).accept(any())
+ verify(mockSizeChangeFunctionTwo, never()).accept(any())
+ }
+
+ @Test
+ fun testUpdateResizeHandleSize_flagEnabled_noListeners() {
+ setFlagsRule.enableFlags(Flags.FLAG_ENABLE_WINDOWING_EDGE_DRAG_RESIZE)
+ operation.invoke(resizeHandleSizeRepository)
+ // Nothing is notified since nothing was registered.
+ verify(mockSizeChangeFunctionOne, never()).accept(any())
+ verify(mockSizeChangeFunctionTwo, never()).accept(any())
+ }
+
+ @Test
+ fun testUpdateResizeHandleSize_flagEnabled_listenersNotified() {
+ setFlagsRule.enableFlags(Flags.FLAG_ENABLE_WINDOWING_EDGE_DRAG_RESIZE)
+ registerSizeChangeFunctions()
+ operation.invoke(resizeHandleSizeRepository)
+ // Functions notified when reset.
+ verify(mockSizeChangeFunctionOne).accept(any())
+ verify(mockSizeChangeFunctionTwo).accept(any())
+ }
+
+ @Test
+ fun testUpdateResizeHandleSize_flagEnabled_listenerFails() {
+ setFlagsRule.enableFlags(Flags.FLAG_ENABLE_WINDOWING_EDGE_DRAG_RESIZE)
+ registerSizeChangeFunctions()
+ operation.invoke(resizeHandleSizeRepository)
+ // Functions notified when reset.
+ verify(mockSizeChangeFunctionOne).accept(any())
+ verify(mockSizeChangeFunctionTwo).accept(any())
+ }
+
+ @Test
+ fun testUpdateResizeHandleSize_flagEnabled_ignoreSecondListener() {
+ setFlagsRule.enableFlags(Flags.FLAG_ENABLE_WINDOWING_EDGE_DRAG_RESIZE)
+ registerSizeChangeFunctions()
+ val extraConsumerMock = mock(Consumer::class.java) as Consumer<ResizeHandleSizeRepository>
+ resizeHandleSizeRepository.registerSizeChangeFunction(extraConsumerMock)
+ // First listener succeeds, second one that fails is ignored.
+ operation.invoke(resizeHandleSizeRepository)
+ // Functions notified when reset.
+ verify(mockSizeChangeFunctionOne).accept(any())
+ verify(mockSizeChangeFunctionTwo).accept(any())
+ verify(extraConsumerMock).accept(any())
+ }
+
+ private fun registerSizeChangeFunctions() {
+ resizeHandleSizeRepository.registerSizeChangeFunction(mockSizeChangeFunctionOne)
+ resizeHandleSizeRepository.registerSizeChangeFunction(mockSizeChangeFunctionTwo)
+ }
+}
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/ResizeHandleSizeRepositoryTests.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/ResizeHandleSizeRepositoryTests.kt
new file mode 100644
index 0000000..59bbc72
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/ResizeHandleSizeRepositoryTests.kt
@@ -0,0 +1,77 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.wm.shell.windowdecor
+
+import android.content.Context
+import android.platform.test.flag.junit.SetFlagsRule
+import android.testing.AndroidTestingRunner
+import androidx.test.core.app.ApplicationProvider
+import androidx.test.filters.SmallTest
+import com.android.window.flags.Flags
+import com.google.common.truth.Truth.assertThat
+import org.junit.Rule
+import org.junit.Test
+import org.junit.runner.RunWith
+
+/**
+ * Tests for {@link ResizeHandleSizeRepository}. Validate that get/reset/set work correctly.
+ *
+ * Build/Install/Run: atest WMShellUnitTests:ResizeHandleSizeRepositoryTests
+ */
+@SmallTest
+@RunWith(AndroidTestingRunner::class)
+class ResizeHandleSizeRepositoryTests {
+ private val resources = ApplicationProvider.getApplicationContext<Context>().resources
+ private val resizeHandleSizeRepository = ResizeHandleSizeRepository()
+
+ @JvmField @Rule val setFlagsRule = SetFlagsRule()
+
+ @Test
+ fun testOverrideResizeEdgeHandlePixels_flagEnabled_resetSucceeds() {
+ setFlagsRule.enableFlags(Flags.FLAG_ENABLE_WINDOWING_EDGE_DRAG_RESIZE)
+ // Reset does nothing when no override is set.
+ val originalEdgeHandle =
+ resizeHandleSizeRepository.getResizeEdgeHandlePixels(resources)
+ resizeHandleSizeRepository.resetResizeEdgeHandlePixels()
+ assertThat(resizeHandleSizeRepository.getResizeEdgeHandlePixels(resources))
+ .isEqualTo(originalEdgeHandle)
+
+ // Now try to set the value; reset should succeed.
+ resizeHandleSizeRepository.setResizeEdgeHandlePixels(originalEdgeHandle + 2)
+ resizeHandleSizeRepository.resetResizeEdgeHandlePixels()
+ assertThat(resizeHandleSizeRepository.getResizeEdgeHandlePixels(resources))
+ .isEqualTo(originalEdgeHandle)
+ }
+
+ @Test
+ fun testOverrideResizeEdgeHandlePixels_flagDisabled_resetFails() {
+ setFlagsRule.disableFlags(Flags.FLAG_ENABLE_WINDOWING_EDGE_DRAG_RESIZE)
+ // Reset does nothing when no override is set.
+ val originalEdgeHandle =
+ resizeHandleSizeRepository.getResizeEdgeHandlePixels(resources)
+ resizeHandleSizeRepository.resetResizeEdgeHandlePixels()
+ assertThat(resizeHandleSizeRepository.getResizeEdgeHandlePixels(resources))
+ .isEqualTo(originalEdgeHandle)
+
+ // Now try to set the value; reset should do nothing.
+ val newEdgeHandle = originalEdgeHandle + 2
+ resizeHandleSizeRepository.setResizeEdgeHandlePixels(newEdgeHandle)
+ resizeHandleSizeRepository.resetResizeEdgeHandlePixels()
+ assertThat(resizeHandleSizeRepository.getResizeEdgeHandlePixels(resources))
+ .isEqualTo(originalEdgeHandle)
+ }
+}
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/WindowDecorationTests.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/WindowDecorationTests.java
index 4eb44d7..4831081 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/WindowDecorationTests.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/WindowDecorationTests.java
@@ -49,7 +49,6 @@
import android.app.ActivityManager;
import android.content.Context;
-import android.content.res.Configuration;
import android.content.res.Resources;
import android.graphics.Color;
import android.graphics.Point;
@@ -75,7 +74,7 @@
import com.android.wm.shell.ShellTestCase;
import com.android.wm.shell.TestRunningTaskInfoBuilder;
import com.android.wm.shell.common.DisplayController;
-import com.android.wm.shell.desktopmode.DesktopModeStatus;
+import com.android.wm.shell.shared.DesktopModeStatus;
import com.android.wm.shell.tests.R;
import org.junit.Before;
@@ -134,7 +133,6 @@
private SurfaceControl.Transaction mMockSurfaceControlFinishT;
private SurfaceControl.Transaction mMockSurfaceControlAddWindowT;
private WindowDecoration.RelayoutParams mRelayoutParams = new WindowDecoration.RelayoutParams();
- private Configuration mWindowConfiguration = new Configuration();
private int mCaptionMenuWidthId;
@Before
@@ -303,7 +301,6 @@
taskInfo.isFocused = true;
// Density is 2. Shadow radius is 10px. Caption height is 64px.
taskInfo.configuration.densityDpi = DisplayMetrics.DENSITY_DEFAULT * 2;
- mWindowConfiguration.densityDpi = taskInfo.configuration.densityDpi;
final TestWindowDecoration windowDecor = createWindowDecoration(taskInfo);
@@ -314,14 +311,16 @@
verify(mMockWindowContainerTransaction, never())
.removeInsetsSource(eq(taskInfo.token), any(), anyInt(), anyInt());
+ final SurfaceControl.Transaction t2 = mock(SurfaceControl.Transaction.class);
+ mMockSurfaceControlTransactions.add(t2);
taskInfo.isVisible = false;
windowDecor.relayout(taskInfo);
- final InOrder releaseOrder = inOrder(t, mMockSurfaceControlViewHost);
+ final InOrder releaseOrder = inOrder(t2, mMockSurfaceControlViewHost);
releaseOrder.verify(mMockSurfaceControlViewHost).release();
- releaseOrder.verify(t).remove(captionContainerSurface);
- releaseOrder.verify(t).remove(decorContainerSurface);
- releaseOrder.verify(t).apply();
+ releaseOrder.verify(t2).remove(captionContainerSurface);
+ releaseOrder.verify(t2).remove(decorContainerSurface);
+ releaseOrder.verify(t2).apply();
// Expect to remove two insets sources, the caption insets and the mandatory gesture insets.
verify(mMockWindowContainerTransaction, Mockito.times(2))
.removeInsetsSource(eq(taskInfo.token), any(), anyInt(), anyInt());
@@ -836,7 +835,7 @@
private TestWindowDecoration createWindowDecoration(ActivityManager.RunningTaskInfo taskInfo) {
return new TestWindowDecoration(mContext, mMockDisplayController, mMockShellTaskOrganizer,
- taskInfo, mMockTaskSurface, mWindowConfiguration,
+ taskInfo, mMockTaskSurface,
new MockObjectSupplier<>(mMockSurfaceControlBuilders,
() -> createMockSurfaceControlBuilder(mock(SurfaceControl.class))),
new MockObjectSupplier<>(mMockSurfaceControlTransactions,
@@ -877,16 +876,15 @@
TestWindowDecoration(Context context, DisplayController displayController,
ShellTaskOrganizer taskOrganizer, ActivityManager.RunningTaskInfo taskInfo,
SurfaceControl taskSurface,
- Configuration windowConfiguration,
Supplier<SurfaceControl.Builder> surfaceControlBuilderSupplier,
Supplier<SurfaceControl.Transaction> surfaceControlTransactionSupplier,
Supplier<WindowContainerTransaction> windowContainerTransactionSupplier,
Supplier<SurfaceControl> surfaceControlSupplier,
SurfaceControlViewHostFactory surfaceControlViewHostFactory) {
super(context, displayController, taskOrganizer, taskInfo, taskSurface,
- windowConfiguration, surfaceControlBuilderSupplier,
- surfaceControlTransactionSupplier, windowContainerTransactionSupplier,
- surfaceControlSupplier, surfaceControlViewHostFactory);
+ surfaceControlBuilderSupplier, surfaceControlTransactionSupplier,
+ windowContainerTransactionSupplier, surfaceControlSupplier,
+ surfaceControlViewHostFactory);
}
@Override
diff --git a/libs/androidfw/fuzz/resourcefile_fuzzer/Android.bp b/libs/androidfw/fuzz/resourcefile_fuzzer/Android.bp
index b511244..6196589 100644
--- a/libs/androidfw/fuzz/resourcefile_fuzzer/Android.bp
+++ b/libs/androidfw/fuzz/resourcefile_fuzzer/Android.bp
@@ -19,6 +19,7 @@
// to get the below license kinds:
// SPDX-license-identifier-Apache-2.0
default_applicable_licenses: ["frameworks_base_libs_androidfw_license"],
+ default_team: "trendy_team_android_resources",
}
cc_fuzz {
@@ -31,7 +32,7 @@
static_libs: ["libgmock"],
target: {
android: {
- shared_libs:[
+ shared_libs: [
"libandroidfw",
"libbase",
"libcutils",
@@ -52,4 +53,15 @@
],
},
},
+ fuzz_config: {
+ cc: [
+ "android-resources@google.com",
+ ],
+ componentid: 568761,
+ description: "The fuzzer targets the APIs of libandroidfw",
+ vector: "local_no_privileges_required",
+ service_privilege: "privileged",
+ users: "multi_user",
+ fuzzed_code_usage: "shipped",
+ },
}
diff --git a/libs/hwui/Android.bp b/libs/hwui/Android.bp
index 753a699..7c1c5b4 100644
--- a/libs/hwui/Android.bp
+++ b/libs/hwui/Android.bp
@@ -336,6 +336,7 @@
"jni/android_graphics_animation_NativeInterpolatorFactory.cpp",
"jni/android_graphics_animation_RenderNodeAnimator.cpp",
"jni/android_graphics_Canvas.cpp",
+ "jni/android_graphics_Color.cpp",
"jni/android_graphics_ColorSpace.cpp",
"jni/android_graphics_drawable_AnimatedVectorDrawable.cpp",
"jni/android_graphics_drawable_VectorDrawable.cpp",
diff --git a/libs/hwui/apex/LayoutlibLoader.cpp b/libs/hwui/apex/LayoutlibLoader.cpp
index fd9915a..70a9ef0 100644
--- a/libs/hwui/apex/LayoutlibLoader.cpp
+++ b/libs/hwui/apex/LayoutlibLoader.cpp
@@ -46,6 +46,7 @@
extern int register_android_graphics_Canvas(JNIEnv* env);
extern int register_android_graphics_CanvasProperty(JNIEnv* env);
+extern int register_android_graphics_Color(JNIEnv* env);
extern int register_android_graphics_ColorFilter(JNIEnv* env);
extern int register_android_graphics_ColorSpace(JNIEnv* env);
extern int register_android_graphics_DrawFilter(JNIEnv* env);
@@ -87,6 +88,7 @@
{"android.graphics.Camera", REG_JNI(register_android_graphics_Camera)},
{"android.graphics.Canvas", REG_JNI(register_android_graphics_Canvas)},
{"android.graphics.CanvasProperty", REG_JNI(register_android_graphics_CanvasProperty)},
+ {"android.graphics.Color", REG_JNI(register_android_graphics_Color)},
{"android.graphics.ColorFilter", REG_JNI(register_android_graphics_ColorFilter)},
{"android.graphics.ColorSpace", REG_JNI(register_android_graphics_ColorSpace)},
{"android.graphics.CreateJavaOutputStreamAdaptor",
diff --git a/libs/hwui/apex/jni_runtime.cpp b/libs/hwui/apex/jni_runtime.cpp
index fb0cdb0..6ace396 100644
--- a/libs/hwui/apex/jni_runtime.cpp
+++ b/libs/hwui/apex/jni_runtime.cpp
@@ -49,6 +49,7 @@
extern int register_android_graphics_Canvas(JNIEnv* env);
extern int register_android_graphics_CanvasProperty(JNIEnv* env);
extern int register_android_graphics_ColorFilter(JNIEnv* env);
+extern int register_android_graphics_Color(JNIEnv* env);
extern int register_android_graphics_ColorSpace(JNIEnv* env);
extern int register_android_graphics_DrawFilter(JNIEnv* env);
extern int register_android_graphics_FontFamily(JNIEnv* env);
@@ -98,6 +99,7 @@
static const RegJNIRec gRegJNI[] = {
REG_JNI(register_android_graphics_Canvas),
+ REG_JNI(register_android_graphics_Color),
// This needs to be before register_android_graphics_Graphics, or the latter
// will not be able to find the jmethodID for ColorSpace.get().
REG_JNI(register_android_graphics_ColorSpace),
diff --git a/libs/hwui/jni/Shader.cpp b/libs/hwui/jni/Shader.cpp
index a952be0..2a057e7 100644
--- a/libs/hwui/jni/Shader.cpp
+++ b/libs/hwui/jni/Shader.cpp
@@ -36,25 +36,6 @@
return 0; \
}
-static void Color_RGBToHSV(JNIEnv* env, jobject, jint red, jint green, jint blue, jfloatArray hsvArray)
-{
- SkScalar hsv[3];
- SkRGBToHSV(red, green, blue, hsv);
-
- AutoJavaFloatArray autoHSV(env, hsvArray, 3);
- float* values = autoHSV.ptr();
- for (int i = 0; i < 3; i++) {
- values[i] = SkScalarToFloat(hsv[i]);
- }
-}
-
-static jint Color_HSVToColor(JNIEnv* env, jobject, jint alpha, jfloatArray hsvArray)
-{
- AutoJavaFloatArray autoHSV(env, hsvArray, 3);
- SkScalar* hsv = autoHSV.ptr();
- return static_cast<jint>(SkHSVToColor(alpha, hsv));
-}
-
///////////////////////////////////////////////////////////////////////////////////////////////
static void Shader_safeUnref(SkShader* shader) {
@@ -409,11 +390,6 @@
///////////////////////////////////////////////////////////////////////////////////////////////
-static const JNINativeMethod gColorMethods[] = {
- { "nativeRGBToHSV", "(III[F)V", (void*)Color_RGBToHSV },
- { "nativeHSVToColor", "(I[F)I", (void*)Color_HSVToColor }
-};
-
static const JNINativeMethod gShaderMethods[] = {
{ "nativeGetFinalizer", "()J", (void*)Shader_getNativeFinalizer },
};
@@ -456,8 +432,6 @@
int register_android_graphics_Shader(JNIEnv* env)
{
- android::RegisterMethodsOrDie(env, "android/graphics/Color", gColorMethods,
- NELEM(gColorMethods));
android::RegisterMethodsOrDie(env, "android/graphics/Shader", gShaderMethods,
NELEM(gShaderMethods));
android::RegisterMethodsOrDie(env, "android/graphics/BitmapShader", gBitmapShaderMethods,
diff --git a/libs/hwui/jni/android_graphics_Color.cpp b/libs/hwui/jni/android_graphics_Color.cpp
new file mode 100644
index 0000000..c22b8b9
--- /dev/null
+++ b/libs/hwui/jni/android_graphics_Color.cpp
@@ -0,0 +1,55 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "GraphicsJNI.h"
+
+#include "SkColor.h"
+
+using namespace android;
+
+static void Color_RGBToHSV(JNIEnv* env, jobject, jint red, jint green, jint blue,
+ jfloatArray hsvArray)
+{
+ SkScalar hsv[3];
+ SkRGBToHSV(red, green, blue, hsv);
+
+ AutoJavaFloatArray autoHSV(env, hsvArray, 3);
+ float* values = autoHSV.ptr();
+ for (int i = 0; i < 3; i++) {
+ values[i] = SkScalarToFloat(hsv[i]);
+ }
+}
+
+static jint Color_HSVToColor(JNIEnv* env, jobject, jint alpha, jfloatArray hsvArray)
+{
+ AutoJavaFloatArray autoHSV(env, hsvArray, 3);
+ SkScalar* hsv = autoHSV.ptr();
+ return static_cast<jint>(SkHSVToColor(alpha, hsv));
+}
+
+static const JNINativeMethod gColorMethods[] = {
+ { "nativeRGBToHSV", "(III[F)V", (void*)Color_RGBToHSV },
+ { "nativeHSVToColor", "(I[F)I", (void*)Color_HSVToColor }
+};
+
+namespace android {
+
+int register_android_graphics_Color(JNIEnv* env) {
+ return android::RegisterMethodsOrDie(env, "android/graphics/Color", gColorMethods,
+ NELEM(gColorMethods));
+}
+
+}; // namespace android
diff --git a/libs/hwui/jni/android_graphics_ColorSpace.cpp b/libs/hwui/jni/android_graphics_ColorSpace.cpp
index 63d3f83..d06206b 100644
--- a/libs/hwui/jni/android_graphics_ColorSpace.cpp
+++ b/libs/hwui/jni/android_graphics_ColorSpace.cpp
@@ -148,7 +148,7 @@
namespace android {
int register_android_graphics_ColorSpace(JNIEnv* env) {
- return android::RegisterMethodsOrDie(env, "android/graphics/ColorSpace$Rgb",
+ return android::RegisterMethodsOrDie(env, "android/graphics/ColorSpace$Rgb$Native",
gColorSpaceRgbMethods, NELEM(gColorSpaceRgbMethods));
}
diff --git a/libs/hwui/jni/android_graphics_Matrix.cpp b/libs/hwui/jni/android_graphics_Matrix.cpp
index c0d791a..eedc069 100644
--- a/libs/hwui/jni/android_graphics_Matrix.cpp
+++ b/libs/hwui/jni/android_graphics_Matrix.cpp
@@ -326,9 +326,6 @@
};
static const JNINativeMethod methods[] = {
- {"nGetNativeFinalizer", "()J", (void*) SkMatrixGlue::getNativeFinalizer},
- {"nCreate","(J)J", (void*) SkMatrixGlue::create},
-
// ------- @FastNative below here ---------------
{"nMapPoints","(J[FI[FIIZ)V", (void*) SkMatrixGlue::mapPoints},
{"nMapRect","(JLandroid/graphics/RectF;Landroid/graphics/RectF;)Z",
@@ -388,9 +385,6 @@
int register_android_graphics_Matrix(JNIEnv* env) {
// Methods only used on Ravenwood (for now). See the javadoc on Matrix$ExtraNativesx
// for why we need it.
- //
- // We don't need it on non-ravenwood, but we don't (yet) have a way to detect ravenwood
- // environment, so we just always run it.
RegisterMethodsOrDie(env, "android/graphics/Matrix$ExtraNatives", extra_methods,
NELEM(extra_methods));
diff --git a/libs/input/MouseCursorController.cpp b/libs/input/MouseCursorController.cpp
index 5cf5a1d..f1ee325 100644
--- a/libs/input/MouseCursorController.cpp
+++ b/libs/input/MouseCursorController.cpp
@@ -467,10 +467,10 @@
std::function<bool(nsecs_t)> func = std::bind(&MouseCursorController::doAnimations, this, _1);
/*
- * Using ui::ADISPLAY_ID_NONE for displayId here to avoid removing the callback
+ * Using ui::LogicalDisplayId::INVALID for displayId here to avoid removing the callback
* if a TouchSpotController with the same display is removed.
*/
- mContext.addAnimationCallback(ui::ADISPLAY_ID_NONE, func);
+ mContext.addAnimationCallback(ui::LogicalDisplayId::INVALID, func);
}
} // namespace android
diff --git a/libs/input/PointerController.h b/libs/input/PointerController.h
index 70e5c24..c6430f7 100644
--- a/libs/input/PointerController.h
+++ b/libs/input/PointerController.h
@@ -109,7 +109,7 @@
struct Locked {
Presentation presentation;
- ui::LogicalDisplayId pointerDisplayId = ui::ADISPLAY_ID_NONE;
+ ui::LogicalDisplayId pointerDisplayId = ui::LogicalDisplayId::INVALID;
std::vector<gui::DisplayInfo> mDisplayInfos;
std::unordered_map<ui::LogicalDisplayId, TouchSpotController> spotControllers;
diff --git a/libs/input/SpriteController.h b/libs/input/SpriteController.h
index 070c90c..e147c56 100644
--- a/libs/input/SpriteController.h
+++ b/libs/input/SpriteController.h
@@ -174,7 +174,7 @@
int32_t layer{0};
float alpha{1.0f};
SpriteTransformationMatrix transformationMatrix;
- ui::LogicalDisplayId displayId{ui::ADISPLAY_ID_DEFAULT};
+ ui::LogicalDisplayId displayId{ui::LogicalDisplayId::DEFAULT};
sp<SurfaceControl> surfaceControl;
int32_t surfaceWidth{0};
diff --git a/libs/input/tests/PointerController_test.cpp b/libs/input/tests/PointerController_test.cpp
index 7a13380..2dcb1f1 100644
--- a/libs/input/tests/PointerController_test.cpp
+++ b/libs/input/tests/PointerController_test.cpp
@@ -166,7 +166,7 @@
PointerControllerTest();
~PointerControllerTest();
- void ensureDisplayViewportIsSet(ui::LogicalDisplayId displayId = ui::ADISPLAY_ID_DEFAULT);
+ void ensureDisplayViewportIsSet(ui::LogicalDisplayId displayId = ui::LogicalDisplayId::DEFAULT);
sp<MockSprite> mPointerSprite;
sp<MockPointerControllerPolicyInterface> mPolicy;
@@ -335,23 +335,23 @@
// Update spots to sync state with sprite
mPointerController->setSpots(&testSpotCoords, testIdToIndex.cbegin(), testIdBits,
- ui::ADISPLAY_ID_DEFAULT);
+ ui::LogicalDisplayId::DEFAULT);
testing::Mock::VerifyAndClearExpectations(testSpotSprite.get());
// Marking the display to skip screenshot should update sprite as well
- mPointerController->setSkipScreenshot(ui::ADISPLAY_ID_DEFAULT, true);
+ mPointerController->setSkipScreenshot(ui::LogicalDisplayId::DEFAULT, true);
EXPECT_CALL(*testSpotSprite, setSkipScreenshot).With(testing::Args<0>(true));
// Update spots to sync state with sprite
mPointerController->setSpots(&testSpotCoords, testIdToIndex.cbegin(), testIdBits,
- ui::ADISPLAY_ID_DEFAULT);
+ ui::LogicalDisplayId::DEFAULT);
testing::Mock::VerifyAndClearExpectations(testSpotSprite.get());
// Reset flag and verify again
- mPointerController->setSkipScreenshot(ui::ADISPLAY_ID_DEFAULT, false);
+ mPointerController->setSkipScreenshot(ui::LogicalDisplayId::DEFAULT, false);
EXPECT_CALL(*testSpotSprite, setSkipScreenshot).With(testing::Args<0>(false));
mPointerController->setSpots(&testSpotCoords, testIdToIndex.cbegin(), testIdBits,
- ui::ADISPLAY_ID_DEFAULT);
+ ui::LogicalDisplayId::DEFAULT);
testing::Mock::VerifyAndClearExpectations(testSpotSprite.get());
}
diff --git a/media/java/android/media/IMediaRouter2.aidl b/media/java/android/media/IMediaRouter2.aidl
index e2dddad..85bc8ef 100644
--- a/media/java/android/media/IMediaRouter2.aidl
+++ b/media/java/android/media/IMediaRouter2.aidl
@@ -36,6 +36,5 @@
* Call MediaRouterService#requestCreateSessionWithRouter2 to pass the result.
*/
void requestCreateSessionByManager(long uniqueRequestId, in RoutingSessionInfo oldSession,
- in MediaRoute2Info route, in UserHandle transferInitiatorUserHandle,
- in String transferInitiatorPackageName);
+ in MediaRoute2Info route);
}
diff --git a/media/java/android/media/IMediaRouterService.aidl b/media/java/android/media/IMediaRouterService.aidl
index 63cb945..efbf8da 100644
--- a/media/java/android/media/IMediaRouterService.aidl
+++ b/media/java/android/media/IMediaRouterService.aidl
@@ -64,8 +64,7 @@
void requestCreateSessionWithRouter2(IMediaRouter2 router, int requestId, long managerRequestId,
in RoutingSessionInfo oldSession, in MediaRoute2Info route,
- in @nullable Bundle sessionHints, in UserHandle transferInitiatorUserHandle,
- in String transferInitiatorPackageName);
+ in @nullable Bundle sessionHints);
void selectRouteWithRouter2(IMediaRouter2 router, String sessionId, in MediaRoute2Info route);
void deselectRouteWithRouter2(IMediaRouter2 router, String sessionId, in MediaRoute2Info route);
void transferToRouteWithRouter2(IMediaRouter2 router, String sessionId,
diff --git a/media/java/android/media/MediaRouter2.java b/media/java/android/media/MediaRouter2.java
index 7ddf11e..679e8a1 100644
--- a/media/java/android/media/MediaRouter2.java
+++ b/media/java/android/media/MediaRouter2.java
@@ -991,9 +991,7 @@
void requestCreateController(
@NonNull RoutingController controller,
@NonNull MediaRoute2Info route,
- long managerRequestId,
- @NonNull UserHandle transferInitiatorUserHandle,
- @NonNull String transferInitiatorPackageName) {
+ long managerRequestId) {
final int requestId = mNextRequestId.getAndIncrement();
@@ -1022,9 +1020,7 @@
managerRequestId,
controller.getRoutingSessionInfo(),
route,
- controllerHints,
- transferInitiatorUserHandle,
- transferInitiatorPackageName);
+ controllerHints);
} catch (RemoteException ex) {
Log.e(TAG, "createControllerForTransfer: "
+ "Failed to request for creating a controller.", ex);
@@ -1366,11 +1362,7 @@
}
void onRequestCreateControllerByManagerOnHandler(
- RoutingSessionInfo oldSession,
- MediaRoute2Info route,
- long managerRequestId,
- @NonNull UserHandle transferInitiatorUserHandle,
- @NonNull String transferInitiatorPackageName) {
+ RoutingSessionInfo oldSession, MediaRoute2Info route, long managerRequestId) {
Log.i(
TAG,
TextUtils.formatSimple(
@@ -1387,8 +1379,7 @@
if (controller == null) {
return;
}
- requestCreateController(controller, route, managerRequestId, transferInitiatorUserHandle,
- transferInitiatorPackageName);
+ requestCreateController(controller, route, managerRequestId);
}
private List<MediaRoute2Info> getSortedRoutes(
@@ -2423,20 +2414,14 @@
@Override
public void requestCreateSessionByManager(
- long managerRequestId,
- RoutingSessionInfo oldSession,
- MediaRoute2Info route,
- UserHandle transferInitiatorUserHandle,
- String transferInitiatorPackageName) {
+ long managerRequestId, RoutingSessionInfo oldSession, MediaRoute2Info route) {
mHandler.sendMessage(
obtainMessage(
MediaRouter2::onRequestCreateControllerByManagerOnHandler,
MediaRouter2.this,
oldSession,
route,
- managerRequestId,
- transferInitiatorUserHandle,
- transferInitiatorPackageName));
+ managerRequestId));
}
}
@@ -3581,12 +3566,7 @@
RoutingController controller = getCurrentController();
if (!controller.tryTransferWithinProvider(route)) {
- requestCreateController(
- controller,
- route,
- MANAGER_REQUEST_ID_NONE,
- Process.myUserHandle(),
- mContext.getPackageName());
+ requestCreateController(controller, route, MANAGER_REQUEST_ID_NONE);
}
}
diff --git a/media/java/android/media/session/MediaController.java b/media/java/android/media/session/MediaController.java
index b43ff63..a488756 100644
--- a/media/java/android/media/session/MediaController.java
+++ b/media/java/android/media/session/MediaController.java
@@ -252,18 +252,14 @@
return 0;
}
- /**
- * Get the current playback info for this session.
- *
- * @return The current playback info or null.
- */
- public @Nullable PlaybackInfo getPlaybackInfo() {
+ /** Returns the current playback info for this session. */
+ @NonNull
+ public PlaybackInfo getPlaybackInfo() {
try {
return mSessionBinder.getVolumeAttributes();
- } catch (RemoteException e) {
- Log.wtf(TAG, "Error calling getAudioInfo.", e);
+ } catch (RemoteException ex) {
+ throw ex.rethrowFromSystemServer();
}
- return null;
}
/**
diff --git a/nfc/java/android/nfc/cardemulation/HostApduService.java b/nfc/java/android/nfc/cardemulation/HostApduService.java
index f674b06a..c3c74a6 100644
--- a/nfc/java/android/nfc/cardemulation/HostApduService.java
+++ b/nfc/java/android/nfc/cardemulation/HostApduService.java
@@ -20,6 +20,7 @@
import android.annotation.NonNull;
import android.annotation.SdkConstant;
import android.annotation.SdkConstant.SdkConstantType;
+import android.annotation.SuppressLint;
import android.app.Service;
import android.content.Intent;
import android.content.pm.PackageManager;
@@ -404,6 +405,7 @@
*
* @param frame A description of the polling frame.
*/
+ @SuppressLint("OnNameExpected")
@FlaggedApi(android.nfc.Flags.FLAG_NFC_READ_POLLING_LOOP)
public void processPollingFrames(@NonNull List<PollingFrame> frame) {
}
diff --git a/packages/CredentialManager/res/values-fr-rCA/strings.xml b/packages/CredentialManager/res/values-fr-rCA/strings.xml
index 7b8093e..d9715ee 100644
--- a/packages/CredentialManager/res/values-fr-rCA/strings.xml
+++ b/packages/CredentialManager/res/values-fr-rCA/strings.xml
@@ -48,7 +48,7 @@
<string name="passwords" msgid="5419394230391253816">"mots de passe"</string>
<string name="sign_ins" msgid="4710739369149469208">"connexions"</string>
<string name="sign_in_info" msgid="2627704710674232328">"renseignements de connexion"</string>
- <string name="save_credential_to_title" msgid="3172811692275634301">"Enregistrer la <xliff:g id="CREDENTIALTYPES">%1$s</xliff:g> dans"</string>
+ <string name="save_credential_to_title" msgid="3172811692275634301">"Enregistrer le <xliff:g id="CREDENTIALTYPES">%1$s</xliff:g> dans"</string>
<string name="create_passkey_in_other_device_title" msgid="2360053098931886245">"Créer une clé d\'accès sur un autre appareil?"</string>
<string name="save_password_on_other_device_title" msgid="5829084591948321207">"Enregistrer le mot de passe sur un autre appareil?"</string>
<string name="save_sign_in_on_other_device_title" msgid="2827990118560134692">"Enregistrer l\'authentifiant de connexion sur un autre appareil?"</string>
@@ -57,9 +57,9 @@
<string name="set_as_default" msgid="4415328591568654603">"Définir par défaut"</string>
<string name="settings" msgid="6536394145760913145">"Paramètres"</string>
<string name="use_once" msgid="9027366575315399714">"Utiliser une fois"</string>
- <string name="more_options_usage_passwords_passkeys" msgid="3470113942332934279">"<xliff:g id="PASSWORDSNUMBER">%1$s</xliff:g> mots de passe • <xliff:g id="PASSKEYSNUMBER">%2$s</xliff:g> clés d\'accès"</string>
- <string name="more_options_usage_passwords" msgid="1632047277723187813">"<xliff:g id="PASSWORDSNUMBER">%1$s</xliff:g> mots de passe"</string>
- <string name="more_options_usage_passkeys" msgid="5390320437243042237">"<xliff:g id="PASSKEYSNUMBER">%1$s</xliff:g> clés d\'accès"</string>
+ <string name="more_options_usage_passwords_passkeys" msgid="3470113942332934279">"<xliff:g id="PASSWORDSNUMBER">%1$s</xliff:g> mot(s) de passe • <xliff:g id="PASSKEYSNUMBER">%2$s</xliff:g> clé(s) d\'accès"</string>
+ <string name="more_options_usage_passwords" msgid="1632047277723187813">"<xliff:g id="PASSWORDSNUMBER">%1$s</xliff:g> mot(s) de passe"</string>
+ <string name="more_options_usage_passkeys" msgid="5390320437243042237">"<xliff:g id="PASSKEYSNUMBER">%1$s</xliff:g> clé(s) d\'accès"</string>
<string name="more_options_usage_credentials" msgid="1785697001787193984">"<xliff:g id="TOTALCREDENTIALSNUMBER">%1$s</xliff:g> authentifiants"</string>
<string name="passkey_before_subtitle" msgid="2448119456208647444">"Clé d\'accès"</string>
<string name="another_device" msgid="5147276802037801217">"Un autre appareil"</string>
diff --git a/packages/CredentialManager/res/values-hy/strings.xml b/packages/CredentialManager/res/values-hy/strings.xml
index 3ec7da3..630a08a 100644
--- a/packages/CredentialManager/res/values-hy/strings.xml
+++ b/packages/CredentialManager/res/values-hy/strings.xml
@@ -24,15 +24,15 @@
<string name="string_learn_more" msgid="4541600451688392447">"Իմանալ ավելին"</string>
<string name="content_description_show_password" msgid="3283502010388521607">"Ցուցադրել գաղտնաբառը"</string>
<string name="content_description_hide_password" msgid="6841375971631767996">"Թաքցնել գաղտնաբառը"</string>
- <string name="passkey_creation_intro_title" msgid="4251037543787718844">"Անցաբառերի հետ ավելի ապահով է"</string>
- <string name="passkey_creation_intro_body_password" msgid="8825872426579958200">"Անցաբառերի շնորհիվ դուք բարդ գաղտնաբառեր ստեղծելու կամ հիշելու անհրաժեշտություն չեք ունենա"</string>
- <string name="passkey_creation_intro_body_fingerprint" msgid="7331338631826254055">"Անցաբառերը գաղտնագրված թվային բանալիներ են, որոնք ստեղծվում են մատնահետքի, դեմքով ապակողպման կամ էկրանի կողպման օգտագործմամբ"</string>
+ <string name="passkey_creation_intro_title" msgid="4251037543787718844">"Մուտքի բանալիների հետ ավելի ապահով է"</string>
+ <string name="passkey_creation_intro_body_password" msgid="8825872426579958200">"Մուտքի բանալիների շնորհիվ դուք բարդ գաղտնաբառեր ստեղծելու կամ հիշելու անհրաժեշտություն չեք ունենա"</string>
+ <string name="passkey_creation_intro_body_fingerprint" msgid="7331338631826254055">"Մուտքի բանալիները գաղտնագրված թվային բանալիներ են, որոնք ստեղծվում են մատնահետքի, դեմքով ապակողպման կամ էկրանի կողպման օգտագործմամբ"</string>
<string name="passkey_creation_intro_body_device" msgid="1203796455762131631">"Դուք կարող եք մուտք գործել այլ սարքերում, քանի որ մուտքի բանալիները պահվում են գաղտնաբառերի կառավարիչում"</string>
<string name="more_about_passkeys_title" msgid="7797903098728837795">"Ավելին՝ մուտքի բանալիների մասին"</string>
<string name="passwordless_technology_title" msgid="2497513482056606668">"Գաղտնաբառեր չպահանջող տեխնոլոգիա"</string>
<string name="passwordless_technology_detail" msgid="6853928846532955882">"Մուտքի բանալիները ձեզ թույլ են տալիս մուտք գործել առանց գաղտնաբառերի։ Ձեզ պարզապես հարկավոր է օգտագործել ձեր մատնահետքը, դիմաճանաչումը, PIN կոդը կամ նախշը՝ ձեր ինքնությունը հաստատելու և մուտքի բանալի ստեղծելու համար։"</string>
<string name="public_key_cryptography_title" msgid="6751970819265298039">"Բաց բանալու կրիպտոգրաֆիա"</string>
- <string name="public_key_cryptography_detail" msgid="6937631710280562213">"Ըստ FIDO դաշինքի (որը ներառում է Google-ը, Apple-ը, Microsoft-ը և այլ ընկերություններ) և W3C ստանդարտների՝ անցաբառերն օգտագործում են կրիպտոգրաֆիկ բանալիների զույգ։ Օգտանվան և գաղտնաբառի փոխարեն հավելվածի կամ կայքի համար մենք ստեղծում ենք բաց-փակ բանալիների զույգ։ Փակ բանալին ապահով պահվում է ձեր սարքում կամ գաղտնաբառերի կառավարիչում և հաստատում է ձեր ինքնությունը։ Բաց բանալին փոխանցվում է հավելվածի կամ կայքի սերվերին։ Համապատասխան բանալիների միջոցով կարող եք ակնթարթորեն գրանցվել և մուտք գործել։"</string>
+ <string name="public_key_cryptography_detail" msgid="6937631710280562213">"Ըստ FIDO դաշինքի (որը ներառում է Google-ը, Apple-ը, Microsoft-ը և այլ ընկերություններ) և W3C ստանդարտների՝ մուտքի բանալիներն օգտագործում են կրիպտոգրաֆիկ բանալիների զույգ։ Օգտանվան և գաղտնաբառի փոխարեն հավելվածի կամ կայքի համար մենք ստեղծում ենք բաց-փակ բանալիների զույգ։ Փակ բանալին ապահով պահվում է ձեր սարքում կամ գաղտնաբառերի կառավարիչում և հաստատում է ձեր ինքնությունը։ Բաց բանալին փոխանցվում է հավելվածի կամ կայքի սերվերին։ Համապատասխան բանալիների միջոցով կարող եք ակնթարթորեն գրանցվել և մուտք գործել։"</string>
<string name="improved_account_security_title" msgid="1069841917893513424">"Հաշվի բարելավված անվտանգություն"</string>
<string name="improved_account_security_detail" msgid="9123750251551844860">"Յուրաքանչյուր բանալի բացառապես կապված է հավելվածի կամ կայքի հետ, որի համար այն ստեղծվել է, ուստի դուք երբեք չեք կարող սխալմամբ մուտք գործել կեղծ հավելված կամ կայք։ Բացի այդ՝ սերվերներում պահվում են միայն բաց բանալիներ, ինչը զգալիորեն դժվարացնում է կոտրումը։"</string>
<string name="seamless_transition_title" msgid="5335622196351371961">"Սահուն անցում"</string>
@@ -61,7 +61,7 @@
<string name="more_options_usage_passwords" msgid="1632047277723187813">"<xliff:g id="PASSWORDSNUMBER">%1$s</xliff:g> գաղտնաբառ"</string>
<string name="more_options_usage_passkeys" msgid="5390320437243042237">"<xliff:g id="PASSKEYSNUMBER">%1$s</xliff:g> մուտքի բանալի"</string>
<string name="more_options_usage_credentials" msgid="1785697001787193984">"Մուտքի տվյալներ (<xliff:g id="TOTALCREDENTIALSNUMBER">%1$s</xliff:g>)"</string>
- <string name="passkey_before_subtitle" msgid="2448119456208647444">"Անցաբառ"</string>
+ <string name="passkey_before_subtitle" msgid="2448119456208647444">"Մուտքի բանալի"</string>
<string name="another_device" msgid="5147276802037801217">"Այլ սարք"</string>
<string name="other_password_manager" msgid="565790221427004141">"Գաղտնաբառերի այլ կառավարիչներ"</string>
<string name="close_sheet" msgid="1393792015338908262">"Փակել թերթը"</string>
diff --git a/packages/CredentialManager/res/values-ka/strings.xml b/packages/CredentialManager/res/values-ka/strings.xml
index 4224da6..452565c 100644
--- a/packages/CredentialManager/res/values-ka/strings.xml
+++ b/packages/CredentialManager/res/values-ka/strings.xml
@@ -74,7 +74,7 @@
<string name="get_dialog_description_single_tap" msgid="2797059565126030879">"გამოიყენეთ თქვენი ეკრანის დაბლოკვის ფუნქცია <xliff:g id="APP_NAME">%1$s</xliff:g>-ში <xliff:g id="USERNAME">%2$s</xliff:g>-ით შესასვლელად"</string>
<string name="get_dialog_title_unlock_options_for" msgid="7096423827682163270">"შესვლის ვარიანტების განბლოკვა <xliff:g id="APP_NAME">%1$s</xliff:g>-ისთვის"</string>
<string name="get_dialog_title_choose_passkey_for" msgid="9175997688078538490">"აირჩიეთ შენახული წვდომის გასაღები <xliff:g id="APP_NAME">%1$s</xliff:g>-სთვის"</string>
- <string name="get_dialog_title_choose_password_for" msgid="1724435823820819221">"აირჩიეთ შენახული პაროლი <xliff:g id="APP_NAME">%1$s</xliff:g>-სთვის"</string>
+ <string name="get_dialog_title_choose_password_for" msgid="1724435823820819221">"აირჩიეთ შენახული პაროლი <xliff:g id="APP_NAME">%1$s</xliff:g>-ისთვის"</string>
<string name="get_dialog_title_choose_saved_sign_in_for" msgid="2420298653461652728">"აირჩიეთ სისტემაში შესვლის ინფორმაცია <xliff:g id="APP_NAME">%1$s</xliff:g>-სთვის"</string>
<string name="get_dialog_title_choose_sign_in_for" msgid="645728947702442421">"აირჩიეთ ანგარიში <xliff:g id="APP_NAME">%1$s</xliff:g>-ისთვის"</string>
<string name="get_dialog_title_choose_option_for" msgid="4976380044745029107">"გსურთ აირჩიოთ ვარიანტი <xliff:g id="APP_NAME">%1$s</xliff:g>-ისთვის?"</string>
diff --git a/packages/CredentialManager/res/values-kk/strings.xml b/packages/CredentialManager/res/values-kk/strings.xml
index 2fd31ee..c3bfc4f 100644
--- a/packages/CredentialManager/res/values-kk/strings.xml
+++ b/packages/CredentialManager/res/values-kk/strings.xml
@@ -90,7 +90,7 @@
<string name="locked_credential_entry_label_subtext_no_sign_in" msgid="8131725029983174901">"Кіру ақпараты жоқ."</string>
<string name="no_sign_in_info_in" msgid="2641118151920288356">"<xliff:g id="SOURCE">%1$s</xliff:g> аккаунтында кіру туралы ешқандай ақпарат жоқ."</string>
<string name="get_dialog_heading_manage_sign_ins" msgid="3522556476480676782">"Кіру әрекеттерін басқару"</string>
- <string name="get_dialog_heading_from_another_device" msgid="1166697017046724072">"Басқа құрылғыдан жасау"</string>
+ <string name="get_dialog_heading_from_another_device" msgid="1166697017046724072">"Басқа құрылғыдан"</string>
<string name="get_dialog_option_headline_use_a_different_device" msgid="8201578814988047549">"Басқа құрылғыны пайдалану"</string>
<string name="request_cancelled_by" msgid="3735222326886267820">"<xliff:g id="APP_NAME">%1$s</xliff:g> қолданбасы сұрауды тоқтатты."</string>
<string name="dropdown_presentation_more_sign_in_options_text" msgid="1693727354272417902">"Кіру опциялары"</string>
diff --git a/packages/CredentialManager/res/values-ne/strings.xml b/packages/CredentialManager/res/values-ne/strings.xml
index 07775e0..7ac1d1a 100644
--- a/packages/CredentialManager/res/values-ne/strings.xml
+++ b/packages/CredentialManager/res/values-ne/strings.xml
@@ -91,7 +91,7 @@
<string name="no_sign_in_info_in" msgid="2641118151920288356">"<xliff:g id="SOURCE">%1$s</xliff:g> मा साइन इन गर्नेसम्बन्धी कुनै पनि जानकारी छैन"</string>
<string name="get_dialog_heading_manage_sign_ins" msgid="3522556476480676782">"साइन इनसम्बन्धी विकल्पहरू व्यवस्थापन गर्नुहोस्"</string>
<string name="get_dialog_heading_from_another_device" msgid="1166697017046724072">"अर्को डिभाइसका लागि"</string>
- <string name="get_dialog_option_headline_use_a_different_device" msgid="8201578814988047549">"अर्कै डिभाइस प्रयोग गरी हेर्नुहोस्"</string>
+ <string name="get_dialog_option_headline_use_a_different_device" msgid="8201578814988047549">"अर्कै डिभाइस प्रयोग गर्नुहोस्"</string>
<string name="request_cancelled_by" msgid="3735222326886267820">"<xliff:g id="APP_NAME">%1$s</xliff:g> ले अनुरोध रद्द गरेको छ"</string>
<string name="dropdown_presentation_more_sign_in_options_text" msgid="1693727354272417902">"साइन इनसम्बन्धी विकल्पहरू"</string>
<string name="more_options_content_description" msgid="1323427365788198808">"थप"</string>
diff --git a/packages/CredentialManager/res/values-pt-rPT/strings.xml b/packages/CredentialManager/res/values-pt-rPT/strings.xml
index a3476d9..9186c59 100644
--- a/packages/CredentialManager/res/values-pt-rPT/strings.xml
+++ b/packages/CredentialManager/res/values-pt-rPT/strings.xml
@@ -91,7 +91,7 @@
<string name="no_sign_in_info_in" msgid="2641118151920288356">"Sem informações de início de sessão em <xliff:g id="SOURCE">%1$s</xliff:g>"</string>
<string name="get_dialog_heading_manage_sign_ins" msgid="3522556476480676782">"Faça a gestão dos inícios de sessão"</string>
<string name="get_dialog_heading_from_another_device" msgid="1166697017046724072">"De outro dispositivo"</string>
- <string name="get_dialog_option_headline_use_a_different_device" msgid="8201578814988047549">"Use um dispositivo diferente"</string>
+ <string name="get_dialog_option_headline_use_a_different_device" msgid="8201578814988047549">"Use outro diferente"</string>
<string name="request_cancelled_by" msgid="3735222326886267820">"Pedido cancelado pela app <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
<string name="dropdown_presentation_more_sign_in_options_text" msgid="1693727354272417902">"Opções de início de sessão"</string>
<string name="more_options_content_description" msgid="1323427365788198808">"Mais"</string>
diff --git a/packages/CredentialManager/src/com/android/credentialmanager/CredentialSelectorViewModel.kt b/packages/CredentialManager/src/com/android/credentialmanager/CredentialSelectorViewModel.kt
index d4a8110..7bc25ed 100644
--- a/packages/CredentialManager/src/com/android/credentialmanager/CredentialSelectorViewModel.kt
+++ b/packages/CredentialManager/src/com/android/credentialmanager/CredentialSelectorViewModel.kt
@@ -174,11 +174,8 @@
onUserCancel()
} else {
Log.d(Constants.LOG_TAG, "The provider activity was cancelled," +
- " re-displaying our UI.")
- uiState = uiState.copy(
- selectedEntry = null,
- providerActivityState = ProviderActivityState.NOT_APPLICABLE,
- )
+ " re-displaying our UI.")
+ resetUiStateForReLaunch()
}
} else {
if (entry != null) {
@@ -202,6 +199,15 @@
}
}
+ // Resets UI states for any situation that re-launches the UI
+ private fun resetUiStateForReLaunch() {
+ onBiometricPromptStateChange(BiometricPromptState.INACTIVE)
+ uiState = uiState.copy(
+ selectedEntry = null,
+ providerActivityState = ProviderActivityState.NOT_APPLICABLE,
+ )
+ }
+
fun onLastLockedAuthEntryNotFoundError() {
Log.d(Constants.LOG_TAG, "Unable to find the last unlocked entry")
onInternalError()
@@ -502,4 +508,4 @@
fun logUiEvent(uiEventEnum: UiEventEnum) {
this.uiMetrics.log(uiEventEnum, credManRepo.requestInfo?.packageName)
}
-}
\ No newline at end of file
+}
diff --git a/packages/CredentialManager/wear/src/com/android/credentialmanager/ui/mappers/CredentialSelectorUiStateGetMapper.kt b/packages/CredentialManager/wear/src/com/android/credentialmanager/ui/mappers/CredentialSelectorUiStateGetMapper.kt
index 0417533..473094c 100644
--- a/packages/CredentialManager/wear/src/com/android/credentialmanager/ui/mappers/CredentialSelectorUiStateGetMapper.kt
+++ b/packages/CredentialManager/wear/src/com/android/credentialmanager/ui/mappers/CredentialSelectorUiStateGetMapper.kt
@@ -22,6 +22,7 @@
import com.android.credentialmanager.CredentialSelectorUiState.Get.MultipleEntry.PerUserNameEntries
import com.android.credentialmanager.model.CredentialType
import com.android.credentialmanager.model.get.CredentialEntryInfo
+import java.time.Instant
fun Request.Get.toGet(isPrimary: Boolean): CredentialSelectorUiState.Get {
val accounts = providerInfos
@@ -67,4 +68,4 @@
val comparator = compareBy<CredentialEntryInfo> { entryInfo ->
// Passkey type always go first
entryInfo.credentialType.let { if (it == CredentialType.PASSKEY) 0 else 1 }
-}.thenByDescending { it.lastUsedTimeMillis ?: 0 }
+}.thenByDescending { it.lastUsedTimeMillis ?: Instant.EPOCH }
diff --git a/packages/DynamicSystemInstallationService/src/com/android/dynsystem/DynamicSystemInstallationService.java b/packages/DynamicSystemInstallationService/src/com/android/dynsystem/DynamicSystemInstallationService.java
index 25ac3c9..635dc42 100644
--- a/packages/DynamicSystemInstallationService/src/com/android/dynsystem/DynamicSystemInstallationService.java
+++ b/packages/DynamicSystemInstallationService/src/com/android/dynsystem/DynamicSystemInstallationService.java
@@ -172,7 +172,7 @@
// This is for testing only now
private boolean mEnableWhenCompleted;
- private boolean mOneShot;
+ private boolean mOneShot = true;
private boolean mHideNotification;
private InstallationAsyncTask.Progress mInstallTaskProgress;
diff --git a/packages/PrintSpooler/TEST_MAPPING b/packages/PrintSpooler/TEST_MAPPING
index 4fa8822..ad3b44f 100644
--- a/packages/PrintSpooler/TEST_MAPPING
+++ b/packages/PrintSpooler/TEST_MAPPING
@@ -8,5 +8,10 @@
}
]
}
+ ],
+ "postsubmit": [
+ {
+ "name": "PrintSpoolerOutOfProcessTests"
+ }
]
}
diff --git a/packages/PrintSpooler/res/values-night/themes.xml b/packages/PrintSpooler/res/values-night/themes.xml
index 3cc64a6..76fa7b9 100644
--- a/packages/PrintSpooler/res/values-night/themes.xml
+++ b/packages/PrintSpooler/res/values-night/themes.xml
@@ -24,6 +24,7 @@
<style name="Theme.SelectPrinterActivity"
parent="android:style/Theme.DeviceDefault">
<item name="android:textAppearanceListItemSecondary">@style/ListItemSecondary</item>
+ <item name="android:windowOptOutEdgeToEdgeEnforcement">true</item>
</style>
<style name="Theme.PrintActivity" parent="@android:style/Theme.DeviceDefault">
diff --git a/packages/PrintSpooler/res/values/themes.xml b/packages/PrintSpooler/res/values/themes.xml
index bd96025..22842f7 100644
--- a/packages/PrintSpooler/res/values/themes.xml
+++ b/packages/PrintSpooler/res/values/themes.xml
@@ -24,6 +24,7 @@
parent="android:style/Theme.DeviceDefault.Light">
<item name="android:textAppearanceListItemSecondary">@style/ListItemSecondary</item>
<item name="android:windowLightStatusBar">true</item>
+ <item name="android:windowOptOutEdgeToEdgeEnforcement">true</item>
</style>
<style name="Theme.PrintActivity" parent="@android:style/Theme.DeviceDefault.Light">
diff --git a/packages/PrintSpooler/src/com/android/printspooler/ui/PrintActivity.java b/packages/PrintSpooler/src/com/android/printspooler/ui/PrintActivity.java
index d25d5dc..ff09084 100644
--- a/packages/PrintSpooler/src/com/android/printspooler/ui/PrintActivity.java
+++ b/packages/PrintSpooler/src/com/android/printspooler/ui/PrintActivity.java
@@ -785,6 +785,9 @@
} else {
onPrinterUnavailable(printerInfo);
}
+ if (mPrinterRegistry != null) {
+ mPrinterRegistry.setTrackedPrinter(mCurrentPrinter.getId());
+ }
mDestinationSpinnerAdapter.ensurePrinterInVisibleAdapterPosition(printerInfo);
diff --git a/packages/SettingsLib/DataStore/README.md b/packages/SettingsLib/DataStore/README.md
index 30cb993..a762ad3 100644
--- a/packages/SettingsLib/DataStore/README.md
+++ b/packages/SettingsLib/DataStore/README.md
@@ -1,55 +1,93 @@
# Datastore library
-This library aims to manage datastore in a consistent way.
+This library provides consistent API for data management (including backup,
+restore, and metrics logging) on Android platform.
+
+Notably, it is designed to be flexible and could be utilized for a wide range of
+data store besides the settings preferences.
## Overview
-A datastore is required to extend the `BackupRestoreStorage` class and implement
-either `Observable` or `KeyedObservable` interface, which enforces:
+In the high-level design, a persistent datastore aims to support two key
+characteristics:
-- Backup and restore: Datastore should support
- [data backup](https://developer.android.com/guide/topics/data/backup) to
- preserve user experiences on a new device.
-- Observer pattern: The
- [observer pattern](https://en.wikipedia.org/wiki/Observer_pattern) allows to
- monitor data change in the datastore and
- - trigger
- [BackupManager.dataChanged](https://developer.android.com/reference/android/app/backup/BackupManager#dataChanged\(\))
- automatically.
- - track data change event to log metrics.
- - update internal state and take action.
+- **observable**: triggers backup and metrics logging whenever data is
+ changed.
+- **transferable**: offers users with a seamless experience by backing up and
+ restoring data on to new devices.
+
+More specifically, Android framework supports
+[data backup](https://developer.android.com/guide/topics/data/backup) to
+preserve user experiences on a new device. And the
+[observer pattern](https://en.wikipedia.org/wiki/Observer_pattern) allows to
+monitor data change.
### Backup and restore
-The Android backup framework provides
+Currently, the Android backup framework provides
[BackupAgentHelper](https://developer.android.com/reference/android/app/backup/BackupAgentHelper)
and
[BackupHelper](https://developer.android.com/reference/android/app/backup/BackupHelper)
-to back up a datastore. However, there are several caveats when implement
-`BackupHelper`:
+to facilitate data backup. However, there are several caveats to consider when
+implementing `BackupHelper`:
-- performBackup: The data is updated incrementally but it is not well
+- *performBackup*: The data is updated incrementally but it is not well
documented. The `ParcelFileDescriptor` state parameters are normally ignored
and data is updated even there is no change.
-- restoreEntity: The implementation must take care not to seek or close the
- underlying data source, nor read more than size() bytes from the stream when
- restore (see
+- *restoreEntity*: The implementation must take care not to seek or close the
+ underlying data source, nor read more than `size()` bytes from the stream
+ when restore (see
[BackupDataInputStream](https://developer.android.com/reference/android/app/backup/BackupDataInputStream)).
- It is possible a `BackupHelper` prevents other `BackupHelper`s from
- restoring data.
-- writeNewStateDescription: Existing implementations rarely notice that this
- callback is invoked after all entities are restored, and check if necessary
- data are all restored in `restoreEntity` (e.g.
+ It is possible that a `BackupHelper` interferes with the restore process of
+ other `BackupHelper`s.
+- *writeNewStateDescription*: Existing implementations rarely notice that this
+ callback is invoked after *all* entities are restored. Instead, they check
+ if necessary data are all restored in the `restoreEntity` (e.g.
[BatteryBackupHelper](https://cs.android.com/android/platform/superproject/main/+/main:packages/apps/Settings/src/com/android/settings/fuelgauge/BatteryBackupHelper.java;l=144;drc=cca804e1ed504e2d477be1e3db00fb881ca32736)),
which is not robust sometimes.
-This library provides more clear API and offers some improvements:
+The datastore library will mitigate these problems by providing alternative
+APIs. For instance, library users make use of `InputStream` / `OutputStream` to
+back up and restore data directly.
-- The implementation only needs to focus on the `BackupRestoreEntity`
- interface. The `InputStream` of restore will ensure bounded data are read,
- and close the stream will be no-op.
-- The library computes checksum of the backup data automatically, so that
- unchanged data will not be sent to Android backup system.
+### Observer pattern
+
+In the current implementation, the Android backup framework requires a manual
+call to
+[BackupManager.dataChanged](https://developer.android.com/reference/android/app/backup/BackupManager#dataChanged\(\)).
+However, it's often observed that this API call is forgotten when using
+`SharedPreferences`. Additionally, there's a common need to log metrics when
+data changed. To address these limitations, datastore API employed the observer
+pattern.
+
+### API design and advantages
+
+Datastore must extend the `BackupRestoreStorage` class (subclass of
+[BackupHelper](https://developer.android.com/reference/android/app/backup/BackupHelper)).
+The data in a datastore is group by entity, which is represented by
+`BackupRestoreEntity`. Basically, a datastore implementation only needs to focus
+on the `BackupRestoreEntity`.
+
+If the datastore is key-value based (e.g. `SharedPreferences`), implements the
+`KeyedObservable` interface to offer fine-grained observer. Otherwise,
+implements `Observable`. There are builtin thread-safe implementations of the
+two interfaces (`KeyedDataObservable` / `DataObservable`). If it is Kotlin, use
+delegation to simplify the code.
+
+Keep in mind that the implementation should call `KeyedObservable.notifyChange`
+/ `Observable.notifyChange` whenever internal data is changed, so that the
+registered observer will be notified properly.
+
+For `SharedPreferences` use case, leverage the `SharedPreferencesStorage`
+directly. To back up other file based storage, extend the
+`BackupRestoreFileStorage` class.
+
+Here are some highlights of the library:
+
+- The restore `InputStream` will ensure bounded data are read, and close the
+ stream is no-op. That being said, all entities are isolated.
+- Data checksum is computed automatically, unchanged data will not be sent to
+ Android backup system.
- Data compression is supported:
- ZIP best compression is enabled by default, no extra effort needs to be
taken.
@@ -67,98 +105,159 @@
successfully restored in those older versions. This is achieved by extending
the `BackupRestoreFileStorage` class, and `BackupRestoreFileArchiver` will
treat each file as an entity and do the backup / restore.
-- Manual `BackupManager.dataChanged` call is unnecessary now, the library will
- do the invocation (see next section).
+- Manual `BackupManager.dataChanged` call is unnecessary now, the framework
+ will invoke the API automatically.
-### Observer pattern
+## Usages
-Manual `BackupManager.dataChanged` call is required by current backup framework.
-In practice, it is found that `SharedPreferences` usages foget to invoke the
-API. Besides, there are common use cases to log metrics when data is changed.
-Consequently, observer pattern is employed to resolve the issues.
+This section provides [examples](example/ExampleStorage.kt) of datastore.
-If the datastore is key-value based (e.g. `SharedPreferences`), implements the
-`KeyedObservable` interface to offer fine-grained observer. Otherwise,
-implements `Observable`. The library provides thread-safe implementations
-(`KeyedDataObservable` / `DataObservable`), and Kotlin delegation will be
-helpful.
-
-Keep in mind that the implementation should call `KeyedObservable.notifyChange`
-/ `Observable.notifyChange` whenever internal data is changed, so that the
-registered observer will be notified properly.
-
-## Usage and example
-
-For `SharedPreferences` use case, leverage the `SharedPreferencesStorage`. To
-back up other file based storage, extend the `BackupRestoreFileStorage` class.
-
-Here is an example of customized datastore, which has a string to back up:
+Here is a datastore with a string data:
```kotlin
-class MyDataStore : ObservableBackupRestoreStorage() {
- // Another option is make it a StringEntity type and maintain a String field inside StringEntity
- @Volatile // backup/restore happens on Binder thread
- var data: String? = null
- private set
+class ExampleStorage : ObservableBackupRestoreStorage() {
+ @Volatile // field is manipulated by multiple threads, synchronization might be needed
+ var data: String? = null
+ private set
- fun setData(data: String?) {
- this.data = data
- notifyChange(ChangeReason.UPDATE)
+ @AnyThread
+ fun setData(data: String?) {
+ this.data = data
+ // call notifyChange to trigger backup and metrics logging whenever data is changed
+ if (data != null) {
+ notifyChange(ChangeReason.UPDATE)
+ } else {
+ notifyChange(ChangeReason.DELETE)
}
-
- override val name: String
- get() = "MyData"
-
- override fun createBackupRestoreEntities(): List<BackupRestoreEntity> =
- listOf(StringEntity("data"))
-
- private inner class StringEntity(override val key: String) : BackupRestoreEntity {
- override fun backup(
- backupContext: BackupContext,
- outputStream: OutputStream,
- ) =
- if (data != null) {
- outputStream.write(data!!.toByteArray(UTF_8))
- EntityBackupResult.UPDATE
- } else {
- EntityBackupResult.DELETE
- }
-
- override fun restore(restoreContext: RestoreContext, inputStream: InputStream) {
- data = String(inputStream.readAllBytes(), UTF_8)
- // NOTE: The library will call notifyChange(ChangeReason.RESTORE) for you
- }
- }
-
- override fun onRestoreFinished() {
- // TODO: Update state with the restored data. Use this callback instead "restore()" in case
- // the restore action involves several entities.
- // NOTE: The library will call notifyChange(ChangeReason.RESTORE) for you
- }
-}
-```
-
-In the application class:
-
-```kotlin
-class MyApplication : Application() {
- override fun onCreate() {
- super.onCreate();
- BackupRestoreStorageManager.getInstance(this).add(MyDataStore());
}
-}
-```
-In the custom `BackupAgentHelper` class:
+ override val name: String
+ get() = "ExampleStorage"
-```kotlin
-class MyBackupAgentHelper : BackupAgentHelper() {
- override fun onCreate() {
- BackupRestoreStorageManager.getInstance(this).addBackupAgentHelpers(this);
+ override fun createBackupRestoreEntities(): List<BackupRestoreEntity> =
+ listOf(StringEntity("data"))
+
+ override fun enableRestore(): Boolean {
+ return true // check condition like flag, environment, etc.
+ }
+
+ override fun enableBackup(backupContext: BackupContext): Boolean {
+ return true // check condition like flag, environment, etc.
+ }
+
+ @BinderThread
+ private inner class StringEntity(override val key: String) : BackupRestoreEntity {
+ override fun backup(backupContext: BackupContext, outputStream: OutputStream) =
+ if (data != null) {
+ outputStream.write(data!!.toByteArray(UTF_8))
+ EntityBackupResult.UPDATE
+ } else {
+ EntityBackupResult.DELETE // delete existing backup data
+ }
+
+ override fun restore(restoreContext: RestoreContext, inputStream: InputStream) {
+ // DO NOT call setData API here, which will trigger notifyChange unexpectedly.
+ // Under the hood, the datastore library will call notifyChange(ChangeReason.RESTORE)
+ // later to notify observers.
+ data = String(inputStream.readBytes(), UTF_8)
+ // Handle restored data in onRestoreFinished() callback
+ }
}
override fun onRestoreFinished() {
- BackupRestoreStorageManager.getInstance(this).onRestoreFinished();
+ // TODO: Update state with the restored data. Use this callback instead of "restore()" in
+ // case the restore action involves several entities.
+ // NOTE: The library will call notifyChange(ChangeReason.RESTORE) for you
}
}
```
+
+And this is a datastore with key value data:
+
+```kotlin
+class ExampleKeyValueStorage :
+ BackupRestoreStorage(), KeyedObservable<String> by KeyedDataObservable() {
+ // thread safe data structure
+ private val map = ConcurrentHashMap<String, String>()
+
+ override val name: String
+ get() = "ExampleKeyValueStorage"
+
+ fun updateData(key: String, value: String?) {
+ if (value != null) {
+ map[key] = value
+ notifyChange(ChangeReason.UPDATE)
+ } else {
+ map.remove(key)
+ notifyChange(ChangeReason.DELETE)
+ }
+ }
+
+ override fun createBackupRestoreEntities(): List<BackupRestoreEntity> =
+ listOf(createMapBackupRestoreEntity())
+
+ private fun createMapBackupRestoreEntity() =
+ object : BackupRestoreEntity {
+ override val key: String
+ get() = "map"
+
+ override fun backup(
+ backupContext: BackupContext,
+ outputStream: OutputStream,
+ ): EntityBackupResult {
+ // Use TreeMap to achieve predictable and stable order, so that data will not be
+ // updated to Android backup backend if there is only order change.
+ val copy = TreeMap(map)
+ if (copy.isEmpty()) return EntityBackupResult.DELETE
+ val dataOutputStream = DataOutputStream(outputStream)
+ dataOutputStream.writeInt(copy.size)
+ for ((key, value) in copy) {
+ dataOutputStream.writeUTF(key)
+ dataOutputStream.writeUTF(value)
+ }
+ return EntityBackupResult.UPDATE
+ }
+
+ override fun restore(restoreContext: RestoreContext, inputStream: InputStream) {
+ val dataInputString = DataInputStream(inputStream)
+ repeat(dataInputString.readInt()) {
+ val key = dataInputString.readUTF()
+ val value = dataInputString.readUTF()
+ map[key] = value
+ }
+ }
+ }
+}
+```
+
+All the datastore should be added in the application class:
+
+```kotlin
+class ExampleApplication : Application() {
+ override fun onCreate() {
+ super.onCreate()
+ BackupRestoreStorageManager.getInstance(this)
+ .add(ExampleStorage(), ExampleKeyValueStorage())
+ }
+}
+```
+
+Additionally, inject datastore to the custom `BackupAgentHelper` class:
+
+```kotlin
+class ExampleBackupAgent : BackupAgentHelper() {
+ override fun onCreate() {
+ super.onCreate()
+ BackupRestoreStorageManager.getInstance(this).addBackupAgentHelpers(this)
+ }
+
+ override fun onRestoreFinished() {
+ BackupRestoreStorageManager.getInstance(this).onRestoreFinished()
+ }
+}
+```
+
+## Development
+
+Please preserve the code coverage ratio during development. The current line
+coverage is **100% (444/444)** and branch coverage is **93.6% (176/188)**.
diff --git a/packages/SettingsLib/DataStore/src/com/android/settingslib/datastore/BackupRestoreEntity.kt b/packages/SettingsLib/DataStore/src/com/android/settingslib/datastore/BackupRestoreEntity.kt
index 817ee4c..6720e5c 100644
--- a/packages/SettingsLib/DataStore/src/com/android/settingslib/datastore/BackupRestoreEntity.kt
+++ b/packages/SettingsLib/DataStore/src/com/android/settingslib/datastore/BackupRestoreEntity.kt
@@ -23,7 +23,11 @@
import java.io.InputStream
import java.io.OutputStream
-/** Entity for back up and restore. */
+/**
+ * Entity for back up and restore.
+ *
+ * Note that backup/restore callback is invoked on the binder thread.
+ */
interface BackupRestoreEntity {
/**
* Key of the entity.
@@ -45,9 +49,12 @@
/**
* Backs up the entity.
*
+ * Back up data in predictable order (e.g. use `TreeMap` instead of `HashMap`), otherwise data
+ * will be backed up needlessly.
+ *
* @param backupContext context for backup
* @param outputStream output stream to back up data
- * @return false if backup file is deleted, otherwise true
+ * @return backup result
*/
@BinderThread
@Throws(IOException::class)
diff --git a/packages/SettingsLib/DataStore/src/com/android/settingslib/datastore/BackupRestoreStorage.kt b/packages/SettingsLib/DataStore/src/com/android/settingslib/datastore/BackupRestoreStorage.kt
index 935f9cc..284c97b 100644
--- a/packages/SettingsLib/DataStore/src/com/android/settingslib/datastore/BackupRestoreStorage.kt
+++ b/packages/SettingsLib/DataStore/src/com/android/settingslib/datastore/BackupRestoreStorage.kt
@@ -22,6 +22,7 @@
import android.app.backup.BackupHelper
import android.os.ParcelFileDescriptor
import android.util.Log
+import androidx.annotation.BinderThread
import androidx.annotation.VisibleForTesting
import androidx.collection.MutableScatterMap
import com.google.common.io.ByteStreams
@@ -38,16 +39,22 @@
import java.util.zip.CheckedInputStream
import java.util.zip.CheckedOutputStream
import java.util.zip.Checksum
+import javax.annotation.concurrent.ThreadSafe
internal const val LOG_TAG = "BackupRestoreStorage"
/**
- * Storage with backup and restore support. Subclass must implement either [Observable] or
- * [KeyedObservable] interface.
+ * Storage with backup and restore support.
+ *
+ * Subclass MUST
+ * - implement either [Observable] or [KeyedObservable] interface.
+ * - be thread safe, backup/restore happens on Binder thread, while general data read/write
+ * operations occur on other threads.
*
* The storage is identified by a unique string [name] and data set is split into entities
* ([BackupRestoreEntity]).
*/
+@ThreadSafe
abstract class BackupRestoreStorage : BackupHelper {
/**
* A unique string used to disambiguate the various storages within backup agent.
@@ -68,7 +75,7 @@
@VisibleForTesting internal var entities: List<BackupRestoreEntity>? = null
/** Entities to back up and restore. */
- abstract fun createBackupRestoreEntities(): List<BackupRestoreEntity>
+ @BinderThread abstract fun createBackupRestoreEntities(): List<BackupRestoreEntity>
/** Default codec used to encode/decode the entity data. */
open fun defaultCodec(): BackupCodec = BackupZipCodec.BEST_COMPRESSION
@@ -134,7 +141,11 @@
Log.i(LOG_TAG, "[$name] Backup end")
}
- /** Returns if backup is enabled. */
+ /**
+ * Returns if backup is enabled.
+ *
+ * If disabled, [performBackup] will be no-op, all entities backup are skipped.
+ */
open fun enableBackup(backupContext: BackupContext): Boolean = true
open fun wrapBackupOutputStream(codec: BackupCodec, outputStream: OutputStream): OutputStream {
@@ -172,7 +183,11 @@
private fun ensureEntities(): List<BackupRestoreEntity> =
entities ?: createBackupRestoreEntities().also { entities = it }
- /** Returns if restore is enabled. */
+ /**
+ * Returns if restore is enabled.
+ *
+ * If disabled, [restoreEntity] will be no-op, all entities restore are skipped.
+ */
open fun enableRestore(): Boolean = true
open fun wrapRestoreInputStream(
@@ -188,12 +203,13 @@
}
final override fun writeNewStateDescription(newState: ParcelFileDescriptor) {
+ if (!enableRestore()) return
entities = null // clear to reduce memory footprint
newState.writeAndClearEntityStates()
onRestoreFinished()
}
- /** Callbacks when restore finished. */
+ /** Callbacks when entity data are all restored. */
open fun onRestoreFinished() {}
@VisibleForTesting
diff --git a/packages/SettingsLib/DataStore/tests/src/com/android/settingslib/datastore/BackupRestoreStorageTest.kt b/packages/SettingsLib/DataStore/tests/src/com/android/settingslib/datastore/BackupRestoreStorageTest.kt
index 99998ff..26534ba 100644
--- a/packages/SettingsLib/DataStore/tests/src/com/android/settingslib/datastore/BackupRestoreStorageTest.kt
+++ b/packages/SettingsLib/DataStore/tests/src/com/android/settingslib/datastore/BackupRestoreStorageTest.kt
@@ -248,6 +248,15 @@
}
@Test
+ fun writeNewStateDescription_restoreDisabled() {
+ val storage = spy(TestStorage().apply { enabled = false })
+ temporaryFolder.newFile().toParcelFileDescriptor(MODE_WRITE_ONLY or MODE_APPEND).use {
+ storage.writeNewStateDescription(it)
+ }
+ verify(storage, never()).onRestoreFinished()
+ }
+
+ @Test
fun backupAndRestore() {
val storage = spy(TestStorage(entity1, entity2))
val backupAgentHelper = BackupAgentHelper()
diff --git a/packages/SettingsLib/FooterPreference/src/com/android/settingslib/widget/FooterPreference.java b/packages/SettingsLib/FooterPreference/src/com/android/settingslib/widget/FooterPreference.java
index 05507e0..493818b 100644
--- a/packages/SettingsLib/FooterPreference/src/com/android/settingslib/widget/FooterPreference.java
+++ b/packages/SettingsLib/FooterPreference/src/com/android/settingslib/widget/FooterPreference.java
@@ -80,14 +80,15 @@
continue;
}
final URLSpan urlSpan = (URLSpan) clickable;
- if (!urlSpan.getURL().startsWith(INTENT_URL_PREFIX)) {
+ final String url = urlSpan.getURL();
+ if (url == null || !url.startsWith(INTENT_URL_PREFIX)) {
continue;
}
final int start = spannable.getSpanStart(urlSpan);
final int end = spannable.getSpanEnd(urlSpan);
spannable.removeSpan(urlSpan);
try {
- final Intent intent = Intent.parseUri(urlSpan.getURL(), Intent.URI_INTENT_SCHEME);
+ final Intent intent = Intent.parseUri(url, Intent.URI_INTENT_SCHEME);
final ClickableSpan clickableSpan =
new ClickableSpan() {
@Override
@@ -98,7 +99,7 @@
};
spannable.setSpan(clickableSpan, start, end, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
} catch (URISyntaxException e) {
- Log.e(TAG, "Invalid URI " + urlSpan.getURL(), e);
+ Log.e(TAG, "Invalid URI " + url, e);
}
}
title.setText(spannable);
diff --git a/packages/SettingsLib/IllustrationPreference/Android.bp b/packages/SettingsLib/IllustrationPreference/Android.bp
index 6407810..c3a91a2 100644
--- a/packages/SettingsLib/IllustrationPreference/Android.bp
+++ b/packages/SettingsLib/IllustrationPreference/Android.bp
@@ -21,6 +21,7 @@
"SettingsLibColor",
"androidx.preference_preference",
"lottie",
+ "settingslib_illustrationpreference_flags_lib",
],
sdk_version: "system_current",
@@ -31,3 +32,24 @@
"com.android.permission",
],
}
+
+aconfig_declarations {
+ name: "settingslib_illustrationpreference_flags",
+ package: "com.android.settingslib.widget.flags",
+ container: "system",
+ srcs: [
+ "aconfig/illustrationpreference.aconfig",
+ ],
+}
+
+java_aconfig_library {
+ name: "settingslib_illustrationpreference_flags_lib",
+ aconfig_declarations: "settingslib_illustrationpreference_flags",
+
+ min_sdk_version: "30",
+
+ apex_available: [
+ "//apex_available:platform",
+ "com.android.permission",
+ ],
+}
diff --git a/packages/SettingsLib/IllustrationPreference/aconfig/illustrationpreference.aconfig b/packages/SettingsLib/IllustrationPreference/aconfig/illustrationpreference.aconfig
new file mode 100644
index 0000000..e566d89
--- /dev/null
+++ b/packages/SettingsLib/IllustrationPreference/aconfig/illustrationpreference.aconfig
@@ -0,0 +1,12 @@
+package: "com.android.settingslib.widget.flags"
+container: "system"
+
+flag {
+ name: "auto_hide_empty_lottie_res"
+ namespace: "android_settings"
+ description: "Hides IllustrationPreference when Lottie resource is an empty file"
+ bug: "337873972"
+ metadata {
+ purpose: PURPOSE_BUGFIX
+ }
+}
\ No newline at end of file
diff --git a/packages/SettingsLib/IllustrationPreference/src/com/android/settingslib/widget/IllustrationPreference.java b/packages/SettingsLib/IllustrationPreference/src/com/android/settingslib/widget/IllustrationPreference.java
index 815a101..bbf0315 100644
--- a/packages/SettingsLib/IllustrationPreference/src/com/android/settingslib/widget/IllustrationPreference.java
+++ b/packages/SettingsLib/IllustrationPreference/src/com/android/settingslib/widget/IllustrationPreference.java
@@ -39,12 +39,14 @@
import androidx.preference.PreferenceViewHolder;
import androidx.vectordrawable.graphics.drawable.Animatable2Compat;
+import com.android.settingslib.widget.flags.Flags;
import com.android.settingslib.widget.preference.illustration.R;
import com.airbnb.lottie.LottieAnimationView;
import com.airbnb.lottie.LottieDrawable;
import java.io.FileNotFoundException;
+import java.io.IOException;
import java.io.InputStream;
/**
@@ -142,7 +144,7 @@
illustrationFrame.setLayoutParams(lp);
illustrationView.setCacheComposition(mCacheComposition);
- handleImageWithAnimation(illustrationView);
+ handleImageWithAnimation(illustrationView, illustrationFrame);
handleImageFrameMaxHeight(backgroundView, illustrationView);
if (mIsAutoScale) {
@@ -332,7 +334,8 @@
}
}
- private void handleImageWithAnimation(LottieAnimationView illustrationView) {
+ private void handleImageWithAnimation(LottieAnimationView illustrationView,
+ ViewGroup container) {
if (mImageDrawable != null) {
resetAnimations(illustrationView);
illustrationView.setImageDrawable(mImageDrawable);
@@ -356,6 +359,25 @@
}
if (mImageResId > 0) {
+ if (Flags.autoHideEmptyLottieRes()) {
+ // Check if resource is empty
+ try (InputStream is = illustrationView.getResources()
+ .openRawResource(mImageResId)) {
+ int check = is.read();
+ // -1 = end of stream. if first read is end of stream, then file is empty
+ if (check == -1) {
+ illustrationView.setVisibility(View.GONE);
+ container.setVisibility(View.GONE);
+ return;
+ }
+ } catch (IOException e) {
+ Log.w(TAG, "Unable to open Lottie raw resource", e);
+ }
+
+ illustrationView.setVisibility(View.VISIBLE);
+ container.setVisibility(View.VISIBLE);
+ }
+
resetAnimations(illustrationView);
illustrationView.setImageResource(mImageResId);
final Drawable drawable = illustrationView.getDrawable();
diff --git a/packages/SettingsLib/SettingsTheme/res/color-v35/settingslib_preference_bg_color.xml b/packages/SettingsLib/SettingsTheme/res/color-v35/settingslib_preference_bg_color.xml
index 4ced9f2..cece966 100644
--- a/packages/SettingsLib/SettingsTheme/res/color-v35/settingslib_preference_bg_color.xml
+++ b/packages/SettingsLib/SettingsTheme/res/color-v35/settingslib_preference_bg_color.xml
@@ -16,8 +16,8 @@
-->
<selector xmlns:android="http://schemas.android.com/apk/res/android">
- <item android:state_pressed="true" android:color="@color/settingslib_materialColorSecondaryContainer"/>
- <item android:state_selected="true" android:color="@color/settingslib_materialColorSecondaryContainer"/>
- <item android:state_activated="true" android:color="@color/settingslib_materialColorSecondaryContainer"/>
- <item android:color="@color/settingslib_materialColorSurfaceContainerHighest"/> <!-- not selected -->
+ <item android:state_pressed="true" android:color="@color/settingslib_materialColorSurfaceContainerHigh"/>
+ <item android:state_selected="true" android:color="@color/settingslib_materialColorSurfaceContainerHigh"/>
+ <item android:state_activated="true" android:color="@color/settingslib_materialColorSurfaceContainerHigh"/>
+ <item android:color="@color/settingslib_materialColorSurfaceBright"/> <!-- not selected -->
</selector>
\ No newline at end of file
diff --git a/packages/SettingsLib/SettingsTheme/res/drawable-v35/settingslib_round_background.xml b/packages/SettingsLib/SettingsTheme/res/drawable-v35/settingslib_round_background.xml
index 285ab73..eba9c2c 100644
--- a/packages/SettingsLib/SettingsTheme/res/drawable-v35/settingslib_round_background.xml
+++ b/packages/SettingsLib/SettingsTheme/res/drawable-v35/settingslib_round_background.xml
@@ -19,12 +19,12 @@
<item
android:left="?android:attr/listPreferredItemPaddingStart"
android:right="?android:attr/listPreferredItemPaddingEnd"
- android:top="1dp">
+ android:top="2dp">
<shape android:shape="rectangle">
<solid
android:color="@color/settingslib_preference_bg_color" />
<corners
- android:radius="?android:attr/dialogCornerRadius" />
+ android:radius="@dimen/settingslib_preference_corner_radius" />
</shape>
</item>
</layer-list>
\ No newline at end of file
diff --git a/packages/SettingsLib/SettingsTheme/res/drawable-v35/settingslib_round_background_bottom.xml b/packages/SettingsLib/SettingsTheme/res/drawable-v35/settingslib_round_background_bottom.xml
index e417307..5c60f37 100644
--- a/packages/SettingsLib/SettingsTheme/res/drawable-v35/settingslib_round_background_bottom.xml
+++ b/packages/SettingsLib/SettingsTheme/res/drawable-v35/settingslib_round_background_bottom.xml
@@ -19,15 +19,15 @@
<item
android:left="?android:attr/listPreferredItemPaddingStart"
android:right="?android:attr/listPreferredItemPaddingEnd"
- android:top="1dp">
+ android:top="2dp">
<shape android:shape="rectangle">
<solid
android:color="@color/settingslib_preference_bg_color" />
<corners
- android:topLeftRadius="0dp"
- android:bottomLeftRadius="?android:attr/dialogCornerRadius"
- android:topRightRadius="0dp"
- android:bottomRightRadius="?android:attr/dialogCornerRadius" />
+ android:topLeftRadius="4dp"
+ android:bottomLeftRadius="@dimen/settingslib_preference_corner_radius"
+ android:topRightRadius="4dp"
+ android:bottomRightRadius="@dimen/settingslib_preference_corner_radius" />
</shape>
</item>
</layer-list>
\ No newline at end of file
diff --git a/packages/SettingsLib/SettingsTheme/res/drawable-v35/settingslib_round_background_bottom_selected.xml b/packages/SettingsLib/SettingsTheme/res/drawable-v35/settingslib_round_background_bottom_selected.xml
new file mode 100644
index 0000000..de64efd
--- /dev/null
+++ b/packages/SettingsLib/SettingsTheme/res/drawable-v35/settingslib_round_background_bottom_selected.xml
@@ -0,0 +1,33 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ Copyright (C) 2024 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
+ <item
+ android:left="?android:attr/listPreferredItemPaddingStart"
+ android:right="?android:attr/listPreferredItemPaddingEnd"
+ android:top="2dp">
+ <shape android:shape="rectangle">
+ <solid
+ android:color="@color/settingslib_materialColorSurfaceContainerHigh" />
+ <corners
+ android:topLeftRadius="4dp"
+ android:bottomLeftRadius="@dimen/settingslib_preference_corner_radius"
+ android:topRightRadius="4dp"
+ android:bottomRightRadius="@dimen/settingslib_preference_corner_radius" />
+ </shape>
+ </item>
+</layer-list>
\ No newline at end of file
diff --git a/packages/SettingsLib/SettingsTheme/res/drawable-v35/settingslib_round_background_center.xml b/packages/SettingsLib/SettingsTheme/res/drawable-v35/settingslib_round_background_center.xml
index e964657..dd70f4f 100644
--- a/packages/SettingsLib/SettingsTheme/res/drawable-v35/settingslib_round_background_center.xml
+++ b/packages/SettingsLib/SettingsTheme/res/drawable-v35/settingslib_round_background_center.xml
@@ -19,12 +19,12 @@
<item
android:left="?android:attr/listPreferredItemPaddingStart"
android:right="?android:attr/listPreferredItemPaddingEnd"
- android:top="1dp">
+ android:top="2dp">
<shape android:shape="rectangle">
<solid
android:color="@color/settingslib_preference_bg_color" />
<corners
- android:radius="1dp" />
+ android:radius="4dp" />
</shape>
</item>
</layer-list>
\ No newline at end of file
diff --git a/packages/SettingsLib/SettingsTheme/res/drawable-v35/settingslib_round_background_center_selected.xml b/packages/SettingsLib/SettingsTheme/res/drawable-v35/settingslib_round_background_center_selected.xml
new file mode 100644
index 0000000..fffc6c8
--- /dev/null
+++ b/packages/SettingsLib/SettingsTheme/res/drawable-v35/settingslib_round_background_center_selected.xml
@@ -0,0 +1,30 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ Copyright (C) 2024 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
+ <item
+ android:left="?android:attr/listPreferredItemPaddingStart"
+ android:right="?android:attr/listPreferredItemPaddingEnd"
+ android:top="2dp">
+ <shape android:shape="rectangle">
+ <solid
+ android:color="@color/settingslib_materialColorSurfaceContainerHigh" />
+ <corners
+ android:radius="4dp" />
+ </shape>
+ </item>
+</layer-list>
\ No newline at end of file
diff --git a/packages/SettingsLib/SettingsTheme/res/drawable-v35/settingslib_round_background_selected.xml b/packages/SettingsLib/SettingsTheme/res/drawable-v35/settingslib_round_background_selected.xml
new file mode 100644
index 0000000..f83e3b1
--- /dev/null
+++ b/packages/SettingsLib/SettingsTheme/res/drawable-v35/settingslib_round_background_selected.xml
@@ -0,0 +1,30 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ Copyright (C) 2024 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
+ <item
+ android:left="?android:attr/listPreferredItemPaddingStart"
+ android:right="?android:attr/listPreferredItemPaddingEnd"
+ android:top="2dp">
+ <shape android:shape="rectangle">
+ <solid
+ android:color="@color/settingslib_materialColorSurfaceContainerHigh" />
+ <corners
+ android:radius="@dimen/settingslib_preference_corner_radius" />
+ </shape>
+ </item>
+</layer-list>
\ No newline at end of file
diff --git a/packages/SettingsLib/SettingsTheme/res/drawable-v35/settingslib_round_background_top.xml b/packages/SettingsLib/SettingsTheme/res/drawable-v35/settingslib_round_background_top.xml
index a9d69c2..ab79d18 100644
--- a/packages/SettingsLib/SettingsTheme/res/drawable-v35/settingslib_round_background_top.xml
+++ b/packages/SettingsLib/SettingsTheme/res/drawable-v35/settingslib_round_background_top.xml
@@ -19,15 +19,15 @@
<item
android:left="?android:attr/listPreferredItemPaddingStart"
android:right="?android:attr/listPreferredItemPaddingEnd"
- android:top="1dp">
+ android:top="2dp">
<shape android:shape="rectangle">
<solid
android:color="@color/settingslib_preference_bg_color" />
<corners
- android:topLeftRadius="?android:attr/dialogCornerRadius"
- android:bottomLeftRadius="0dp"
- android:topRightRadius="?android:attr/dialogCornerRadius"
- android:bottomRightRadius="0dp" />
+ android:topLeftRadius="@dimen/settingslib_preference_corner_radius"
+ android:bottomLeftRadius="4dp"
+ android:topRightRadius="@dimen/settingslib_preference_corner_radius"
+ android:bottomRightRadius="4dp" />
</shape>
</item>
</layer-list>
\ No newline at end of file
diff --git a/packages/SettingsLib/SettingsTheme/res/drawable-v35/settingslib_round_background_top_selected.xml b/packages/SettingsLib/SettingsTheme/res/drawable-v35/settingslib_round_background_top_selected.xml
new file mode 100644
index 0000000..112ec73
--- /dev/null
+++ b/packages/SettingsLib/SettingsTheme/res/drawable-v35/settingslib_round_background_top_selected.xml
@@ -0,0 +1,33 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ Copyright (C) 2024 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
+ <item
+ android:left="?android:attr/listPreferredItemPaddingStart"
+ android:right="?android:attr/listPreferredItemPaddingEnd"
+ android:top="2dp">
+ <shape android:shape="rectangle">
+ <solid
+ android:color="@color/settingslib_materialColorSurfaceContainerHigh" />
+ <corners
+ android:topLeftRadius="@dimen/settingslib_preference_corner_radius"
+ android:bottomLeftRadius="4dp"
+ android:topRightRadius="@dimen/settingslib_preference_corner_radius"
+ android:bottomRightRadius="4dp" />
+ </shape>
+ </item>
+</layer-list>
\ No newline at end of file
diff --git a/packages/SettingsLib/SettingsTheme/res/layout-v35/settingslib_preference_category_no_title.xml b/packages/SettingsLib/SettingsTheme/res/layout-v35/settingslib_preference_category_no_title.xml
new file mode 100644
index 0000000..eda7daa
--- /dev/null
+++ b/packages/SettingsLib/SettingsTheme/res/layout-v35/settingslib_preference_category_no_title.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ Copyright (C) 2024 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<LinearLayout
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:baselineAligned="false"
+ android:layout_marginTop="16dp">
+</LinearLayout>
diff --git a/packages/SettingsLib/SettingsTheme/res/values-night-v35/colors.xml b/packages/SettingsLib/SettingsTheme/res/values-night-v35/colors.xml
index 221e8f5..94ff02d 100644
--- a/packages/SettingsLib/SettingsTheme/res/values-night-v35/colors.xml
+++ b/packages/SettingsLib/SettingsTheme/res/values-night-v35/colors.xml
@@ -37,8 +37,11 @@
<!-- Material next track off color-->
<color name="settingslib_track_off_color">@color/settingslib_materialColorSurfaceContainerHighest</color>
+ <!-- Dialog text button color. -->
+ <color name="settingslib_dialog_accent">@color/settingslib_materialColorPrimary</color>
+
<!-- Dialog background color. -->
- <color name="settingslib_dialog_background">@color/settingslib_materialColorSurfaceContainer</color>
+ <color name="settingslib_dialog_background">@color/settingslib_materialColorSurfaceContainerHigh</color>
<color name="settingslib_colorSurfaceHeader">@color/settingslib_materialColorSurfaceVariant</color>
diff --git a/packages/SettingsLib/SettingsTheme/res/values-v35/colors.xml b/packages/SettingsLib/SettingsTheme/res/values-v35/colors.xml
index dc2d3dc..8b95016 100644
--- a/packages/SettingsLib/SettingsTheme/res/values-v35/colors.xml
+++ b/packages/SettingsLib/SettingsTheme/res/values-v35/colors.xml
@@ -37,8 +37,11 @@
<!-- Material next track off color-->
<color name="settingslib_track_off_color">@color/settingslib_materialColorSurfaceContainerHighest</color>
+ <!-- Dialog text button color. -->
+ <color name="settingslib_dialog_accent">@color/settingslib_materialColorPrimary</color>
+
<!-- Dialog background color. -->
- <color name="settingslib_dialog_background">@color/settingslib_materialColorSurfaceContainer</color>
+ <color name="settingslib_dialog_background">@color/settingslib_materialColorSurfaceContainerHigh</color>
<!-- Material next track outline color-->
<color name="settingslib_track_online_color">@color/settingslib_switch_track_outline_color</color>
diff --git a/packages/SettingsLib/SettingsTheme/res/values-v35/dimens.xml b/packages/SettingsLib/SettingsTheme/res/values-v35/dimens.xml
new file mode 100644
index 0000000..d783956
--- /dev/null
+++ b/packages/SettingsLib/SettingsTheme/res/values-v35/dimens.xml
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ Copyright (C) 2024 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<resources>
+ <dimen name="settingslib_preference_corner_radius">20dp</dimen>
+</resources>
\ No newline at end of file
diff --git a/packages/SettingsLib/Spa/build.gradle.kts b/packages/SettingsLib/Spa/build.gradle.kts
index e7823df..45667f5 100644
--- a/packages/SettingsLib/Spa/build.gradle.kts
+++ b/packages/SettingsLib/Spa/build.gradle.kts
@@ -41,7 +41,7 @@
defaultConfig {
minSdk = 21
- targetSdk = 34
+ targetSdk = 35
}
}
diff --git a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/framework/BrowseActivity.kt b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/framework/BrowseActivity.kt
index da1ee77..e867a8f 100644
--- a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/framework/BrowseActivity.kt
+++ b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/framework/BrowseActivity.kt
@@ -21,6 +21,7 @@
import android.util.Log
import androidx.activity.ComponentActivity
import androidx.activity.compose.setContent
+import androidx.activity.enableEdgeToEdge
import androidx.annotation.VisibleForTesting
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.runtime.Composable
@@ -30,7 +31,6 @@
import androidx.compose.runtime.remember
import androidx.compose.runtime.saveable.rememberSaveable
import androidx.compose.ui.Modifier
-import androidx.core.view.WindowCompat
import androidx.navigation.NavBackStackEntry
import androidx.navigation.NavGraph.Companion.findStartDestination
import androidx.navigation.NavGraphBuilder
@@ -82,7 +82,7 @@
override fun onCreate(savedInstanceState: Bundle?) {
setTheme(R.style.Theme_SpaLib)
super.onCreate(savedInstanceState)
- WindowCompat.setDecorFitsSystemWindows(window, false)
+ enableEdgeToEdge()
spaEnvironment.logger.message(TAG, "onCreate", category = LogCategory.FRAMEWORK)
setContent {
diff --git a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/scaffold/CustomizedAppBar.kt b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/scaffold/CustomizedAppBar.kt
index 36cd136..9a344c3 100644
--- a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/scaffold/CustomizedAppBar.kt
+++ b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/scaffold/CustomizedAppBar.kt
@@ -35,6 +35,7 @@
import androidx.compose.foundation.layout.WindowInsetsSides
import androidx.compose.foundation.layout.only
import androidx.compose.foundation.layout.padding
+import androidx.compose.foundation.layout.safeDrawing
import androidx.compose.foundation.layout.windowInsetsPadding
import androidx.compose.material3.ExperimentalMaterial3Api
import androidx.compose.material3.LocalContentColor
@@ -42,11 +43,11 @@
import androidx.compose.material3.ProvideTextStyle
import androidx.compose.material3.Surface
import androidx.compose.material3.Text
-import androidx.compose.material3.TopAppBarDefaults
import androidx.compose.material3.TopAppBarScrollBehavior
import androidx.compose.material3.TopAppBarState
import androidx.compose.runtime.Composable
import androidx.compose.runtime.CompositionLocalProvider
+import androidx.compose.runtime.NonRestartableComposable
import androidx.compose.runtime.SideEffect
import androidx.compose.runtime.Stable
import androidx.compose.runtime.mutableFloatStateOf
@@ -79,7 +80,12 @@
import kotlin.math.max
import kotlin.math.roundToInt
-@OptIn(ExperimentalMaterial3Api::class)
+private val windowInsets: WindowInsets
+ @Composable
+ @NonRestartableComposable
+ get() = WindowInsets.safeDrawing
+ .only(WindowInsetsSides.Horizontal + WindowInsetsSides.Top)
+
@Composable
internal fun CustomizedTopAppBar(
title: @Composable () -> Unit,
@@ -91,7 +97,7 @@
titleTextStyle = MaterialTheme.typography.titleMedium,
navigationIcon = navigationIcon,
actions = actions,
- windowInsets = TopAppBarDefaults.windowInsets,
+ windowInsets = windowInsets,
colors = topAppBarColors(),
)
}
@@ -118,7 +124,7 @@
navigationIcon = navigationIcon,
actions = actions,
colors = topAppBarColors(),
- windowInsets = TopAppBarDefaults.windowInsets,
+ windowInsets = windowInsets,
pinnedHeight = ContainerHeight,
scrollBehavior = scrollBehavior,
)
@@ -336,7 +342,7 @@
Modifier.draggable(
orientation = Orientation.Vertical,
state = rememberDraggableState { delta ->
- scrollBehavior.state.heightOffset = scrollBehavior.state.heightOffset + delta
+ scrollBehavior.state.heightOffset += delta
},
onDragStopped = { velocity ->
settleAppBar(
@@ -411,6 +417,7 @@
* (leading icon), a title (header), and action icons (trailing icons). Note that the navigation and
* the actions are optional.
*
+ * @param modifier a [Modifier]
* @param heightPx the total height this layout is capped to
* @param navigationIconContentColor the content color that will be applied via a
* [LocalContentColor] when composing the navigation icon
diff --git a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/scaffold/SearchScaffold.kt b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/scaffold/SearchScaffold.kt
index a49b358..4a7937a 100644
--- a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/scaffold/SearchScaffold.kt
+++ b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/scaffold/SearchScaffold.kt
@@ -22,9 +22,11 @@
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.RowScope
+import androidx.compose.foundation.layout.WindowInsets
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.padding
+import androidx.compose.foundation.layout.safeDrawing
import androidx.compose.foundation.text.KeyboardActions
import androidx.compose.foundation.text.KeyboardOptions
import androidx.compose.material3.ExperimentalMaterial3Api
@@ -92,6 +94,7 @@
)
},
containerColor = MaterialTheme.colorScheme.settingsBackground,
+ contentWindowInsets = WindowInsets.safeDrawing,
) { paddingValues ->
Box(
Modifier
diff --git a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/scaffold/SettingsScaffold.kt b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/scaffold/SettingsScaffold.kt
index af7a146..4cf741e 100644
--- a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/scaffold/SettingsScaffold.kt
+++ b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/scaffold/SettingsScaffold.kt
@@ -23,7 +23,9 @@
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.PaddingValues
import androidx.compose.foundation.layout.RowScope
+import androidx.compose.foundation.layout.WindowInsets
import androidx.compose.foundation.layout.padding
+import androidx.compose.foundation.layout.safeDrawing
import androidx.compose.material3.ExperimentalMaterial3Api
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Scaffold
@@ -57,6 +59,7 @@
modifier = Modifier.nestedScroll(scrollBehavior.nestedScrollConnection),
topBar = { SettingsTopAppBar(title, scrollBehavior, actions) },
containerColor = MaterialTheme.colorScheme.settingsBackground,
+ contentWindowInsets = WindowInsets.safeDrawing,
) { paddingValues ->
Box(Modifier.padding(paddingValues.horizontalValues())) {
content(paddingValues.verticalValues())
diff --git a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/scaffold/SuwScaffold.kt b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/scaffold/SuwScaffold.kt
index fc40930..4726dad 100644
--- a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/scaffold/SuwScaffold.kt
+++ b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/scaffold/SuwScaffold.kt
@@ -21,7 +21,9 @@
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.Spacer
+import androidx.compose.foundation.layout.WindowInsets
import androidx.compose.foundation.layout.padding
+import androidx.compose.foundation.layout.safeDrawing
import androidx.compose.foundation.layout.size
import androidx.compose.foundation.rememberScrollState
import androidx.compose.foundation.verticalScroll
@@ -55,7 +57,10 @@
content: @Composable () -> Unit,
) {
ActivityTitle(title)
- Scaffold(containerColor = MaterialTheme.colorScheme.settingsBackground) { innerPadding ->
+ Scaffold(
+ containerColor = MaterialTheme.colorScheme.settingsBackground,
+ contentWindowInsets = WindowInsets.safeDrawing,
+ ) { innerPadding ->
BoxWithConstraints(
Modifier
.padding(innerPadding)
diff --git a/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/model/app/AppOpsController.kt b/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/model/app/AppOpsController.kt
index 6e9bde4..8276e18 100644
--- a/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/model/app/AppOpsController.kt
+++ b/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/model/app/AppOpsController.kt
@@ -29,36 +29,46 @@
import kotlinx.coroutines.flow.map
interface IAppOpsController {
- val mode: Flow<Int>
+ val modeFlow: Flow<Int>
val isAllowed: Flow<Boolean>
- get() = mode.map { it == MODE_ALLOWED }
+ get() = modeFlow.map { it == MODE_ALLOWED }
fun setAllowed(allowed: Boolean)
@Mode fun getMode(): Int
}
+data class AppOps(
+ val op: Int,
+ val modeForNotAllowed: Int = MODE_ERRORED,
+
+ /**
+ * Use AppOpsManager#setUidMode() instead of AppOpsManager#setMode() when set allowed.
+ *
+ * Security or privacy related app-ops should be set with setUidMode() instead of setMode().
+ */
+ val setModeByUid: Boolean = false,
+)
+
class AppOpsController(
context: Context,
private val app: ApplicationInfo,
- private val op: Int,
- private val modeForNotAllowed: Int = MODE_ERRORED,
- private val setModeByUid: Boolean = false,
+ private val appOps: AppOps,
) : IAppOpsController {
private val appOpsManager = context.appOpsManager
private val packageManager = context.packageManager
- override val mode = appOpsManager.opModeFlow(op, app)
+ override val modeFlow = appOpsManager.opModeFlow(appOps.op, app)
override fun setAllowed(allowed: Boolean) {
- val mode = if (allowed) MODE_ALLOWED else modeForNotAllowed
+ val mode = if (allowed) MODE_ALLOWED else appOps.modeForNotAllowed
- if (setModeByUid) {
- appOpsManager.setUidMode(op, app.uid, mode)
+ if (appOps.setModeByUid) {
+ appOpsManager.setUidMode(appOps.op, app.uid, mode)
} else {
- appOpsManager.setMode(op, app.uid, app.packageName, mode)
+ appOpsManager.setMode(appOps.op, app.uid, app.packageName, mode)
}
- val permission = AppOpsManager.opToPermission(op)
+ val permission = AppOpsManager.opToPermission(appOps.op)
if (permission != null) {
packageManager.updatePermissionFlags(permission, app.packageName,
PackageManager.FLAG_PERMISSION_USER_SET,
@@ -67,5 +77,6 @@
}
}
- @Mode override fun getMode(): Int = appOpsManager.getOpMode(op, app)
+ @Mode
+ override fun getMode(): Int = appOpsManager.getOpMode(appOps.op, app)
}
diff --git a/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/model/app/AppOpsPermissionController.kt b/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/model/app/AppOpsPermissionController.kt
new file mode 100644
index 0000000..9350f98
--- /dev/null
+++ b/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/model/app/AppOpsPermissionController.kt
@@ -0,0 +1,62 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settingslib.spaprivileged.model.app
+
+import android.app.AppOpsManager
+import android.content.Context
+import android.content.pm.ApplicationInfo
+import android.util.Log
+import kotlinx.coroutines.Dispatchers
+import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.conflate
+import kotlinx.coroutines.flow.flowOn
+import kotlinx.coroutines.flow.map
+import kotlinx.coroutines.flow.onEach
+
+interface IAppOpsPermissionController {
+ val isAllowedFlow: Flow<Boolean>
+ fun setAllowed(allowed: Boolean)
+}
+
+class AppOpsPermissionController(
+ context: Context,
+ private val app: ApplicationInfo,
+ appOps: AppOps,
+ private val permission: String,
+ private val packageManagers: IPackageManagers = PackageManagers,
+ private val appOpsController: IAppOpsController = AppOpsController(context, app, appOps),
+) : IAppOpsPermissionController {
+ override val isAllowedFlow: Flow<Boolean> = appOpsController.modeFlow.map { mode ->
+ when (mode) {
+ AppOpsManager.MODE_ALLOWED -> true
+
+ AppOpsManager.MODE_DEFAULT -> {
+ with(packageManagers) { app.hasGrantPermission(permission) }
+ }
+
+ else -> false
+ }
+ }.conflate().onEach { Log.d(TAG, "isAllowed: $it") }.flowOn(Dispatchers.Default)
+
+ override fun setAllowed(allowed: Boolean) {
+ appOpsController.setAllowed(allowed)
+ }
+
+ private companion object {
+ private const val TAG = "AppOpsPermissionControl"
+ }
+}
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 5db5eae..120b75e 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
@@ -20,12 +20,14 @@
import android.content.Context
import android.content.pm.ApplicationInfo
import androidx.compose.runtime.Composable
+import androidx.compose.runtime.getValue
import androidx.lifecycle.compose.collectAsStateWithLifecycle
import com.android.settingslib.spa.framework.util.asyncMapItem
import com.android.settingslib.spa.framework.util.filterItem
-import com.android.settingslib.spaprivileged.model.app.AppOpsController
+import com.android.settingslib.spaprivileged.model.app.AppOps
+import com.android.settingslib.spaprivileged.model.app.AppOpsPermissionController
import com.android.settingslib.spaprivileged.model.app.AppRecord
-import com.android.settingslib.spaprivileged.model.app.IAppOpsController
+import com.android.settingslib.spaprivileged.model.app.IAppOpsPermissionController
import com.android.settingslib.spaprivileged.model.app.IPackageManagers
import com.android.settingslib.spaprivileged.model.app.PackageManagers
import kotlinx.coroutines.flow.Flow
@@ -36,7 +38,7 @@
override val app: ApplicationInfo,
val hasRequestBroaderPermission: Boolean,
val hasRequestPermission: Boolean,
- var appOpsController: IAppOpsController,
+ var appOpsPermissionController: IAppOpsPermissionController,
) : AppRecord
abstract class AppOpPermissionListModel(
@@ -44,11 +46,11 @@
private val packageManagers: IPackageManagers = PackageManagers,
) : TogglePermissionAppListModel<AppOpPermissionRecord> {
- abstract val appOp: Int
+ abstract val appOps: AppOps
abstract val permission: String
override val enhancedConfirmationKey: String?
- get() = AppOpsManager.opToPublicName(appOp)
+ get() = AppOpsManager.opToPublicName(appOps.op)
/**
* When set, specifies the broader permission who trumps the [permission].
@@ -65,27 +67,12 @@
*/
open val permissionHasAppOpFlag: Boolean = true
- open val modeForNotAllowed: Int = AppOpsManager.MODE_ERRORED
-
- /**
- * Use AppOpsManager#setUidMode() instead of AppOpsManager#setMode() when set allowed.
- *
- * Security or privacy related app-ops should be set with setUidMode() instead of setMode().
- */
- open val setModeByUid = false
-
/** These not changeable packages will also be hidden from app list. */
private val notChangeablePackages =
setOf("android", "com.android.systemui", context.packageName)
- private fun createAppOpsController(app: ApplicationInfo) =
- AppOpsController(
- context = context,
- app = app,
- op = appOp,
- setModeByUid = setModeByUid,
- modeForNotAllowed = modeForNotAllowed,
- )
+ private fun createAppOpsPermissionController(app: ApplicationInfo) =
+ AppOpsPermissionController(context, app, appOps, permission)
private fun createRecord(
app: ApplicationInfo,
@@ -98,7 +85,7 @@
app.hasRequestPermission(it)
} ?: false,
hasRequestPermission = hasRequestPermission,
- appOpsController = createAppOpsController(app),
+ appOpsPermissionController = createAppOpsPermissionController(app),
)
}
@@ -131,14 +118,20 @@
override fun filter(userIdFlow: Flow<Int>, recordListFlow: Flow<List<AppOpPermissionRecord>>) =
recordListFlow.filterItem(::isChangeable)
+ /**
+ * Defining the default behavior as permissible as long as the package requested this permission
+ * (This means pre-M gets approval during install time; M apps gets approval during runtime).
+ */
@Composable
- override fun isAllowed(record: AppOpPermissionRecord): () -> Boolean? =
- isAllowed(
- record = record,
- appOpsController = record.appOpsController,
- permission = permission,
- packageManagers = packageManagers,
- )
+ override fun isAllowed(record: AppOpPermissionRecord): () -> Boolean? {
+ if (record.hasRequestBroaderPermission) {
+ // Broader permission trumps the specific permission.
+ return { true }
+ }
+ val isAllowed by record.appOpsPermissionController.isAllowedFlow
+ .collectAsStateWithLifecycle(initialValue = null)
+ return { isAllowed }
+ }
override fun isChangeable(record: AppOpPermissionRecord) =
record.hasRequestPermission &&
@@ -146,36 +139,6 @@
record.app.packageName !in notChangeablePackages
override fun setAllowed(record: AppOpPermissionRecord, newAllowed: Boolean) {
- record.appOpsController.setAllowed(newAllowed)
- }
-}
-
-/**
- * Defining the default behavior as permissible as long as the package requested this permission
- * (This means pre-M gets approval during install time; M apps gets approval during runtime).
- */
-@Composable
-internal fun isAllowed(
- record: AppOpPermissionRecord,
- appOpsController: IAppOpsController,
- permission: String,
- packageManagers: IPackageManagers = PackageManagers,
-): () -> Boolean? {
- if (record.hasRequestBroaderPermission) {
- // Broader permission trumps the specific permission.
- return { true }
- }
-
- val mode = appOpsController.mode.collectAsStateWithLifecycle(initialValue = null)
- return {
- when (mode.value) {
- null -> null
- AppOpsManager.MODE_ALLOWED -> true
- AppOpsManager.MODE_DEFAULT -> {
- with(packageManagers) { record.app.hasGrantPermission(permission) }
- }
-
- else -> false
- }
+ record.appOpsPermissionController.setAllowed(newAllowed)
}
}
diff --git a/packages/SettingsLib/SpaPrivileged/tests/src/com/android/settingslib/spaprivileged/model/app/AppOpsControllerTest.kt b/packages/SettingsLib/SpaPrivileged/tests/src/com/android/settingslib/spaprivileged/model/app/AppOpsControllerTest.kt
index 91bbd9f..74a7c14 100644
--- a/packages/SettingsLib/SpaPrivileged/tests/src/com/android/settingslib/spaprivileged/model/app/AppOpsControllerTest.kt
+++ b/packages/SettingsLib/SpaPrivileged/tests/src/com/android/settingslib/spaprivileged/model/app/AppOpsControllerTest.kt
@@ -27,16 +27,14 @@
import androidx.test.ext.junit.runners.AndroidJUnit4
import com.android.settingslib.spaprivileged.framework.common.appOpsManager
import com.google.common.truth.Truth.assertThat
-import org.junit.Before
import org.junit.Rule
import org.junit.Test
import org.junit.runner.RunWith
-import org.mockito.Mock
-import org.mockito.Spy
import org.mockito.junit.MockitoJUnit
import org.mockito.junit.MockitoRule
-import org.mockito.kotlin.any
-import org.mockito.kotlin.doNothing
+import org.mockito.kotlin.doReturn
+import org.mockito.kotlin.mock
+import org.mockito.kotlin.spy
import org.mockito.kotlin.verify
import org.mockito.kotlin.whenever
@@ -44,28 +42,18 @@
class AppOpsControllerTest {
@get:Rule val mockito: MockitoRule = MockitoJUnit.rule()
- @Spy private val context: Context = ApplicationProvider.getApplicationContext()
+ private val appOpsManager = mock<AppOpsManager>()
- @Mock private lateinit var appOpsManager: AppOpsManager
+ private val packageManager = mock<PackageManager>()
- @Mock private lateinit var packageManager: PackageManager
-
- @Before
- fun setUp() {
- whenever(context.appOpsManager).thenReturn(appOpsManager)
- whenever(context.packageManager).thenReturn(packageManager)
- doNothing().whenever(packageManager)
- .updatePermissionFlags(any(), any(), any(), any(), any())
+ private val context: Context = spy(ApplicationProvider.getApplicationContext()) {
+ on { appOpsManager } doReturn appOpsManager
+ on { packageManager } doReturn packageManager
}
@Test
fun setAllowed_setToTrue() {
- val controller =
- AppOpsController(
- context = context,
- app = APP,
- op = OP,
- )
+ val controller = AppOpsController(context = context, app = APP, appOps = AppOps(OP))
controller.setAllowed(true)
@@ -74,12 +62,7 @@
@Test
fun setAllowed_setToFalse() {
- val controller =
- AppOpsController(
- context = context,
- app = APP,
- op = OP,
- )
+ val controller = AppOpsController(context = context, app = APP, appOps = AppOps(OP))
controller.setAllowed(false)
@@ -88,13 +71,11 @@
@Test
fun setAllowed_setToFalseWithModeForNotAllowed() {
- val controller =
- AppOpsController(
- context = context,
- app = APP,
- op = OP,
- modeForNotAllowed = MODE_IGNORED,
- )
+ val controller = AppOpsController(
+ context = context,
+ app = APP,
+ appOps = AppOps(op = OP, modeForNotAllowed = MODE_IGNORED),
+ )
controller.setAllowed(false)
@@ -103,13 +84,11 @@
@Test
fun setAllowed_setToTrueByUid() {
- val controller =
- AppOpsController(
- context = context,
- app = APP,
- op = OP,
- setModeByUid = true,
- )
+ val controller = AppOpsController(
+ context = context,
+ app = APP,
+ appOps = AppOps(op = OP, setModeByUid = true),
+ )
controller.setAllowed(true)
@@ -118,13 +97,11 @@
@Test
fun setAllowed_setToFalseByUid() {
- val controller =
- AppOpsController(
- context = context,
- app = APP,
- op = OP,
- setModeByUid = true,
- )
+ val controller = AppOpsController(
+ context = context,
+ app = APP,
+ appOps = AppOps(op = OP, setModeByUid = true),
+ )
controller.setAllowed(false)
@@ -135,12 +112,7 @@
fun getMode() {
whenever(appOpsManager.checkOpNoThrow(OP, APP.uid, APP.packageName))
.thenReturn(MODE_ALLOWED)
- val controller =
- AppOpsController(
- context = context,
- app = APP,
- op = OP,
- )
+ val controller = AppOpsController(context = context, app = APP, appOps = AppOps(OP))
val mode = controller.getMode()
diff --git a/packages/SettingsLib/SpaPrivileged/tests/src/com/android/settingslib/spaprivileged/model/app/AppOpsPermissionControllerTest.kt b/packages/SettingsLib/SpaPrivileged/tests/src/com/android/settingslib/spaprivileged/model/app/AppOpsPermissionControllerTest.kt
new file mode 100644
index 0000000..9f80b92
--- /dev/null
+++ b/packages/SettingsLib/SpaPrivileged/tests/src/com/android/settingslib/spaprivileged/model/app/AppOpsPermissionControllerTest.kt
@@ -0,0 +1,167 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settingslib.spaprivileged.model.app
+
+import android.app.AppOpsManager
+import android.content.Context
+import android.content.pm.ApplicationInfo
+import android.content.pm.PackageManager
+import androidx.test.core.app.ApplicationProvider
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import com.android.settingslib.spa.testutils.firstWithTimeoutOrNull
+import com.android.settingslib.spaprivileged.framework.common.appOpsManager
+import com.google.common.truth.Truth.assertThat
+import kotlinx.coroutines.flow.flowOf
+import kotlinx.coroutines.runBlocking
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.kotlin.doReturn
+import org.mockito.kotlin.mock
+import org.mockito.kotlin.spy
+import org.mockito.kotlin.stub
+import org.mockito.kotlin.verify
+
+@RunWith(AndroidJUnit4::class)
+class AppOpsPermissionControllerTest {
+
+ private val appOpsManager = mock<AppOpsManager>()
+ private val packageManager = mock<PackageManager>()
+ private val packageManagers = mock<IPackageManagers>()
+ private val appOpsController = mock<IAppOpsController>()
+
+ private val context: Context = spy(ApplicationProvider.getApplicationContext()) {
+ on { appOpsManager } doReturn appOpsManager
+ on { packageManager } doReturn packageManager
+ }
+
+ @Test
+ fun isAllowedFlow_appOpsAllowed_returnTrue() = runBlocking {
+ appOpsController.stub {
+ on { modeFlow } doReturn flowOf(AppOpsManager.MODE_ALLOWED)
+ }
+ val controller = AppOpsPermissionController(
+ context = context,
+ app = APP,
+ appOps = AppOps(op = OP),
+ permission = PERMISSION,
+ appOpsController = appOpsController,
+ )
+
+ val isAllowed = controller.isAllowedFlow.firstWithTimeoutOrNull()
+
+ assertThat(isAllowed).isTrue()
+ }
+
+ @Test
+ fun isAllowedFlow_appOpsDefaultAndPermissionGranted_returnTrue() = runBlocking {
+ appOpsController.stub {
+ on { modeFlow } doReturn flowOf(AppOpsManager.MODE_DEFAULT)
+ }
+ packageManagers.stub {
+ on { APP.hasGrantPermission(PERMISSION) } doReturn true
+ }
+ val controller = AppOpsPermissionController(
+ context = context,
+ app = APP,
+ appOps = AppOps(op = OP),
+ permission = PERMISSION,
+ packageManagers = packageManagers,
+ appOpsController = appOpsController,
+ )
+
+ val isAllowed = controller.isAllowedFlow.firstWithTimeoutOrNull()
+
+ assertThat(isAllowed).isTrue()
+ }
+
+ @Test
+ fun isAllowedFlow_appOpsDefaultAndPermissionNotGranted_returnFalse() = runBlocking {
+ appOpsController.stub {
+ on { modeFlow } doReturn flowOf(AppOpsManager.MODE_DEFAULT)
+ }
+ packageManagers.stub {
+ on { APP.hasGrantPermission(PERMISSION) } doReturn false
+ }
+ val controller = AppOpsPermissionController(
+ context = context,
+ app = APP,
+ appOps = AppOps(op = OP),
+ permission = PERMISSION,
+ packageManagers = packageManagers,
+ appOpsController = appOpsController,
+ )
+
+ val isAllowed = controller.isAllowedFlow.firstWithTimeoutOrNull()
+
+ assertThat(isAllowed).isFalse()
+ }
+
+ @Test
+ fun isAllowedFlow_appOpsError_returnFalse() = runBlocking {
+ appOpsController.stub {
+ on { modeFlow } doReturn flowOf(AppOpsManager.MODE_ERRORED)
+ }
+ val controller = AppOpsPermissionController(
+ context = context,
+ app = APP,
+ appOps = AppOps(op = OP),
+ permission = PERMISSION,
+ appOpsController = appOpsController,
+ )
+
+ val isAllowed = controller.isAllowedFlow.firstWithTimeoutOrNull()
+
+ assertThat(isAllowed).isFalse()
+ }
+
+ @Test
+ fun setAllowed_notSetModeByUid() {
+ val controller = AppOpsPermissionController(
+ context = context,
+ app = APP,
+ appOps = AppOps(op = OP, setModeByUid = false),
+ permission = PERMISSION,
+ )
+
+ controller.setAllowed(true)
+
+ verify(appOpsManager).setMode(OP, APP.uid, APP.packageName, AppOpsManager.MODE_ALLOWED)
+ }
+
+ @Test
+ fun setAllowed_setModeByUid() {
+ val controller = AppOpsPermissionController(
+ context = context,
+ app = APP,
+ appOps = AppOps(op = OP, setModeByUid = true),
+ permission = PERMISSION,
+ )
+
+ controller.setAllowed(true)
+
+ verify(appOpsManager).setUidMode(OP, APP.uid, AppOpsManager.MODE_ALLOWED)
+ }
+
+ private companion object {
+ const val OP = 1
+ const val PERMISSION = "Permission"
+ val APP = ApplicationInfo().apply {
+ packageName = "package.name"
+ uid = 123
+ }
+ }
+}
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 bb25cf3..9d12fc7 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
@@ -25,7 +25,8 @@
import androidx.test.ext.junit.runners.AndroidJUnit4
import com.android.settingslib.spa.testutils.firstWithTimeoutOrNull
import com.android.settingslib.spaprivileged.framework.common.appOpsManager
-import com.android.settingslib.spaprivileged.model.app.IAppOpsController
+import com.android.settingslib.spaprivileged.model.app.AppOps
+import com.android.settingslib.spaprivileged.model.app.IAppOpsPermissionController
import com.android.settingslib.spaprivileged.model.app.IPackageManagers
import com.android.settingslib.spaprivileged.test.R
import com.google.common.truth.Truth.assertThat
@@ -39,7 +40,6 @@
import org.mockito.kotlin.doReturn
import org.mockito.kotlin.mock
import org.mockito.kotlin.spy
-import org.mockito.kotlin.verify
import org.mockito.kotlin.whenever
@RunWith(AndroidJUnit4::class)
@@ -119,7 +119,7 @@
app = APP,
hasRequestBroaderPermission = false,
hasRequestPermission = false,
- appOpsController = FakeAppOpsController(fakeMode = AppOpsManager.MODE_DEFAULT),
+ appOpsPermissionController = FakeAppOpsPermissionController(false),
)
val recordListFlow = listModel.filter(flowOf(USER_ID), flowOf(listOf(record)))
@@ -135,7 +135,7 @@
app = APP,
hasRequestBroaderPermission = false,
hasRequestPermission = true,
- appOpsController = FakeAppOpsController(fakeMode = AppOpsManager.MODE_ALLOWED),
+ appOpsPermissionController = FakeAppOpsPermissionController(true),
)
val isAllowed = getIsAllowed(record)
@@ -144,38 +144,6 @@
}
@Test
- fun isAllowed_defaultAndHasGrantPermission() {
- with(packageManagers) { whenever(APP.hasGrantPermission(PERMISSION)).thenReturn(true) }
- val record =
- AppOpPermissionRecord(
- app = APP,
- hasRequestBroaderPermission = false,
- hasRequestPermission = true,
- appOpsController = FakeAppOpsController(fakeMode = AppOpsManager.MODE_DEFAULT),
- )
-
- val isAllowed = getIsAllowed(record)
-
- assertThat(isAllowed).isTrue()
- }
-
- @Test
- fun isAllowed_defaultAndNotGrantPermission() {
- with(packageManagers) { whenever(APP.hasGrantPermission(PERMISSION)).thenReturn(false) }
- val record =
- AppOpPermissionRecord(
- app = APP,
- hasRequestBroaderPermission = false,
- hasRequestPermission = true,
- appOpsController = FakeAppOpsController(fakeMode = AppOpsManager.MODE_DEFAULT),
- )
-
- val isAllowed = getIsAllowed(record)
-
- assertThat(isAllowed).isFalse()
- }
-
- @Test
fun isAllowed_broaderPermissionTrumps() {
listModel.broaderPermission = BROADER_PERMISSION
with(packageManagers) {
@@ -187,7 +155,7 @@
app = APP,
hasRequestBroaderPermission = true,
hasRequestPermission = false,
- appOpsController = FakeAppOpsController(fakeMode = AppOpsManager.MODE_ERRORED),
+ appOpsPermissionController = FakeAppOpsPermissionController(false),
)
val isAllowed = getIsAllowed(record)
@@ -202,7 +170,7 @@
app = APP,
hasRequestBroaderPermission = false,
hasRequestPermission = true,
- appOpsController = FakeAppOpsController(fakeMode = AppOpsManager.MODE_ERRORED),
+ appOpsPermissionController = FakeAppOpsPermissionController(false),
)
val isAllowed = getIsAllowed(record)
@@ -217,7 +185,7 @@
app = APP,
hasRequestBroaderPermission = false,
hasRequestPermission = false,
- appOpsController = FakeAppOpsController(fakeMode = AppOpsManager.MODE_DEFAULT),
+ appOpsPermissionController = FakeAppOpsPermissionController(false),
)
val isChangeable = listModel.isChangeable(record)
@@ -232,7 +200,7 @@
app = NOT_CHANGEABLE_APP,
hasRequestBroaderPermission = false,
hasRequestPermission = true,
- appOpsController = FakeAppOpsController(fakeMode = AppOpsManager.MODE_DEFAULT),
+ appOpsPermissionController = FakeAppOpsPermissionController(false),
)
val isChangeable = listModel.isChangeable(record)
@@ -247,7 +215,7 @@
app = APP,
hasRequestBroaderPermission = false,
hasRequestPermission = true,
- appOpsController = FakeAppOpsController(fakeMode = AppOpsManager.MODE_DEFAULT),
+ appOpsPermissionController = FakeAppOpsPermissionController(false),
)
val isChangeable = listModel.isChangeable(record)
@@ -263,7 +231,7 @@
app = APP,
hasRequestBroaderPermission = true,
hasRequestPermission = true,
- appOpsController = FakeAppOpsController(fakeMode = AppOpsManager.MODE_DEFAULT),
+ appOpsPermissionController = FakeAppOpsPermissionController(false),
)
val isChangeable = listModel.isChangeable(record)
@@ -273,28 +241,18 @@
@Test
fun setAllowed() {
- val appOpsController = FakeAppOpsController(fakeMode = AppOpsManager.MODE_DEFAULT)
+ val appOpsPermissionController = FakeAppOpsPermissionController(false)
val record =
AppOpPermissionRecord(
app = APP,
hasRequestBroaderPermission = false,
hasRequestPermission = true,
- appOpsController = appOpsController,
+ appOpsPermissionController = appOpsPermissionController,
)
listModel.setAllowed(record = record, newAllowed = true)
- assertThat(appOpsController.setAllowedCalledWith).isTrue()
- }
-
- @Test
- fun setAllowed_setModeByUid() {
- listModel.setModeByUid = true
- val record = listModel.transformItem(APP)
-
- listModel.setAllowed(record = record, newAllowed = true)
-
- verify(appOpsManager).setUidMode(listModel.appOp, APP.uid, AppOpsManager.MODE_ALLOWED)
+ assertThat(appOpsPermissionController.setAllowedCalledWith).isTrue()
}
private fun getIsAllowed(record: AppOpPermissionRecord): Boolean? {
@@ -309,11 +267,9 @@
override val switchTitleResId = R.string.test_app_op_permission_switch_title
override val footerResId = R.string.test_app_op_permission_footer
- override val appOp = AppOpsManager.OP_MANAGE_MEDIA
+ override val appOps = AppOps(AppOpsManager.OP_MANAGE_MEDIA)
override val permission = PERMISSION
override var broaderPermission: String? = null
-
- override var setModeByUid = false
}
private companion object {
@@ -326,14 +282,12 @@
}
}
-private class FakeAppOpsController(private val fakeMode: Int) : IAppOpsController {
+private class FakeAppOpsPermissionController(allowed: Boolean) : IAppOpsPermissionController {
var setAllowedCalledWith: Boolean? = null
- override val mode = flowOf(fakeMode)
+ override val isAllowedFlow = flowOf(allowed)
override fun setAllowed(allowed: Boolean) {
setAllowedCalledWith = allowed
}
-
- override fun getMode() = fakeMode
}
diff --git a/packages/SettingsLib/aconfig/settingslib.aconfig b/packages/SettingsLib/aconfig/settingslib.aconfig
index 89f54d9..32557b9 100644
--- a/packages/SettingsLib/aconfig/settingslib.aconfig
+++ b/packages/SettingsLib/aconfig/settingslib.aconfig
@@ -62,3 +62,20 @@
purpose: PURPOSE_BUGFIX
}
}
+
+flag {
+ name: "allow_all_widgets_on_lockscreen_by_default"
+ namespace: "systemui"
+ description: "Allow all widgets on the lock screen by default."
+ bug: "328261690"
+}
+
+flag {
+ name: "enable_determining_advanced_details_header_with_metadata"
+ namespace: "pixel_cross_device_control"
+ description: "Use metadata instead of device type to determine whether a bluetooth device should use advanced details header."
+ bug: "328556903"
+ metadata {
+ purpose: PURPOSE_BUGFIX
+ }
+}
diff --git a/packages/SettingsLib/res/values/strings.xml b/packages/SettingsLib/res/values/strings.xml
index 4ea7460..363045e 100644
--- a/packages/SettingsLib/res/values/strings.xml
+++ b/packages/SettingsLib/res/values/strings.xml
@@ -214,6 +214,10 @@
<string name="bluetooth_battery_level_untethered_left">Left: <xliff:g id="battery_level_as_percentage" example="25%">%1$s</xliff:g> battery</string>
<!-- Connected devices settings. Message when Bluetooth is connected but not in use, showing remote device battery level for the right part of the untethered headset. [CHAR LIMIT=NONE] -->
<string name="bluetooth_battery_level_untethered_right">Right: <xliff:g id="battery_level_as_percentage" example="25%">%1$s</xliff:g> battery</string>
+ <!-- Connected devices settings. Message when Bluetooth is connected, showing remote device battery level for the left part of the untethered headset. [CHAR LIMIT=NONE] -->
+ <string name="tv_bluetooth_battery_level_untethered_left">Left <xliff:g id="battery_level_as_percentage" example="25%">%1$s</xliff:g></string>
+ <!-- Connected devices settings. Message when Bluetooth is connected, showing remote device battery level for the right part of the untethered headset. [CHAR LIMIT=NONE] -->
+ <string name="tv_bluetooth_battery_level_untethered_right">Right <xliff:g id="battery_level_as_percentage" example="25%">%1$s</xliff:g></string>
<!-- Connected devices settings. Message when Bluetooth is connected and active but no battery information, showing remote device status. [CHAR LIMIT=NONE] -->
<string name="bluetooth_active_no_battery_level">Active</string>
<!-- Connected devices settings. Message shown when bluetooth device is disconnected but is a known, previously connected device [CHAR LIMIT=NONE] -->
diff --git a/packages/SettingsLib/src/com/android/settingslib/applications/RecentAppOpsAccess.java b/packages/SettingsLib/src/com/android/settingslib/applications/RecentAppOpsAccess.java
index 57bde56..c365142 100644
--- a/packages/SettingsLib/src/com/android/settingslib/applications/RecentAppOpsAccess.java
+++ b/packages/SettingsLib/src/com/android/settingslib/applications/RecentAppOpsAccess.java
@@ -128,7 +128,7 @@
final long now = mClock.millis();
final UserManager um = mContext.getSystemService(UserManager.class);
final List<UserHandle> profiles = um.getUserProfiles();
- ArrayMap<UserHandle, Boolean> shouldIncludeAppsByUsers = new ArrayMap<>();
+ ArrayMap<UserHandle, Boolean> shouldHideAppsByUsers = new ArrayMap<>();
for (int i = 0; i < appOpsCount; ++i) {
AppOpsManager.PackageOps ops = appOps.get(i);
@@ -136,13 +136,13 @@
int uid = ops.getUid();
UserHandle user = UserHandle.getUserHandleForUid(uid);
- if (!shouldIncludeAppsByUsers.containsKey(user)) {
- shouldIncludeAppsByUsers.put(user, shouldHideUser(um, user));
+ if (!shouldHideAppsByUsers.containsKey(user)) {
+ shouldHideAppsByUsers.put(user, shouldHideUser(um, user));
}
// Don't show apps belonging to background users except for profiles that shouldn't
// be shown in quiet mode.
- if (!profiles.contains(user) || !shouldIncludeAppsByUsers.get(user)) {
+ if (!profiles.contains(user) || shouldHideAppsByUsers.get(user)) {
continue;
}
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/BluetoothUtils.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/BluetoothUtils.java
index 8917412..721e7b9 100644
--- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/BluetoothUtils.java
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/BluetoothUtils.java
@@ -276,6 +276,14 @@
if (isUntetheredHeadset(bluetoothDevice)) {
return true;
}
+ if (Flags.enableDeterminingAdvancedDetailsHeaderWithMetadata()) {
+ // A FastPair device that use advanced details header must have METADATA_MAIN_ICON
+ if (getUriMetaData(bluetoothDevice, BluetoothDevice.METADATA_MAIN_ICON) != null) {
+ Log.d(TAG, "isAdvancedDetailsHeader is true with main icon uri");
+ return true;
+ }
+ return false;
+ }
// The metadata is for Android S
String deviceType =
getStringMetaData(bluetoothDevice, BluetoothDevice.METADATA_DEVICE_TYPE);
@@ -302,12 +310,15 @@
if (isUntetheredHeadset(bluetoothDevice)) {
return true;
}
- // The metadata is for Android S
- String deviceType =
- getStringMetaData(bluetoothDevice, BluetoothDevice.METADATA_DEVICE_TYPE);
- if (TextUtils.equals(deviceType, BluetoothDevice.DEVICE_TYPE_UNTETHERED_HEADSET)) {
- Log.d(TAG, "isAdvancedUntetheredDevice: is untethered device ");
- return true;
+ if (!Flags.enableDeterminingAdvancedDetailsHeaderWithMetadata()) {
+ // The METADATA_IS_UNTETHERED_HEADSET of an untethered FastPair headset is always true,
+ // so there's no need to check the device type.
+ String deviceType =
+ getStringMetaData(bluetoothDevice, BluetoothDevice.METADATA_DEVICE_TYPE);
+ if (TextUtils.equals(deviceType, BluetoothDevice.DEVICE_TYPE_UNTETHERED_HEADSET)) {
+ Log.d(TAG, "isAdvancedUntetheredDevice: is untethered device");
+ return true;
+ }
}
return false;
}
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDevice.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDevice.java
index c2a83b1..0fec61c 100644
--- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDevice.java
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDevice.java
@@ -1532,7 +1532,7 @@
// the left.
if (leftBattery >= 0) {
String left = res.getString(
- R.string.bluetooth_battery_level_untethered_left,
+ R.string.tv_bluetooth_battery_level_untethered_left,
Utils.formatPercentage(leftBattery));
addBatterySpan(spannableBuilder, left, isBatteryLow(leftBattery,
BluetoothDevice.METADATA_UNTETHERED_LEFT_LOW_BATTERY_THRESHOLD),
@@ -1543,7 +1543,7 @@
spannableBuilder.append(" ");
}
String right = res.getString(
- R.string.bluetooth_battery_level_untethered_right,
+ R.string.tv_bluetooth_battery_level_untethered_right,
Utils.formatPercentage(rightBattery));
addBatterySpan(spannableBuilder, right, isBatteryLow(rightBattery,
BluetoothDevice.METADATA_UNTETHERED_RIGHT_LOW_BATTERY_THRESHOLD),
diff --git a/packages/SettingsLib/src/com/android/settingslib/fuelgauge/PowerAllowlistBackend.java b/packages/SettingsLib/src/com/android/settingslib/fuelgauge/PowerAllowlistBackend.java
index 822a608..1040ac6 100644
--- a/packages/SettingsLib/src/com/android/settingslib/fuelgauge/PowerAllowlistBackend.java
+++ b/packages/SettingsLib/src/com/android/settingslib/fuelgauge/PowerAllowlistBackend.java
@@ -36,6 +36,7 @@
import android.util.ArraySet;
import android.util.Log;
+import androidx.annotation.GuardedBy;
import androidx.annotation.VisibleForTesting;
import com.android.internal.telephony.SmsApplication;
@@ -56,13 +57,22 @@
private static PowerAllowlistBackend sInstance;
+ private final Object mAllowlistedAppsLock = new Object();
+ private final Object mSysAllowlistedAppsLock = new Object();
+ private final Object mDefaultActiveAppsLock = new Object();
+
private final Context mAppContext;
private final IDeviceIdleController mDeviceIdleService;
+
+ @GuardedBy("mAllowlistedAppsLock")
private final ArraySet<String> mAllowlistedApps = new ArraySet<>();
+ @GuardedBy("mSysAllowlistedAppsLock")
private final ArraySet<String> mSysAllowlistedApps = new ArraySet<>();
+ @GuardedBy("mDefaultActiveAppsLock")
private final ArraySet<String> mDefaultActiveApps = new ArraySet<>();
- public PowerAllowlistBackend(Context context) {
+ @VisibleForTesting
+ PowerAllowlistBackend(Context context) {
this(context, IDeviceIdleController.Stub.asInterface(
ServiceManager.getService(DEVICE_IDLE_SERVICE)));
}
@@ -75,24 +85,25 @@
}
public int getAllowlistSize() {
- return mAllowlistedApps.size();
- }
-
- /**
- * Check if target package is in System allow list
- */
- public boolean isSysAllowlisted(String pkg) {
- return mSysAllowlistedApps.contains(pkg);
- }
-
- /**
- * Check if target package is in allow list
- */
- public boolean isAllowlisted(String pkg, int uid) {
- if (mAllowlistedApps.contains(pkg)) {
- return true;
+ synchronized (mAllowlistedAppsLock) {
+ return mAllowlistedApps.size();
}
+ }
+ /** Check if target package is in System allow list */
+ public boolean isSysAllowlisted(String pkg) {
+ synchronized (mSysAllowlistedAppsLock) {
+ return mSysAllowlistedApps.contains(pkg);
+ }
+ }
+
+ /** Check if target package is in allow list */
+ public boolean isAllowlisted(String pkg, int uid) {
+ synchronized (mAllowlistedAppsLock) {
+ if (mAllowlistedApps.contains(pkg)) {
+ return true;
+ }
+ }
if (isDefaultActiveApp(pkg, uid)) {
return true;
}
@@ -100,16 +111,16 @@
return false;
}
- /**
- * Check if it is default active app in multiple area(i.e. SMS, Dialer, Device admin..)
- */
+ /** Check if it is default active app in multiple area */
public boolean isDefaultActiveApp(String pkg, int uid) {
// Additionally, check if pkg is default dialer/sms. They are considered essential apps and
// should be automatically allowlisted (otherwise user may be able to set restriction on
// them, leading to bad device behavior.)
- if (mDefaultActiveApps.contains(pkg)) {
- return true;
+ synchronized (mDefaultActiveAppsLock) {
+ if (mDefaultActiveApps.contains(pkg)) {
+ return true;
+ }
}
final DevicePolicyManager devicePolicyManager = mAppContext.getSystemService(
@@ -143,9 +154,7 @@
DEFAULT_SYSTEM_EXEMPT_POWER_RESTRICTIONS_ENABLED);
}
- /**
- * Check if target package is in allow list except idle app
- */
+ /** Check if target package is in allow list except idle app */
public boolean isAllowlistedExceptIdle(String pkg) {
try {
return mDeviceIdleService.isPowerSaveWhitelistExceptIdleApp(pkg);
@@ -156,6 +165,7 @@
}
/**
+ * Check if target package is in allow list except idle app
*
* @param pkgs a list of packageName
* @return true when one of package is in allow list
@@ -174,20 +184,21 @@
}
/**
- * Add app into power save allow list.
+ * Add app into power save allow list
+ *
* @param pkg packageName of the app
*/
- // TODO: Fix all callers to pass in UID
public void addApp(String pkg) {
addApp(pkg, Process.INVALID_UID);
}
/**
- * Add app into power save allow list.
+ * Add app into power save allow list
+ *
* @param pkg packageName of the app
* @param uid uid of the app
*/
- public void addApp(String pkg, int uid) {
+ public synchronized void addApp(String pkg, int uid) {
try {
if (android.app.Flags.appRestrictionsApi()) {
if (uid == Process.INVALID_UID) {
@@ -204,7 +215,9 @@
}
mDeviceIdleService.addPowerSaveWhitelistApp(pkg);
- mAllowlistedApps.add(pkg);
+ synchronized (mAllowlistedAppsLock) {
+ mAllowlistedApps.add(pkg);
+ }
} catch (RemoteException e) {
Log.w(TAG, "Unable to reach IDeviceIdleController", e);
} catch (NameNotFoundException e) {
@@ -213,7 +226,8 @@
}
/**
- * Remove package from power save allow list.
+ * Remove package from power save allow list
+ *
* @param pkg packageName of the app
*/
public void removeApp(String pkg) {
@@ -222,10 +236,11 @@
/**
* Remove package from power save allow list.
+ *
* @param pkg packageName of the app
* @param uid uid of the app
*/
- public void removeApp(String pkg, int uid) {
+ public synchronized void removeApp(String pkg, int uid) {
try {
if (android.app.Flags.appRestrictionsApi()) {
if (uid == Process.INVALID_UID) {
@@ -241,7 +256,9 @@
}
mDeviceIdleService.removePowerSaveWhitelistApp(pkg);
- mAllowlistedApps.remove(pkg);
+ synchronized (mAllowlistedAppsLock) {
+ mAllowlistedApps.remove(pkg);
+ }
} catch (RemoteException e) {
Log.w(TAG, "Unable to reach IDeviceIdleController", e);
} catch (NameNotFoundException e) {
@@ -249,25 +266,33 @@
}
}
- /**
- * Refresh all of lists
- */
+ /** Refresh all of lists */
@VisibleForTesting
- public void refreshList() {
- mSysAllowlistedApps.clear();
- mAllowlistedApps.clear();
- mDefaultActiveApps.clear();
+ public synchronized void refreshList() {
+ synchronized (mSysAllowlistedAppsLock) {
+ mSysAllowlistedApps.clear();
+ }
+ synchronized (mAllowlistedAppsLock) {
+ mAllowlistedApps.clear();
+ }
+ synchronized (mDefaultActiveAppsLock) {
+ mDefaultActiveApps.clear();
+ }
if (mDeviceIdleService == null) {
return;
}
try {
final String[] allowlistedApps = mDeviceIdleService.getFullPowerWhitelist();
- for (String app : allowlistedApps) {
- mAllowlistedApps.add(app);
+ synchronized (mAllowlistedAppsLock) {
+ for (String app : allowlistedApps) {
+ mAllowlistedApps.add(app);
+ }
}
final String[] sysAllowlistedApps = mDeviceIdleService.getSystemPowerWhitelist();
- for (String app : sysAllowlistedApps) {
- mSysAllowlistedApps.add(app);
+ synchronized (mSysAllowlistedAppsLock) {
+ for (String app : sysAllowlistedApps) {
+ mSysAllowlistedApps.add(app);
+ }
}
final boolean hasTelephony = mAppContext.getPackageManager().hasSystemFeature(
PackageManager.FEATURE_TELEPHONY);
@@ -278,26 +303,28 @@
if (hasTelephony) {
if (defaultSms != null) {
- mDefaultActiveApps.add(defaultSms.getPackageName());
+ synchronized (mDefaultActiveAppsLock) {
+ mDefaultActiveApps.add(defaultSms.getPackageName());
+ }
}
if (!TextUtils.isEmpty(defaultDialer)) {
- mDefaultActiveApps.add(defaultDialer);
+ synchronized (mDefaultActiveAppsLock) {
+ mDefaultActiveApps.add(defaultDialer);
+ }
}
}
- } catch (RemoteException e) {
- Log.w(TAG, "Unable to reach IDeviceIdleController", e);
+ } catch (Exception e) {
+ Log.e(TAG, "Failed to invoke refreshList()", e);
}
}
- /**
- * @param context
- * @return a PowerAllowlistBackend object
- */
+ /** Get the {@link PowerAllowlistBackend} instance */
public static PowerAllowlistBackend getInstance(Context context) {
- if (sInstance == null) {
- sInstance = new PowerAllowlistBackend(context);
+ synchronized (PowerAllowlistBackend.class) {
+ if (sInstance == null) {
+ sInstance = new PowerAllowlistBackend(context);
+ }
+ return sInstance;
}
- return sInstance;
}
-
}
diff --git a/packages/SettingsLib/src/com/android/settingslib/satellite/SatelliteDialogUtils.kt b/packages/SettingsLib/src/com/android/settingslib/satellite/SatelliteDialogUtils.kt
new file mode 100644
index 0000000..d69c87b
--- /dev/null
+++ b/packages/SettingsLib/src/com/android/settingslib/satellite/SatelliteDialogUtils.kt
@@ -0,0 +1,145 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settingslib.satellite
+
+import android.content.ComponentName
+import android.content.Context
+import android.content.Intent
+import android.os.OutcomeReceiver
+import android.telephony.satellite.SatelliteManager
+import android.util.Log
+import android.view.WindowManager
+import androidx.lifecycle.LifecycleOwner
+import androidx.lifecycle.lifecycleScope
+import com.android.settingslib.wifi.WifiUtils
+import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.Dispatchers
+import kotlinx.coroutines.Dispatchers.Default
+import kotlinx.coroutines.Job
+import kotlinx.coroutines.asExecutor
+import kotlinx.coroutines.launch
+import kotlinx.coroutines.suspendCancellableCoroutine
+import kotlinx.coroutines.withContext
+import java.util.concurrent.ExecutionException
+import java.util.concurrent.TimeoutException
+import kotlin.coroutines.resume
+
+/** A util for Satellite dialog */
+object SatelliteDialogUtils {
+
+ /**
+ * Uses to start Satellite dialog to prevent users from using the BT, Airplane Mode, and
+ * Wifi during the satellite mode is on.
+ */
+ @JvmStatic
+ fun mayStartSatelliteWarningDialog(
+ context: Context,
+ lifecycleOwner: LifecycleOwner,
+ type: Int,
+ allowClick: (isAllowed: Boolean) -> Unit
+ ): Job {
+ return mayStartSatelliteWarningDialog(
+ context, lifecycleOwner.lifecycleScope, type, allowClick)
+ }
+
+ /**
+ * Uses to start Satellite dialog to prevent users from using the BT, Airplane Mode, and
+ * Wifi during the satellite mode is on.
+ */
+ @JvmStatic
+ fun mayStartSatelliteWarningDialog(
+ context: Context,
+ coroutineScope: CoroutineScope,
+ type: Int,
+ allowClick: (isAllowed: Boolean) -> Unit
+ ): Job =
+ coroutineScope.launch {
+ var isSatelliteModeOn = false
+ try {
+ isSatelliteModeOn = requestIsEnabled(context)
+ } catch (e: InterruptedException) {
+ Log.w(TAG, "Error to get satellite status : $e")
+ } catch (e: ExecutionException) {
+ Log.w(TAG, "Error to get satellite status : $e")
+ } catch (e: TimeoutException) {
+ Log.w(TAG, "Error to get satellite status : $e")
+ }
+
+ if (isSatelliteModeOn) {
+ startSatelliteWarningDialog(context, type)
+ }
+ withContext(Dispatchers.Main) {
+ allowClick(!isSatelliteModeOn)
+ }
+ }
+
+ private fun startSatelliteWarningDialog(context: Context, type: Int) {
+ context.startActivity(Intent(Intent.ACTION_MAIN).apply {
+ component = ComponentName(
+ "com.android.settings",
+ "com.android.settings.network.SatelliteWarningDialogActivity"
+ )
+ putExtra(WifiUtils.DIALOG_WINDOW_TYPE,
+ WindowManager.LayoutParams.TYPE_KEYGUARD_DIALOG)
+ putExtra(EXTRA_TYPE_OF_SATELLITE_WARNING_DIALOG, type)
+ addFlags(Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_SINGLE_TOP)
+ })
+ }
+
+ /**
+ * Checks if the satellite modem is enabled.
+ *
+ * @param executor The executor to run the asynchronous operation on
+ * @return A ListenableFuture that will resolve to `true` if the satellite modem enabled,
+ * `false` otherwise.
+ */
+ private suspend fun requestIsEnabled(
+ context: Context,
+ ): Boolean = withContext(Default) {
+ val satelliteManager: SatelliteManager? =
+ context.getSystemService(SatelliteManager::class.java)
+ if (satelliteManager == null) {
+ Log.w(TAG, "SatelliteManager is null")
+ return@withContext false
+ }
+
+ suspendCancellableCoroutine {continuation ->
+ satelliteManager?.requestIsEnabled(Default.asExecutor(),
+ object : OutcomeReceiver<Boolean, SatelliteManager.SatelliteException> {
+ override fun onResult(result: Boolean) {
+ Log.i(TAG, "Satellite modem enabled status: $result")
+ continuation.resume(result)
+ }
+
+ override fun onError(error: SatelliteManager.SatelliteException) {
+ super.onError(error)
+ Log.w(TAG, "Can't get satellite modem enabled status", error)
+ continuation.resume(false)
+ }
+ })
+ }
+ }
+
+ const val TAG = "SatelliteDialogUtils"
+
+ const val EXTRA_TYPE_OF_SATELLITE_WARNING_DIALOG: String =
+ "extra_type_of_satellite_warning_dialog"
+ const val TYPE_IS_UNKNOWN = -1
+ const val TYPE_IS_WIFI = 0
+ const val TYPE_IS_BLUETOOTH = 1
+ const val TYPE_IS_AIRPLANE_MODE = 2
+}
\ No newline at end of file
diff --git a/packages/SettingsLib/src/com/android/settingslib/statusbar/notification/data/repository/FakeNotificationsSoundPolicyRepository.kt b/packages/SettingsLib/src/com/android/settingslib/statusbar/notification/data/repository/FakeNotificationsSoundPolicyRepository.kt
index 2a44511..a939ed1 100644
--- a/packages/SettingsLib/src/com/android/settingslib/statusbar/notification/data/repository/FakeNotificationsSoundPolicyRepository.kt
+++ b/packages/SettingsLib/src/com/android/settingslib/statusbar/notification/data/repository/FakeNotificationsSoundPolicyRepository.kt
@@ -17,6 +17,7 @@
package com.android.settingslib.statusbar.notification.data.repository
import android.app.NotificationManager
+import android.provider.Settings
import com.android.settingslib.statusbar.notification.data.model.ZenMode
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.StateFlow
@@ -28,10 +29,14 @@
override val notificationPolicy: StateFlow<NotificationManager.Policy?>
get() = mutableNotificationPolicy.asStateFlow()
- private val mutableZenMode = MutableStateFlow<ZenMode?>(null)
+ private val mutableZenMode = MutableStateFlow<ZenMode?>(ZenMode(Settings.Global.ZEN_MODE_OFF))
override val zenMode: StateFlow<ZenMode?>
get() = mutableZenMode.asStateFlow()
+ init {
+ updateNotificationPolicy()
+ }
+
fun updateNotificationPolicy(policy: NotificationManager.Policy?) {
mutableNotificationPolicy.value = policy
}
@@ -48,13 +53,14 @@
suppressedVisualEffects: Int = NotificationManager.Policy.SUPPRESSED_EFFECTS_UNSET,
state: Int = NotificationManager.Policy.STATE_UNSET,
priorityConversationSenders: Int = NotificationManager.Policy.CONVERSATION_SENDERS_NONE,
-) = updateNotificationPolicy(
- NotificationManager.Policy(
- priorityCategories,
- priorityCallSenders,
- priorityMessageSenders,
- suppressedVisualEffects,
- state,
- priorityConversationSenders,
+) =
+ updateNotificationPolicy(
+ NotificationManager.Policy(
+ priorityCategories,
+ priorityCallSenders,
+ priorityMessageSenders,
+ suppressedVisualEffects,
+ state,
+ priorityConversationSenders,
+ )
)
-)
\ No newline at end of file
diff --git a/packages/SettingsLib/src/com/android/settingslib/volume/data/repository/AudioRepository.kt b/packages/SettingsLib/src/com/android/settingslib/volume/data/repository/AudioRepository.kt
index 65a5317..36e396fb 100644
--- a/packages/SettingsLib/src/com/android/settingslib/volume/data/repository/AudioRepository.kt
+++ b/packages/SettingsLib/src/com/android/settingslib/volume/data/repository/AudioRepository.kt
@@ -72,7 +72,11 @@
suspend fun setVolume(audioStream: AudioStream, volume: Int)
- suspend fun setMuted(audioStream: AudioStream, isMuted: Boolean)
+ /**
+ * Mutes and un-mutes [audioStream]. Returns true when the state changes and false the
+ * otherwise.
+ */
+ suspend fun setMuted(audioStream: AudioStream, isMuted: Boolean): Boolean
suspend fun setRingerMode(audioStream: AudioStream, mode: RingerMode)
@@ -164,14 +168,20 @@
audioManager.setStreamVolume(audioStream.value, volume, 0)
}
- override suspend fun setMuted(audioStream: AudioStream, isMuted: Boolean) =
- withContext(backgroundCoroutineContext) {
- audioManager.adjustStreamVolume(
- audioStream.value,
- if (isMuted) AudioManager.ADJUST_MUTE else AudioManager.ADJUST_UNMUTE,
- 0,
- )
+ override suspend fun setMuted(audioStream: AudioStream, isMuted: Boolean): Boolean {
+ return withContext(backgroundCoroutineContext) {
+ if (isMuted == audioManager.isStreamMute(audioStream.value)) {
+ false
+ } else {
+ audioManager.adjustStreamVolume(
+ audioStream.value,
+ if (isMuted) AudioManager.ADJUST_MUTE else AudioManager.ADJUST_UNMUTE,
+ 0,
+ )
+ true
+ }
}
+ }
override suspend fun setRingerMode(audioStream: AudioStream, mode: RingerMode) {
withContext(backgroundCoroutineContext) { audioManager.ringerMode = mode.value }
diff --git a/packages/SettingsLib/src/com/android/settingslib/volume/data/repository/MediaControllerExt.kt b/packages/SettingsLib/src/com/android/settingslib/volume/data/repository/MediaControllerExt.kt
deleted file mode 100644
index 1f037c0..0000000
--- a/packages/SettingsLib/src/com/android/settingslib/volume/data/repository/MediaControllerExt.kt
+++ /dev/null
@@ -1,100 +0,0 @@
-/*
- * Copyright (C) 2024 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.settingslib.volume.data.repository
-
-import android.media.MediaMetadata
-import android.media.session.MediaController
-import android.media.session.MediaSession
-import android.media.session.PlaybackState
-import android.os.Bundle
-import android.os.Handler
-import kotlinx.coroutines.channels.ProducerScope
-import kotlinx.coroutines.channels.awaitClose
-import kotlinx.coroutines.flow.Flow
-import kotlinx.coroutines.flow.callbackFlow
-import kotlinx.coroutines.launch
-
-/** [MediaController.Callback] flow representation. */
-fun MediaController.stateChanges(handler: Handler): Flow<MediaControllerChange> {
- return callbackFlow {
- val callback = MediaControllerCallbackProducer(this)
- registerCallback(callback, handler)
- awaitClose { unregisterCallback(callback) }
- }
-}
-
-/** Models particular change event received by [MediaController.Callback]. */
-sealed interface MediaControllerChange {
-
- data object SessionDestroyed : MediaControllerChange
-
- data class SessionEvent(val event: String, val extras: Bundle?) : MediaControllerChange
-
- data class PlaybackStateChanged(val state: PlaybackState?) : MediaControllerChange
-
- data class MetadataChanged(val metadata: MediaMetadata?) : MediaControllerChange
-
- data class QueueChanged(val queue: MutableList<MediaSession.QueueItem>?) :
- MediaControllerChange
-
- data class QueueTitleChanged(val title: CharSequence?) : MediaControllerChange
-
- data class ExtrasChanged(val extras: Bundle?) : MediaControllerChange
-
- data class AudioInfoChanged(val info: MediaController.PlaybackInfo?) : MediaControllerChange
-}
-
-private class MediaControllerCallbackProducer(
- private val producingScope: ProducerScope<MediaControllerChange>
-) : MediaController.Callback() {
-
- override fun onSessionDestroyed() {
- send(MediaControllerChange.SessionDestroyed)
- }
-
- override fun onSessionEvent(event: String, extras: Bundle?) {
- send(MediaControllerChange.SessionEvent(event, extras))
- }
-
- override fun onPlaybackStateChanged(state: PlaybackState?) {
- send(MediaControllerChange.PlaybackStateChanged(state))
- }
-
- override fun onMetadataChanged(metadata: MediaMetadata?) {
- send(MediaControllerChange.MetadataChanged(metadata))
- }
-
- override fun onQueueChanged(queue: MutableList<MediaSession.QueueItem>?) {
- send(MediaControllerChange.QueueChanged(queue))
- }
-
- override fun onQueueTitleChanged(title: CharSequence?) {
- send(MediaControllerChange.QueueTitleChanged(title))
- }
-
- override fun onExtrasChanged(extras: Bundle?) {
- send(MediaControllerChange.ExtrasChanged(extras))
- }
-
- override fun onAudioInfoChanged(info: MediaController.PlaybackInfo?) {
- send(MediaControllerChange.AudioInfoChanged(info))
- }
-
- private fun send(change: MediaControllerChange) {
- producingScope.launch { producingScope.send(change) }
- }
-}
diff --git a/packages/SettingsLib/src/com/android/settingslib/volume/domain/interactor/AudioVolumeInteractor.kt b/packages/SettingsLib/src/com/android/settingslib/volume/domain/interactor/AudioVolumeInteractor.kt
index 33f917e..0e5ebda 100644
--- a/packages/SettingsLib/src/com/android/settingslib/volume/domain/interactor/AudioVolumeInteractor.kt
+++ b/packages/SettingsLib/src/com/android/settingslib/volume/domain/interactor/AudioVolumeInteractor.kt
@@ -25,6 +25,7 @@
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.combine
+import kotlinx.coroutines.flow.first
import kotlinx.coroutines.flow.map
/** Provides audio stream state and an ability to change it */
@@ -46,8 +47,16 @@
val ringerMode: StateFlow<RingerMode>
get() = audioRepository.ringerMode
- suspend fun setVolume(audioStream: AudioStream, volume: Int) =
+ suspend fun setVolume(audioStream: AudioStream, volume: Int) {
+ val streamModel = getAudioStream(audioStream).first()
+ val oldVolume = streamModel.volume
audioRepository.setVolume(audioStream, volume)
+ when {
+ volume == streamModel.minVolume -> setMuted(audioStream, true)
+ oldVolume == streamModel.minVolume && volume > streamModel.minVolume ->
+ setMuted(audioStream, false)
+ }
+ }
suspend fun setMuted(audioStream: AudioStream, isMuted: Boolean) {
if (audioStream.value == AudioManager.STREAM_RING) {
@@ -55,7 +64,16 @@
if (isMuted) AudioManager.RINGER_MODE_VIBRATE else AudioManager.RINGER_MODE_NORMAL
audioRepository.setRingerMode(audioStream, RingerMode(mode))
}
- audioRepository.setMuted(audioStream, isMuted)
+ val mutedChanged = audioRepository.setMuted(audioStream, isMuted)
+ if (mutedChanged && !isMuted) {
+ with(getAudioStream(audioStream).first()) {
+ if (volume == minVolume) {
+ // Slightly increase volume when user un-mutes the stream that is lowered
+ // down to its minimum
+ setVolume(audioStream, volume + 1)
+ }
+ }
+ }
}
/** Checks if the volume can be changed via the UI. */
diff --git a/packages/SettingsLib/tests/robotests/Android.bp b/packages/SettingsLib/tests/robotests/Android.bp
index f4ddd0a..e125083 100644
--- a/packages/SettingsLib/tests/robotests/Android.bp
+++ b/packages/SettingsLib/tests/robotests/Android.bp
@@ -41,7 +41,10 @@
//###########################################################
android_robolectric_test {
name: "SettingsLibRoboTests",
- srcs: ["src/**/*.java"],
+ srcs: [
+ "src/**/*.java",
+ "src/**/*.kt",
+ ],
static_libs: [
"Settings_robolectric_meta_service_file",
"Robolectric_shadows_androidx_fragment_upstream",
@@ -51,6 +54,7 @@
"androidx.core_core",
"flag-junit",
"settingslib_media_flags_lib",
+ "settingslib_illustrationpreference_flags_lib",
"testng", // TODO: remove once JUnit on Android provides assertThrows
],
java_resource_dirs: ["config"],
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/BluetoothUtilsTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/BluetoothUtilsTest.java
index 7a2818d..a638df5 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/BluetoothUtilsTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/BluetoothUtilsTest.java
@@ -15,6 +15,8 @@
*/
package com.android.settingslib.bluetooth;
+import static com.android.settingslib.flags.Flags.FLAG_ENABLE_DETERMINING_ADVANCED_DETAILS_HEADER_WITH_METADATA;
+
import static com.google.common.truth.Truth.assertThat;
import static org.mockito.ArgumentMatchers.any;
@@ -33,11 +35,13 @@
import android.graphics.drawable.Drawable;
import android.media.AudioManager;
import android.net.Uri;
+import android.platform.test.flag.junit.SetFlagsRule;
import android.util.Pair;
import com.android.settingslib.widget.AdaptiveIcon;
import org.junit.Before;
+import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Answers;
@@ -83,11 +87,15 @@
private static final String TEST_EXCLUSIVE_MANAGER_PACKAGE = "com.test.manager";
private static final String TEST_EXCLUSIVE_MANAGER_COMPONENT = "com.test.manager/.component";
+ @Rule
+ public final SetFlagsRule mSetFlagsRule = new SetFlagsRule();
+
@Before
public void setUp() {
MockitoAnnotations.initMocks(this);
mContext = spy(RuntimeEnvironment.application);
+ mSetFlagsRule.disableFlags(FLAG_ENABLE_DETERMINING_ADVANCED_DETAILS_HEADER_WITH_METADATA);
when(mLocalBluetoothManager.getProfileManager()).thenReturn(mProfileManager);
when(mProfileManager.getLeAudioBroadcastProfile()).thenReturn(mBroadcast);
when(mProfileManager.getLeAudioBroadcastAssistantProfile()).thenReturn(mAssistant);
@@ -253,6 +261,25 @@
}
@Test
+ public void isAdvancedDetailsHeader_noMainIcon_returnFalse() {
+ mSetFlagsRule.enableFlags(FLAG_ENABLE_DETERMINING_ADVANCED_DETAILS_HEADER_WITH_METADATA);
+
+ when(mBluetoothDevice.getMetadata(BluetoothDevice.METADATA_MAIN_ICON)).thenReturn(null);
+
+ assertThat(BluetoothUtils.isAdvancedDetailsHeader(mBluetoothDevice)).isFalse();
+ }
+
+ @Test
+ public void isAdvancedDetailsHeader_hasMainIcon_returnTrue() {
+ mSetFlagsRule.enableFlags(FLAG_ENABLE_DETERMINING_ADVANCED_DETAILS_HEADER_WITH_METADATA);
+
+ when(mBluetoothDevice.getMetadata(BluetoothDevice.METADATA_MAIN_ICON))
+ .thenReturn(STRING_METADATA.getBytes());
+
+ assertThat(BluetoothUtils.isAdvancedDetailsHeader(mBluetoothDevice)).isTrue();
+ }
+
+ @Test
public void isAdvancedUntetheredDevice_untetheredHeadset_returnTrue() {
when(mBluetoothDevice.getMetadata(
BluetoothDevice.METADATA_IS_UNTETHERED_HEADSET)).thenReturn(
@@ -294,6 +321,18 @@
}
@Test
+ public void isAdvancedUntetheredDevice_untetheredHeadsetMetadataIsFalse_returnFalse() {
+ mSetFlagsRule.enableFlags(FLAG_ENABLE_DETERMINING_ADVANCED_DETAILS_HEADER_WITH_METADATA);
+
+ when(mBluetoothDevice.getMetadata(BluetoothDevice.METADATA_IS_UNTETHERED_HEADSET))
+ .thenReturn("false".getBytes());
+ when(mBluetoothDevice.getMetadata(BluetoothDevice.METADATA_DEVICE_TYPE))
+ .thenReturn(BluetoothDevice.DEVICE_TYPE_UNTETHERED_HEADSET.getBytes());
+
+ assertThat(BluetoothUtils.isAdvancedUntetheredDevice(mBluetoothDevice)).isFalse();
+ }
+
+ @Test
public void isAvailableMediaBluetoothDevice_isConnectedLeAudioDevice_returnTrue() {
when(mCachedBluetoothDevice.isConnectedLeAudioDevice()).thenReturn(true);
when(mCachedBluetoothDevice.getDevice()).thenReturn(mBluetoothDevice);
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/CachedBluetoothDeviceTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/CachedBluetoothDeviceTest.java
index b9bf9ca..0d81494 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/CachedBluetoothDeviceTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/CachedBluetoothDeviceTest.java
@@ -780,9 +780,8 @@
mBatteryLevel = 10;
// Act & Assert:
- // Get "Left: 10% battery" result with Battery Level 10.
- assertThat(mCachedDevice.getTvConnectionSummary().toString()).isEqualTo(
- "Left: 10% battery");
+ // Get "Left 10%" result with Battery Level 10.
+ assertThat(mCachedDevice.getTvConnectionSummary().toString()).isEqualTo("Left 10%");
}
@Test
@@ -815,9 +814,9 @@
mBatteryLevel = 10;
// Act & Assert:
- // Get "Left: 10% battery" result with Battery Level 10.
+ // Get "Left 10%" result with Battery Level 10.
assertThat(mCachedDevice.getTvConnectionSummary().toString()).isEqualTo(
- "Left: 10% battery");
+ "Left 10%");
}
@Test
@@ -925,9 +924,9 @@
mBatteryLevel = 10;
// Act & Assert:
- // Get "Left: 10% battery Right: 10% battery" result with Battery Level 10.
+ // Get "Left 10% Right 10%" result with Battery Level 10.
assertThat(mCachedDevice.getTvConnectionSummary().toString())
- .isEqualTo("Left: 10% battery Right: 10% battery");
+ .isEqualTo("Left 10% Right 10%");
}
@Test
@@ -1226,7 +1225,7 @@
TWS_BATTERY_RIGHT.getBytes());
assertThat(mCachedDevice.getTvConnectionSummary().toString()).isEqualTo(
- "Left: 15% battery Right: 25% battery");
+ "Left 15% Right 25%");
}
@Test
@@ -1262,11 +1261,7 @@
TWS_BATTERY_RIGHT.getBytes());
assertThat(mCachedDevice.getTvConnectionSummary().toString())
- .isEqualTo(
- mContext.getString(R.string.bluetooth_battery_level_untethered_left, "15%")
- + " "
- + mContext.getString(
- R.string.bluetooth_battery_level_untethered_right, "25%"));
+ .isEqualTo("Left 15% Right 25%");
}
@Test
@@ -1283,10 +1278,8 @@
.thenReturn(TWS_BATTERY_RIGHT.getBytes());
int lowBatteryColor = mContext.getColor(LOW_BATTERY_COLOR);
- String leftBattery =
- mContext.getString(R.string.bluetooth_battery_level_untethered_left, "15%");
- String rightBattery =
- mContext.getString(R.string.bluetooth_battery_level_untethered_right, "25%");
+ String leftBattery = "Left 15%";
+ String rightBattery = "Right 25%";
// Default low battery threshold, only left battery is low
CharSequence summary = mCachedDevice.getTvConnectionSummary(LOW_BATTERY_COLOR);
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/satellite/SatelliteDialogUtilsTest.kt b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/satellite/SatelliteDialogUtilsTest.kt
new file mode 100644
index 0000000..aeda1ed6
--- /dev/null
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/satellite/SatelliteDialogUtilsTest.kt
@@ -0,0 +1,162 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settingslib.satellite
+
+import android.content.Context
+import android.content.Intent
+import android.os.OutcomeReceiver
+import android.platform.test.annotations.RequiresFlagsEnabled
+import android.telephony.satellite.SatelliteManager
+import android.telephony.satellite.SatelliteManager.SatelliteException
+import android.util.AndroidRuntimeException
+import androidx.test.core.app.ApplicationProvider
+import com.android.internal.telephony.flags.Flags
+import com.android.settingslib.satellite.SatelliteDialogUtils.TYPE_IS_WIFI
+import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.Dispatchers
+import kotlinx.coroutines.runBlocking
+import org.junit.Assert.assertFalse
+import org.junit.Assert.assertTrue
+import org.junit.Before
+import org.junit.Rule
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.Mock
+import org.mockito.Mockito.any
+import org.mockito.Mockito.`when`
+import org.mockito.Spy
+import org.mockito.junit.MockitoJUnit
+import org.mockito.junit.MockitoRule
+import org.mockito.Mockito.verify
+import org.mockito.internal.verification.Times
+import org.robolectric.RobolectricTestRunner
+
+@RunWith(RobolectricTestRunner::class)
+class SatelliteDialogUtilsTest {
+ @JvmField
+ @Rule
+ val mockitoRule: MockitoRule = MockitoJUnit.rule()
+
+ @Spy
+ var context: Context = ApplicationProvider.getApplicationContext()
+ @Mock
+ private lateinit var satelliteManager: SatelliteManager
+
+ private val coroutineScope = CoroutineScope(Dispatchers.Main)
+
+ @Before
+ fun setUp() {
+ `when`(context.getSystemService(SatelliteManager::class.java))
+ .thenReturn(satelliteManager)
+ }
+
+ @Test
+ @RequiresFlagsEnabled(Flags.FLAG_OEM_ENABLED_SATELLITE_FLAG)
+ fun mayStartSatelliteWarningDialog_satelliteIsOn_showWarningDialog() = runBlocking {
+ `when`(
+ satelliteManager.requestIsEnabled(
+ any(), any<OutcomeReceiver<Boolean, SatelliteManager.SatelliteException>>()
+ )
+ )
+ .thenAnswer { invocation ->
+ val receiver = invocation
+ .getArgument<
+ OutcomeReceiver<Boolean, SatelliteManager.SatelliteException>>(
+ 1
+ )
+ receiver.onResult(true)
+ null
+ }
+
+ try {
+ SatelliteDialogUtils.mayStartSatelliteWarningDialog(
+ context, coroutineScope, TYPE_IS_WIFI, allowClick = {
+ assertTrue(it)
+ })
+ } catch (e: AndroidRuntimeException) {
+ // Catch exception of starting activity .
+ }
+ }
+
+ @Test
+ @RequiresFlagsEnabled(Flags.FLAG_OEM_ENABLED_SATELLITE_FLAG)
+ fun mayStartSatelliteWarningDialog_satelliteIsOff_notShowWarningDialog() = runBlocking {
+ `when`(
+ satelliteManager.requestIsEnabled(
+ any(), any<OutcomeReceiver<Boolean, SatelliteManager.SatelliteException>>()
+ )
+ )
+ .thenAnswer { invocation ->
+ val receiver = invocation
+ .getArgument<
+ OutcomeReceiver<Boolean, SatelliteManager.SatelliteException>>(
+ 1
+ )
+ receiver.onResult(false)
+ null
+ }
+
+
+ SatelliteDialogUtils.mayStartSatelliteWarningDialog(
+ context, coroutineScope, TYPE_IS_WIFI, allowClick = {
+ assertFalse(it)
+ })
+
+ verify(context, Times(0)).startActivity(any<Intent>())
+ }
+
+ @Test
+ @RequiresFlagsEnabled(Flags.FLAG_OEM_ENABLED_SATELLITE_FLAG)
+ fun mayStartSatelliteWarningDialog_noSatelliteManager_notShowWarningDialog() = runBlocking {
+ `when`(context.getSystemService(SatelliteManager::class.java))
+ .thenReturn(null)
+
+ SatelliteDialogUtils.mayStartSatelliteWarningDialog(
+ context, coroutineScope, TYPE_IS_WIFI, allowClick = {
+ assertFalse(it)
+ })
+
+ verify(context, Times(0)).startActivity(any<Intent>())
+ }
+
+ @Test
+ @RequiresFlagsEnabled(Flags.FLAG_OEM_ENABLED_SATELLITE_FLAG)
+ fun mayStartSatelliteWarningDialog_satelliteErrorResult_notShowWarningDialog() = runBlocking {
+ `when`(
+ satelliteManager.requestIsEnabled(
+ any(), any<OutcomeReceiver<Boolean, SatelliteManager.SatelliteException>>()
+ )
+ )
+ .thenAnswer { invocation ->
+ val receiver = invocation
+ .getArgument<
+ OutcomeReceiver<Boolean, SatelliteManager.SatelliteException>>(
+ 1
+ )
+ receiver.onError(SatelliteException(SatelliteManager.SATELLITE_RESULT_ERROR))
+ null
+ }
+
+
+ SatelliteDialogUtils.mayStartSatelliteWarningDialog(
+ context, coroutineScope, TYPE_IS_WIFI, allowClick = {
+ assertFalse(it)
+ })
+
+ verify(context, Times(0)).startActivity(any<Intent>())
+ }
+}
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/widget/IllustrationPreferenceTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/widget/IllustrationPreferenceTest.java
index 6590bbd..ca53fc2 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/widget/IllustrationPreferenceTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/widget/IllustrationPreferenceTest.java
@@ -26,10 +26,14 @@
import static org.mockito.Mockito.verify;
import android.content.Context;
+import android.content.res.Resources;
import android.graphics.drawable.AnimatedImageDrawable;
import android.graphics.drawable.AnimatedVectorDrawable;
import android.graphics.drawable.AnimationDrawable;
import android.net.Uri;
+import android.platform.test.annotations.DisableFlags;
+import android.platform.test.annotations.EnableFlags;
+import android.platform.test.flag.junit.SetFlagsRule;
import android.util.AttributeSet;
import android.view.View;
import android.view.ViewGroup;
@@ -39,11 +43,13 @@
import androidx.preference.PreferenceViewHolder;
import androidx.test.core.app.ApplicationProvider;
+import com.android.settingslib.widget.flags.Flags;
import com.android.settingslib.widget.preference.illustration.R;
import com.airbnb.lottie.LottieAnimationView;
import org.junit.Before;
+import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
@@ -51,10 +57,14 @@
import org.robolectric.Robolectric;
import org.robolectric.RobolectricTestRunner;
+import java.io.ByteArrayInputStream;
+
@RunWith(RobolectricTestRunner.class)
public class IllustrationPreferenceTest {
+ @Rule public final SetFlagsRule mSetFlagsRule = new SetFlagsRule();
+
@Mock
private ViewGroup mRootView;
private Uri mImageUri;
@@ -66,6 +76,7 @@
private final Context mContext = ApplicationProvider.getApplicationContext();
private IllustrationPreference.OnBindListener mOnBindListener;
private LottieAnimationView mOnBindListenerAnimationView;
+ private FrameLayout mIllustrationFrame;
@Before
public void setUp() {
@@ -75,14 +86,14 @@
mBackgroundView = new ImageView(mContext);
mAnimationView = spy(new LottieAnimationView(mContext));
mMiddleGroundLayout = new FrameLayout(mContext);
- final FrameLayout illustrationFrame = new FrameLayout(mContext);
- illustrationFrame.setLayoutParams(
+ mIllustrationFrame = new FrameLayout(mContext);
+ mIllustrationFrame.setLayoutParams(
new FrameLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT,
ViewGroup.LayoutParams.WRAP_CONTENT));
doReturn(mMiddleGroundLayout).when(mRootView).findViewById(R.id.middleground_layout);
doReturn(mBackgroundView).when(mRootView).findViewById(R.id.background_view);
doReturn(mAnimationView).when(mRootView).findViewById(R.id.lottie_view);
- doReturn(illustrationFrame).when(mRootView).findViewById(R.id.illustration_frame);
+ doReturn(mIllustrationFrame).when(mRootView).findViewById(R.id.illustration_frame);
mViewHolder = spy(PreferenceViewHolder.createInstanceForTests(mRootView));
final AttributeSet attributeSet = Robolectric.buildAttributeSet().build();
@@ -158,11 +169,13 @@
}
@Test
+ @DisableFlags(Flags.FLAG_AUTO_HIDE_EMPTY_LOTTIE_RES)
public void playLottieAnimationWithResource_verifyFailureListener() {
// fake the valid lottie image
final int fakeValidResId = 111;
doNothing().when(mAnimationView).setImageResource(fakeValidResId);
doReturn(null).when(mAnimationView).getDrawable();
+ doNothing().when(mAnimationView).setAnimation(fakeValidResId);
mPreference.setLottieAnimationResId(fakeValidResId);
mPreference.onBindViewHolder(mViewHolder);
@@ -171,6 +184,50 @@
}
@Test
+ @DisableFlags(Flags.FLAG_AUTO_HIDE_EMPTY_LOTTIE_RES)
+ public void handleImageWithAnimation_emptyInputStreamDisabledFlag_verifyContainerVisible() {
+ doNothing().when(mAnimationView).setImageResource(111);
+ doReturn(null).when(mAnimationView).getDrawable();
+
+ mPreference.setLottieAnimationResId(111);
+ mPreference.onBindViewHolder(mViewHolder);
+
+ assertThat(mAnimationView.getVisibility()).isEqualTo(View.VISIBLE);
+ assertThat(mIllustrationFrame.getVisibility()).isEqualTo(View.VISIBLE);
+ }
+
+ @Test
+ @EnableFlags(Flags.FLAG_AUTO_HIDE_EMPTY_LOTTIE_RES)
+ public void handleImageWithAnimation_emptyInputStreamEnabledFlag_verifyContainerHidden() {
+ Resources res = spy(mContext.getResources());
+ doReturn(res).when(mAnimationView).getResources();
+ doReturn(new ByteArrayInputStream(new byte[] {})).when(res).openRawResource(111);
+
+ mPreference.setLottieAnimationResId(111);
+ mPreference.onBindViewHolder(mViewHolder);
+
+ assertThat(mAnimationView.getVisibility()).isEqualTo(View.GONE);
+ assertThat(mIllustrationFrame.getVisibility()).isEqualTo(View.GONE);
+ }
+
+ @Test
+ @EnableFlags(Flags.FLAG_AUTO_HIDE_EMPTY_LOTTIE_RES)
+ public void handleImageWithAnimation_nonEmptyInputStreamEnabledFlag_verifyContainerVisible() {
+ Resources res = spy(mContext.getResources());
+ doReturn(res).when(mAnimationView).getResources();
+ doReturn(new ByteArrayInputStream(new byte[] { 1, 2, 3 })).when(res).openRawResource(111);
+ doNothing().when(mAnimationView).setImageResource(111);
+ doNothing().when(mAnimationView).setAnimation(111);
+ doReturn(null).when(mAnimationView).getDrawable();
+
+ mPreference.setLottieAnimationResId(111);
+ mPreference.onBindViewHolder(mViewHolder);
+
+ assertThat(mAnimationView.getVisibility()).isEqualTo(View.VISIBLE);
+ assertThat(mIllustrationFrame.getVisibility()).isEqualTo(View.VISIBLE);
+ }
+
+ @Test
public void setMaxHeight_smallerThanRestrictedHeight_matchResult() {
final int restrictedHeight =
mContext.getResources().getDimensionPixelSize(
diff --git a/packages/SettingsProvider/src/android/provider/settings/backup/SecureSettings.java b/packages/SettingsProvider/src/android/provider/settings/backup/SecureSettings.java
index be3f410..888e395 100644
--- a/packages/SettingsProvider/src/android/provider/settings/backup/SecureSettings.java
+++ b/packages/SettingsProvider/src/android/provider/settings/backup/SecureSettings.java
@@ -274,6 +274,9 @@
Settings.Secure.SCREEN_RESOLUTION_MODE,
Settings.Secure.ACCESSIBILITY_GESTURE_TARGETS,
Settings.Secure.ACCESSIBILITY_DISPLAY_DALTONIZER_SATURATION_LEVEL,
- Settings.Secure.CHARGE_OPTIMIZATION_MODE
+ Settings.Secure.CHARGE_OPTIMIZATION_MODE,
+ Settings.Secure.ON_DEVICE_INTELLIGENCE_UNBIND_TIMEOUT_MS,
+ Settings.Secure.ON_DEVICE_INFERENCE_UNBIND_TIMEOUT_MS,
+ Settings.Secure.ON_DEVICE_INTELLIGENCE_IDLE_TIMEOUT_MS,
};
}
diff --git a/packages/SettingsProvider/src/android/provider/settings/validators/SecureSettingsValidators.java b/packages/SettingsProvider/src/android/provider/settings/validators/SecureSettingsValidators.java
index b1feede..b992ddc 100644
--- a/packages/SettingsProvider/src/android/provider/settings/validators/SecureSettingsValidators.java
+++ b/packages/SettingsProvider/src/android/provider/settings/validators/SecureSettingsValidators.java
@@ -18,6 +18,7 @@
import static android.provider.settings.validators.SettingsValidators.ACCESSIBILITY_SHORTCUT_TARGET_LIST_VALIDATOR;
import static android.provider.settings.validators.SettingsValidators.ANY_INTEGER_VALIDATOR;
+import static android.provider.settings.validators.SettingsValidators.ANY_LONG_VALIDATOR;
import static android.provider.settings.validators.SettingsValidators.ANY_STRING_VALIDATOR;
import static android.provider.settings.validators.SettingsValidators.AUTOFILL_SERVICE_VALIDATOR;
import static android.provider.settings.validators.SettingsValidators.BOOLEAN_VALIDATOR;
@@ -433,5 +434,8 @@
VALIDATORS.put(Secure.ACCESSIBILITY_DISPLAY_DALTONIZER_SATURATION_LEVEL,
new InclusiveIntegerRangeValidator(0, 10));
VALIDATORS.put(Secure.CHARGE_OPTIMIZATION_MODE, new InclusiveIntegerRangeValidator(0, 10));
+ VALIDATORS.put(Secure.ON_DEVICE_INFERENCE_UNBIND_TIMEOUT_MS, ANY_LONG_VALIDATOR);
+ VALIDATORS.put(Secure.ON_DEVICE_INTELLIGENCE_UNBIND_TIMEOUT_MS, ANY_LONG_VALIDATOR);
+ VALIDATORS.put(Secure.ON_DEVICE_INTELLIGENCE_IDLE_TIMEOUT_MS, NONE_NEGATIVE_LONG_VALIDATOR);
}
}
diff --git a/packages/SettingsProvider/src/android/provider/settings/validators/SettingsValidators.java b/packages/SettingsProvider/src/android/provider/settings/validators/SettingsValidators.java
index 677c81a..255b1ad 100644
--- a/packages/SettingsProvider/src/android/provider/settings/validators/SettingsValidators.java
+++ b/packages/SettingsProvider/src/android/provider/settings/validators/SettingsValidators.java
@@ -239,6 +239,18 @@
}
};
+ static final Validator ANY_LONG_VALIDATOR = value -> {
+ if (value == null) {
+ return true;
+ }
+ try {
+ Long.parseLong(value);
+ return true;
+ } catch (NumberFormatException e) {
+ return false;
+ }
+ };
+
static final Validator CREDENTIAL_SERVICE_VALIDATOR = new Validator() {
@Override
public boolean validate(String value) {
diff --git a/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java b/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java
index 92167ee..ab9a30b 100644
--- a/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java
+++ b/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java
@@ -233,6 +233,7 @@
Settings.Global.ENHANCED_4G_MODE_ENABLED,
Settings.Global.ENABLE_16K_PAGES, // Added for 16K developer option
Settings.Global.EPHEMERAL_COOKIE_MAX_SIZE_BYTES,
+ Settings.Global.ERROR_KERNEL_LOG_PREFIX,
Settings.Global.ERROR_LOGCAT_PREFIX,
Settings.Global.EUICC_PROVISIONED,
Settings.Global.EUICC_SUPPORTED_COUNTRIES,
diff --git a/packages/Shell/AndroidManifest.xml b/packages/Shell/AndroidManifest.xml
index 46bf494..374240b 100644
--- a/packages/Shell/AndroidManifest.xml
+++ b/packages/Shell/AndroidManifest.xml
@@ -347,6 +347,7 @@
<uses-permission android:name="android.permission.REQUEST_COMPANION_SELF_MANAGED" />
<uses-permission android:name="android.permission.USE_COMPANION_TRANSPORTS" />
<uses-permission android:name="android.permission.REQUEST_OBSERVE_DEVICE_UUID_PRESENCE" />
+ <uses-permission android:name="android.permission.DELIVER_COMPANION_MESSAGES" />
<uses-permission android:name="android.permission.MANAGE_APPOPS" />
<uses-permission android:name="android.permission.WATCH_APPOPS" />
diff --git a/packages/SystemUI/Android.bp b/packages/SystemUI/Android.bp
index c04ec4f..8b60ed0 100644
--- a/packages/SystemUI/Android.bp
+++ b/packages/SystemUI/Android.bp
@@ -78,11 +78,69 @@
visibility: ["//visibility:private"],
}
+filegroup {
+ name: "SystemUI-tests-broken-robofiles-run",
+ srcs: [
+ "tests/src/**/systemui/util/LifecycleFragmentTest.java",
+ "tests/src/**/systemui/util/TestableAlertDialogTest.kt",
+ "tests/src/**/systemui/util/kotlin/PairwiseFlowTest",
+ "tests/src/**/systemui/util/sensors/AsyncManagerTest.java",
+ "tests/src/**/systemui/util/sensors/ThresholdSensorImplTest.java",
+ "tests/src/**/systemui/util/wakelock/KeepAwakeAnimationListenerTest.java",
+ "tests/src/**/systemui/statusbar/KeyboardShortcutListSearchTest.java",
+ "tests/src/**/systemui/statusbar/KeyboardShortcutsTest.java",
+ "tests/src/**/systemui/statusbar/KeyguardIndicationControllerWithCoroutinesTest.kt",
+ "tests/src/**/systemui/statusbar/notification/AssistantFeedbackControllerTest.java",
+ "tests/src/**/systemui/statusbar/notification/collection/NotifCollectionTest.java",
+ "tests/src/**/systemui/statusbar/notification/collection/NotificationEntryTest.java",
+ "tests/src/**/systemui/statusbar/notification/collection/render/GroupExpansionManagerTest.kt",
+ "tests/src/**/systemui/statusbar/notification/collection/ShadeListBuilderTest.java",
+ "tests/src/**/systemui/statusbar/notification/footer/ui/view/FooterViewTest.java",
+ "tests/src/**/systemui/statusbar/notification/interruption/NotificationInterruptStateProviderImplTest.java",
+ "tests/src/**/systemui/statusbar/notification/interruption/NotificationInterruptStateProviderWrapperTest.kt",
+ "tests/src/**/systemui/statusbar/notification/interruption/VisualInterruptionDecisionProviderImplTest.kt",
+ "tests/src/**/systemui/statusbar/NotificationLockscreenUserManagerTest.java",
+ "tests/src/**/systemui/statusbar/notification/logging/NotificationLoggerTest.java",
+ "tests/src/**/systemui/statusbar/notification/row/ExpandableNotificationRowTest.java",
+ "tests/src/**/systemui/statusbar/notification/row/NotificationContentInflaterTest.java",
+ "tests/src/**/systemui/statusbar/notification/row/NotificationContentViewTest.kt",
+ "tests/src/**/systemui/statusbar/notification/row/NotificationConversationInfoTest.java",
+ "tests/src/**/systemui/statusbar/notification/row/NotificationGutsManagerTest.java",
+ "tests/src/**/systemui/statusbar/notification/row/NotificationGutsManagerWithScenesTest.kt",
+ "tests/src/**/systemui/statusbar/notification/row/NotifLayoutInflaterFactoryTest.kt",
+ "tests/src/**/systemui/statusbar/notification/row/wrapper/NotificationTemplateViewWrapperTest.kt",
+ "tests/src/**/systemui/statusbar/notification/stack/NotificationSectionsManagerTest.java",
+ "tests/src/**/systemui/statusbar/notification/stack/NotificationStackScrollLayoutTest.java",
+ "tests/src/**/systemui/statusbar/notification/stack/ui/viewmodel/SharedNotificationContainerViewModelTest.kt",
+ "tests/src/**/systemui/statusbar/phone/AutoTileManagerTest.java",
+ "tests/src/**/systemui/statusbar/phone/CentralSurfacesImplTest.java",
+ "tests/src/**/systemui/statusbar/phone/fragment/CollapsedStatusBarFragmentTest.java",
+ "tests/src/**/systemui/statusbar/phone/PhoneStatusBarTransitionsTest.kt",
+ "tests/src/**/systemui/statusbar/phone/PhoneStatusBarViewControllerTest.kt",
+ "tests/src/**/systemui/statusbar/phone/PhoneStatusBarView.java",
+ "tests/src/**/systemui/statusbar/phone/PhoneStatusBarViewTest.kt",
+ "tests/src/**/systemui/statusbar/phone/StatusBarBoundsProviderTest.kt",
+ "tests/src/**/systemui/statusbar/phone/StatusBarMoveFromCenterAnimationControllerTest.kt",
+ "tests/src/**/systemui/statusbar/pipeline/airplane/ui/viewmodel/AirplaneModeViewModelImplTest.kt",
+ "tests/src/**/systemui/statusbar/pipeline/mobile/data/repository/prod/MobileConnectionsRepositoryTest.kt",
+ "tests/src/**/systemui/statusbar/pipeline/mobile/ui/view/ModernStatusBarMobileViewTest.kt",
+ "tests/src/**/systemui/statusbar/pipeline/wifi/data/repository/prod/WifiRepositoryImplTest.kt",
+ "tests/src/**/systemui/statusbar/pipeline/wifi/ui/view/ModernStatusBarWifiViewTest.kt",
+ "tests/src/**/systemui/statusbar/policy/CallbackControllerTest.java",
+ "tests/src/**/systemui/statusbar/policy/DeviceStateRotationLockSettingControllerTest.java",
+ "tests/src/**/systemui/statusbar/policy/InflatedSmartRepliesTest.java",
+ "tests/src/**/systemui/statusbar/policy/LocationControllerImplTest.java",
+ "tests/src/**/systemui/statusbar/policy/RemoteInputViewTest.java",
+ "tests/src/**/systemui/statusbar/policy/SmartReplyViewTest.java",
+ "tests/src/**/systemui/statusbar/StatusBarStateControllerImplTest.kt",
+ ],
+}
+
// We are running robolectric tests in the tests directory as well as
// multivalent tests. If you add a test, and it doesn't run in robolectric,
// it should be added to this exclusion list. go/multivalent-tests
filegroup {
- name: "SystemUI-tests-broken-robofiles",
+ name: "SystemUI-tests-broken-robofiles-compile",
srcs: [
"tests/src/**/*DeviceOnlyTest.java",
"tests/src/**/*DeviceOnlyTest.kt",
@@ -703,7 +761,8 @@
":SystemUI-tests-robofiles",
],
exclude_srcs: [
- ":SystemUI-tests-broken-robofiles",
+ ":SystemUI-tests-broken-robofiles-compile",
+ ":SystemUI-tests-broken-robofiles-run",
],
static_libs: [
"RoboTestLibraries",
diff --git a/packages/SystemUI/AndroidManifest.xml b/packages/SystemUI/AndroidManifest.xml
index b9e70ef..9c58371 100644
--- a/packages/SystemUI/AndroidManifest.xml
+++ b/packages/SystemUI/AndroidManifest.xml
@@ -826,20 +826,6 @@
</intent-filter>
</activity>
- <activity
- android:name=".contrast.ContrastDialogActivity"
- android:label="@string/quick_settings_contrast_label"
- android:theme="@style/Theme.SystemUI.ContrastDialog"
- android:finishOnCloseSystemDialogs="true"
- android:launchMode="singleInstance"
- android:excludeFromRecents="true"
- android:exported="true">
- <intent-filter>
- <action android:name="com.android.intent.action.SHOW_CONTRAST_DIALOG" />
- <category android:name="android.intent.category.DEFAULT" />
- </intent-filter>
- </activity>
-
<activity android:name=".ForegroundServicesDialog"
android:process=":fgservices"
android:excludeFromRecents="true"
diff --git a/packages/SystemUI/accessibility/accessibilitymenu/aconfig/accessibility.aconfig b/packages/SystemUI/accessibility/accessibilitymenu/aconfig/accessibility.aconfig
index d868d5c..ba84287 100644
--- a/packages/SystemUI/accessibility/accessibilitymenu/aconfig/accessibility.aconfig
+++ b/packages/SystemUI/accessibility/accessibilitymenu/aconfig/accessibility.aconfig
@@ -9,3 +9,13 @@
description: "Hides the AccessibilityMenuService UI before taking action instead of after."
bug: "292020123"
}
+
+flag {
+ name: "a11y_menu_snackbar_live_region"
+ namespace: "accessibility"
+ description: "configures live region on snackbar so its contents are announced when it appears."
+ bug: "338351484"
+ metadata {
+ purpose: PURPOSE_BUGFIX
+ }
+}
diff --git a/packages/SystemUI/accessibility/accessibilitymenu/src/com/android/systemui/accessibility/accessibilitymenu/view/A11yMenuOverlayLayout.java b/packages/SystemUI/accessibility/accessibilitymenu/src/com/android/systemui/accessibility/accessibilitymenu/view/A11yMenuOverlayLayout.java
index 1be04f8..7b43b72 100644
--- a/packages/SystemUI/accessibility/accessibilitymenu/src/com/android/systemui/accessibility/accessibilitymenu/view/A11yMenuOverlayLayout.java
+++ b/packages/SystemUI/accessibility/accessibilitymenu/src/com/android/systemui/accessibility/accessibilitymenu/view/A11yMenuOverlayLayout.java
@@ -17,6 +17,7 @@
package com.android.systemui.accessibility.accessibilitymenu.view;
import static android.view.Display.DEFAULT_DISPLAY;
+import static android.view.View.ACCESSIBILITY_LIVE_REGION_POLITE;
import static android.view.WindowManager.LayoutParams.TYPE_ACCESSIBILITY_OVERLAY;
import static java.lang.Math.max;
@@ -321,7 +322,14 @@
AccessibilityManager.FLAG_CONTENT_TEXT);
final TextView snackbar = mLayout.findViewById(R.id.snackbar);
+ if (snackbar == null) {
+ return;
+ }
snackbar.setText(text);
+ if (com.android.systemui.accessibility.accessibilitymenu
+ .Flags.a11yMenuSnackbarLiveRegion()) {
+ snackbar.setAccessibilityLiveRegion(ACCESSIBILITY_LIVE_REGION_POLITE);
+ }
// Remove any existing fade-out animation before starting any new animations.
mHandler.removeCallbacksAndMessages(null);
diff --git a/packages/SystemUI/aconfig/accessibility.aconfig b/packages/SystemUI/aconfig/accessibility.aconfig
index 755fe2a..55edff6 100644
--- a/packages/SystemUI/aconfig/accessibility.aconfig
+++ b/packages/SystemUI/aconfig/accessibility.aconfig
@@ -4,6 +4,13 @@
# NOTE: Keep alphabetized to help limit merge conflicts from multiple simultaneous editors.
flag {
+ name: "create_windowless_window_magnifier"
+ namespace: "accessibility"
+ description: "Uses SurfaceControlViewHost to create the magnifier for window magnification."
+ bug: "280992417"
+}
+
+flag {
name: "delay_show_magnification_button"
namespace: "accessibility"
description: "Delays the showing of magnification mode switch button."
@@ -66,8 +73,11 @@
}
flag {
- name: "create_windowless_window_magnifier"
+ name: "save_and_restore_magnification_settings_buttons"
namespace: "accessibility"
- description: "Uses SurfaceControlViewHost to create the magnifier for window magnification."
- bug: "280992417"
+ description: "Saves the selected button status in magnification settings and restore the status when revisiting the same smallest screen DP."
+ bug: "325567876"
+ metadata {
+ purpose: PURPOSE_BUGFIX
+ }
}
diff --git a/packages/SystemUI/aconfig/systemui.aconfig b/packages/SystemUI/aconfig/systemui.aconfig
index 626e219..f3e2272 100644
--- a/packages/SystemUI/aconfig/systemui.aconfig
+++ b/packages/SystemUI/aconfig/systemui.aconfig
@@ -26,11 +26,10 @@
}
flag {
-
- name: "notification_heads_up_cycling"
- namespace: "systemui"
- description: "Heads-up notification cycling animation for the Notification Avalanche feature."
- bug: "316404716"
+ name: "priority_people_section"
+ namespace: "systemui"
+ description: "Add a new section for priority people (aka important conversations)."
+ bug: "340294566"
}
flag {
@@ -197,7 +196,16 @@
description: "Re-enable the codepath that removed tinting of notifications when the"
" standard background color is desired. This was the behavior before we discovered"
" a resources threading issue, which we worked around by tinting the notification"
- " backgrounds and footer buttons."
+ " backgrounds."
+ bug: "294830092"
+}
+
+flag {
+ name: "notification_footer_background_tint_optimization"
+ namespace: "systemui"
+ description: "Remove duplicative tinting of notification footer buttons. This was the behavior"
+ " before we discovered a resources threading issue, which we worked around by applying the"
+ " same color as a tint to the background drawable of footer buttons."
bug: "294830092"
}
@@ -348,6 +356,14 @@
}
flag {
+ name: "status_bar_screen_sharing_chips"
+ namespace: "systemui"
+ description: "Show chips on the left side of the status bar when a user is screen sharing, "
+ "recording, or casting"
+ bug: "332662551"
+}
+
+flag {
name: "compose_bouncer"
namespace: "systemui"
description: "Use the new compose bouncer in SystemUI"
@@ -398,6 +414,23 @@
}
flag {
+ name: "confine_notification_touch_to_view_width"
+ namespace: "systemui"
+ description: "Use notification view width when detecting gestures."
+ bug: "335828150"
+}
+
+flag {
+ name: "fix_image_wallpaper_crash_surface_already_released"
+ namespace: "systemui"
+ description: "Make sure ImageWallpaper doesn't return from OnSurfaceDestroyed until any drawing is finished"
+ bug: "337287154"
+ metadata {
+ purpose: PURPOSE_BUGFIX
+ }
+}
+
+flag {
name: "activity_transition_use_largest_window"
namespace: "systemui"
description: "Target largest opening window during activity transitions."
@@ -470,6 +503,26 @@
}
flag {
+ name: "fix_screenshot_action_dismiss_system_windows"
+ namespace: "systemui"
+ description: "Dismiss existing system windows when starting action from screenshot UI"
+ bug: "309933761"
+ metadata {
+ purpose: PURPOSE_BUGFIX
+ }
+}
+
+flag {
+ name: "screenshot_scroll_crop_view_crash_fix"
+ namespace: "systemui"
+ description: "Mitigate crash on invalid computed range in CropView"
+ bug: "232633995"
+ metadata {
+ purpose: PURPOSE_BUGFIX
+ }
+}
+
+flag {
name: "screenshot_private_profile_behavior_fix"
namespace: "systemui"
description: "Private profile support for screenshots"
@@ -844,6 +897,9 @@
namespace: "systemui"
description: "Enforce BaseUserRestriction for DISALLOW_CONFIG_BRIGHTNESS."
bug: "329205638"
+ metadata {
+ purpose: PURPOSE_BUGFIX
+ }
}
flag {
@@ -891,4 +947,41 @@
metadata {
purpose: PURPOSE_BUGFIX
}
-}
\ No newline at end of file
+}
+
+flag {
+ name: "media_controls_user_initiated_dismiss"
+ namespace: "systemui"
+ description: "Only dismiss media notifications when the control was removed by the user."
+ bug: "335875159"
+ metadata {
+ purpose: PURPOSE_BUGFIX
+ }
+}
+
+flag {
+ name: "validate_keyboard_shortcut_helper_icon_uri"
+ namespace: "systemui"
+ description: "Adds a check that the caller can access the content URI of an icon in the shortcut helper."
+ bug: "331180422"
+ metadata {
+ purpose: PURPOSE_BUGFIX
+ }
+}
+
+flag {
+ name: "glanceable_hub_gesture_handle"
+ namespace: "systemui"
+ description: "Shows a vertical bar at the right edge to indicate the user can swipe to open the glanceable hub"
+ bug: "339667383"
+}
+
+flag {
+ name: "register_wallpaper_notifier_background"
+ namespace: "systemui"
+ description: "Decide whether to register wallpaper change broadcast receiver on background executor."
+ bug: "327315860"
+ metadata {
+ purpose: PURPOSE_BUGFIX
+ }
+}
diff --git a/packages/SystemUI/animation/src/com/android/systemui/animation/ActivityTransitionAnimator.kt b/packages/SystemUI/animation/src/com/android/systemui/animation/ActivityTransitionAnimator.kt
index d4660fa..23df26f 100644
--- a/packages/SystemUI/animation/src/com/android/systemui/animation/ActivityTransitionAnimator.kt
+++ b/packages/SystemUI/animation/src/com/android/systemui/animation/ActivityTransitionAnimator.kt
@@ -23,6 +23,7 @@
import android.graphics.Matrix
import android.graphics.Rect
import android.graphics.RectF
+import android.os.Binder
import android.os.Build
import android.os.Handler
import android.os.Looper
@@ -36,7 +37,11 @@
import android.view.View
import android.view.ViewGroup
import android.view.WindowManager
+import android.view.WindowManager.TRANSIT_CLOSE
+import android.view.WindowManager.TRANSIT_TO_BACK
import android.view.animation.PathInterpolator
+import android.window.RemoteTransition
+import android.window.TransitionFilter
import androidx.annotation.AnyThread
import androidx.annotation.BinderThread
import androidx.annotation.UiThread
@@ -44,6 +49,9 @@
import com.android.internal.annotations.VisibleForTesting
import com.android.internal.policy.ScreenDecorationsUtils
import com.android.systemui.Flags.activityTransitionUseLargestWindow
+import com.android.systemui.shared.Flags.returnAnimationFrameworkLibrary
+import com.android.wm.shell.shared.IShellTransitions
+import com.android.wm.shell.shared.ShellTransitions
import java.util.concurrent.Executor
import kotlin.math.roundToInt
@@ -59,6 +67,9 @@
/** The executor that runs on the main thread. */
private val mainExecutor: Executor,
+ /** The object used to register ephemeral returns and long-lived transitions. */
+ private val transitionRegister: TransitionRegister? = null,
+
/** The animator used when animating a View into an app. */
private val transitionAnimator: TransitionAnimator = defaultTransitionAnimator(mainExecutor),
@@ -74,6 +85,36 @@
// TODO(b/301385865): Remove this flag.
private val disableWmTimeout: Boolean = false,
) {
+ @JvmOverloads
+ constructor(
+ mainExecutor: Executor,
+ shellTransitions: ShellTransitions,
+ transitionAnimator: TransitionAnimator = defaultTransitionAnimator(mainExecutor),
+ dialogToAppAnimator: TransitionAnimator = defaultDialogToAppAnimator(mainExecutor),
+ disableWmTimeout: Boolean = false,
+ ) : this(
+ mainExecutor,
+ TransitionRegister.fromShellTransitions(shellTransitions),
+ transitionAnimator,
+ dialogToAppAnimator,
+ disableWmTimeout,
+ )
+
+ @JvmOverloads
+ constructor(
+ mainExecutor: Executor,
+ iShellTransitions: IShellTransitions,
+ transitionAnimator: TransitionAnimator = defaultTransitionAnimator(mainExecutor),
+ dialogToAppAnimator: TransitionAnimator = defaultDialogToAppAnimator(mainExecutor),
+ disableWmTimeout: Boolean = false,
+ ) : this(
+ mainExecutor,
+ TransitionRegister.fromIShellTransitions(iShellTransitions),
+ transitionAnimator,
+ dialogToAppAnimator,
+ disableWmTimeout,
+ )
+
companion object {
/** The timings when animating a View into an app. */
@JvmField
@@ -233,6 +274,10 @@
}
}
+ if (animationAdapter != null && controller.transitionCookie != null) {
+ registerEphemeralReturnAnimation(controller, transitionRegister)
+ }
+
val launchResult = intentStarter(animationAdapter)
// Only animate if the app is not already on top and will be opened, unless we are on the
@@ -302,6 +347,66 @@
}
}
+ /**
+ * Uses [transitionRegister] to set up the return animation for the given [launchController].
+ *
+ * De-registration is set up automatically once the return animation is run.
+ *
+ * TODO(b/339194555): automatically de-register when the launchable is detached.
+ */
+ private fun registerEphemeralReturnAnimation(
+ launchController: Controller,
+ transitionRegister: TransitionRegister?
+ ) {
+ if (!returnAnimationFrameworkLibrary()) return
+
+ var cleanUpRunnable: Runnable? = null
+ val returnRunner =
+ createRunner(
+ object : DelegateTransitionAnimatorController(launchController) {
+ override val isLaunching = false
+
+ override fun onTransitionAnimationCancelled(
+ newKeyguardOccludedState: Boolean?
+ ) {
+ super.onTransitionAnimationCancelled(newKeyguardOccludedState)
+ cleanUp()
+ }
+
+ override fun onTransitionAnimationEnd(isExpandingFullyAbove: Boolean) {
+ super.onTransitionAnimationEnd(isExpandingFullyAbove)
+ cleanUp()
+ }
+
+ private fun cleanUp() {
+ cleanUpRunnable?.run()
+ }
+ }
+ )
+
+ // mTypeSet and mModes match back signals only, and not home. This is on purpose, because
+ // we only want ephemeral return animations triggered in these scenarios.
+ val filter =
+ TransitionFilter().apply {
+ mTypeSet = intArrayOf(TRANSIT_CLOSE, TRANSIT_TO_BACK)
+ mRequirements =
+ arrayOf(
+ TransitionFilter.Requirement().apply {
+ mLaunchCookie = launchController.transitionCookie
+ mModes = intArrayOf(TRANSIT_CLOSE, TRANSIT_TO_BACK)
+ }
+ )
+ }
+ val transition =
+ RemoteTransition(
+ RemoteAnimationRunnerCompat.wrap(returnRunner),
+ "${launchController.transitionCookie}_returnTransition"
+ )
+
+ transitionRegister?.register(filter, transition)
+ cleanUpRunnable = Runnable { transitionRegister?.unregister(transition) }
+ }
+
/** Add a [Listener] that can listen to transition animations. */
fun addListener(listener: Listener) {
listeners.add(listener)
@@ -386,8 +491,14 @@
* Note: The background of [view] should be a (rounded) rectangle so that it can be
* properly animated.
*/
+ @JvmOverloads
@JvmStatic
- fun fromView(view: View, cujType: Int? = null): Controller? {
+ fun fromView(
+ view: View,
+ cujType: Int? = null,
+ cookie: TransitionCookie? = null,
+ returnCujType: Int? = null
+ ): Controller? {
// Make sure the View we launch from implements LaunchableView to avoid visibility
// issues.
if (view !is LaunchableView) {
@@ -408,7 +519,7 @@
return null
}
- return GhostedViewTransitionAnimatorController(view, cujType)
+ return GhostedViewTransitionAnimatorController(view, cujType, cookie, returnCujType)
}
}
@@ -432,6 +543,17 @@
get() = false
/**
+ * The cookie associated with the transition controlled by this [Controller].
+ *
+ * This should be defined for all return [Controller] (when [isLaunching] is false) and for
+ * their associated launch [Controller]s.
+ *
+ * For the recommended format, see [TransitionCookie].
+ */
+ val transitionCookie: TransitionCookie?
+ get() = null
+
+ /**
* The intent was started. If [willAnimate] is false, nothing else will happen and the
* animation will not be started.
*/
@@ -652,7 +774,7 @@
return
}
- val window = findRootTaskIfPossible(apps)
+ val window = findTargetWindowIfPossible(apps)
if (window == null) {
Log.i(TAG, "Aborting the animation as no window is opening")
callback?.invoke()
@@ -676,7 +798,7 @@
startAnimation(window, navigationBar, callback)
}
- private fun findRootTaskIfPossible(
+ private fun findTargetWindowIfPossible(
apps: Array<out RemoteAnimationTarget>?
): RemoteAnimationTarget? {
if (apps == null) {
@@ -694,6 +816,19 @@
for (it in apps) {
if (it.mode == targetMode) {
if (activityTransitionUseLargestWindow()) {
+ if (returnAnimationFrameworkLibrary()) {
+ // If the controller contains a cookie, _only_ match if the candidate
+ // contains the matching cookie.
+ if (
+ controller.transitionCookie != null &&
+ it.taskInfo
+ ?.launchCookies
+ ?.contains(controller.transitionCookie) != true
+ ) {
+ continue
+ }
+ }
+
if (
candidate == null ||
!it.hasAnimatingParent && candidate.hasAnimatingParent
@@ -806,11 +941,7 @@
progress: Float,
linearProgress: Float
) {
- // Apply the state to the window only if it is visible, i.e. when the
- // expanding view is *not* visible.
- if (!state.visible) {
- applyStateToWindow(window, state, linearProgress)
- }
+ applyStateToWindow(window, state, linearProgress)
navigationBar?.let { applyStateToNavigationBar(it, state, linearProgress) }
listener?.onTransitionAnimationProgress(linearProgress)
@@ -1048,4 +1179,72 @@
return (this.width() * this.height()) > (other.width() * other.height())
}
}
+
+ /**
+ * Wraps one of the two methods we have to register remote transitions with WM Shell:
+ * - for in-process registrations (e.g. System UI) we use [ShellTransitions]
+ * - for cross-process registrations (e.g. Launcher) we use [IShellTransitions]
+ *
+ * Important: each instance of this class must wrap exactly one of the two.
+ */
+ class TransitionRegister
+ private constructor(
+ private val shellTransitions: ShellTransitions? = null,
+ private val iShellTransitions: IShellTransitions? = null,
+ ) {
+ init {
+ assert((shellTransitions != null).xor(iShellTransitions != null))
+ }
+
+ companion object {
+ /** Provides a [TransitionRegister] instance wrapping [ShellTransitions]. */
+ fun fromShellTransitions(shellTransitions: ShellTransitions): TransitionRegister {
+ return TransitionRegister(shellTransitions = shellTransitions)
+ }
+
+ /** Provides a [TransitionRegister] instance wrapping [IShellTransitions]. */
+ fun fromIShellTransitions(iShellTransitions: IShellTransitions): TransitionRegister {
+ return TransitionRegister(iShellTransitions = iShellTransitions)
+ }
+ }
+
+ /** Register [remoteTransition] with WM Shell using the given [filter]. */
+ internal fun register(
+ filter: TransitionFilter,
+ remoteTransition: RemoteTransition,
+ ) {
+ shellTransitions?.registerRemote(filter, remoteTransition)
+ iShellTransitions?.registerRemote(filter, remoteTransition)
+ }
+
+ /** Unregister [remoteTransition] from WM Shell. */
+ internal fun unregister(remoteTransition: RemoteTransition) {
+ shellTransitions?.unregisterRemote(remoteTransition)
+ iShellTransitions?.unregisterRemote(remoteTransition)
+ }
+ }
+
+ /**
+ * A cookie used to uniquely identify a task launched using an
+ * [ActivityTransitionAnimator.Controller].
+ *
+ * The [String] encapsulated by this class should be formatted in such a way to be unique across
+ * the system, but reliably constant for the same associated launchable.
+ *
+ * Recommended naming scheme:
+ * - DO use the fully qualified name of the class that owns the instance of the launchable,
+ * along with a concise and precise description of the purpose of the launchable in question.
+ * - DO NOT introduce uniqueness through the use of timestamps or other runtime variables that
+ * will change if the instance is destroyed and re-created.
+ *
+ * Example: "com.not.the.real.class.name.ShadeController_openSettingsButton"
+ *
+ * Note that sometimes (e.g. in recycler views) there could be multiple instances of the same
+ * launchable, and no static knowledge to adequately differentiate between them using a single
+ * description. In this case, the recommendation is to append a unique identifier related to the
+ * contents of the launchable.
+ *
+ * Example: “com.not.the.real.class.name.ToastWebResult_launchAga_id143256”
+ */
+ data class TransitionCookie(private val cookie: String) : Binder()
}
diff --git a/packages/SystemUI/animation/src/com/android/systemui/animation/Expandable.kt b/packages/SystemUI/animation/src/com/android/systemui/animation/Expandable.kt
index e4bb2ad..21557b8 100644
--- a/packages/SystemUI/animation/src/com/android/systemui/animation/Expandable.kt
+++ b/packages/SystemUI/animation/src/com/android/systemui/animation/Expandable.kt
@@ -25,10 +25,30 @@
* [Expandable] into an Activity, or return `null` if this [Expandable] should not be animated
* (e.g. if it is currently not attached or visible).
*
- * @param cujType the CUJ type from the [com.android.internal.jank.InteractionJankMonitor]
+ * @param launchCujType The CUJ type from the [com.android.internal.jank.InteractionJankMonitor]
* associated to the launch that will use this controller.
+ * @param cookie The unique cookie associated with the launch that will use this controller.
+ * This is required iff the a return animation should be included.
+ * @param returnCujType The CUJ type from the [com.android.internal.jank.InteractionJankMonitor]
+ * associated to the return animation that will use this controller.
*/
- fun activityTransitionController(cujType: Int? = null): ActivityTransitionAnimator.Controller?
+ fun activityTransitionController(
+ launchCujType: Int? = null,
+ cookie: ActivityTransitionAnimator.TransitionCookie? = null,
+ returnCujType: Int? = null
+ ): ActivityTransitionAnimator.Controller?
+
+ /**
+ * See [activityTransitionController] above.
+ *
+ * Interfaces don't support [JvmOverloads], so this is a useful overload for Java usages that
+ * don't use the return-related parameters.
+ */
+ fun activityTransitionController(
+ launchCujType: Int? = null
+ ): ActivityTransitionAnimator.Controller? {
+ return activityTransitionController(launchCujType, cookie = null, returnCujType = null)
+ }
/**
* Create a [DialogTransitionAnimator.Controller] that can be used to expand this [Expandable]
@@ -48,9 +68,16 @@
fun fromView(view: View): Expandable {
return object : Expandable {
override fun activityTransitionController(
- cujType: Int?,
+ launchCujType: Int?,
+ cookie: ActivityTransitionAnimator.TransitionCookie?,
+ returnCujType: Int?
): ActivityTransitionAnimator.Controller? {
- return ActivityTransitionAnimator.Controller.fromView(view, cujType)
+ return ActivityTransitionAnimator.Controller.fromView(
+ view,
+ launchCujType,
+ cookie,
+ returnCujType
+ )
}
override fun dialogTransitionController(
diff --git a/packages/SystemUI/animation/src/com/android/systemui/animation/GhostedViewTransitionAnimatorController.kt b/packages/SystemUI/animation/src/com/android/systemui/animation/GhostedViewTransitionAnimatorController.kt
index fd79f62..9d45073 100644
--- a/packages/SystemUI/animation/src/com/android/systemui/animation/GhostedViewTransitionAnimatorController.kt
+++ b/packages/SystemUI/animation/src/com/android/systemui/animation/GhostedViewTransitionAnimatorController.kt
@@ -59,8 +59,12 @@
/** The view that will be ghosted and from which the background will be extracted. */
private val ghostedView: View,
- /** The [CujType] associated to this animation. */
- private val cujType: Int? = null,
+ /** The [CujType] associated to this launch animation. */
+ private val launchCujType: Int? = null,
+ override val transitionCookie: ActivityTransitionAnimator.TransitionCookie? = null,
+
+ /** The [CujType] associated to this return animation. */
+ private val returnCujType: Int? = null,
private var interactionJankMonitor: InteractionJankMonitor =
InteractionJankMonitor.getInstance(),
) : ActivityTransitionAnimator.Controller {
@@ -104,6 +108,15 @@
*/
private val background: Drawable?
+ /** CUJ identifier accounting for whether this controller is for a launch or a return. */
+ private val cujType: Int?
+ get() =
+ if (isLaunching) {
+ launchCujType
+ } else {
+ returnCujType
+ }
+
init {
// Make sure the View we launch from implements LaunchableView to avoid visibility issues.
if (ghostedView !is LaunchableView) {
diff --git a/packages/SystemUI/checks/src/com/android/internal/systemui/lint/CollectAsStateDetector.kt b/packages/SystemUI/checks/src/com/android/internal/systemui/lint/CollectAsStateDetector.kt
new file mode 100644
index 0000000..94620c4
--- /dev/null
+++ b/packages/SystemUI/checks/src/com/android/internal/systemui/lint/CollectAsStateDetector.kt
@@ -0,0 +1,97 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.internal.systemui.lint
+
+import com.android.tools.lint.client.api.UElementHandler
+import com.android.tools.lint.detector.api.Category
+import com.android.tools.lint.detector.api.Detector
+import com.android.tools.lint.detector.api.Implementation
+import com.android.tools.lint.detector.api.Issue
+import com.android.tools.lint.detector.api.JavaContext
+import com.android.tools.lint.detector.api.Scope
+import com.android.tools.lint.detector.api.Severity
+import com.android.tools.lint.detector.api.SourceCodeScanner
+import org.jetbrains.uast.UElement
+import org.jetbrains.uast.UFile
+import org.jetbrains.uast.UImportStatement
+
+class CollectAsStateDetector : Detector(), SourceCodeScanner {
+
+ override fun getApplicableUastTypes(): List<Class<out UElement>> {
+ return listOf(UFile::class.java)
+ }
+
+ override fun createUastHandler(context: JavaContext): UElementHandler {
+ return object : UElementHandler() {
+ override fun visitFile(node: UFile) {
+ node.imports.forEach { importStatement ->
+ visitImportStatement(context, importStatement)
+ }
+ }
+ }
+ }
+
+ private fun visitImportStatement(
+ context: JavaContext,
+ importStatement: UImportStatement,
+ ) {
+ val importText = importStatement.importReference?.asSourceString() ?: return
+ if (ILLEGAL_IMPORT == importText) {
+ context.report(
+ issue = ISSUE,
+ scope = importStatement,
+ location = context.getLocation(importStatement),
+ message = "collectAsState considered harmful",
+ )
+ }
+ }
+
+ companion object {
+ @JvmField
+ val ISSUE =
+ Issue.create(
+ id = "OverlyEagerCollectAsState",
+ briefDescription = "collectAsState considered harmful",
+ explanation =
+ """
+ go/sysui-compose#collect-as-state
+
+ Don't use collectAsState as it will set up a coroutine that keeps collecting from a
+ flow until its coroutine scope becomes inactive. This prevents the work from being
+ properly paused while the surrounding lifecycle becomes paused or stopped and is
+ therefore considered harmful.
+
+ Instead, use Flow.collectAsStateWithLifecycle(initial: T) or
+ StateFlow.collectAsStateWithLifecycle(). These APIs correctly pause the collection
+ coroutine while the lifecycle drops below the specified minActiveState (which
+ defaults to STARTED meaning that it will pause when the Compose-hosting window
+ becomes invisible).
+ """
+ .trimIndent(),
+ category = Category.PERFORMANCE,
+ priority = 8,
+ severity = Severity.ERROR,
+ implementation =
+ Implementation(
+ CollectAsStateDetector::class.java,
+ Scope.JAVA_FILE_SCOPE,
+ ),
+ )
+
+ private val ILLEGAL_IMPORT = "androidx.compose.runtime.collectAsState"
+ }
+}
diff --git a/packages/SystemUI/checks/src/com/android/internal/systemui/lint/SystemUIIssueRegistry.kt b/packages/SystemUI/checks/src/com/android/internal/systemui/lint/SystemUIIssueRegistry.kt
index cecbc47..73ac6cc 100644
--- a/packages/SystemUI/checks/src/com/android/internal/systemui/lint/SystemUIIssueRegistry.kt
+++ b/packages/SystemUI/checks/src/com/android/internal/systemui/lint/SystemUIIssueRegistry.kt
@@ -32,6 +32,7 @@
BindServiceOnMainThreadDetector.ISSUE,
BroadcastSentViaContextDetector.ISSUE,
CleanArchitectureDependencyViolationDetector.ISSUE,
+ CollectAsStateDetector.ISSUE,
DumpableNotRegisteredDetector.ISSUE,
FlowDetector.SHARED_FLOW_CREATION,
SlowUserQueryDetector.ISSUE_SLOW_USER_ID_QUERY,
diff --git a/packages/SystemUI/checks/tests/com/android/internal/systemui/lint/CollectAsStateDetectorTest.kt b/packages/SystemUI/checks/tests/com/android/internal/systemui/lint/CollectAsStateDetectorTest.kt
new file mode 100644
index 0000000..6962b4e
--- /dev/null
+++ b/packages/SystemUI/checks/tests/com/android/internal/systemui/lint/CollectAsStateDetectorTest.kt
@@ -0,0 +1,106 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.internal.systemui.lint
+
+import com.android.tools.lint.checks.infrastructure.TestFiles
+import com.android.tools.lint.detector.api.Detector
+import com.android.tools.lint.detector.api.Issue
+import org.junit.Test
+
+class CollectAsStateDetectorTest : SystemUILintDetectorTest() {
+
+ override fun getDetector(): Detector {
+ return CollectAsStateDetector()
+ }
+
+ override fun getIssues(): List<Issue> {
+ return listOf(
+ CollectAsStateDetector.ISSUE,
+ )
+ }
+
+ @Test
+ fun testViolation() {
+ lint()
+ .files(COLLECT_AS_STATE_STUB, COLLECT_WITH_LIFECYCLE_AS_STATE_STUB, GOOD_FILE, BAD_FILE)
+ .issues(CollectAsStateDetector.ISSUE)
+ .run()
+ .expect(
+ """
+src/com/android/internal/systemui/lint/Bad.kt:3: Error: collectAsState considered harmful [OverlyEagerCollectAsState]
+import androidx.compose.runtime.collectAsState
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+1 errors, 0 warnings
+ """
+ .trimIndent()
+ )
+ }
+
+ @Test
+ fun testNoViolation() {
+ lint()
+ .files(COLLECT_AS_STATE_STUB, COLLECT_WITH_LIFECYCLE_AS_STATE_STUB, GOOD_FILE)
+ .issues(CollectAsStateDetector.ISSUE)
+ .run()
+ .expectClean()
+ }
+
+ companion object {
+ private val COLLECT_AS_STATE_STUB =
+ TestFiles.kotlin(
+ """
+ package androidx.compose.runtime
+
+ fun collectAsState() {}
+ """
+ .trimIndent()
+ )
+ private val COLLECT_WITH_LIFECYCLE_AS_STATE_STUB =
+ TestFiles.kotlin(
+ """
+ package androidx.lifecycle.compose
+
+ fun collectAsStateWithLifecycle() {}
+ """
+ .trimIndent()
+ )
+
+ private val BAD_FILE =
+ TestFiles.kotlin(
+ """
+ package com.android.internal.systemui.lint
+
+ import androidx.compose.runtime.collectAsState
+
+ class Bad
+ """
+ .trimIndent()
+ )
+
+ private val GOOD_FILE =
+ TestFiles.kotlin(
+ """
+ package com.android.internal.systemui.lint
+
+ import androidx.lifecycle.compose.collectAsStateWithLifecycle
+
+ class Good
+ """
+ .trimIndent()
+ )
+ }
+}
diff --git a/packages/SystemUI/compose/core/src/com/android/compose/animation/ExpandableController.kt b/packages/SystemUI/compose/core/src/com/android/compose/animation/ExpandableController.kt
index c7f0a96..17a6061 100644
--- a/packages/SystemUI/compose/core/src/com/android/compose/animation/ExpandableController.kt
+++ b/packages/SystemUI/compose/core/src/com/android/compose/animation/ExpandableController.kt
@@ -134,13 +134,15 @@
override val expandable: Expandable =
object : Expandable {
override fun activityTransitionController(
- cujType: Int?,
+ launchCujType: Int?,
+ cookie: ActivityTransitionAnimator.TransitionCookie?,
+ returnCujType: Int?
): ActivityTransitionAnimator.Controller? {
if (!isComposed.value) {
return null
}
- return activityController(cujType)
+ return activityController(launchCujType, cookie, returnCujType)
}
override fun dialogTransitionController(
@@ -262,10 +264,27 @@
}
/** Create an [ActivityTransitionAnimator.Controller] that can be used to animate activities. */
- private fun activityController(cujType: Int?): ActivityTransitionAnimator.Controller {
+ private fun activityController(
+ launchCujType: Int?,
+ cookie: ActivityTransitionAnimator.TransitionCookie?,
+ returnCujType: Int?
+ ): ActivityTransitionAnimator.Controller {
val delegate = transitionController()
return object :
ActivityTransitionAnimator.Controller, TransitionAnimator.Controller by delegate {
+ /**
+ * CUJ identifier accounting for whether this controller is for a launch or a return.
+ */
+ private val cujType: Int?
+ get() =
+ if (isLaunching) {
+ launchCujType
+ } else {
+ returnCujType
+ }
+
+ override val transitionCookie = cookie
+
override fun onTransitionAnimationStart(isExpandingFullyAbove: Boolean) {
delegate.onTransitionAnimationStart(isExpandingFullyAbove)
overlay.value = composeViewRoot.rootView.overlay as ViewGroupOverlay
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/bouncer/ui/composable/BouncerContent.kt b/packages/SystemUI/compose/features/src/com/android/systemui/bouncer/ui/composable/BouncerContent.kt
index c22b50d..fa01a4b 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/bouncer/ui/composable/BouncerContent.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/bouncer/ui/composable/BouncerContent.kt
@@ -56,7 +56,6 @@
import androidx.compose.runtime.Composable
import androidx.compose.runtime.DisposableEffect
import androidx.compose.runtime.LaunchedEffect
-import androidx.compose.runtime.collectAsState
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
@@ -77,6 +76,7 @@
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
import androidx.compose.ui.unit.times
+import androidx.lifecycle.compose.collectAsStateWithLifecycle
import com.android.compose.PlatformButton
import com.android.compose.animation.Easings
import com.android.compose.animation.scene.ElementKey
@@ -111,7 +111,7 @@
dialogFactory: BouncerDialogFactory,
modifier: Modifier = Modifier,
) {
- val isSideBySideSupported by viewModel.isSideBySideSupported.collectAsState()
+ val isSideBySideSupported by viewModel.isSideBySideSupported.collectAsStateWithLifecycle()
val layout = calculateLayout(isSideBySideSupported = isSideBySideSupported)
Box(
@@ -220,7 +220,7 @@
viewModel: BouncerViewModel,
modifier: Modifier = Modifier,
) {
- val authMethod by viewModel.authMethodViewModel.collectAsState()
+ val authMethod by viewModel.authMethodViewModel.collectAsStateWithLifecycle()
Row(
modifier =
@@ -316,7 +316,7 @@
val (isSwapped, setSwapped) = rememberSaveable(isLeftToRight) { mutableStateOf(!isLeftToRight) }
val isHeightExpanded =
LocalWindowSizeClass.current.heightSizeClass == WindowHeightSizeClass.Expanded
- val authMethod by viewModel.authMethodViewModel.collectAsState()
+ val authMethod by viewModel.authMethodViewModel.collectAsStateWithLifecycle()
Row(
modifier =
@@ -480,7 +480,7 @@
modifier: Modifier = Modifier,
) {
val foldPosture: FoldPosture by foldPosture()
- val isSplitAroundTheFoldRequired by viewModel.isFoldSplitRequired.collectAsState()
+ val isSplitAroundTheFoldRequired by viewModel.isFoldSplitRequired.collectAsStateWithLifecycle()
val isSplitAroundTheFold = foldPosture == FoldPosture.Tabletop && isSplitAroundTheFoldRequired
val currentSceneKey =
if (isSplitAroundTheFold) SceneKeys.SplitSceneKey else SceneKeys.ContiguousSceneKey
@@ -562,7 +562,7 @@
viewModel: BouncerMessageViewModel,
modifier: Modifier = Modifier,
) {
- val message: MessageViewModel? by viewModel.message.collectAsState()
+ val message: MessageViewModel? by viewModel.message.collectAsStateWithLifecycle()
DisposableEffect(Unit) {
viewModel.onShown()
@@ -612,7 +612,7 @@
modifier: Modifier = Modifier,
) {
val authMethodViewModel: AuthMethodBouncerViewModel? by
- viewModel.authMethodViewModel.collectAsState()
+ viewModel.authMethodViewModel.collectAsStateWithLifecycle()
when (val nonNullViewModel = authMethodViewModel) {
is PinBouncerViewModel ->
@@ -642,7 +642,7 @@
modifier: Modifier = Modifier,
) {
val authMethodViewModel: AuthMethodBouncerViewModel? by
- viewModel.authMethodViewModel.collectAsState()
+ viewModel.authMethodViewModel.collectAsStateWithLifecycle()
when (val nonNullViewModel = authMethodViewModel) {
is PinBouncerViewModel -> {
@@ -668,7 +668,8 @@
viewModel: BouncerViewModel,
modifier: Modifier = Modifier,
) {
- val actionButton: BouncerActionButtonModel? by viewModel.actionButton.collectAsState()
+ val actionButton: BouncerActionButtonModel? by
+ viewModel.actionButton.collectAsStateWithLifecycle()
val appearFadeInAnimatable = remember { Animatable(0f) }
val appearMoveAnimatable = remember { Animatable(0f) }
val appearAnimationInitialOffset = with(LocalDensity.current) { 80.dp.toPx() }
@@ -735,7 +736,7 @@
bouncerViewModel: BouncerViewModel,
dialogFactory: BouncerDialogFactory,
) {
- val dialogViewModel by bouncerViewModel.dialogViewModel.collectAsState()
+ val dialogViewModel by bouncerViewModel.dialogViewModel.collectAsStateWithLifecycle()
var dialog: AlertDialog? by remember { mutableStateOf(null) }
dialogViewModel?.let { viewModel ->
@@ -772,8 +773,8 @@
return
}
- val selectedUserImage by viewModel.selectedUserImage.collectAsState(null)
- val dropdownItems by viewModel.userSwitcherDropdown.collectAsState(emptyList())
+ val selectedUserImage by viewModel.selectedUserImage.collectAsStateWithLifecycle(null)
+ val dropdownItems by viewModel.userSwitcherDropdown.collectAsStateWithLifecycle(emptyList())
Column(
horizontalAlignment = Alignment.CenterHorizontally,
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/bouncer/ui/composable/PasswordBouncer.kt b/packages/SystemUI/compose/features/src/com/android/systemui/bouncer/ui/composable/PasswordBouncer.kt
index 2dcd0ff..203bd7a 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/bouncer/ui/composable/PasswordBouncer.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/bouncer/ui/composable/PasswordBouncer.kt
@@ -27,7 +27,6 @@
import androidx.compose.runtime.Composable
import androidx.compose.runtime.DisposableEffect
import androidx.compose.runtime.LaunchedEffect
-import androidx.compose.runtime.collectAsState
import androidx.compose.runtime.getValue
import androidx.compose.runtime.remember
import androidx.compose.ui.ExperimentalComposeUiApi
@@ -49,6 +48,7 @@
import androidx.compose.ui.text.input.PasswordVisualTransformation
import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.unit.dp
+import androidx.lifecycle.compose.collectAsStateWithLifecycle
import com.android.compose.PlatformIconButton
import com.android.systemui.bouncer.ui.viewmodel.PasswordBouncerViewModel
import com.android.systemui.common.ui.compose.SelectedUserAwareInputConnection
@@ -62,18 +62,20 @@
modifier: Modifier = Modifier,
) {
val focusRequester = remember { FocusRequester() }
- val isTextFieldFocusRequested by viewModel.isTextFieldFocusRequested.collectAsState()
+ val isTextFieldFocusRequested by
+ viewModel.isTextFieldFocusRequested.collectAsStateWithLifecycle()
LaunchedEffect(isTextFieldFocusRequested) {
if (isTextFieldFocusRequested) {
focusRequester.requestFocus()
}
}
- val password: String by viewModel.password.collectAsState()
- val isInputEnabled: Boolean by viewModel.isInputEnabled.collectAsState()
- val animateFailure: Boolean by viewModel.animateFailure.collectAsState()
- val isImeSwitcherButtonVisible by viewModel.isImeSwitcherButtonVisible.collectAsState()
- val selectedUserId by viewModel.selectedUserId.collectAsState()
+ val password: String by viewModel.password.collectAsStateWithLifecycle()
+ val isInputEnabled: Boolean by viewModel.isInputEnabled.collectAsStateWithLifecycle()
+ val animateFailure: Boolean by viewModel.animateFailure.collectAsStateWithLifecycle()
+ val isImeSwitcherButtonVisible by
+ viewModel.isImeSwitcherButtonVisible.collectAsStateWithLifecycle()
+ val selectedUserId by viewModel.selectedUserId.collectAsStateWithLifecycle()
DisposableEffect(Unit) { onDispose { viewModel.onHidden() } }
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/bouncer/ui/composable/PatternBouncer.kt b/packages/SystemUI/compose/features/src/com/android/systemui/bouncer/ui/composable/PatternBouncer.kt
index d7e9c10..9c2fd64 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/bouncer/ui/composable/PatternBouncer.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/bouncer/ui/composable/PatternBouncer.kt
@@ -30,7 +30,6 @@
import androidx.compose.runtime.Composable
import androidx.compose.runtime.DisposableEffect
import androidx.compose.runtime.LaunchedEffect
-import androidx.compose.runtime.collectAsState
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
@@ -47,6 +46,7 @@
import androidx.compose.ui.platform.LocalView
import androidx.compose.ui.res.integerResource
import androidx.compose.ui.unit.dp
+import androidx.lifecycle.compose.collectAsStateWithLifecycle
import com.android.compose.animation.Easings
import com.android.compose.modifiers.thenIf
import com.android.internal.R
@@ -86,14 +86,15 @@
val lineStrokeWidth = with(density) { LINE_STROKE_WIDTH_DP.dp.toPx() }
// All dots that should be rendered on the grid.
- val dots: List<PatternDotViewModel> by viewModel.dots.collectAsState()
+ val dots: List<PatternDotViewModel> by viewModel.dots.collectAsStateWithLifecycle()
// The most recently selected dot, if the user is currently dragging.
- val currentDot: PatternDotViewModel? by viewModel.currentDot.collectAsState()
+ val currentDot: PatternDotViewModel? by viewModel.currentDot.collectAsStateWithLifecycle()
// The dots selected so far, if the user is currently dragging.
- val selectedDots: List<PatternDotViewModel> by viewModel.selectedDots.collectAsState()
- val isInputEnabled: Boolean by viewModel.isInputEnabled.collectAsState()
- val isAnimationEnabled: Boolean by viewModel.isPatternVisible.collectAsState()
- val animateFailure: Boolean by viewModel.animateFailure.collectAsState()
+ val selectedDots: List<PatternDotViewModel> by
+ viewModel.selectedDots.collectAsStateWithLifecycle()
+ val isInputEnabled: Boolean by viewModel.isInputEnabled.collectAsStateWithLifecycle()
+ val isAnimationEnabled: Boolean by viewModel.isPatternVisible.collectAsStateWithLifecycle()
+ val animateFailure: Boolean by viewModel.animateFailure.collectAsStateWithLifecycle()
// Map of animatables for the scale of each dot, keyed by dot.
val dotScalingAnimatables = remember(dots) { dots.associateWith { Animatable(1f) } }
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/bouncer/ui/composable/PinBouncer.kt b/packages/SystemUI/compose/features/src/com/android/systemui/bouncer/ui/composable/PinBouncer.kt
index 5651a46..64ace2f 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/bouncer/ui/composable/PinBouncer.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/bouncer/ui/composable/PinBouncer.kt
@@ -33,7 +33,6 @@
import androidx.compose.runtime.Composable
import androidx.compose.runtime.DisposableEffect
import androidx.compose.runtime.LaunchedEffect
-import androidx.compose.runtime.collectAsState
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
@@ -49,6 +48,7 @@
import androidx.compose.ui.platform.LocalView
import androidx.compose.ui.unit.Dp
import androidx.compose.ui.unit.dp
+import androidx.lifecycle.compose.collectAsStateWithLifecycle
import com.android.compose.animation.Easings
import com.android.compose.grid.VerticalGrid
import com.android.compose.modifiers.thenIf
@@ -74,12 +74,13 @@
) {
DisposableEffect(Unit) { onDispose { viewModel.onHidden() } }
- val isInputEnabled: Boolean by viewModel.isInputEnabled.collectAsState()
- val backspaceButtonAppearance by viewModel.backspaceButtonAppearance.collectAsState()
- val confirmButtonAppearance by viewModel.confirmButtonAppearance.collectAsState()
- val animateFailure: Boolean by viewModel.animateFailure.collectAsState()
+ val isInputEnabled: Boolean by viewModel.isInputEnabled.collectAsStateWithLifecycle()
+ val backspaceButtonAppearance by
+ viewModel.backspaceButtonAppearance.collectAsStateWithLifecycle()
+ val confirmButtonAppearance by viewModel.confirmButtonAppearance.collectAsStateWithLifecycle()
+ val animateFailure: Boolean by viewModel.animateFailure.collectAsStateWithLifecycle()
val isDigitButtonAnimationEnabled: Boolean by
- viewModel.isDigitButtonAnimationEnabled.collectAsState()
+ viewModel.isDigitButtonAnimationEnabled.collectAsStateWithLifecycle()
val buttonScaleAnimatables = remember { List(12) { Animatable(1f) } }
LaunchedEffect(animateFailure) {
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/bouncer/ui/composable/PinInputDisplay.kt b/packages/SystemUI/compose/features/src/com/android/systemui/bouncer/ui/composable/PinInputDisplay.kt
index 1a97912..465eade 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/bouncer/ui/composable/PinInputDisplay.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/bouncer/ui/composable/PinInputDisplay.kt
@@ -42,7 +42,6 @@
import androidx.compose.runtime.Composable
import androidx.compose.runtime.DisposableEffect
import androidx.compose.runtime.LaunchedEffect
-import androidx.compose.runtime.collectAsState
import androidx.compose.runtime.getValue
import androidx.compose.runtime.key
import androidx.compose.runtime.mutableStateListOf
@@ -65,6 +64,7 @@
import androidx.compose.ui.unit.Dp
import androidx.compose.ui.unit.dp
import androidx.compose.ui.window.Dialog
+import androidx.lifecycle.compose.collectAsStateWithLifecycle
import com.android.compose.PlatformOutlinedButton
import com.android.compose.animation.Easings
import com.android.keyguard.PinShapeAdapter
@@ -86,7 +86,7 @@
viewModel: PinBouncerViewModel,
modifier: Modifier = Modifier,
) {
- val hintedPinLength: Int? by viewModel.hintedPinLength.collectAsState()
+ val hintedPinLength: Int? by viewModel.hintedPinLength.collectAsStateWithLifecycle()
val shapeAnimations = rememberShapeAnimations(viewModel.pinShapes)
// The display comes in two different flavors:
@@ -119,7 +119,7 @@
hintedPinLength: Int,
modifier: Modifier = Modifier,
) {
- val pinInput: PinInputViewModel by viewModel.pinInput.collectAsState()
+ val pinInput: PinInputViewModel by viewModel.pinInput.collectAsStateWithLifecycle()
// [ClearAll] marker pointing at the beginning of the current pin input.
// When a new [ClearAll] token is added to the [pinInput], the clear-all animation is played
// and the marker is advanced manually to the most recent marker. See LaunchedEffect below.
@@ -257,9 +257,10 @@
@Composable
private fun SimArea(viewModel: PinBouncerViewModel) {
- val isLockedEsim by viewModel.isLockedEsim.collectAsState()
- val isSimUnlockingDialogVisible by viewModel.isSimUnlockingDialogVisible.collectAsState()
- val errorDialogMessage by viewModel.errorDialogMessage.collectAsState()
+ val isLockedEsim by viewModel.isLockedEsim.collectAsStateWithLifecycle()
+ val isSimUnlockingDialogVisible by
+ viewModel.isSimUnlockingDialogVisible.collectAsStateWithLifecycle()
+ val errorDialogMessage by viewModel.errorDialogMessage.collectAsStateWithLifecycle()
var unlockDialog: Dialog? by remember { mutableStateOf(null) }
var errorDialog: Dialog? by remember { mutableStateOf(null) }
val context = LocalView.current.context
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/common/ui/compose/SelectedUserAwareInputConnection.kt b/packages/SystemUI/compose/features/src/com/android/systemui/common/ui/compose/SelectedUserAwareInputConnection.kt
index c8e1450..694326d 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/common/ui/compose/SelectedUserAwareInputConnection.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/common/ui/compose/SelectedUserAwareInputConnection.kt
@@ -35,7 +35,7 @@
* ```
* @Composable
* fun YourFunction(viewModel: YourViewModel) {
- * val selectedUserId by viewModel.selectedUserId.collectAsState()
+ * val selectedUserId by viewModel.selectedUserId.collectAsStateWithLifecycle()
*
* SelectedUserAwareInputConnection(selectedUserId) {
* TextField(...)
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/common/ui/compose/windowinsets/ScreenDecorProvider.kt b/packages/SystemUI/compose/features/src/com/android/systemui/common/ui/compose/windowinsets/ScreenDecorProvider.kt
index 8144d15..296fc27 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/common/ui/compose/windowinsets/ScreenDecorProvider.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/common/ui/compose/windowinsets/ScreenDecorProvider.kt
@@ -22,12 +22,12 @@
import androidx.compose.foundation.layout.systemBars
import androidx.compose.runtime.Composable
import androidx.compose.runtime.CompositionLocalProvider
-import androidx.compose.runtime.collectAsState
import androidx.compose.runtime.getValue
import androidx.compose.runtime.staticCompositionLocalOf
import androidx.compose.ui.platform.LocalConfiguration
import androidx.compose.ui.platform.LocalDensity
import androidx.compose.ui.unit.dp
+import androidx.lifecycle.compose.collectAsStateWithLifecycle
import kotlinx.coroutines.flow.StateFlow
/** The bounds and [CutoutLocation] of the current display. */
@@ -45,7 +45,7 @@
screenCornerRadius: Float,
content: @Composable () -> Unit,
) {
- val cutout by displayCutout.collectAsState()
+ val cutout by displayCutout.collectAsStateWithLifecycle()
val screenCornerRadiusDp = with(LocalDensity.current) { screenCornerRadius.toDp() }
val density = LocalDensity.current
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/CommunalContainer.kt b/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/CommunalContainer.kt
index 08e452c..feb1f5b 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/CommunalContainer.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/CommunalContainer.kt
@@ -2,17 +2,25 @@
import androidx.compose.animation.core.tween
import androidx.compose.foundation.background
+import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Box
+import androidx.compose.foundation.layout.Row
+import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.fillMaxSize
+import androidx.compose.foundation.layout.height
+import androidx.compose.foundation.layout.width
+import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.runtime.Composable
import androidx.compose.runtime.DisposableEffect
-import androidx.compose.runtime.collectAsState
import androidx.compose.runtime.getValue
import androidx.compose.runtime.remember
import androidx.compose.runtime.rememberCoroutineScope
+import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.res.dimensionResource
+import androidx.compose.ui.unit.dp
+import androidx.lifecycle.compose.collectAsStateWithLifecycle
import com.android.compose.animation.scene.Edge
import com.android.compose.animation.scene.ElementKey
import com.android.compose.animation.scene.ElementMatcher
@@ -26,6 +34,7 @@
import com.android.compose.animation.scene.SwipeDirection
import com.android.compose.animation.scene.observableTransitionState
import com.android.compose.animation.scene.transitions
+import com.android.systemui.Flags
import com.android.systemui.communal.shared.model.CommunalScenes
import com.android.systemui.communal.shared.model.CommunalTransitionKeys
import com.android.systemui.communal.ui.compose.extensions.allowGestures
@@ -85,8 +94,11 @@
content: CommunalContent,
) {
val coroutineScope = rememberCoroutineScope()
- val currentSceneKey: SceneKey by viewModel.currentScene.collectAsState(CommunalScenes.Blank)
- val touchesAllowed by viewModel.touchesAllowed.collectAsState(initial = false)
+ val currentSceneKey: SceneKey by
+ viewModel.currentScene.collectAsStateWithLifecycle(CommunalScenes.Blank)
+ val touchesAllowed by viewModel.touchesAllowed.collectAsStateWithLifecycle(initialValue = false)
+ val showGestureIndicator by
+ viewModel.showGestureIndicator.collectAsStateWithLifecycle(initialValue = false)
val state: MutableSceneTransitionLayoutState = remember {
MutableSceneTransitionLayoutState(
initialScene = currentSceneKey,
@@ -125,7 +137,19 @@
)
) {
// This scene shows nothing only allowing for transitions to the communal scene.
- Box(modifier = Modifier.fillMaxSize())
+ // TODO(b/339667383): remove this temporary swipe gesture handle
+ Row(modifier = Modifier.fillMaxSize(), horizontalArrangement = Arrangement.End) {
+ if (showGestureIndicator && Flags.glanceableHubGestureHandle()) {
+ Box(
+ modifier =
+ Modifier.height(220.dp)
+ .width(4.dp)
+ .align(Alignment.CenterVertically)
+ .background(color = Color.White, RoundedCornerShape(4.dp))
+ )
+ Spacer(modifier = Modifier.width(12.dp))
+ }
+ }
}
scene(
@@ -149,7 +173,8 @@
content: CommunalContent,
modifier: Modifier = Modifier,
) {
- val backgroundColor by colors.backgroundColor.collectAsState()
+ val backgroundColor by colors.backgroundColor.collectAsStateWithLifecycle()
+
Box(
modifier =
Modifier.element(Communal.Elements.Scrim)
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/CommunalHub.kt b/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/CommunalHub.kt
index 02621f6..cd27d57 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/CommunalHub.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/CommunalHub.kt
@@ -48,6 +48,7 @@
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.padding
+import androidx.compose.foundation.layout.requiredSize
import androidx.compose.foundation.layout.size
import androidx.compose.foundation.layout.width
import androidx.compose.foundation.layout.wrapContentHeight
@@ -78,7 +79,6 @@
import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.State
-import androidx.compose.runtime.collectAsState
import androidx.compose.runtime.derivedStateOf
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
@@ -88,8 +88,6 @@
import androidx.compose.ui.Alignment
import androidx.compose.ui.ExperimentalComposeUiApi
import androidx.compose.ui.Modifier
-import androidx.compose.ui.draw.alpha
-import androidx.compose.ui.draw.scale
import androidx.compose.ui.geometry.Offset
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.graphics.ColorFilter
@@ -121,9 +119,11 @@
import androidx.compose.ui.unit.IntSize
import androidx.compose.ui.unit.LayoutDirection
import androidx.compose.ui.unit.dp
+import androidx.compose.ui.unit.sp
+import androidx.compose.ui.unit.times
import androidx.compose.ui.viewinterop.AndroidView
import androidx.compose.ui.window.Popup
-import androidx.core.view.setPadding
+import androidx.lifecycle.compose.collectAsStateWithLifecycle
import androidx.window.layout.WindowMetricsCalculator
import com.android.compose.modifiers.thenIf
import com.android.compose.theme.LocalAndroidColorScheme
@@ -156,20 +156,21 @@
onOpenWidgetPicker: (() -> Unit)? = null,
onEditDone: (() -> Unit)? = null,
) {
- val communalContent by viewModel.communalContent.collectAsState(initial = emptyList())
- val currentPopup by viewModel.currentPopup.collectAsState(initial = null)
+ val communalContent by
+ viewModel.communalContent.collectAsStateWithLifecycle(initialValue = emptyList())
+ val currentPopup by viewModel.currentPopup.collectAsStateWithLifecycle(initialValue = null)
var removeButtonCoordinates: LayoutCoordinates? by remember { mutableStateOf(null) }
var toolbarSize: IntSize? by remember { mutableStateOf(null) }
var gridCoordinates: LayoutCoordinates? by remember { mutableStateOf(null) }
var isDraggingToRemove by remember { mutableStateOf(false) }
val gridState = rememberLazyGridState()
val contentListState = rememberContentListState(widgetConfigurator, communalContent, viewModel)
- val reorderingWidgets by viewModel.reorderingWidgets.collectAsState()
- val selectedKey = viewModel.selectedKey.collectAsState()
+ val reorderingWidgets by viewModel.reorderingWidgets.collectAsStateWithLifecycle()
+ val selectedKey = viewModel.selectedKey.collectAsStateWithLifecycle()
val removeButtonEnabled by remember {
derivedStateOf { selectedKey.value != null || reorderingWidgets }
}
- val isEmptyState by viewModel.isEmptyState.collectAsState(initial = false)
+ val isEmptyState by viewModel.isEmptyState.collectAsStateWithLifecycle(initialValue = false)
val contentPadding = gridContentPadding(viewModel.isEditMode, toolbarSize)
val contentOffset = beforeContentPadding(contentPadding).toOffset()
@@ -303,9 +304,9 @@
if (viewModel is CommunalViewModel && dialogFactory != null) {
val isEnableWidgetDialogShowing by
- viewModel.isEnableWidgetDialogShowing.collectAsState(false)
+ viewModel.isEnableWidgetDialogShowing.collectAsStateWithLifecycle(false)
val isEnableWorkProfileDialogShowing by
- viewModel.isEnableWorkProfileDialogShowing.collectAsState(false)
+ viewModel.isEnableWorkProfileDialogShowing.collectAsStateWithLifecycle(false)
EnableWidgetDialog(
isEnableWidgetDialogVisible = isEnableWidgetDialogShowing,
@@ -426,8 +427,8 @@
state = gridState,
rows = GridCells.Fixed(CommunalContentSize.FULL.span),
contentPadding = contentPadding,
- horizontalArrangement = Arrangement.spacedBy(32.dp),
- verticalArrangement = Arrangement.spacedBy(32.dp),
+ horizontalArrangement = Arrangement.spacedBy(Dimensions.ItemSpacing),
+ verticalArrangement = Arrangement.spacedBy(Dimensions.ItemSpacing),
) {
items(
count = list.size,
@@ -440,7 +441,7 @@
Dimensions.CardWidth.value,
list[index].size.dp().value,
)
- val cardModifier = Modifier.size(width = size.width.dp, height = size.height.dp)
+ val cardModifier = Modifier.requiredSize(width = size.width.dp, height = size.height.dp)
if (viewModel.isEditMode && dragDropState != null) {
val selected by
remember(index) { derivedStateOf { list[index].key == selectedKey.value } }
@@ -794,12 +795,10 @@
containerColor = colors.primary,
contentColor = colors.onPrimary,
),
- shape = RoundedCornerShape(80.dp, 40.dp, 80.dp, 40.dp)
+ shape = RoundedCornerShape(68.dp, 34.dp, 68.dp, 34.dp)
) {
Column(
- modifier = Modifier.fillMaxSize().padding(horizontal = 82.dp),
- verticalArrangement =
- Arrangement.spacedBy(Dimensions.Spacing, Alignment.CenterVertically),
+ modifier = Modifier.fillMaxSize().padding(vertical = 38.dp, horizontal = 70.dp),
horizontalAlignment = Alignment.CenterHorizontally,
) {
Icon(
@@ -807,11 +806,13 @@
contentDescription = stringResource(R.string.cta_label_to_open_widget_picker),
modifier = Modifier.size(Dimensions.IconSize),
)
+ Spacer(modifier = Modifier.size(6.dp))
Text(
text = stringResource(R.string.cta_label_to_edit_widget),
- style = MaterialTheme.typography.titleLarge,
+ style = MaterialTheme.typography.titleMedium,
textAlign = TextAlign.Center,
)
+ Spacer(modifier = Modifier.size(20.dp))
Row(
modifier = Modifier.fillMaxWidth(),
horizontalArrangement = Arrangement.Center,
@@ -827,9 +828,10 @@
) {
Text(
text = stringResource(R.string.cta_tile_button_to_dismiss),
+ fontSize = 12.sp,
)
}
- Spacer(modifier = Modifier.size(Dimensions.Spacing))
+ Spacer(modifier = Modifier.size(14.dp))
Button(
colors =
ButtonDefaults.buttonColors(
@@ -841,6 +843,7 @@
) {
Text(
text = stringResource(R.string.cta_tile_button_to_open_widget_editor),
+ fontSize = 12.sp,
)
}
}
@@ -860,7 +863,7 @@
contentListState: ContentListState,
) {
val context = LocalContext.current
- val isFocusable by viewModel.isFocusable.collectAsState(initial = false)
+ val isFocusable by viewModel.isFocusable.collectAsStateWithLifecycle(initialValue = false)
val accessibilityLabel =
remember(model, context) {
model.providerInfo.loadLabel(context.packageManager).toString().trim()
@@ -868,7 +871,7 @@
val clickActionLabel = stringResource(R.string.accessibility_action_label_select_widget)
val removeWidgetActionLabel = stringResource(R.string.accessibility_action_label_remove_widget)
val placeWidgetActionLabel = stringResource(R.string.accessibility_action_label_place_widget)
- val selectedKey by viewModel.selectedKey.collectAsState()
+ val selectedKey by viewModel.selectedKey.collectAsStateWithLifecycle()
val selectedIndex =
selectedKey?.let { key -> contentListState.list.indexOfFirst { it.key == key } }
Box(
@@ -926,10 +929,14 @@
model.appWidgetHost
.createViewForCommunal(context, model.appWidgetId, model.providerInfo)
.apply {
- updateAppWidgetSize(Bundle.EMPTY, listOf(size))
- // Remove the extra padding applied to AppWidgetHostView to allow widgets to
- // occupy the entire box.
- setPadding(0)
+ updateAppWidgetSize(
+ /* newOptions = */ Bundle(),
+ /* minWidth = */ size.width.toInt(),
+ /* minHeight = */ size.height.toInt(),
+ /* maxWidth = */ size.width.toInt(),
+ /* maxHeight = */ size.height.toInt(),
+ /* ignorePadding = */ true
+ )
accessibilityDelegate = viewModel.widgetAccessibilityDelegate
}
},
@@ -1109,7 +1116,7 @@
@Composable
fun AccessibilityContainer(viewModel: BaseCommunalViewModel, content: @Composable () -> Unit) {
val context = LocalContext.current
- val isFocusable by viewModel.isFocusable.collectAsState(initial = false)
+ val isFocusable by viewModel.isFocusable.collectAsStateWithLifecycle(initialValue = false)
Box(
modifier =
Modifier.fillMaxWidth().wrapContentHeight().thenIf(
@@ -1152,7 +1159,11 @@
@Composable
private fun gridContentPadding(isEditMode: Boolean, toolbarSize: IntSize?): PaddingValues {
if (!isEditMode || toolbarSize == null) {
- return PaddingValues(start = 48.dp, end = 48.dp, top = Dimensions.GridTopSpacing)
+ return PaddingValues(
+ start = Dimensions.ItemSpacing,
+ end = Dimensions.ItemSpacing,
+ top = Dimensions.GridTopSpacing,
+ )
}
val context = LocalContext.current
val density = LocalDensity.current
@@ -1215,18 +1226,19 @@
}
object Dimensions {
- val CardWidth = 424.dp
- val CardHeightFull = 596.dp
- val CardHeightHalf = 282.dp
- val CardHeightThird = 177.33.dp
- val CardOutlineWidth = 3.dp
- val GridTopSpacing = 64.dp
+ val CardHeightFull = 530.dp
+ val GridTopSpacing = 114.dp
val GridHeight = CardHeightFull + GridTopSpacing
- val Spacing = 16.dp
+ val ItemSpacing = 50.dp
+ val CardHeightHalf = (CardHeightFull - ItemSpacing) / 2
+ val CardHeightThird = (CardHeightFull - (2 * ItemSpacing)) / 3
+ val CardWidth = 360.dp
+ val CardOutlineWidth = 3.dp
+ val Spacing = ItemSpacing / 2
// The sizing/padding of the toolbar in glanceable hub edit mode
val ToolbarPaddingTop = 27.dp
- val ToolbarPaddingHorizontal = 16.dp
+ val ToolbarPaddingHorizontal = ItemSpacing
val ToolbarButtonPaddingHorizontal = 24.dp
val ToolbarButtonPaddingVertical = 16.dp
val ButtonPadding =
@@ -1234,10 +1246,7 @@
vertical = ToolbarButtonPaddingVertical,
horizontal = ToolbarButtonPaddingHorizontal,
)
- val IconSize = 48.dp
-
- val LockIconSize = 52.dp
- val LockIconBottomPadding = 70.dp
+ val IconSize = 40.dp
}
private object Colors {
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/fold/ui/composable/FoldPosture.kt b/packages/SystemUI/compose/features/src/com/android/systemui/fold/ui/composable/FoldPosture.kt
index e77ade9..17dac7e 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/fold/ui/composable/FoldPosture.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/fold/ui/composable/FoldPosture.kt
@@ -18,11 +18,11 @@
import androidx.compose.runtime.Composable
import androidx.compose.runtime.State
-import androidx.compose.runtime.collectAsState
import androidx.compose.runtime.getValue
import androidx.compose.runtime.produceState
import androidx.compose.runtime.remember
import androidx.compose.ui.platform.LocalContext
+import androidx.lifecycle.compose.collectAsStateWithLifecycle
import androidx.window.layout.WindowInfoTracker
import com.android.systemui.fold.ui.helper.FoldPosture
import com.android.systemui.fold.ui.helper.foldPostureInternal
@@ -32,7 +32,8 @@
fun foldPosture(): State<FoldPosture> {
val context = LocalContext.current
val infoTracker = remember(context) { WindowInfoTracker.getOrCreate(context) }
- val layoutInfo by infoTracker.windowLayoutInfo(context).collectAsState(initial = null)
+ val layoutInfo by
+ infoTracker.windowLayoutInfo(context).collectAsStateWithLifecycle(initialValue = null)
return produceState<FoldPosture>(
initialValue = FoldPosture.Folded,
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/keyboard/stickykeys/ui/view/StickyKeysIndicator.kt b/packages/SystemUI/compose/features/src/com/android/systemui/keyboard/stickykeys/ui/view/StickyKeysIndicator.kt
index a8d801a..67840c7 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/keyboard/stickykeys/ui/view/StickyKeysIndicator.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/keyboard/stickykeys/ui/view/StickyKeysIndicator.kt
@@ -29,7 +29,6 @@
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.CompositionLocalProvider
-import androidx.compose.runtime.collectAsState
import androidx.compose.runtime.getValue
import androidx.compose.runtime.key
import androidx.compose.ui.Alignment
@@ -37,6 +36,7 @@
import androidx.compose.ui.platform.ComposeView
import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.unit.dp
+import androidx.lifecycle.compose.collectAsStateWithLifecycle
import com.android.compose.theme.PlatformTheme
import com.android.systemui.keyboard.stickykeys.shared.model.Locked
import com.android.systemui.keyboard.stickykeys.shared.model.ModifierKey
@@ -57,7 +57,7 @@
@Composable
fun StickyKeysIndicator(viewModel: StickyKeysIndicatorViewModel) {
- val stickyKeys by viewModel.indicatorContent.collectAsState(emptyMap())
+ val stickyKeys by viewModel.indicatorContent.collectAsStateWithLifecycle(emptyMap())
StickyKeysIndicator(stickyKeys)
}
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/LockscreenContent.kt b/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/LockscreenContent.kt
index 4bef9ef..ca4ff83 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/LockscreenContent.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/LockscreenContent.kt
@@ -18,12 +18,13 @@
import androidx.compose.runtime.Composable
import androidx.compose.runtime.DisposableEffect
-import androidx.compose.runtime.collectAsState
import androidx.compose.runtime.getValue
import androidx.compose.runtime.rememberCoroutineScope
import androidx.compose.ui.Modifier
import androidx.compose.ui.platform.LocalView
+import androidx.lifecycle.compose.collectAsStateWithLifecycle
import com.android.compose.animation.scene.SceneScope
+import com.android.systemui.compose.modifiers.sysuiResTag
import com.android.systemui.keyguard.domain.interactor.KeyguardClockInteractor
import com.android.systemui.keyguard.ui.composable.blueprint.ComposableLockscreenSceneBlueprint
import com.android.systemui.keyguard.ui.viewmodel.LockscreenContentViewModel
@@ -51,7 +52,7 @@
modifier: Modifier = Modifier,
) {
val coroutineScope = rememberCoroutineScope()
- val blueprintId by viewModel.blueprintId(coroutineScope).collectAsState()
+ val blueprintId by viewModel.blueprintId(coroutineScope).collectAsStateWithLifecycle()
val view = LocalView.current
DisposableEffect(view) {
clockInteractor.clockEventController.registerListeners(view)
@@ -60,6 +61,6 @@
}
val blueprint = blueprintByBlueprintId[blueprintId] ?: return
- with(blueprint) { Content(modifier) }
+ with(blueprint) { Content(modifier.sysuiResTag("keyguard_root_view")) }
}
}
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/LockscreenLongPress.kt b/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/LockscreenLongPress.kt
index 472484a..4555f13 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/LockscreenLongPress.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/LockscreenLongPress.kt
@@ -26,13 +26,13 @@
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.BoxScope
import androidx.compose.runtime.Composable
-import androidx.compose.runtime.collectAsState
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.ui.Modifier
import androidx.compose.ui.geometry.Rect
import androidx.compose.ui.input.pointer.pointerInput
+import androidx.lifecycle.compose.collectAsStateWithLifecycle
import com.android.systemui.keyguard.ui.viewmodel.KeyguardLongPressViewModel
/** Container for lockscreen content that handles long-press to bring up the settings menu. */
@@ -42,7 +42,8 @@
modifier: Modifier = Modifier,
content: @Composable BoxScope.(onSettingsMenuPlaces: (coordinates: Rect?) -> Unit) -> Unit,
) {
- val isEnabled: Boolean by viewModel.isLongPressHandlingEnabled.collectAsState(initial = false)
+ val isEnabled: Boolean by
+ viewModel.isLongPressHandlingEnabled.collectAsStateWithLifecycle(initialValue = false)
val (settingsMenuBounds, setSettingsMenuBounds) = remember { mutableStateOf<Rect?>(null) }
val interactionSource = remember { MutableInteractionSource() }
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/blueprint/BurnInState.kt b/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/blueprint/BurnInState.kt
index 8129e41..ba25719 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/blueprint/BurnInState.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/blueprint/BurnInState.kt
@@ -21,11 +21,11 @@
import androidx.compose.foundation.layout.systemBars
import androidx.compose.foundation.layout.union
import androidx.compose.runtime.Composable
-import androidx.compose.runtime.collectAsState
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.ui.platform.LocalDensity
+import androidx.lifecycle.compose.collectAsStateWithLifecycle
import com.android.systemui.keyguard.domain.interactor.KeyguardClockInteractor
import com.android.systemui.keyguard.ui.viewmodel.BurnInParameters
import com.android.systemui.plugins.clocks.ClockController
@@ -37,7 +37,7 @@
fun rememberBurnIn(
clockInteractor: KeyguardClockInteractor,
): BurnInState {
- val clock by clockInteractor.currentClock.collectAsState()
+ val clock by clockInteractor.currentClock.collectAsStateWithLifecycle()
val (smartspaceTop, onSmartspaceTopChanged) = remember { mutableStateOf<Float?>(null) }
val (smallClockTop, onSmallClockTopChanged) = remember { mutableStateOf<Float?>(null) }
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/blueprint/DefaultBlueprint.kt b/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/blueprint/DefaultBlueprint.kt
index 3152535..a39fa64 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/blueprint/DefaultBlueprint.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/blueprint/DefaultBlueprint.kt
@@ -22,15 +22,16 @@
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.runtime.Composable
-import androidx.compose.runtime.collectAsState
import androidx.compose.runtime.getValue
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.graphicsLayer
import androidx.compose.ui.layout.Layout
import androidx.compose.ui.unit.IntRect
+import androidx.lifecycle.compose.collectAsStateWithLifecycle
import com.android.compose.animation.scene.SceneScope
import com.android.compose.modifiers.padding
+import com.android.systemui.compose.modifiers.sysuiResTag
import com.android.systemui.keyguard.ui.composable.LockscreenLongPress
import com.android.systemui.keyguard.ui.composable.section.AmbientIndicationSection
import com.android.systemui.keyguard.ui.composable.section.BottomAreaSection
@@ -67,8 +68,8 @@
override fun SceneScope.Content(modifier: Modifier) {
val isUdfpsVisible = viewModel.isUdfpsVisible
val shouldUseSplitNotificationShade by
- viewModel.shouldUseSplitNotificationShade.collectAsState()
- val unfoldTranslations by viewModel.unfoldTranslations.collectAsState()
+ viewModel.shouldUseSplitNotificationShade.collectAsStateWithLifecycle()
+ val unfoldTranslations by viewModel.unfoldTranslations.collectAsStateWithLifecycle()
LockscreenLongPress(
viewModel = viewModel.longPress,
@@ -129,7 +130,7 @@
with(lockSection) { LockIcon() }
// Aligned to bottom and constrained to below the lock icon.
- Column(modifier = Modifier.fillMaxWidth()) {
+ Column(modifier = Modifier.fillMaxWidth().sysuiResTag("keyguard_bottom_area")) {
if (isUdfpsVisible && ambientIndicationSectionOptional.isPresent) {
with(ambientIndicationSectionOptional.get()) {
AmbientIndication(modifier = Modifier.fillMaxWidth())
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/blueprint/ShortcutsBesideUdfpsBlueprint.kt b/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/blueprint/ShortcutsBesideUdfpsBlueprint.kt
index 9d31955..c83f62c 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/blueprint/ShortcutsBesideUdfpsBlueprint.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/blueprint/ShortcutsBesideUdfpsBlueprint.kt
@@ -22,13 +22,13 @@
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.runtime.Composable
-import androidx.compose.runtime.collectAsState
import androidx.compose.runtime.getValue
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.graphicsLayer
import androidx.compose.ui.layout.Layout
import androidx.compose.ui.unit.IntRect
+import androidx.lifecycle.compose.collectAsStateWithLifecycle
import com.android.compose.animation.scene.SceneScope
import com.android.compose.modifiers.padding
import com.android.systemui.keyguard.ui.composable.LockscreenLongPress
@@ -70,8 +70,8 @@
override fun SceneScope.Content(modifier: Modifier) {
val isUdfpsVisible = viewModel.isUdfpsVisible
val shouldUseSplitNotificationShade by
- viewModel.shouldUseSplitNotificationShade.collectAsState()
- val unfoldTranslations by viewModel.unfoldTranslations.collectAsState()
+ viewModel.shouldUseSplitNotificationShade.collectAsStateWithLifecycle()
+ val unfoldTranslations by viewModel.unfoldTranslations.collectAsStateWithLifecycle()
LockscreenLongPress(
viewModel = viewModel.longPress,
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/modifier/BurnInModifiers.kt b/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/modifier/BurnInModifiers.kt
index abbf0ea..aaf49ff 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/modifier/BurnInModifiers.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/modifier/BurnInModifiers.kt
@@ -17,7 +17,6 @@
package com.android.systemui.keyguard.ui.composable.modifier
import androidx.compose.runtime.Composable
-import androidx.compose.runtime.collectAsState
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
@@ -25,6 +24,7 @@
import androidx.compose.ui.graphics.graphicsLayer
import androidx.compose.ui.layout.boundsInWindow
import androidx.compose.ui.layout.onPlaced
+import androidx.lifecycle.compose.collectAsStateWithLifecycle
import com.android.systemui.keyguard.ui.viewmodel.AodBurnInViewModel
import com.android.systemui.keyguard.ui.viewmodel.BurnInParameters
import com.android.systemui.keyguard.ui.viewmodel.BurnInScaleViewModel
@@ -44,8 +44,10 @@
val translationYState = remember { mutableStateOf(0F) }
val copiedParams = params.copy(translationY = { translationYState.value })
val burnIn = viewModel.movement(copiedParams)
- val translationX by burnIn.map { it.translationX.toFloat() }.collectAsState(initial = 0f)
- val translationY by burnIn.map { it.translationY.toFloat() }.collectAsState(initial = 0f)
+ val translationX by
+ burnIn.map { it.translationX.toFloat() }.collectAsStateWithLifecycle(initialValue = 0f)
+ val translationY by
+ burnIn.map { it.translationY.toFloat() }.collectAsStateWithLifecycle(initialValue = 0f)
translationYState.value = translationY
val scaleViewModel by
burnIn
@@ -55,7 +57,7 @@
scaleClockOnly = it.scaleClockOnly,
)
}
- .collectAsState(initial = BurnInScaleViewModel())
+ .collectAsStateWithLifecycle(initialValue = BurnInScaleViewModel())
return this.graphicsLayer {
this.translationX = if (isClock) 0F else translationX
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/section/DefaultClockSection.kt b/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/section/DefaultClockSection.kt
index 09ec76d..218779d 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/section/DefaultClockSection.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/section/DefaultClockSection.kt
@@ -25,13 +25,13 @@
import androidx.compose.foundation.layout.padding
import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
-import androidx.compose.runtime.collectAsState
import androidx.compose.runtime.getValue
import androidx.compose.ui.Modifier
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.res.dimensionResource
import androidx.compose.ui.viewinterop.AndroidView
import androidx.core.view.contains
+import androidx.lifecycle.compose.collectAsStateWithLifecycle
import com.android.compose.animation.scene.SceneScope
import com.android.compose.modifiers.padding
import com.android.systemui.customization.R
@@ -59,9 +59,11 @@
onTopChanged: (top: Float?) -> Unit,
modifier: Modifier = Modifier,
) {
- val currentClock by viewModel.currentClock.collectAsState()
+ val currentClock by viewModel.currentClock.collectAsStateWithLifecycle()
val smallTopMargin by
- viewModel.smallClockTopMargin.collectAsState(viewModel.getSmallClockTopMargin())
+ viewModel.smallClockTopMargin.collectAsStateWithLifecycle(
+ viewModel.getSmallClockTopMargin()
+ )
if (currentClock?.smallClock?.view == null) {
return
}
@@ -89,7 +91,7 @@
@Composable
fun SceneScope.LargeClock(burnInParams: BurnInParameters, modifier: Modifier = Modifier) {
- val currentClock by viewModel.currentClock.collectAsState()
+ val currentClock by viewModel.currentClock.collectAsStateWithLifecycle()
if (currentClock?.largeClock?.view == null) {
return
}
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/section/MediaCarouselSection.kt b/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/section/MediaCarouselSection.kt
index c37d626..3ca2b9c 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/section/MediaCarouselSection.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/section/MediaCarouselSection.kt
@@ -18,9 +18,9 @@
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.runtime.Composable
-import androidx.compose.runtime.collectAsState
import androidx.compose.runtime.getValue
import androidx.compose.ui.Modifier
+import androidx.lifecycle.compose.collectAsStateWithLifecycle
import com.android.compose.animation.scene.SceneScope
import com.android.systemui.keyguard.ui.viewmodel.KeyguardMediaViewModel
import com.android.systemui.media.controls.ui.composable.MediaCarousel
@@ -40,7 +40,7 @@
@Composable
fun SceneScope.KeyguardMediaCarousel() {
- val isMediaVisible by keyguardMediaViewModel.isMediaVisible.collectAsState()
+ val isMediaVisible by keyguardMediaViewModel.isMediaVisible.collectAsStateWithLifecycle()
MediaCarousel(
isVisible = isMediaVisible,
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/section/NotificationSection.kt b/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/section/NotificationSection.kt
index f48fa88..7f80dfa 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/section/NotificationSection.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/section/NotificationSection.kt
@@ -20,13 +20,13 @@
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.padding
import androidx.compose.runtime.Composable
-import androidx.compose.runtime.collectAsState
import androidx.compose.runtime.getValue
import androidx.compose.ui.Modifier
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.res.dimensionResource
import androidx.compose.ui.unit.Dp
import androidx.compose.ui.unit.dp
+import androidx.lifecycle.compose.collectAsStateWithLifecycle
import com.android.compose.animation.scene.SceneScope
import com.android.compose.modifiers.thenIf
import com.android.systemui.Flags
@@ -40,16 +40,19 @@
import com.android.systemui.res.R
import com.android.systemui.shade.LargeScreenHeaderHelper
import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayout
+import com.android.systemui.statusbar.notification.stack.ui.view.NotificationScrollView
import com.android.systemui.statusbar.notification.stack.ui.view.SharedNotificationContainer
import com.android.systemui.statusbar.notification.stack.ui.viewbinder.SharedNotificationContainerBinder
import com.android.systemui.statusbar.notification.stack.ui.viewmodel.NotificationsPlaceholderViewModel
import com.android.systemui.statusbar.notification.stack.ui.viewmodel.SharedNotificationContainerViewModel
+import dagger.Lazy
import javax.inject.Inject
@SysUISingleton
class NotificationSection
@Inject
constructor(
+ private val stackScrollView: Lazy<NotificationScrollView>,
private val viewModel: NotificationsPlaceholderViewModel,
private val aodBurnInViewModel: AodBurnInViewModel,
sharedNotificationContainer: SharedNotificationContainer,
@@ -88,9 +91,9 @@
@Composable
fun SceneScope.Notifications(burnInParams: BurnInParameters?, modifier: Modifier = Modifier) {
val shouldUseSplitNotificationShade by
- lockscreenContentViewModel.shouldUseSplitNotificationShade.collectAsState()
+ lockscreenContentViewModel.shouldUseSplitNotificationShade.collectAsStateWithLifecycle()
val areNotificationsVisible by
- lockscreenContentViewModel.areNotificationsVisible.collectAsState()
+ lockscreenContentViewModel.areNotificationsVisible.collectAsStateWithLifecycle()
val splitShadeTopMargin: Dp =
if (Flags.centralizedStatusBarHeightFix()) {
LargeScreenHeaderHelper.getLargeScreenHeaderHeight(LocalContext.current).dp
@@ -103,6 +106,7 @@
}
ConstrainedNotificationStack(
+ stackScrollView = stackScrollView.get(),
viewModel = viewModel,
modifier =
modifier
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/section/SmartSpaceSection.kt b/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/section/SmartSpaceSection.kt
index fc8b3b9..44bda95 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/section/SmartSpaceSection.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/section/SmartSpaceSection.kt
@@ -26,7 +26,6 @@
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.width
import androidx.compose.runtime.Composable
-import androidx.compose.runtime.collectAsState
import androidx.compose.runtime.getValue
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
@@ -34,6 +33,7 @@
import androidx.compose.ui.res.dimensionResource
import androidx.compose.ui.unit.dp
import androidx.compose.ui.viewinterop.AndroidView
+import androidx.lifecycle.compose.collectAsStateWithLifecycle
import com.android.compose.animation.scene.SceneScope
import com.android.compose.modifiers.padding
import com.android.systemui.keyguard.KeyguardUnlockAnimationController
@@ -160,7 +160,7 @@
private fun Weather(
modifier: Modifier = Modifier,
) {
- val isVisible by keyguardSmartspaceViewModel.isWeatherVisible.collectAsState()
+ val isVisible by keyguardSmartspaceViewModel.isWeatherVisible.collectAsStateWithLifecycle()
if (!isVisible) {
return
}
@@ -187,7 +187,7 @@
private fun Date(
modifier: Modifier = Modifier,
) {
- val isVisible by keyguardSmartspaceViewModel.isDateVisible.collectAsState()
+ val isVisible by keyguardSmartspaceViewModel.isDateVisible.collectAsStateWithLifecycle()
if (!isVisible) {
return
}
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/section/TopAreaSection.kt b/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/section/TopAreaSection.kt
index 63c70c9..0673153 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/section/TopAreaSection.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/section/TopAreaSection.kt
@@ -25,7 +25,6 @@
import androidx.compose.foundation.layout.wrapContentSize
import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
-import androidx.compose.runtime.collectAsState
import androidx.compose.runtime.getValue
import androidx.compose.ui.Modifier
import androidx.compose.ui.platform.LocalContext
@@ -33,10 +32,10 @@
import androidx.compose.ui.unit.Density
import androidx.compose.ui.unit.Dp
import androidx.compose.ui.unit.IntOffset
+import androidx.lifecycle.compose.collectAsStateWithLifecycle
import com.android.compose.animation.scene.SceneScope
import com.android.compose.animation.scene.SceneTransitionLayout
import com.android.compose.modifiers.thenIf
-import com.android.systemui.compose.modifiers.sysuiResTag
import com.android.systemui.keyguard.domain.interactor.KeyguardClockInteractor
import com.android.systemui.keyguard.ui.composable.blueprint.ClockScenes.largeClockScene
import com.android.systemui.keyguard.ui.composable.blueprint.ClockScenes.smallClockScene
@@ -62,9 +61,9 @@
fun DefaultClockLayout(
modifier: Modifier = Modifier,
) {
- val currentClockLayout by clockViewModel.currentClockLayout.collectAsState()
+ val currentClockLayout by clockViewModel.currentClockLayout.collectAsStateWithLifecycle()
val hasCustomPositionUpdatedAnimation by
- clockViewModel.hasCustomPositionUpdatedAnimation.collectAsState()
+ clockViewModel.hasCustomPositionUpdatedAnimation.collectAsStateWithLifecycle()
val currentScene =
when (currentClockLayout) {
KeyguardClockViewModel.ClockLayout.SPLIT_SHADE_LARGE_CLOCK ->
@@ -80,7 +79,7 @@
}
SceneTransitionLayout(
- modifier = modifier.sysuiResTag("keyguard_clock_container"),
+ modifier = modifier,
currentScene = currentScene,
onChangeScene = {},
transitions = ClockTransition.defaultClockTransitions,
@@ -133,7 +132,7 @@
@Composable
private fun SceneScope.LargeClockWithSmartSpace(shouldOffSetClockToOneHalf: Boolean = false) {
val burnIn = rememberBurnIn(clockInteractor)
- val isLargeClockVisible by clockViewModel.isLargeClockVisible.collectAsState()
+ val isLargeClockVisible by clockViewModel.isLargeClockVisible.collectAsStateWithLifecycle()
LaunchedEffect(isLargeClockVisible) {
if (isLargeClockVisible) {
@@ -170,8 +169,8 @@
@Composable
private fun SceneScope.WeatherLargeClockWithSmartSpace(modifier: Modifier = Modifier) {
val burnIn = rememberBurnIn(clockInteractor)
- val isLargeClockVisible by clockViewModel.isLargeClockVisible.collectAsState()
- val currentClockState = clockViewModel.currentClock.collectAsState()
+ val isLargeClockVisible by clockViewModel.isLargeClockVisible.collectAsStateWithLifecycle()
+ val currentClockState = clockViewModel.currentClock.collectAsStateWithLifecycle()
LaunchedEffect(isLargeClockVisible) {
if (isLargeClockVisible) {
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/notifications/ui/composable/Notifications.kt b/packages/SystemUI/compose/features/src/com/android/systemui/notifications/ui/composable/Notifications.kt
index 01d62a3..cf2e895 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/notifications/ui/composable/Notifications.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/notifications/ui/composable/Notifications.kt
@@ -38,7 +38,6 @@
import androidx.compose.runtime.Composable
import androidx.compose.runtime.DisposableEffect
import androidx.compose.runtime.LaunchedEffect
-import androidx.compose.runtime.collectAsState
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableIntStateOf
import androidx.compose.runtime.remember
@@ -64,6 +63,7 @@
import androidx.compose.ui.unit.IntOffset
import androidx.compose.ui.unit.dp
import androidx.compose.ui.util.lerp
+import androidx.lifecycle.compose.collectAsStateWithLifecycle
import com.android.compose.animation.scene.ElementKey
import com.android.compose.animation.scene.NestedScrollBehavior
import com.android.compose.animation.scene.SceneScope
@@ -112,7 +112,7 @@
modifier: Modifier = Modifier,
isPeekFromBottom: Boolean = false,
) {
- val headsUpHeight = viewModel.headsUpHeight.collectAsState()
+ val headsUpHeight = viewModel.headsUpHeight.collectAsStateWithLifecycle()
Element(
Notifications.Elements.HeadsUpNotificationPlaceholder,
@@ -138,6 +138,7 @@
/** Adds the space where notification stack should appear in the scene. */
@Composable
fun SceneScope.ConstrainedNotificationStack(
+ stackScrollView: NotificationScrollView,
viewModel: NotificationsPlaceholderViewModel,
modifier: Modifier = Modifier,
) {
@@ -146,6 +147,7 @@
modifier.onSizeChanged { viewModel.onConstrainedAvailableSpaceChanged(it.height) }
) {
NotificationPlaceholder(
+ stackScrollView = stackScrollView,
viewModel = viewModel,
modifier = Modifier.fillMaxSize(),
)
@@ -178,9 +180,10 @@
shadeSession.rememberSaveableSession(saver = ScrollState.Saver, key = null) {
ScrollState(initial = 0)
}
- val syntheticScroll = viewModel.syntheticScroll.collectAsState(0f)
- val isCurrentGestureOverscroll = viewModel.isCurrentGestureOverscroll.collectAsState(false)
- val expansionFraction by viewModel.expandFraction.collectAsState(0f)
+ val syntheticScroll = viewModel.syntheticScroll.collectAsStateWithLifecycle(0f)
+ val isCurrentGestureOverscroll =
+ viewModel.isCurrentGestureOverscroll.collectAsStateWithLifecycle(false)
+ val expansionFraction by viewModel.expandFraction.collectAsStateWithLifecycle(0f)
val navBarHeight =
with(density) { WindowInsets.systemBars.asPaddingValues().calculateBottomPadding().toPx() }
@@ -193,7 +196,8 @@
*/
val stackHeight = remember { mutableIntStateOf(0) }
- val scrimRounding = viewModel.shadeScrimRounding.collectAsState(ShadeScrimRounding())
+ val scrimRounding =
+ viewModel.shadeScrimRounding.collectAsStateWithLifecycle(ShadeScrimRounding())
// the offset for the notifications scrim. Its upper bound is 0, and its lower bound is
// calculated in minScrimOffset. The scrim is the same height as the screen minus the
@@ -334,6 +338,7 @@
.debugBackground(viewModel, DEBUG_BOX_COLOR)
) {
NotificationPlaceholder(
+ stackScrollView = stackScrollView,
viewModel = viewModel,
modifier =
Modifier.verticalNestedScrollToScene(
@@ -390,6 +395,7 @@
@Composable
private fun SceneScope.NotificationPlaceholder(
+ stackScrollView: NotificationScrollView,
viewModel: NotificationsPlaceholderViewModel,
modifier: Modifier = Modifier,
) {
@@ -408,10 +414,8 @@
" bounds=${coordinates.boundsInWindow()}"
}
// NOTE: positionInWindow.y scrolls off screen, but boundsInWindow.top will not
- viewModel.onStackBoundsChanged(
- top = positionInWindow.y,
- bottom = positionInWindow.y + coordinates.size.height,
- )
+ stackScrollView.setStackTop(positionInWindow.y)
+ stackScrollView.setStackBottom(positionInWindow.y + coordinates.size.height)
}
) {
content {}
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/people/ui/compose/PeopleScreen.kt b/packages/SystemUI/compose/features/src/com/android/systemui/people/ui/compose/PeopleScreen.kt
index 73cb72c..b808044 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/people/ui/compose/PeopleScreen.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/people/ui/compose/PeopleScreen.kt
@@ -36,7 +36,6 @@
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
-import androidx.compose.runtime.collectAsState
import androidx.compose.runtime.getValue
import androidx.compose.runtime.key
import androidx.compose.ui.Alignment
@@ -46,6 +45,7 @@
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.unit.dp
+import androidx.lifecycle.compose.collectAsStateWithLifecycle
import com.android.compose.theme.LocalAndroidColorScheme
import com.android.systemui.compose.modifiers.sysuiResTag
import com.android.systemui.people.ui.viewmodel.PeopleTileViewModel
@@ -64,8 +64,8 @@
viewModel: PeopleViewModel,
onResult: (PeopleViewModel.Result) -> Unit,
) {
- val priorityTiles by viewModel.priorityTiles.collectAsState()
- val recentTiles by viewModel.recentTiles.collectAsState()
+ val priorityTiles by viewModel.priorityTiles.collectAsStateWithLifecycle()
+ val recentTiles by viewModel.recentTiles.collectAsStateWithLifecycle()
// Call [onResult] this activity when the ViewModel tells us so.
LaunchedEffect(viewModel.result) {
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/qs/footer/ui/compose/FooterActions.kt b/packages/SystemUI/compose/features/src/com/android/systemui/qs/footer/ui/compose/FooterActions.kt
index 2f241ce..e8da4bd 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/qs/footer/ui/compose/FooterActions.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/qs/footer/ui/compose/FooterActions.kt
@@ -44,7 +44,6 @@
import androidx.compose.runtime.Composable
import androidx.compose.runtime.CompositionLocalProvider
import androidx.compose.runtime.LaunchedEffect
-import androidx.compose.runtime.collectAsState
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
@@ -69,6 +68,7 @@
import androidx.compose.ui.unit.sp
import androidx.lifecycle.Lifecycle
import androidx.lifecycle.LifecycleOwner
+import androidx.lifecycle.compose.collectAsStateWithLifecycle
import androidx.lifecycle.repeatOnLifecycle
import com.android.compose.animation.Expandable
import com.android.compose.animation.scene.SceneScope
@@ -132,8 +132,8 @@
val context = LocalContext.current
// Collect alphas as soon as we are composed, even when not visible.
- val alpha by viewModel.alpha.collectAsState()
- val backgroundAlpha = viewModel.backgroundAlpha.collectAsState()
+ val alpha by viewModel.alpha.collectAsStateWithLifecycle()
+ val backgroundAlpha = viewModel.backgroundAlpha.collectAsStateWithLifecycle()
var security by remember { mutableStateOf<FooterActionsSecurityButtonViewModel?>(null) }
var foregroundServices by remember {
@@ -181,7 +181,6 @@
val horizontalPadding = dimensionResource(R.dimen.qs_content_horizontal_padding)
Row(
modifier
- .sysuiResTag("qs_footer_actions")
.fillMaxWidth()
.graphicsLayer { this.alpha = alpha }
.then(backgroundModifier)
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/qs/ui/composable/BrightnessMirror.kt b/packages/SystemUI/compose/features/src/com/android/systemui/qs/ui/composable/BrightnessMirror.kt
index ca6b343..73a624a 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/qs/ui/composable/BrightnessMirror.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/qs/ui/composable/BrightnessMirror.kt
@@ -21,13 +21,13 @@
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.offset
import androidx.compose.runtime.Composable
-import androidx.compose.runtime.collectAsState
import androidx.compose.runtime.getValue
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.graphicsLayer
import androidx.compose.ui.unit.IntOffset
import androidx.compose.ui.viewinterop.AndroidView
+import androidx.lifecycle.compose.collectAsStateWithLifecycle
import com.android.compose.modifiers.height
import com.android.compose.modifiers.width
import com.android.systemui.qs.ui.adapter.QSSceneAdapter
@@ -40,13 +40,13 @@
qsSceneAdapter: QSSceneAdapter,
modifier: Modifier = Modifier,
) {
- val isShowing by viewModel.isShowing.collectAsState()
+ val isShowing by viewModel.isShowing.collectAsStateWithLifecycle()
val mirrorAlpha by
animateFloatAsState(
targetValue = if (isShowing) 1f else 0f,
label = "alphaAnimationBrightnessMirrorShowing",
)
- val mirrorOffsetAndSize by viewModel.locationAndSize.collectAsState()
+ val mirrorOffsetAndSize by viewModel.locationAndSize.collectAsStateWithLifecycle()
val offset = IntOffset(0, mirrorOffsetAndSize.yOffset)
Box(modifier = modifier.fillMaxSize().graphicsLayer { alpha = mirrorAlpha }) {
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/qs/ui/composable/QuickSettings.kt b/packages/SystemUI/compose/features/src/com/android/systemui/qs/ui/composable/QuickSettings.kt
index 46be6b8..54a98dd 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/qs/ui/composable/QuickSettings.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/qs/ui/composable/QuickSettings.kt
@@ -22,18 +22,19 @@
import androidx.compose.runtime.Composable
import androidx.compose.runtime.DisposableEffect
import androidx.compose.runtime.LaunchedEffect
-import androidx.compose.runtime.collectAsState
import androidx.compose.runtime.getValue
import androidx.compose.ui.Modifier
import androidx.compose.ui.layout.layout
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.viewinterop.AndroidView
+import androidx.lifecycle.compose.collectAsStateWithLifecycle
import com.android.compose.animation.scene.ElementKey
import com.android.compose.animation.scene.MovableElementScenePicker
import com.android.compose.animation.scene.SceneScope
import com.android.compose.animation.scene.TransitionState
import com.android.compose.animation.scene.ValueKey
import com.android.compose.modifiers.thenIf
+import com.android.systemui.compose.modifiers.sysuiResTag
import com.android.systemui.qs.ui.adapter.QSSceneAdapter
import com.android.systemui.qs.ui.adapter.QSSceneAdapter.State.Companion.Collapsing
import com.android.systemui.qs.ui.adapter.QSSceneAdapter.State.Expanding
@@ -68,7 +69,7 @@
private fun SceneScope.stateForQuickSettingsContent(
isSplitShade: Boolean,
- squishiness: Float = QuickSettings.SharedValues.SquishinessValues.Default
+ squishiness: () -> Float = { QuickSettings.SharedValues.SquishinessValues.Default }
): QSSceneAdapter.State {
return when (val transitionState = layoutState.transitionState) {
is TransitionState.Idle -> {
@@ -124,9 +125,9 @@
heightProvider: () -> Int,
isSplitShade: Boolean,
modifier: Modifier = Modifier,
- squishiness: Float = QuickSettings.SharedValues.SquishinessValues.Default,
+ squishiness: () -> Float = { QuickSettings.SharedValues.SquishinessValues.Default },
) {
- val contentState = stateForQuickSettingsContent(isSplitShade, squishiness)
+ val contentState = { stateForQuickSettingsContent(isSplitShade, squishiness) }
val transitionState = layoutState.transitionState
val isClosing =
transitionState is TransitionState.Transition &&
@@ -143,7 +144,9 @@
MovableElement(
key = QuickSettings.Elements.Content,
modifier =
- modifier.fillMaxWidth().layout { measurable, constraints ->
+ modifier.sysuiResTag("quick_settings_panel").fillMaxWidth().layout {
+ measurable,
+ constraints ->
val placeable = measurable.measure(constraints)
// Use the height of the correct view based on the scene it is being composed in
val height = heightProvider().coerceAtLeast(0)
@@ -158,12 +161,14 @@
@Composable
private fun QuickSettingsContent(
qsSceneAdapter: QSSceneAdapter,
- state: QSSceneAdapter.State,
+ state: () -> QSSceneAdapter.State,
modifier: Modifier = Modifier,
) {
- val qsView by qsSceneAdapter.qsView.collectAsState(null)
+ val qsView by qsSceneAdapter.qsView.collectAsStateWithLifecycle(null)
val isCustomizing by
- qsSceneAdapter.isCustomizerShowing.collectAsState(qsSceneAdapter.isCustomizerShowing.value)
+ qsSceneAdapter.isCustomizerShowing.collectAsStateWithLifecycle(
+ qsSceneAdapter.isCustomizerShowing.value
+ )
QuickSettingsTheme {
val context = LocalContext.current
@@ -180,10 +185,10 @@
AndroidView(
modifier = Modifier.fillMaxWidth(),
factory = { _ ->
- qsSceneAdapter.setState(state)
+ qsSceneAdapter.setState(state())
view
},
- update = { qsSceneAdapter.setState(state) }
+ update = { qsSceneAdapter.setState(state()) }
)
}
}
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/qs/ui/composable/QuickSettingsScene.kt b/packages/SystemUI/compose/features/src/com/android/systemui/qs/ui/composable/QuickSettingsScene.kt
index 6ae0efa..d76b19f 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/qs/ui/composable/QuickSettingsScene.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/qs/ui/composable/QuickSettingsScene.kt
@@ -51,7 +51,6 @@
import androidx.compose.runtime.Composable
import androidx.compose.runtime.DisposableEffect
import androidx.compose.runtime.LaunchedEffect
-import androidx.compose.runtime.collectAsState
import androidx.compose.runtime.getValue
import androidx.compose.runtime.remember
import androidx.compose.ui.Alignment
@@ -63,6 +62,7 @@
import androidx.compose.ui.res.colorResource
import androidx.compose.ui.unit.IntOffset
import androidx.compose.ui.unit.dp
+import androidx.lifecycle.compose.collectAsStateWithLifecycle
import com.android.compose.animation.scene.SceneScope
import com.android.compose.animation.scene.TransitionState
import com.android.compose.animation.scene.animateSceneFloatAsState
@@ -163,7 +163,8 @@
) {
val cutoutLocation = LocalDisplayCutout.current.location
- val brightnessMirrorShowing by viewModel.brightnessMirrorViewModel.isShowing.collectAsState()
+ val brightnessMirrorShowing by
+ viewModel.brightnessMirrorViewModel.isShowing.collectAsStateWithLifecycle()
val contentAlpha by
animateFloatAsState(
targetValue = if (brightnessMirrorShowing) 0f else 1f,
@@ -198,10 +199,11 @@
Modifier.displayCutoutPadding()
},
) {
- val isCustomizing by viewModel.qsSceneAdapter.isCustomizing.collectAsState()
- val isCustomizerShowing by viewModel.qsSceneAdapter.isCustomizerShowing.collectAsState()
+ val isCustomizing by viewModel.qsSceneAdapter.isCustomizing.collectAsStateWithLifecycle()
+ val isCustomizerShowing by
+ viewModel.qsSceneAdapter.isCustomizerShowing.collectAsStateWithLifecycle()
val customizingAnimationDuration by
- viewModel.qsSceneAdapter.customizerAnimationDuration.collectAsState()
+ viewModel.qsSceneAdapter.customizerAnimationDuration.collectAsStateWithLifecycle()
val screenHeight = LocalRawScreenHeight.current
BackHandler(
@@ -343,10 +345,10 @@
viewModel.qsSceneAdapter,
{ viewModel.qsSceneAdapter.qsHeight },
isSplitShade = false,
- modifier = Modifier.sysuiResTag("quick_settings_panel")
+ modifier = Modifier
)
- val isMediaVisible by viewModel.isMediaVisible.collectAsState()
+ val isMediaVisible by viewModel.isMediaVisible.collectAsStateWithLifecycle()
MediaCarousel(
isVisible = isMediaVisible,
@@ -362,7 +364,8 @@
isCustomizing = isCustomizing,
customizingAnimationDuration = customizingAnimationDuration,
lifecycleOwner = lifecycleOwner,
- modifier = Modifier.align(Alignment.CenterHorizontally),
+ modifier =
+ Modifier.align(Alignment.CenterHorizontally).sysuiResTag("qs_footer_actions"),
)
}
NotificationScrollingStack(
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 7af9b7b..92b2b4e 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
@@ -23,7 +23,6 @@
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.DisposableEffect
-import androidx.compose.runtime.collectAsState
import androidx.compose.runtime.getValue
import androidx.compose.runtime.remember
import androidx.compose.runtime.rememberCoroutineScope
@@ -34,6 +33,7 @@
import androidx.compose.ui.input.pointer.PointerEventPass
import androidx.compose.ui.input.pointer.motionEventSpy
import androidx.compose.ui.input.pointer.pointerInput
+import androidx.lifecycle.compose.collectAsStateWithLifecycle
import com.android.compose.animation.scene.MutableSceneTransitionLayoutState
import com.android.compose.animation.scene.SceneKey
import com.android.compose.animation.scene.SceneTransitionLayout
@@ -68,8 +68,9 @@
modifier: Modifier = Modifier,
) {
val coroutineScope = rememberCoroutineScope()
- val currentSceneKey: SceneKey by viewModel.currentScene.collectAsState()
- val currentDestinations by viewModel.currentDestinationScenes(coroutineScope).collectAsState()
+ val currentSceneKey: SceneKey by viewModel.currentScene.collectAsStateWithLifecycle()
+ val currentDestinations by
+ viewModel.currentDestinationScenes(coroutineScope).collectAsStateWithLifecycle()
val state: MutableSceneTransitionLayoutState = remember {
MutableSceneTransitionLayoutState(
initialScene = currentSceneKey,
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/shade/ui/composable/OverlayShade.kt b/packages/SystemUI/compose/features/src/com/android/systemui/shade/ui/composable/OverlayShade.kt
index d528736..00ef11d 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/shade/ui/composable/OverlayShade.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/shade/ui/composable/OverlayShade.kt
@@ -30,13 +30,13 @@
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.ReadOnlyComposable
-import androidx.compose.runtime.collectAsState
import androidx.compose.runtime.getValue
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.clip
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.unit.dp
+import androidx.lifecycle.compose.collectAsStateWithLifecycle
import com.android.compose.animation.scene.ElementKey
import com.android.compose.animation.scene.LowestZIndexScenePicker
import com.android.compose.animation.scene.SceneScope
@@ -51,7 +51,7 @@
modifier: Modifier = Modifier,
content: @Composable () -> Unit,
) {
- val backgroundScene by viewModel.backgroundScene.collectAsState()
+ val backgroundScene by viewModel.backgroundScene.collectAsStateWithLifecycle()
Box(modifier) {
if (backgroundScene == Scenes.Lockscreen) {
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/shade/ui/composable/ShadeHeader.kt b/packages/SystemUI/compose/features/src/com/android/systemui/shade/ui/composable/ShadeHeader.kt
index 709a416..ac3e015 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/shade/ui/composable/ShadeHeader.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/shade/ui/composable/ShadeHeader.kt
@@ -35,7 +35,6 @@
import androidx.compose.material3.ColorScheme
import androidx.compose.material3.windowsizeclass.WindowWidthSizeClass
import androidx.compose.runtime.Composable
-import androidx.compose.runtime.collectAsState
import androidx.compose.runtime.derivedStateOf
import androidx.compose.runtime.getValue
import androidx.compose.runtime.remember
@@ -52,6 +51,7 @@
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.max
import androidx.compose.ui.viewinterop.AndroidView
+import androidx.lifecycle.compose.collectAsStateWithLifecycle
import com.android.compose.animation.scene.ElementKey
import com.android.compose.animation.scene.LowestZIndexScenePicker
import com.android.compose.animation.scene.SceneScope
@@ -118,7 +118,7 @@
statusBarIconController: StatusBarIconController,
modifier: Modifier = Modifier,
) {
- val isDisabled by viewModel.isDisabled.collectAsState()
+ val isDisabled by viewModel.isDisabled.collectAsStateWithLifecycle()
if (isDisabled) {
return
}
@@ -138,7 +138,7 @@
}
}
- val isPrivacyChipVisible by viewModel.isPrivacyChipVisible.collectAsState()
+ val isPrivacyChipVisible by viewModel.isPrivacyChipVisible.collectAsStateWithLifecycle()
// This layout assumes it is globally positioned at (0, 0) and is the
// same size as the screen.
@@ -271,7 +271,7 @@
statusBarIconController: StatusBarIconController,
modifier: Modifier = Modifier,
) {
- val isDisabled by viewModel.isDisabled.collectAsState()
+ val isDisabled by viewModel.isDisabled.collectAsStateWithLifecycle()
if (isDisabled) {
return
}
@@ -280,7 +280,7 @@
derivedStateOf { shouldUseExpandedFormat(layoutState.transitionState) }
}
- val isPrivacyChipVisible by viewModel.isPrivacyChipVisible.collectAsState()
+ val isPrivacyChipVisible by viewModel.isPrivacyChipVisible.collectAsStateWithLifecycle()
Box(modifier = modifier.sysuiResTag(ShadeHeader.TestTags.Root)) {
if (isPrivacyChipVisible) {
@@ -435,7 +435,7 @@
modifier: Modifier = Modifier,
) {
Row(modifier = modifier) {
- val subIds by viewModel.mobileSubIds.collectAsState()
+ val subIds by viewModel.mobileSubIds.collectAsStateWithLifecycle()
for (subId in subIds) {
Spacer(modifier = Modifier.width(5.dp))
@@ -472,10 +472,12 @@
val micSlot = stringResource(id = com.android.internal.R.string.status_bar_microphone)
val locationSlot = stringResource(id = com.android.internal.R.string.status_bar_location)
- val isSingleCarrier by viewModel.isSingleCarrier.collectAsState()
- val isPrivacyChipEnabled by viewModel.isPrivacyChipEnabled.collectAsState()
- val isMicCameraIndicationEnabled by viewModel.isMicCameraIndicationEnabled.collectAsState()
- val isLocationIndicationEnabled by viewModel.isLocationIndicationEnabled.collectAsState()
+ val isSingleCarrier by viewModel.isSingleCarrier.collectAsStateWithLifecycle()
+ val isPrivacyChipEnabled by viewModel.isPrivacyChipEnabled.collectAsStateWithLifecycle()
+ val isMicCameraIndicationEnabled by
+ viewModel.isMicCameraIndicationEnabled.collectAsStateWithLifecycle()
+ val isLocationIndicationEnabled by
+ viewModel.isLocationIndicationEnabled.collectAsStateWithLifecycle()
AndroidView(
factory = { context ->
@@ -544,7 +546,7 @@
viewModel: ShadeHeaderViewModel,
modifier: Modifier = Modifier,
) {
- val privacyList by viewModel.privacyItems.collectAsState()
+ val privacyList by viewModel.privacyItems.collectAsStateWithLifecycle()
AndroidView(
factory = { context ->
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/shade/ui/composable/ShadeScene.kt b/packages/SystemUI/compose/features/src/com/android/systemui/shade/ui/composable/ShadeScene.kt
index 42e6fcc..9d689fc 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/shade/ui/composable/ShadeScene.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/shade/ui/composable/ShadeScene.kt
@@ -45,7 +45,6 @@
import androidx.compose.runtime.Composable
import androidx.compose.runtime.DisposableEffect
import androidx.compose.runtime.LaunchedEffect
-import androidx.compose.runtime.collectAsState
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
@@ -58,6 +57,7 @@
import androidx.compose.ui.platform.LocalLifecycleOwner
import androidx.compose.ui.res.colorResource
import androidx.compose.ui.unit.dp
+import androidx.lifecycle.compose.collectAsStateWithLifecycle
import com.android.compose.animation.scene.ElementKey
import com.android.compose.animation.scene.LowestZIndexScenePicker
import com.android.compose.animation.scene.SceneScope
@@ -71,6 +71,7 @@
import com.android.systemui.common.ui.compose.windowinsets.CutoutLocation
import com.android.systemui.common.ui.compose.windowinsets.LocalDisplayCutout
import com.android.systemui.common.ui.compose.windowinsets.LocalScreenCornerRadius
+import com.android.systemui.compose.modifiers.sysuiResTag
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.media.controls.ui.composable.MediaCarousel
import com.android.systemui.media.controls.ui.controller.MediaCarouselController
@@ -177,7 +178,7 @@
modifier: Modifier = Modifier,
shadeSession: SaveableSession,
) {
- val shadeMode by viewModel.shadeMode.collectAsState()
+ val shadeMode by viewModel.shadeMode.collectAsStateWithLifecycle()
when (shadeMode) {
is ShadeMode.Single ->
SingleShade(
@@ -228,8 +229,8 @@
key = QuickSettings.SharedValues.TilesSquishiness,
canOverflow = false
)
- val isClickable by viewModel.isClickable.collectAsState()
- val isMediaVisible by viewModel.isMediaVisible.collectAsState()
+ val isClickable by viewModel.isClickable.collectAsStateWithLifecycle()
+ val isMediaVisible by viewModel.isMediaVisible.collectAsStateWithLifecycle()
val shouldPunchHoleBehindScrim =
layoutState.isTransitioningBetween(Scenes.Gone, Scenes.Shade) ||
@@ -278,7 +279,7 @@
viewModel.qsSceneAdapter,
{ viewModel.qsSceneAdapter.qqsHeight },
isSplitShade = false,
- squishiness = tileSquishiness,
+ squishiness = { tileSquishiness },
)
}
@@ -335,10 +336,11 @@
) {
val screenCornerRadius = LocalScreenCornerRadius.current
- val isCustomizing by viewModel.qsSceneAdapter.isCustomizing.collectAsState()
- val isCustomizerShowing by viewModel.qsSceneAdapter.isCustomizerShowing.collectAsState()
+ val isCustomizing by viewModel.qsSceneAdapter.isCustomizing.collectAsStateWithLifecycle()
+ val isCustomizerShowing by
+ viewModel.qsSceneAdapter.isCustomizerShowing.collectAsStateWithLifecycle()
val customizingAnimationDuration by
- viewModel.qsSceneAdapter.customizerAnimationDuration.collectAsState()
+ viewModel.qsSceneAdapter.customizerAnimationDuration.collectAsStateWithLifecycle()
val lifecycleOwner = LocalLifecycleOwner.current
val footerActionsViewModel =
remember(lifecycleOwner, viewModel) { viewModel.getFooterActionsViewModel(lifecycleOwner) }
@@ -353,13 +355,13 @@
.unfoldTranslationX(
isOnStartSide = true,
)
- .collectAsState(0f)
+ .collectAsStateWithLifecycle(0f)
val unfoldTranslationXForEndSide by
viewModel
.unfoldTranslationX(
isOnStartSide = false,
)
- .collectAsState(0f)
+ .collectAsStateWithLifecycle(0f)
val navBarBottomHeight = WindowInsets.navigationBars.asPaddingValues().calculateBottomPadding()
val bottomPadding by
@@ -383,7 +385,8 @@
}
}
- val brightnessMirrorShowing by viewModel.brightnessMirrorViewModel.isShowing.collectAsState()
+ val brightnessMirrorShowing by
+ viewModel.brightnessMirrorViewModel.isShowing.collectAsStateWithLifecycle()
val contentAlpha by
animateFloatAsState(
targetValue = if (brightnessMirrorShowing) 0f else 1f,
@@ -393,7 +396,7 @@
viewModel.notifications.setAlphaForBrightnessMirror(contentAlpha)
DisposableEffect(Unit) { onDispose { viewModel.notifications.setAlphaForBrightnessMirror(1f) } }
- val isMediaVisible by viewModel.isMediaVisible.collectAsState()
+ val isMediaVisible by viewModel.isMediaVisible.collectAsStateWithLifecycle()
val brightnessMirrorShowingModifier = Modifier.graphicsLayer { alpha = contentAlpha }
@@ -444,6 +447,7 @@
Column(
modifier =
Modifier.fillMaxSize()
+ .sysuiResTag("expanded_qs_scroll_view")
.weight(1f)
.thenIf(!isCustomizerShowing) {
Modifier.verticalNestedScrollToScene()
@@ -464,7 +468,7 @@
heightProvider = { viewModel.qsSceneAdapter.qsHeight },
isSplitShade = true,
modifier = Modifier.fillMaxWidth(),
- squishiness = tileSquishiness,
+ squishiness = { tileSquishiness },
)
}
@@ -482,6 +486,7 @@
lifecycleOwner = lifecycleOwner,
modifier =
Modifier.align(Alignment.CenterHorizontally)
+ .sysuiResTag("qs_footer_actions")
.then(brightnessMirrorShowingModifier),
)
}
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/shade/ui/composable/VariableDayDate.kt b/packages/SystemUI/compose/features/src/com/android/systemui/shade/ui/composable/VariableDayDate.kt
index 5e107c6..931ff56 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/shade/ui/composable/VariableDayDate.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/shade/ui/composable/VariableDayDate.kt
@@ -3,9 +3,9 @@
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
-import androidx.compose.runtime.collectAsState
import androidx.compose.ui.Modifier
import androidx.compose.ui.layout.Layout
+import androidx.lifecycle.compose.collectAsStateWithLifecycle
import com.android.systemui.shade.ui.composable.ShadeHeader.Colors.shadeHeaderText
import com.android.systemui.shade.ui.viewmodel.ShadeHeaderViewModel
@@ -14,8 +14,8 @@
viewModel: ShadeHeaderViewModel,
modifier: Modifier = Modifier,
) {
- val longerText = viewModel.longerDateText.collectAsState()
- val shorterText = viewModel.shorterDateText.collectAsState()
+ val longerText = viewModel.longerDateText.collectAsStateWithLifecycle()
+ val shorterText = viewModel.shorterDateText.collectAsStateWithLifecycle()
Layout(
contents =
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/volume/panel/component/anc/ui/composable/AncButtonComponent.kt b/packages/SystemUI/compose/features/src/com/android/systemui/volume/panel/component/anc/ui/composable/AncButtonComponent.kt
index 79d17ef..3976c61 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/volume/panel/component/anc/ui/composable/AncButtonComponent.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/volume/panel/component/anc/ui/composable/AncButtonComponent.kt
@@ -16,29 +16,40 @@
package com.android.systemui.volume.panel.component.anc.ui.composable
+import android.view.Gravity
import androidx.compose.foundation.basicMarquee
import androidx.compose.foundation.layout.Arrangement
+import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column
-import androidx.compose.foundation.layout.fillMaxWidth
+import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.height
-import androidx.compose.foundation.shape.RoundedCornerShape
+import androidx.compose.foundation.layout.padding
+import androidx.compose.material3.Button
+import androidx.compose.material3.ButtonColors
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
-import androidx.compose.runtime.collectAsState
import androidx.compose.runtime.getValue
+import androidx.compose.runtime.mutableIntStateOf
+import androidx.compose.runtime.remember
+import androidx.compose.runtime.setValue
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
-import androidx.compose.ui.draw.clip
+import androidx.compose.ui.graphics.Color
+import androidx.compose.ui.layout.onGloballyPositioned
+import androidx.compose.ui.platform.LocalConfiguration
+import androidx.compose.ui.platform.LocalDensity
import androidx.compose.ui.res.stringResource
-import androidx.compose.ui.semantics.Role
+import androidx.compose.ui.semantics.LiveRegionMode
import androidx.compose.ui.semantics.clearAndSetSemantics
import androidx.compose.ui.semantics.contentDescription
-import androidx.compose.ui.semantics.role
+import androidx.compose.ui.semantics.liveRegion
import androidx.compose.ui.semantics.semantics
import androidx.compose.ui.unit.dp
+import androidx.lifecycle.compose.collectAsStateWithLifecycle
import com.android.systemui.res.R
import com.android.systemui.volume.panel.component.anc.ui.viewmodel.AncViewModel
+import com.android.systemui.volume.panel.component.popup.ui.composable.VolumePanelPopup
import com.android.systemui.volume.panel.ui.composable.ComposeVolumePanelUiComponent
import com.android.systemui.volume.panel.ui.composable.VolumePanelComposeScope
import javax.inject.Inject
@@ -52,34 +63,47 @@
@Composable
override fun VolumePanelComposeScope.Content(modifier: Modifier) {
- val slice by viewModel.buttonSlice.collectAsState()
+ val slice by viewModel.buttonSlice.collectAsStateWithLifecycle()
val label = stringResource(R.string.volume_panel_noise_control_title)
+ val screenWidth: Float =
+ with(LocalDensity.current) { LocalConfiguration.current.screenWidthDp.dp.toPx() }
+ var gravity by remember { mutableIntStateOf(Gravity.CENTER_HORIZONTAL) }
val isClickable = viewModel.isClickable(slice)
- val onClick =
- if (isClickable) {
- { ancPopup.show(null) }
- } else {
- null
- }
Column(
- modifier = modifier,
+ modifier =
+ modifier.onGloballyPositioned {
+ gravity = VolumePanelPopup.calculateGravity(it, screenWidth)
+ },
verticalArrangement = Arrangement.spacedBy(12.dp),
horizontalAlignment = Alignment.CenterHorizontally,
) {
- SliceAndroidView(
- modifier =
- Modifier.height(64.dp)
- .fillMaxWidth()
- .semantics {
- role = Role.Button
+ Box(
+ modifier = Modifier.height(64.dp),
+ ) {
+ SliceAndroidView(
+ modifier = modifier.fillMaxSize(),
+ slice = slice,
+ onWidthChanged = viewModel::onButtonSliceWidthChanged,
+ enableAccessibility = false,
+ )
+ Button(
+ modifier =
+ modifier.fillMaxSize().padding(8.dp).semantics {
+ liveRegion = LiveRegionMode.Polite
contentDescription = label
- }
- .clip(RoundedCornerShape(28.dp)),
- slice = slice,
- isEnabled = onClick != null,
- onWidthChanged = viewModel::onButtonSliceWidthChanged,
- onClick = onClick,
- )
+ },
+ enabled = isClickable,
+ onClick = { with(ancPopup) { show(null, gravity) } },
+ colors =
+ ButtonColors(
+ contentColor = Color.Transparent,
+ containerColor = Color.Transparent,
+ disabledContentColor = Color.Transparent,
+ disabledContainerColor = Color.Transparent,
+ )
+ ) {}
+ }
+
Text(
modifier = Modifier.clearAndSetSemantics {}.basicMarquee(),
text = label,
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/volume/panel/component/anc/ui/composable/AncPopup.kt b/packages/SystemUI/compose/features/src/com/android/systemui/volume/panel/component/anc/ui/composable/AncPopup.kt
index e1ee01e..15df1be 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/volume/panel/component/anc/ui/composable/AncPopup.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/volume/panel/component/anc/ui/composable/AncPopup.kt
@@ -16,17 +16,18 @@
package com.android.systemui.volume.panel.component.anc.ui.composable
+import android.view.Gravity
import androidx.compose.foundation.basicMarquee
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.SideEffect
-import androidx.compose.runtime.collectAsState
import androidx.compose.runtime.getValue
import androidx.compose.ui.Modifier
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.text.style.TextAlign
+import androidx.lifecycle.compose.collectAsStateWithLifecycle
import androidx.slice.Slice
import com.android.internal.logging.UiEventLogger
import com.android.systemui.animation.Expandable
@@ -47,9 +48,10 @@
) {
/** Shows a popup with the [expandable] animation. */
- fun show(expandable: Expandable?) {
+ fun show(expandable: Expandable?, horizontalGravity: Int) {
uiEventLogger.log(VolumePanelUiEvent.VOLUME_PANEL_ANC_POPUP_SHOWN)
- volumePanelPopup.show(expandable, { Title() }, { Content(it) })
+ val gravity = horizontalGravity or Gravity.BOTTOM
+ volumePanelPopup.show(expandable, gravity, { Title() }, { Content(it) })
}
@Composable
@@ -65,14 +67,14 @@
@Composable
private fun Content(dialog: SystemUIDialog) {
- val isAvailable by viewModel.isAvailable.collectAsState(true)
+ val isAvailable by viewModel.isAvailable.collectAsStateWithLifecycle(true)
if (!isAvailable) {
SideEffect { dialog.dismiss() }
return
}
- val slice by viewModel.popupSlice.collectAsState()
+ val slice by viewModel.popupSlice.collectAsStateWithLifecycle()
SliceAndroidView(
modifier = Modifier.fillMaxWidth(),
slice = slice,
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/volume/panel/component/anc/ui/composable/SliceAndroidView.kt b/packages/SystemUI/compose/features/src/com/android/systemui/volume/panel/component/anc/ui/composable/SliceAndroidView.kt
index fc5d212..23d50c5 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/volume/panel/component/anc/ui/composable/SliceAndroidView.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/volume/panel/component/anc/ui/composable/SliceAndroidView.kt
@@ -16,11 +16,12 @@
package com.android.systemui.volume.panel.component.anc.ui.composable
-import android.annotation.SuppressLint
import android.content.Context
+import android.os.Bundle
import android.view.ContextThemeWrapper
-import android.view.MotionEvent
import android.view.View
+import android.view.accessibility.AccessibilityEvent
+import android.view.accessibility.AccessibilityNodeInfo
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.ui.viewinterop.AndroidView
@@ -32,14 +33,13 @@
fun SliceAndroidView(
slice: Slice?,
modifier: Modifier = Modifier,
- isEnabled: Boolean = true,
onWidthChanged: ((Int) -> Unit)? = null,
- onClick: (() -> Unit)? = null,
+ enableAccessibility: Boolean = true,
) {
AndroidView(
modifier = modifier,
factory = { context: Context ->
- ClickableSliceView(
+ ComposeSliceView(
ContextThemeWrapper(context, R.style.Widget_SliceView_VolumePanel),
)
.apply {
@@ -47,17 +47,18 @@
isScrollable = false
importantForAccessibility = View.IMPORTANT_FOR_ACCESSIBILITY_NO
setShowTitleItems(true)
- if (onWidthChanged != null) {
- addOnLayoutChangeListener(OnWidthChangedLayoutListener(onWidthChanged))
- }
}
},
- update = { sliceView: ClickableSliceView ->
+ update = { sliceView: ComposeSliceView ->
sliceView.slice = slice
- sliceView.onClick = onClick
- sliceView.isEnabled = isEnabled
- sliceView.isClickable = isEnabled
- }
+ sliceView.layoutListener = onWidthChanged?.let(::OnWidthChangedLayoutListener)
+ sliceView.enableAccessibility = enableAccessibility
+ },
+ onRelease = { sliceView: ComposeSliceView ->
+ sliceView.layoutListener = null
+ sliceView.slice = null
+ sliceView.enableAccessibility = true
+ },
)
}
@@ -83,26 +84,39 @@
}
}
-/**
- * [SliceView] that prioritises [onClick] when its clicked instead of passing the event to the slice
- * first.
- */
-@SuppressLint("ViewConstructor") // only used in this class
-private class ClickableSliceView(context: Context) : SliceView(context) {
+private class ComposeSliceView(context: Context) : SliceView(context) {
- var onClick: (() -> Unit)? = null
+ var enableAccessibility: Boolean = true
+ var layoutListener: OnLayoutChangeListener? = null
+ set(value) {
+ field?.let { removeOnLayoutChangeListener(it) }
+ field = value
+ field?.let { addOnLayoutChangeListener(it) }
+ }
- init {
- if (onClick != null) {
- setOnClickListener {}
+ override fun onInitializeAccessibilityNodeInfo(info: AccessibilityNodeInfo?) {
+ if (enableAccessibility) {
+ super.onInitializeAccessibilityNodeInfo(info)
}
}
- override fun onInterceptTouchEvent(ev: MotionEvent?): Boolean {
- return (isSliceViewClickable && onClick != null) || super.onInterceptTouchEvent(ev)
+ override fun onInitializeAccessibilityEvent(event: AccessibilityEvent?) {
+ if (enableAccessibility) {
+ super.onInitializeAccessibilityEvent(event)
+ }
}
- override fun onClick(v: View?) {
- onClick?.takeIf { isSliceViewClickable }?.let { it() } ?: super.onClick(v)
+ override fun performAccessibilityAction(action: Int, arguments: Bundle?): Boolean {
+ return if (enableAccessibility) {
+ super.performAccessibilityAction(action, arguments)
+ } else {
+ false
+ }
+ }
+
+ override fun addChildrenForAccessibility(outChildren: ArrayList<View>?) {
+ if (enableAccessibility) {
+ super.addChildrenForAccessibility(outChildren)
+ }
}
}
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/volume/panel/component/button/ui/composable/ButtonComponent.kt b/packages/SystemUI/compose/features/src/com/android/systemui/volume/panel/component/button/ui/composable/ButtonComponent.kt
index 0893b9d..e1ae80f 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/volume/panel/component/button/ui/composable/ButtonComponent.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/volume/panel/component/button/ui/composable/ButtonComponent.kt
@@ -16,6 +16,7 @@
package com.android.systemui.volume.panel.component.button.ui.composable
+import android.view.Gravity
import androidx.compose.foundation.basicMarquee
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Box
@@ -27,20 +28,27 @@
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
-import androidx.compose.runtime.collectAsState
import androidx.compose.runtime.getValue
+import androidx.compose.runtime.mutableIntStateOf
+import androidx.compose.runtime.remember
+import androidx.compose.runtime.setValue
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
+import androidx.compose.ui.layout.onGloballyPositioned
+import androidx.compose.ui.platform.LocalConfiguration
+import androidx.compose.ui.platform.LocalDensity
import androidx.compose.ui.semantics.Role
import androidx.compose.ui.semantics.clearAndSetSemantics
import androidx.compose.ui.semantics.contentDescription
import androidx.compose.ui.semantics.role
import androidx.compose.ui.semantics.semantics
import androidx.compose.ui.unit.dp
+import androidx.lifecycle.compose.collectAsStateWithLifecycle
import com.android.compose.animation.Expandable
import com.android.systemui.animation.Expandable
import com.android.systemui.common.ui.compose.Icon
import com.android.systemui.volume.panel.component.button.ui.viewmodel.ButtonViewModel
+import com.android.systemui.volume.panel.component.popup.ui.composable.VolumePanelPopup.Companion.calculateGravity
import com.android.systemui.volume.panel.ui.composable.ComposeVolumePanelUiComponent
import com.android.systemui.volume.panel.ui.composable.VolumePanelComposeScope
import kotlinx.coroutines.flow.StateFlow
@@ -48,17 +56,22 @@
/** [ComposeVolumePanelUiComponent] implementing a clickable button from a bottom row. */
class ButtonComponent(
private val viewModelFlow: StateFlow<ButtonViewModel?>,
- private val onClick: (Expandable) -> Unit
+ private val onClick: (expandable: Expandable, horizontalGravity: Int) -> Unit
) : ComposeVolumePanelUiComponent {
@Composable
override fun VolumePanelComposeScope.Content(modifier: Modifier) {
- val viewModelByState by viewModelFlow.collectAsState()
+ val viewModelByState by viewModelFlow.collectAsStateWithLifecycle()
val viewModel = viewModelByState ?: return
val label = viewModel.label.toString()
+ val screenWidth: Float =
+ with(LocalDensity.current) { LocalConfiguration.current.screenWidthDp.dp.toPx() }
+ var gravity by remember { mutableIntStateOf(Gravity.CENTER_HORIZONTAL) }
+
Column(
- modifier = modifier,
+ modifier =
+ modifier.onGloballyPositioned { gravity = calculateGravity(it, screenWidth) },
verticalArrangement = Arrangement.spacedBy(12.dp),
horizontalAlignment = Alignment.CenterHorizontally,
) {
@@ -82,7 +95,7 @@
} else {
MaterialTheme.colorScheme.onSurface
},
- onClick = onClick,
+ onClick = { onClick(it, gravity) },
) {
Box(modifier = Modifier.fillMaxSize(), contentAlignment = Alignment.Center) {
Icon(modifier = Modifier.size(24.dp), icon = viewModel.icon)
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/volume/panel/component/button/ui/composable/ToggleButtonComponent.kt b/packages/SystemUI/compose/features/src/com/android/systemui/volume/panel/component/button/ui/composable/ToggleButtonComponent.kt
index 12debbc..1b821d3 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/volume/panel/component/button/ui/composable/ToggleButtonComponent.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/volume/panel/component/button/ui/composable/ToggleButtonComponent.kt
@@ -29,7 +29,6 @@
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
-import androidx.compose.runtime.collectAsState
import androidx.compose.runtime.getValue
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
@@ -42,6 +41,7 @@
import androidx.compose.ui.semantics.toggleableState
import androidx.compose.ui.state.ToggleableState
import androidx.compose.ui.unit.dp
+import androidx.lifecycle.compose.collectAsStateWithLifecycle
import com.android.systemui.common.ui.compose.Icon
import com.android.systemui.volume.panel.component.button.ui.viewmodel.ButtonViewModel
import com.android.systemui.volume.panel.ui.composable.ComposeVolumePanelUiComponent
@@ -56,7 +56,7 @@
@Composable
override fun VolumePanelComposeScope.Content(modifier: Modifier) {
- val viewModelByState by viewModelFlow.collectAsState()
+ val viewModelByState by viewModelFlow.collectAsStateWithLifecycle()
val viewModel = viewModelByState ?: return
val label = viewModel.label.toString()
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/volume/panel/component/mediaoutput/ui/composable/MediaOutputComponent.kt b/packages/SystemUI/compose/features/src/com/android/systemui/volume/panel/component/mediaoutput/ui/composable/MediaOutputComponent.kt
index ded63a1..237bbfd 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/volume/panel/component/mediaoutput/ui/composable/MediaOutputComponent.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/volume/panel/component/mediaoutput/ui/composable/MediaOutputComponent.kt
@@ -45,7 +45,6 @@
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
-import androidx.compose.runtime.collectAsState
import androidx.compose.runtime.getValue
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
@@ -55,6 +54,7 @@
import androidx.compose.ui.semantics.onClick
import androidx.compose.ui.semantics.semantics
import androidx.compose.ui.unit.dp
+import androidx.lifecycle.compose.collectAsStateWithLifecycle
import com.android.compose.animation.Expandable
import com.android.systemui.common.ui.compose.Icon
import com.android.systemui.common.ui.compose.toColor
@@ -77,9 +77,9 @@
@Composable
override fun VolumePanelComposeScope.Content(modifier: Modifier) {
val connectedDeviceViewModel: ConnectedDeviceViewModel? by
- viewModel.connectedDeviceViewModel.collectAsState()
+ viewModel.connectedDeviceViewModel.collectAsStateWithLifecycle()
val deviceIconViewModel: DeviceIconViewModel? by
- viewModel.deviceIconViewModel.collectAsState()
+ viewModel.deviceIconViewModel.collectAsStateWithLifecycle()
val clickLabel = stringResource(R.string.volume_panel_enter_media_output_settings)
Expandable(
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/volume/panel/component/popup/ui/composable/VolumePanelPopup.kt b/packages/SystemUI/compose/features/src/com/android/systemui/volume/panel/component/popup/ui/composable/VolumePanelPopup.kt
index bb4e957..3b1bf2a 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/volume/panel/component/popup/ui/composable/VolumePanelPopup.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/volume/panel/component/popup/ui/composable/VolumePanelPopup.kt
@@ -17,6 +17,7 @@
package com.android.systemui.volume.panel.component.popup.ui.composable
import android.view.Gravity
+import androidx.annotation.GravityInt
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column
@@ -31,6 +32,8 @@
import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
+import androidx.compose.ui.layout.LayoutCoordinates
+import androidx.compose.ui.layout.boundsInRoot
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.semantics.paneTitle
@@ -60,13 +63,14 @@
*/
fun show(
expandable: Expandable?,
+ @GravityInt gravity: Int,
title: @Composable (SystemUIDialog) -> Unit,
content: @Composable (SystemUIDialog) -> Unit,
) {
val dialog =
dialogFactory.create(
theme = R.style.Theme_VolumePanel_Popup,
- dialogGravity = Gravity.BOTTOM,
+ dialogGravity = gravity,
) {
PopupComposable(it, title, content)
}
@@ -122,4 +126,23 @@
}
}
}
+
+ companion object {
+
+ /**
+ * Returns absolute ([Gravity.LEFT], [Gravity.RIGHT] or [Gravity.CENTER_HORIZONTAL])
+ * [GravityInt] for the popup based on the [coordinates] global position relative to the
+ * [screenWidthPx].
+ */
+ @GravityInt
+ fun calculateGravity(coordinates: LayoutCoordinates, screenWidthPx: Float): Int {
+ val bottomCenter: Float = coordinates.boundsInRoot().bottomCenter.x
+ val rootBottomCenter: Float = screenWidthPx / 2
+ return when {
+ bottomCenter < rootBottomCenter -> Gravity.LEFT
+ bottomCenter > rootBottomCenter -> Gravity.RIGHT
+ else -> Gravity.CENTER_HORIZONTAL
+ }
+ }
+ }
}
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/volume/panel/component/spatialaudio/ui/composable/SpatialAudioPopup.kt b/packages/SystemUI/compose/features/src/com/android/systemui/volume/panel/component/spatialaudio/ui/composable/SpatialAudioPopup.kt
index 12d2bc2..9891b5b 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/volume/panel/component/spatialaudio/ui/composable/SpatialAudioPopup.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/volume/panel/component/spatialaudio/ui/composable/SpatialAudioPopup.kt
@@ -16,17 +16,18 @@
package com.android.systemui.volume.panel.component.spatialaudio.ui.composable
+import android.view.Gravity
import androidx.compose.foundation.basicMarquee
import androidx.compose.material3.LocalContentColor
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.SideEffect
-import androidx.compose.runtime.collectAsState
import androidx.compose.runtime.getValue
import androidx.compose.ui.Modifier
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.text.style.TextAlign
+import androidx.lifecycle.compose.collectAsStateWithLifecycle
import com.android.internal.logging.UiEventLogger
import com.android.systemui.animation.Expandable
import com.android.systemui.common.ui.compose.Icon
@@ -47,14 +48,15 @@
) {
/** Shows a popup with the [expandable] animation. */
- fun show(expandable: Expandable) {
+ fun show(expandable: Expandable, horizontalGravity: Int) {
uiEventLogger.logWithPosition(
VolumePanelUiEvent.VOLUME_PANEL_SPATIAL_AUDIO_POP_UP_SHOWN,
0,
null,
viewModel.spatialAudioButtons.value.indexOfFirst { it.button.isActive }
)
- volumePanelPopup.show(expandable, { Title() }, { Content(it) })
+ val gravity = horizontalGravity or Gravity.BOTTOM
+ volumePanelPopup.show(expandable, gravity, { Title() }, { Content(it) })
}
@Composable
@@ -70,14 +72,14 @@
@Composable
private fun Content(dialog: SystemUIDialog) {
- val isAvailable by viewModel.isAvailable.collectAsState()
+ val isAvailable by viewModel.isAvailable.collectAsStateWithLifecycle()
if (!isAvailable) {
SideEffect { dialog.dismiss() }
return
}
- val enabledModelStates by viewModel.spatialAudioButtons.collectAsState()
+ val enabledModelStates by viewModel.spatialAudioButtons.collectAsStateWithLifecycle()
if (enabledModelStates.isEmpty()) {
return
}
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/volume/panel/component/volume/ui/composable/ColumnVolumeSliders.kt b/packages/SystemUI/compose/features/src/com/android/systemui/volume/panel/component/volume/ui/composable/ColumnVolumeSliders.kt
index 1def7fe..072e91a 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/volume/panel/component/volume/ui/composable/ColumnVolumeSliders.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/volume/panel/component/volume/ui/composable/ColumnVolumeSliders.kt
@@ -40,7 +40,6 @@
import androidx.compose.material3.IconButtonDefaults
import androidx.compose.runtime.Composable
import androidx.compose.runtime.State
-import androidx.compose.runtime.collectAsState
import androidx.compose.runtime.getValue
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
@@ -52,6 +51,7 @@
import androidx.compose.ui.semantics.stateDescription
import androidx.compose.ui.unit.Dp
import androidx.compose.ui.unit.dp
+import androidx.lifecycle.compose.collectAsStateWithLifecycle
import com.android.compose.PlatformSliderColors
import com.android.compose.modifiers.padding
import com.android.systemui.res.R
@@ -84,7 +84,7 @@
modifier = Modifier.fillMaxWidth(),
) {
val sliderViewModel: SliderViewModel = viewModels.first()
- val sliderState by viewModels.first().slider.collectAsState()
+ val sliderState by viewModels.first().slider.collectAsStateWithLifecycle()
val sliderPadding by topSliderPadding(isExpandable)
VolumeSlider(
@@ -119,7 +119,7 @@
Column {
for (index in 1..viewModels.lastIndex) {
val sliderViewModel: SliderViewModel = viewModels[index]
- val sliderState by sliderViewModel.slider.collectAsState()
+ val sliderState by sliderViewModel.slider.collectAsStateWithLifecycle()
transition.AnimatedVisibility(
modifier = Modifier.padding(top = 16.dp),
visible = { it || !isExpandable },
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/volume/panel/component/volume/ui/composable/GridVolumeSliders.kt b/packages/SystemUI/compose/features/src/com/android/systemui/volume/panel/component/volume/ui/composable/GridVolumeSliders.kt
index bb17499..d15430f 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/volume/panel/component/volume/ui/composable/GridVolumeSliders.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/volume/panel/component/volume/ui/composable/GridVolumeSliders.kt
@@ -18,9 +18,9 @@
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.runtime.Composable
-import androidx.compose.runtime.collectAsState
import androidx.compose.ui.Modifier
import androidx.compose.ui.unit.dp
+import androidx.lifecycle.compose.collectAsStateWithLifecycle
import com.android.compose.PlatformSliderColors
import com.android.compose.grid.VerticalGrid
import com.android.systemui.volume.panel.component.volume.slider.ui.viewmodel.SliderViewModel
@@ -39,7 +39,7 @@
horizontalSpacing = 24.dp,
) {
for (sliderViewModel in viewModels) {
- val sliderState = sliderViewModel.slider.collectAsState().value
+ val sliderState = sliderViewModel.slider.collectAsStateWithLifecycle().value
VolumeSlider(
modifier = Modifier.fillMaxWidth(),
state = sliderState,
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/volume/panel/component/volume/ui/composable/VolumeSlider.kt b/packages/SystemUI/compose/features/src/com/android/systemui/volume/panel/component/volume/ui/composable/VolumeSlider.kt
index cb3867f..271eb96 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/volume/panel/component/volume/ui/composable/VolumeSlider.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/volume/panel/component/volume/ui/composable/VolumeSlider.kt
@@ -78,12 +78,7 @@
}
state.a11yStateDescription?.let { stateDescription = it }
- ?: run {
- // provide a not animated value to the a11y because it fails to announce
- // the settled value when it changes rapidly.
- progressBarRangeInfo =
- ProgressBarRangeInfo(state.value, state.valueRange)
- }
+ progressBarRangeInfo = ProgressBarRangeInfo(state.value, state.valueRange)
} else {
disabled()
contentDescription =
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/volume/panel/component/volume/ui/composable/VolumeSlidersComponent.kt b/packages/SystemUI/compose/features/src/com/android/systemui/volume/panel/component/volume/ui/composable/VolumeSlidersComponent.kt
index 79056b2..770c5d5 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/volume/panel/component/volume/ui/composable/VolumeSlidersComponent.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/volume/panel/component/volume/ui/composable/VolumeSlidersComponent.kt
@@ -18,9 +18,9 @@
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.runtime.Composable
-import androidx.compose.runtime.collectAsState
import androidx.compose.runtime.getValue
import androidx.compose.ui.Modifier
+import androidx.lifecycle.compose.collectAsStateWithLifecycle
import com.android.compose.PlatformSliderDefaults
import com.android.systemui.volume.panel.component.volume.slider.ui.viewmodel.SliderViewModel
import com.android.systemui.volume.panel.component.volume.ui.viewmodel.AudioVolumeComponentViewModel
@@ -38,7 +38,8 @@
@Composable
override fun VolumePanelComposeScope.Content(modifier: Modifier) {
- val sliderViewModels: List<SliderViewModel> by viewModel.sliderViewModels.collectAsState()
+ val sliderViewModels: List<SliderViewModel> by
+ viewModel.sliderViewModels.collectAsStateWithLifecycle()
if (sliderViewModels.isEmpty()) {
return
}
@@ -52,7 +53,7 @@
val expandableViewModel: SlidersExpandableViewModel by
viewModel
.isExpandable(isPortrait)
- .collectAsState(SlidersExpandableViewModel.Unavailable)
+ .collectAsStateWithLifecycle(SlidersExpandableViewModel.Unavailable)
if (expandableViewModel is SlidersExpandableViewModel.Unavailable) {
return
}
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/volume/panel/ui/composable/VolumePanelRoot.kt b/packages/SystemUI/compose/features/src/com/android/systemui/volume/panel/ui/composable/VolumePanelRoot.kt
index a602e25..83b8158 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/volume/panel/ui/composable/VolumePanelRoot.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/volume/panel/ui/composable/VolumePanelRoot.kt
@@ -24,7 +24,6 @@
import androidx.compose.foundation.layout.padding
import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
-import androidx.compose.runtime.collectAsState
import androidx.compose.runtime.getValue
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
@@ -32,6 +31,7 @@
import androidx.compose.ui.semantics.paneTitle
import androidx.compose.ui.semantics.semantics
import androidx.compose.ui.unit.dp
+import androidx.lifecycle.compose.collectAsStateWithLifecycle
import com.android.systemui.res.R
import com.android.systemui.volume.panel.ui.layout.ComponentsLayout
import com.android.systemui.volume.panel.ui.viewmodel.VolumePanelState
@@ -54,8 +54,8 @@
}
val accessibilityTitle = stringResource(R.string.accessibility_volume_settings)
- val state: VolumePanelState by viewModel.volumePanelState.collectAsState()
- val components by viewModel.componentsLayout.collectAsState(null)
+ val state: VolumePanelState by viewModel.volumePanelState.collectAsStateWithLifecycle()
+ val components by viewModel.componentsLayout.collectAsStateWithLifecycle(null)
with(VolumePanelComposeScope(state)) {
components?.let { componentsState ->
diff --git a/packages/SystemUI/flag_check.py b/packages/SystemUI/flag_check.py
index bac3553..95a25c5 100755
--- a/packages/SystemUI/flag_check.py
+++ b/packages/SystemUI/flag_check.py
@@ -12,19 +12,20 @@
%s
The Flag: stanza is regex matched and should describe whether your change is behind a flag or flags.
-
-As a CL author, you'll have a consistent place to describe the risk of the proposed change by explicitly calling out the name of the
-flag in addition to its state (ENABLED|DISABLED|DEVELOPMENT|STAGING|TEAMFOOD|TRUNKFOOD|NEXTFOOD).
+As a CL author, you'll have a consistent place to describe the risk of the proposed change by explicitly calling out the name of the flag.
+For legacy flags use EXEMPT with your flag name.
Some examples below:
-Flag: NONE
-Flag: NA
-Flag: LEGACY ENABLE_ONE_SEARCH DISABLED
-Flag: ACONFIG com.android.launcher3.enable_twoline_allapps DEVELOPMENT
-Flag: ACONFIG com.android.launcher3.enable_twoline_allapps TRUNKFOOD
+Flag: NONE Repohook Update
+Flag: TEST_ONLY
+Flag: EXEMPT resource only update
+Flag: EXEMPT bugfix
+Flag: EXEMPT refactor
+Flag: com.android.launcher3.enable_twoline_allapps
+Flag: com.google.android.apps.nexuslauncher.zero_state_web_data_loader
-Check the git history for more examples. It's a regex matched field.
+Check the git history for more examples. It's a regex matched field. See go/android-flag-directive for more details on various formats.
"""
def main():
@@ -63,28 +64,31 @@
return
field = 'Flag'
- none = '(NONE|NA|N\/A)' # NONE|NA|N/A
+ none = 'NONE'
+ testOnly = 'TEST_ONLY'
+ docsOnly = 'DOCS_ONLY'
+ exempt = 'EXEMPT'
+ justification = '<justification>'
- typeExpression = '\s*(LEGACY|ACONFIG)' # [type:LEGACY|ACONFIG]
-
- # legacyFlagName contains only uppercase alphabets with '_' - Ex: ENABLE_ONE_SEARCH
- # Aconfig Flag name format = "packageName"."flagName"
+ # Aconfig Flag name format = <packageName>.<flagName>
# package name - Contains only lowercase alphabets + digits + '.' - Ex: com.android.launcher3
- # For now alphabets, digits, "_", "." characters are allowed in flag name and not adding stricter format check.
+ # For now alphabets, digits, "_", "." characters are allowed in flag name.
+ # Checks if there is "one dot" between packageName and flagName and not adding stricter format check
#common_typos_disable
- flagName = '([a-zA-z0-9_.])+'
+ flagName = '([a-zA-Z0-9.]+)([.]+)([a-zA-Z0-9_.]+)'
- #[state:ENABLED|DISABLED|DEVELOPMENT|TEAM*(TEAMFOOD)|STAGING|TRUNK*(TRUNK_STAGING, TRUNK_FOOD)|NEXT*(NEXTFOOD)]
- stateExpression = '\s*(ENABLED|DISABLED|DEVELOPMENT|TEAM[a-zA-z]*|STAGING|TRUNK[a-zA-z]*|NEXT[a-zA-z]*)'
+ # None and Exempt needs justification
+ exemptRegex = fr'{exempt}\s*[a-zA-Z]+'
+ noneRegex = fr'{none}\s*[a-zA-Z]+'
#common_typos_enable
- readableRegexMsg = '\n\tFlag: (NONE|NA)\n\tFlag: LEGACY|ACONFIG FlagName|packageName.flagName ENABLED|DISABLED|DEVELOPMENT|TEAMFOOD|STAGING|TRUNKFOOD|NEXTFOOD'
+ readableRegexMsg = '\n\tFlag: '+none+' '+justification+'\n\tFlag: <packageName>.<flagName>\n\tFlag: ' +exempt+' '+justification+'\n\tFlag: '+testOnly+'\n\tFlag: '+docsOnly
flagRegex = fr'^{field}: .*$'
check_flag = re.compile(flagRegex) #Flag:
# Ignore case for flag name format.
- flagNameRegex = fr'(?i)^{field}:\s*({none}|{typeExpression}\s*{flagName}\s*{stateExpression})\s*'
+ flagNameRegex = fr'(?i)^{field}:\s*({noneRegex}|{flagName}|{testOnly}|{docsOnly}|{exemptRegex})\s*'
check_flagName = re.compile(flagNameRegex) #Flag: <flag name format>
flagError = False
diff --git a/packages/SystemUI/monet/src/com/android/systemui/monet/ColorScheme.kt b/packages/SystemUI/monet/src/com/android/systemui/monet/ColorScheme.kt
index 47a00f4..624f18d 100644
--- a/packages/SystemUI/monet/src/com/android/systemui/monet/ColorScheme.kt
+++ b/packages/SystemUI/monet/src/com/android/systemui/monet/ColorScheme.kt
@@ -133,7 +133,7 @@
@ColorInt seed: Int,
darkTheme: Boolean,
style: Style
- ) : this(seed, darkTheme, style, 0.5)
+ ) : this(seed, darkTheme, style, 0.0)
@JvmOverloads
constructor(
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/accessibility/data/repository/NightDisplayRepositoryTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/accessibility/data/repository/NightDisplayRepositoryTest.kt
index ca824cb..5757f67 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/accessibility/data/repository/NightDisplayRepositoryTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/accessibility/data/repository/NightDisplayRepositoryTest.kt
@@ -42,7 +42,6 @@
import kotlinx.coroutines.test.TestScope
import kotlinx.coroutines.test.runCurrent
import kotlinx.coroutines.test.runTest
-import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
import org.mockito.ArgumentMatchers
@@ -90,11 +89,6 @@
locationController,
)
- @Before
- fun setup() {
- enrollInForcedNightDisplayAutoMode(INITIALLY_FORCE_AUTO_MODE, testUser)
- }
-
@Test
fun nightDisplayState_matchesAutoMode() =
scope.runTest {
@@ -126,6 +120,8 @@
@Test
fun nightDisplayState_matchesIsNightDisplayActivated() =
scope.runTest {
+ enrollInForcedNightDisplayAutoMode(INITIALLY_FORCE_AUTO_MODE, testUser)
+
val callbackCaptor = argumentCaptor<NightDisplayListener.Callback>()
val lastState by collectLastValue(underTest.nightDisplayState(testUser))
@@ -148,6 +144,7 @@
scope.runTest {
whenever(colorDisplayManager.nightDisplayAutoMode)
.thenReturn(ColorDisplayManager.AUTO_MODE_CUSTOM_TIME)
+ enrollInForcedNightDisplayAutoMode(INITIALLY_FORCE_AUTO_MODE, testUser)
val lastState by collectLastValue(underTest.nightDisplayState(testUser))
runCurrent()
@@ -160,6 +157,7 @@
scope.runTest {
whenever(colorDisplayManager.nightDisplayAutoMode)
.thenReturn(ColorDisplayManager.AUTO_MODE_TWILIGHT)
+ enrollInForcedNightDisplayAutoMode(INITIALLY_FORCE_AUTO_MODE, testUser)
val lastState by collectLastValue(underTest.nightDisplayState(testUser))
runCurrent()
@@ -167,6 +165,24 @@
assertThat(lastState!!.autoMode).isEqualTo(ColorDisplayManager.AUTO_MODE_TWILIGHT)
}
+ /**
+ * When the value of the raw auto mode is missing the call to nightDisplayState should not crash
+ */
+ @Test
+ fun nightDisplayState_whenAutoModeSettingIsNotInitialized_loadsDataWithoutException() =
+ scope.runTest {
+ // only auto mode_available is set, and the raw auto_mode has nothing set
+ globalSettings.putString(
+ Settings.Global.NIGHT_DISPLAY_FORCED_AUTO_MODE_AVAILABLE,
+ NIGHT_DISPLAY_FORCED_AUTO_MODE_AVAILABLE
+ )
+
+ val lastState by collectLastValue(underTest.nightDisplayState(testUser))
+ runCurrent()
+
+ assertThat(lastState!!.shouldForceAutoMode).isTrue()
+ }
+
@Test
fun nightDisplayState_matchesForceAutoMode() =
scope.runTest {
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/ambient/touch/ShadeTouchHandlerTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/ambient/touch/ShadeTouchHandlerTest.java
index 11a4241..27bffd0 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/ambient/touch/ShadeTouchHandlerTest.java
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/ambient/touch/ShadeTouchHandlerTest.java
@@ -18,10 +18,8 @@
import static com.google.common.truth.Truth.assertThat;
-import static org.mockito.ArgumentMatchers.any;
-import static org.mockito.Mockito.never;
-import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
import android.view.GestureDetector;
import android.view.MotionEvent;
@@ -30,6 +28,7 @@
import androidx.test.filters.SmallTest;
import com.android.systemui.SysuiTestCase;
+import com.android.systemui.shade.ShadeViewController;
import com.android.systemui.shared.system.InputChannelCompat;
import com.android.systemui.statusbar.phone.CentralSurfaces;
@@ -37,7 +36,6 @@
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.ArgumentCaptor;
-import org.mockito.Captor;
import org.mockito.Mock;
import org.mockito.Mockito;
import org.mockito.MockitoAnnotations;
@@ -51,89 +49,66 @@
CentralSurfaces mCentralSurfaces;
@Mock
+ ShadeViewController mShadeViewController;
+
+ @Mock
TouchHandler.TouchSession mTouchSession;
ShadeTouchHandler mTouchHandler;
- @Captor
- ArgumentCaptor<GestureDetector.OnGestureListener> mGestureListenerCaptor;
- @Captor
- ArgumentCaptor<InputChannelCompat.InputEventListener> mInputListenerCaptor;
-
private static final int TOUCH_HEIGHT = 20;
@Before
public void setup() {
MockitoAnnotations.initMocks(this);
-
- mTouchHandler = new ShadeTouchHandler(Optional.of(mCentralSurfaces), TOUCH_HEIGHT);
- }
-
- // Verifies that a swipe down in the gesture region is captured by the shade touch handler.
- @Test
- public void testSwipeDown_captured() {
- final boolean captured = swipe(Direction.DOWN);
-
- assertThat(captured).isTrue();
- }
-
- // Verifies that a swipe in the upward direction is not catpured.
- @Test
- public void testSwipeUp_notCaptured() {
- final boolean captured = swipe(Direction.UP);
-
- // Motion events not captured as the swipe is going in the wrong direction.
- assertThat(captured).isFalse();
- }
-
- // Verifies that a swipe down forwards captured touches to the shade window for handling.
- @Test
- public void testSwipeDown_sentToShadeWindow() {
- swipe(Direction.DOWN);
-
- // Both motion events are sent for the shade window to process.
- verify(mCentralSurfaces, times(2)).handleExternalShadeWindowTouch(any());
- }
-
- // Verifies that a swipe down is not forwarded to the shade window.
- @Test
- public void testSwipeUp_touchesNotSent() {
- swipe(Direction.UP);
-
- // Motion events are not sent for the shade window to process as the swipe is going in the
- // wrong direction.
- verify(mCentralSurfaces, never()).handleExternalShadeWindowTouch(any());
+ mTouchHandler = new ShadeTouchHandler(Optional.of(mCentralSurfaces), mShadeViewController,
+ TOUCH_HEIGHT);
}
/**
- * Simulates a swipe in the given direction and returns true if the touch was intercepted by the
- * touch handler's gesture listener.
- * <p>
- * Swipe down starts from a Y coordinate of 0 and goes downward. Swipe up starts from the edge
- * of the gesture region, {@link #TOUCH_HEIGHT}, and goes upward to 0.
+ * Verify that touches aren't handled when the bouncer is showing.
*/
- private boolean swipe(Direction direction) {
- Mockito.clearInvocations(mTouchSession);
+ @Test
+ public void testInactiveOnBouncer() {
+ when(mCentralSurfaces.isBouncerShowing()).thenReturn(true);
mTouchHandler.onSessionStart(mTouchSession);
-
- verify(mTouchSession).registerGestureListener(mGestureListenerCaptor.capture());
- verify(mTouchSession).registerInputListener(mInputListenerCaptor.capture());
-
- final float startY = direction == Direction.UP ? TOUCH_HEIGHT : 0;
- final float endY = direction == Direction.UP ? 0 : TOUCH_HEIGHT;
-
- // Send touches to the input and gesture listener.
- final MotionEvent event1 = MotionEvent.obtain(0, 0, MotionEvent.ACTION_MOVE, 0, startY, 0);
- final MotionEvent event2 = MotionEvent.obtain(0, 0, MotionEvent.ACTION_MOVE, 0, endY, 0);
- mInputListenerCaptor.getValue().onInputEvent(event1);
- mInputListenerCaptor.getValue().onInputEvent(event2);
- final boolean captured = mGestureListenerCaptor.getValue().onScroll(event1, event2, 0,
- startY - endY);
-
- return captured;
+ verify(mTouchSession).pop();
}
- private enum Direction {
- DOWN, UP,
+ /**
+ * Make sure {@link ShadeTouchHandler}
+ */
+ @Test
+ public void testTouchPilferingOnScroll() {
+ final MotionEvent motionEvent1 = Mockito.mock(MotionEvent.class);
+ final MotionEvent motionEvent2 = Mockito.mock(MotionEvent.class);
+
+ final ArgumentCaptor<GestureDetector.OnGestureListener> gestureListenerArgumentCaptor =
+ ArgumentCaptor.forClass(GestureDetector.OnGestureListener.class);
+
+ mTouchHandler.onSessionStart(mTouchSession);
+ verify(mTouchSession).registerGestureListener(gestureListenerArgumentCaptor.capture());
+
+ assertThat(gestureListenerArgumentCaptor.getValue()
+ .onScroll(motionEvent1, motionEvent2, 1, 1))
+ .isTrue();
}
+
+ /**
+ * Ensure touches are propagated to the {@link ShadeViewController}.
+ */
+ @Test
+ public void testEventPropagation() {
+ final MotionEvent motionEvent = Mockito.mock(MotionEvent.class);
+
+ final ArgumentCaptor<InputChannelCompat.InputEventListener>
+ inputEventListenerArgumentCaptor =
+ ArgumentCaptor.forClass(InputChannelCompat.InputEventListener.class);
+
+ mTouchHandler.onSessionStart(mTouchSession);
+ verify(mTouchSession).registerInputListener(inputEventListenerArgumentCaptor.capture());
+ inputEventListenerArgumentCaptor.getValue().onInputEvent(motionEvent);
+ verify(mShadeViewController).handleExternalTouch(motionEvent);
+ }
+
}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/bouncer/ui/viewmodel/BouncerViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/bouncer/ui/viewmodel/BouncerViewModelTest.kt
index ecfcc90..a5acf72 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/bouncer/ui/viewmodel/BouncerViewModelTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/bouncer/ui/viewmodel/BouncerViewModelTest.kt
@@ -66,15 +66,12 @@
private val kosmos = testKosmos()
private val testScope = kosmos.testScope
- private val authenticationInteractor by lazy { kosmos.authenticationInteractor }
- private val bouncerInteractor by lazy { kosmos.bouncerInteractor }
- private val sceneContainerStartable = kosmos.sceneContainerStartable
private lateinit var underTest: BouncerViewModel
@Before
fun setUp() {
- sceneContainerStartable.start()
+ kosmos.sceneContainerStartable.start()
underTest = kosmos.bouncerViewModel
}
@@ -164,11 +161,11 @@
assertThat(isInputEnabled).isTrue()
repeat(FakeAuthenticationRepository.MAX_FAILED_AUTH_TRIES_BEFORE_LOCKOUT) {
- bouncerInteractor.authenticate(WRONG_PIN)
+ kosmos.bouncerInteractor.authenticate(WRONG_PIN)
}
assertThat(isInputEnabled).isFalse()
- val lockoutEndMs = authenticationInteractor.lockoutEndTimestamp ?: 0
+ val lockoutEndMs = kosmos.authenticationInteractor.lockoutEndTimestamp ?: 0
advanceTimeBy(lockoutEndMs - testScope.currentTime)
assertThat(isInputEnabled).isTrue()
}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/brightness/data/repository/BrightnessPolicyRepositoryImplTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/brightness/data/repository/BrightnessPolicyRepositoryImplTest.kt
index 312c14d..fec56ed 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/brightness/data/repository/BrightnessPolicyRepositoryImplTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/brightness/data/repository/BrightnessPolicyRepositoryImplTest.kt
@@ -18,9 +18,13 @@
import android.content.applicationContext
import android.os.UserManager
-import androidx.test.ext.junit.runners.AndroidJUnit4
+import android.platform.test.annotations.DisableFlags
+import android.platform.test.annotations.EnableFlags
+import android.platform.test.flag.junit.FlagsParameterization
+import android.platform.test.flag.junit.FlagsParameterization.allCombinationsOf
import androidx.test.filters.SmallTest
import com.android.settingslib.RestrictedLockUtils.EnforcedAdmin
+import com.android.systemui.Flags.FLAG_ENFORCE_BRIGHTNESS_BASE_USER_RESTRICTION
import com.android.systemui.SysuiTestCase
import com.android.systemui.coroutines.collectLastValue
import com.android.systemui.kosmos.testDispatcher
@@ -40,15 +44,19 @@
import org.junit.runner.RunWith
import org.mockito.ArgumentMatchers.anyInt
import org.mockito.ArgumentMatchers.anyString
+import platform.test.runner.parameterized.ParameterizedAndroidJunit4
+import platform.test.runner.parameterized.Parameters
@SmallTest
-@RunWith(AndroidJUnit4::class)
-class BrightnessPolicyRepositoryImplTest : SysuiTestCase() {
+@RunWith(ParameterizedAndroidJunit4::class)
+class BrightnessPolicyRepositoryImplTest(flags: FlagsParameterization) : SysuiTestCase() {
+
+ init {
+ mSetFlagsRule.setFlagsParameterization(flags)
+ }
private val kosmos = testKosmos()
- private val fakeUserRepository = kosmos.fakeUserRepository
-
private val mockUserRestrictionChecker: UserRestrictionChecker = mock {
whenever(checkIfRestrictionEnforced(any(), anyString(), anyInt())).thenReturn(null)
whenever(hasBaseUserRestriction(any(), anyString(), anyInt())).thenReturn(false)
@@ -130,7 +138,83 @@
}
}
- private companion object {
- val RESTRICTION = UserManager.DISALLOW_CONFIG_BRIGHTNESS
+ @Test
+ @DisableFlags(FLAG_ENFORCE_BRIGHTNESS_BASE_USER_RESTRICTION)
+ fun brightnessBaseUserRestriction_flagOff_noRestriction() =
+ with(kosmos) {
+ testScope.runTest {
+ whenever(
+ mockUserRestrictionChecker.hasBaseUserRestriction(
+ any(),
+ eq(RESTRICTION),
+ eq(userRepository.getSelectedUserInfo().id)
+ )
+ )
+ .thenReturn(true)
+
+ val restrictions by collectLastValue(underTest.restrictionPolicy)
+
+ assertThat(restrictions).isEqualTo(PolicyRestriction.NoRestriction)
+ }
+ }
+
+ @Test
+ fun bothRestrictions_returnsSetEnforcedAdminFromCheck() =
+ with(kosmos) {
+ testScope.runTest {
+ val enforcedAdmin: EnforcedAdmin =
+ EnforcedAdmin.createDefaultEnforcedAdminWithRestriction(RESTRICTION)
+
+ whenever(
+ mockUserRestrictionChecker.checkIfRestrictionEnforced(
+ any(),
+ eq(RESTRICTION),
+ eq(userRepository.getSelectedUserInfo().id)
+ )
+ )
+ .thenReturn(enforcedAdmin)
+
+ whenever(
+ mockUserRestrictionChecker.hasBaseUserRestriction(
+ any(),
+ eq(RESTRICTION),
+ eq(userRepository.getSelectedUserInfo().id)
+ )
+ )
+ .thenReturn(true)
+
+ val restrictions by collectLastValue(underTest.restrictionPolicy)
+
+ assertThat(restrictions).isEqualTo(PolicyRestriction.Restricted(enforcedAdmin))
+ }
+ }
+
+ @Test
+ @EnableFlags(FLAG_ENFORCE_BRIGHTNESS_BASE_USER_RESTRICTION)
+ fun brightnessBaseUserRestriction_flagOn_emptyRestriction() =
+ with(kosmos) {
+ testScope.runTest {
+ whenever(
+ mockUserRestrictionChecker.hasBaseUserRestriction(
+ any(),
+ eq(RESTRICTION),
+ eq(userRepository.getSelectedUserInfo().id)
+ )
+ )
+ .thenReturn(true)
+
+ val restrictions by collectLastValue(underTest.restrictionPolicy)
+
+ assertThat(restrictions).isEqualTo(PolicyRestriction.Restricted(EnforcedAdmin()))
+ }
+ }
+
+ companion object {
+ private const val RESTRICTION = UserManager.DISALLOW_CONFIG_BRIGHTNESS
+ @JvmStatic
+ @Parameters(name = "{0}")
+ fun getParams(): List<FlagsParameterization> {
+ return allCombinationsOf(FLAG_ENFORCE_BRIGHTNESS_BASE_USER_RESTRICTION)
+ }
}
}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/brightness/domain/interactor/BrightnessPolicyEnforcementInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/brightness/domain/interactor/BrightnessPolicyEnforcementInteractorTest.kt
index 85a4bcf..11f5238 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/brightness/domain/interactor/BrightnessPolicyEnforcementInteractorTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/brightness/domain/interactor/BrightnessPolicyEnforcementInteractorTest.kt
@@ -48,7 +48,6 @@
private val kosmos = testKosmos()
private val mockActivityStarter = kosmos.activityStarter
- private val fakeBrightnessPolicyEnforcementInteractor = kosmos.fakeBrightnessPolicyRepository
private val underTest =
with(kosmos) {
@@ -70,7 +69,18 @@
fakeBrightnessPolicyRepository.setCurrentUserRestricted()
- assertThat(restriction).isInstanceOf(PolicyRestriction.Restricted::class.java)
+ assertThat(restriction)
+ .isEqualTo(
+ PolicyRestriction.Restricted(
+ EnforcedAdmin.createDefaultEnforcedAdminWithRestriction(
+ BrightnessPolicyRepository.RESTRICTION
+ )
+ )
+ )
+
+ fakeBrightnessPolicyRepository.setBaseUserRestriction()
+
+ assertThat(restriction).isEqualTo(PolicyRestriction.Restricted(EnforcedAdmin()))
}
}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/CommunalSceneStartableTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/CommunalSceneStartableTest.kt
index b4b812d..0ab0959 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/CommunalSceneStartableTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/CommunalSceneStartableTest.kt
@@ -265,7 +265,7 @@
with(kosmos) {
testScope.runTest {
// Device is dreaming and on communal.
- fakeKeyguardRepository.setDreaming(true)
+ updateDreaming(true)
communalInteractor.changeScene(CommunalScenes.Communal)
val scene by collectLastValue(communalInteractor.desiredScene)
@@ -282,7 +282,7 @@
with(kosmos) {
testScope.runTest {
// Device is not dreaming and on communal.
- fakeKeyguardRepository.setDreaming(false)
+ updateDreaming(false)
communalInteractor.changeScene(CommunalScenes.Communal)
// Scene stays as Communal
@@ -297,7 +297,7 @@
with(kosmos) {
testScope.runTest {
// Device is dreaming and on communal.
- fakeKeyguardRepository.setDreaming(true)
+ updateDreaming(true)
communalInteractor.changeScene(CommunalScenes.Communal)
val scene by collectLastValue(communalInteractor.desiredScene)
@@ -309,7 +309,7 @@
// Dream stops, timeout is cancelled and device stays on hub, because the regular
// screen timeout will take effect at this point.
- fakeKeyguardRepository.setDreaming(false)
+ updateDreaming(false)
advanceTimeBy((SCREEN_TIMEOUT / 2).milliseconds)
assertThat(scene).isEqualTo(CommunalScenes.Communal)
}
@@ -320,7 +320,7 @@
with(kosmos) {
testScope.runTest {
// Device is on communal, but not dreaming.
- fakeKeyguardRepository.setDreaming(false)
+ updateDreaming(false)
communalInteractor.changeScene(CommunalScenes.Communal)
val scene by collectLastValue(communalInteractor.desiredScene)
@@ -328,7 +328,7 @@
// Wait a bit, but not long enough to timeout, then start dreaming.
advanceTimeBy((SCREEN_TIMEOUT / 2).milliseconds)
- fakeKeyguardRepository.setDreaming(true)
+ updateDreaming(true)
assertThat(scene).isEqualTo(CommunalScenes.Communal)
// Device times out after one screen timeout interval, dream doesn't reset timeout.
@@ -338,11 +338,31 @@
}
@Test
+ fun hubTimeout_dreamAfterInitialTimeout_goesToBlank() =
+ with(kosmos) {
+ testScope.runTest {
+ // Device is on communal.
+ communalInteractor.changeScene(CommunalScenes.Communal)
+
+ // Device stays on the hub after the timeout since we're not dreaming.
+ advanceTimeBy(SCREEN_TIMEOUT.milliseconds * 2)
+ val scene by collectLastValue(communalInteractor.desiredScene)
+ assertThat(scene).isEqualTo(CommunalScenes.Communal)
+
+ // Start dreaming.
+ updateDreaming(true)
+
+ // Hub times out immediately.
+ assertThat(scene).isEqualTo(CommunalScenes.Blank)
+ }
+ }
+
+ @Test
fun hubTimeout_userActivityTriggered_resetsTimeout() =
with(kosmos) {
testScope.runTest {
// Device is dreaming and on communal.
- fakeKeyguardRepository.setDreaming(true)
+ updateDreaming(true)
communalInteractor.changeScene(CommunalScenes.Communal)
val scene by collectLastValue(communalInteractor.desiredScene)
@@ -371,7 +391,7 @@
fakeSettings.putInt(Settings.System.SCREEN_OFF_TIMEOUT, SCREEN_TIMEOUT * 2)
// Device is dreaming and on communal.
- fakeKeyguardRepository.setDreaming(true)
+ updateDreaming(true)
communalInteractor.changeScene(CommunalScenes.Communal)
val scene by collectLastValue(communalInteractor.desiredScene)
@@ -395,6 +415,12 @@
runCurrent()
}
+ private fun TestScope.updateDreaming(dreaming: Boolean) =
+ with(kosmos) {
+ fakeKeyguardRepository.setDreaming(dreaming)
+ runCurrent()
+ }
+
private suspend fun TestScope.enableCommunal() =
with(kosmos) {
setCommunalAvailable(true)
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/data/repository/CommunalMediaRepositoryImplTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/data/repository/CommunalMediaRepositoryImplTest.kt
index 1cdc2b6..407bf4c 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/data/repository/CommunalMediaRepositoryImplTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/data/repository/CommunalMediaRepositoryImplTest.kt
@@ -114,7 +114,7 @@
// Change to media unavailable and notify the listener.
whenever(mediaDataManager.hasActiveMediaOrRecommendation()).thenReturn(false)
- mediaDataListenerCaptor.value.onMediaDataRemoved("key")
+ mediaDataListenerCaptor.value.onMediaDataRemoved("key", false)
runCurrent()
// Media active now returns false.
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/data/repository/CommunalSettingsRepositoryImplTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/data/repository/CommunalSettingsRepositoryImplTest.kt
index ce7b60e..325a324 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/data/repository/CommunalSettingsRepositoryImplTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/data/repository/CommunalSettingsRepositoryImplTest.kt
@@ -29,6 +29,7 @@
import android.provider.Settings
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
+import com.android.settingslib.flags.Flags.FLAG_ALLOW_ALL_WIDGETS_ON_LOCKSCREEN_BY_DEFAULT
import com.android.systemui.Flags.FLAG_COMMUNAL_HUB
import com.android.systemui.SysuiTestCase
import com.android.systemui.broadcast.broadcastDispatcher
@@ -202,6 +203,18 @@
.isEqualTo(AppWidgetProviderInfo.WIDGET_CATEGORY_KEYGUARD)
}
+ @EnableFlags(FLAG_COMMUNAL_HUB, FLAG_ALLOW_ALL_WIDGETS_ON_LOCKSCREEN_BY_DEFAULT)
+ @Test
+ fun hubShowsAllWidgetsByDefaultWhenFlagEnabled() =
+ testScope.runTest {
+ val setting by collectLastValue(underTest.getWidgetCategories(PRIMARY_USER))
+ assertThat(setting?.categories)
+ .isEqualTo(
+ AppWidgetProviderInfo.WIDGET_CATEGORY_KEYGUARD +
+ AppWidgetProviderInfo.WIDGET_CATEGORY_HOME_SCREEN
+ )
+ }
+
private fun setKeyguardFeaturesDisabled(user: UserInfo, disabledFlags: Int) {
whenever(kosmos.devicePolicyManager.getKeyguardDisabledFeatures(nullable(), eq(user.id)))
.thenReturn(disabledFlags)
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/domain/interactor/CommunalInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/domain/interactor/CommunalInteractorTest.kt
index 766798c..83227e1 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/domain/interactor/CommunalInteractorTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/domain/interactor/CommunalInteractorTest.kt
@@ -204,14 +204,14 @@
}
@Test
- fun isCommunalAvailable_whenDreaming_true() =
+ fun isCommunalAvailable_whenKeyguardShowing_true() =
testScope.runTest {
val isAvailable by collectLastValue(underTest.isCommunalAvailable)
assertThat(isAvailable).isFalse()
keyguardRepository.setIsEncryptedOrLockdown(false)
userRepository.setSelectedUserInfo(mainUser)
- keyguardRepository.setDreaming(true)
+ keyguardRepository.setKeyguardShowing(true)
assertThat(isAvailable).isTrue()
}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/view/viewmodel/CommunalViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/view/viewmodel/CommunalViewModelTest.kt
index 2d079d7..be44339 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/view/viewmodel/CommunalViewModelTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/view/viewmodel/CommunalViewModelTest.kt
@@ -51,6 +51,7 @@
import com.android.systemui.keyguard.data.repository.FakeKeyguardTransitionRepository
import com.android.systemui.keyguard.data.repository.fakeKeyguardRepository
import com.android.systemui.keyguard.data.repository.fakeKeyguardTransitionRepository
+import com.android.systemui.keyguard.domain.interactor.keyguardInteractor
import com.android.systemui.keyguard.domain.interactor.keyguardTransitionInteractor
import com.android.systemui.keyguard.shared.model.KeyguardState
import com.android.systemui.keyguard.shared.model.StatusBarState
@@ -141,6 +142,7 @@
testScope,
context.resources,
kosmos.keyguardTransitionInteractor,
+ kosmos.keyguardInteractor,
kosmos.communalInteractor,
kosmos.communalTutorialInteractor,
kosmos.shadeInteractor,
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/dreams/DreamOverlayContainerViewControllerTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/dreams/DreamOverlayContainerViewControllerTest.java
index e3dd9ae..f5c86e0 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/dreams/DreamOverlayContainerViewControllerTest.java
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/dreams/DreamOverlayContainerViewControllerTest.java
@@ -27,11 +27,15 @@
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
+import android.app.DreamManager;
import android.content.res.Resources;
import android.graphics.Region;
import android.os.Handler;
+import android.platform.test.annotations.DisableFlags;
+import android.platform.test.annotations.EnableFlags;
import android.testing.TestableLooper.RunWithLooper;
import android.view.AttachedSurfaceControl;
+import android.view.View;
import android.view.ViewGroup;
import android.view.ViewRootImpl;
import android.view.ViewTreeObserver;
@@ -41,12 +45,15 @@
import com.android.dream.lowlight.LowLightTransitionCoordinator;
import com.android.keyguard.BouncerPanelExpansionCalculator;
+import com.android.systemui.Flags;
import com.android.systemui.SysuiTestCase;
import com.android.systemui.ambient.touch.scrim.BouncerlessScrimController;
import com.android.systemui.bouncer.domain.interactor.PrimaryBouncerCallbackInteractor;
import com.android.systemui.bouncer.domain.interactor.PrimaryBouncerCallbackInteractor.PrimaryBouncerExpansionCallback;
+import com.android.systemui.communal.domain.interactor.CommunalInteractor;
import com.android.systemui.complication.ComplicationHostViewController;
import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor;
+import com.android.systemui.shade.domain.interactor.ShadeInteractor;
import com.android.systemui.statusbar.BlurUtils;
import kotlinx.coroutines.CoroutineDispatcher;
@@ -91,6 +98,9 @@
ViewGroup mDreamOverlayContentView;
@Mock
+ View mHubGestureIndicatorView;
+
+ @Mock
Handler mHandler;
@Mock
@@ -115,6 +125,12 @@
DreamOverlayStateController mStateController;
@Mock
KeyguardTransitionInteractor mKeyguardTransitionInteractor;
+ @Mock
+ ShadeInteractor mShadeInteractor;
+ @Mock
+ CommunalInteractor mCommunalInteractor;
+ @Mock
+ private DreamManager mDreamManager;
DreamOverlayContainerViewController mController;
@@ -133,6 +149,7 @@
mDreamOverlayContainerView,
mComplicationHostViewController,
mDreamOverlayContentView,
+ mHubGestureIndicatorView,
mDreamOverlayStatusBarViewController,
mLowLightTransitionCoordinator,
mBlurUtils,
@@ -146,7 +163,22 @@
mAnimationsController,
mStateController,
mBouncerlessScrimController,
- mKeyguardTransitionInteractor);
+ mKeyguardTransitionInteractor,
+ mShadeInteractor,
+ mCommunalInteractor,
+ mDreamManager);
+ }
+
+ @DisableFlags(Flags.FLAG_COMMUNAL_HUB)
+ @Test
+ public void testHubGestureIndicatorGoneWhenFlagOff() {
+ verify(mHubGestureIndicatorView, never()).setVisibility(View.VISIBLE);
+ }
+
+ @EnableFlags({Flags.FLAG_COMMUNAL_HUB, Flags.FLAG_GLANCEABLE_HUB_GESTURE_HANDLE})
+ @Test
+ public void testHubGestureIndicatorVisibleWhenFlagOn() {
+ verify(mHubGestureIndicatorView).setVisibility(View.VISIBLE);
}
@Test
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/dreams/touch/CommunalTouchHandlerTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/dreams/touch/CommunalTouchHandlerTest.java
index e3c6dee..29fbee0 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/dreams/touch/CommunalTouchHandlerTest.java
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/dreams/touch/CommunalTouchHandlerTest.java
@@ -108,7 +108,7 @@
mTouchHandler.onSessionStart(mTouchSession);
verify(mTouchSession).registerInputListener(inputEventListenerArgumentCaptor.capture());
inputEventListenerArgumentCaptor.getValue().onInputEvent(motionEvent);
- verify(mCentralSurfaces).handleExternalShadeWindowTouch(motionEvent);
+ verify(mCentralSurfaces).handleDreamTouch(motionEvent);
}
@Test
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/KeyguardInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/KeyguardInteractorTest.kt
index cb2d4e0..addbdb6 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/KeyguardInteractorTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/KeyguardInteractorTest.kt
@@ -60,17 +60,19 @@
private val kosmos = testKosmos()
private val testScope = kosmos.testScope
- private val repository = kosmos.fakeKeyguardRepository
- private val sceneInteractor = kosmos.sceneInteractor
- private val fromGoneTransitionInteractor = kosmos.fromGoneTransitionInteractor
- private val commandQueue = kosmos.fakeCommandQueue
- private val configRepository = kosmos.fakeConfigurationRepository
- private val bouncerRepository = kosmos.keyguardBouncerRepository
- private val shadeRepository = kosmos.shadeRepository
- private val keyguardTransitionRepository = kosmos.fakeKeyguardTransitionRepository
+ private val repository by lazy { kosmos.fakeKeyguardRepository }
+ private val sceneInteractor by lazy { kosmos.sceneInteractor }
+ private val fromGoneTransitionInteractor by lazy { kosmos.fromGoneTransitionInteractor }
+ private val commandQueue by lazy { kosmos.fakeCommandQueue }
+ private val configRepository by lazy { kosmos.fakeConfigurationRepository }
+ private val bouncerRepository by lazy { kosmos.keyguardBouncerRepository }
+ private val shadeRepository by lazy { kosmos.shadeRepository }
+ private val keyguardTransitionRepository by lazy { kosmos.fakeKeyguardTransitionRepository }
+
private val transitionState: MutableStateFlow<ObservableTransitionState> =
MutableStateFlow(ObservableTransitionState.Idle(Scenes.Gone))
- private val underTest = kosmos.keyguardInteractor
+
+ private val underTest by lazy { kosmos.keyguardInteractor }
@Before
fun setUp() {
@@ -275,6 +277,28 @@
}
@Test
+ fun keyguardTranslationY_whenNotGoneAndShadeIsReesetEmitsZero() =
+ testScope.runTest {
+ val keyguardTranslationY by collectLastValue(underTest.keyguardTranslationY)
+
+ configRepository.setDimensionPixelSize(
+ R.dimen.keyguard_translate_distance_on_swipe_up,
+ 100
+ )
+ configRepository.onAnyConfigurationChange()
+
+ shadeRepository.setLegacyShadeExpansion(1f)
+
+ keyguardTransitionRepository.sendTransitionSteps(
+ from = KeyguardState.AOD,
+ to = KeyguardState.LOCKSCREEN,
+ testScope,
+ )
+
+ assertThat(keyguardTranslationY).isEqualTo(0f)
+ }
+
+ @Test
fun keyguardTranslationY_whenTransitioningToGoneAndShadeIsExpandingEmitsNonZero() =
testScope.runTest {
val keyguardTranslationY by collectLastValue(underTest.keyguardTranslationY)
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionInteractorTest.kt
index bf0939c..99cccb2 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionInteractorTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionInteractorTest.kt
@@ -19,9 +19,13 @@
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
+import com.android.compose.animation.scene.ObservableTransitionState
import com.android.systemui.SysuiTestCase
import com.android.systemui.coroutines.collectValues
+import com.android.systemui.flags.DisableSceneContainer
+import com.android.systemui.flags.EnableSceneContainer
import com.android.systemui.keyguard.data.repository.fakeKeyguardTransitionRepository
+import com.android.systemui.keyguard.shared.model.Edge
import com.android.systemui.keyguard.shared.model.KeyguardState
import com.android.systemui.keyguard.shared.model.KeyguardState.AOD
import com.android.systemui.keyguard.shared.model.KeyguardState.DOZING
@@ -29,36 +33,74 @@
import com.android.systemui.keyguard.shared.model.KeyguardState.LOCKSCREEN
import com.android.systemui.keyguard.shared.model.KeyguardState.OFF
import com.android.systemui.keyguard.shared.model.KeyguardState.PRIMARY_BOUNCER
+import com.android.systemui.keyguard.shared.model.KeyguardState.UNDEFINED
import com.android.systemui.keyguard.shared.model.TransitionState.CANCELED
import com.android.systemui.keyguard.shared.model.TransitionState.FINISHED
import com.android.systemui.keyguard.shared.model.TransitionState.RUNNING
import com.android.systemui.keyguard.shared.model.TransitionState.STARTED
import com.android.systemui.keyguard.shared.model.TransitionStep
import com.android.systemui.kosmos.testScope
+import com.android.systemui.scene.data.repository.sceneContainerRepository
+import com.android.systemui.scene.shared.model.Scenes
import com.android.systemui.testKosmos
import com.google.common.truth.Truth.assertThat
import junit.framework.Assert.assertEquals
import kotlinx.coroutines.flow.MutableSharedFlow
+import kotlinx.coroutines.flow.MutableStateFlow
+import kotlinx.coroutines.flow.flowOf
import kotlinx.coroutines.test.runCurrent
import kotlinx.coroutines.test.runTest
+import org.junit.Assert.assertThrows
+import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
@SmallTest
@RunWith(AndroidJUnit4::class)
@kotlinx.coroutines.ExperimentalCoroutinesApi
-@android.platform.test.annotations.EnabledOnRavenwood
class KeyguardTransitionInteractorTest : SysuiTestCase() {
val kosmos = testKosmos()
val underTest = kosmos.keyguardTransitionInteractor
val repository = kosmos.fakeKeyguardTransitionRepository
val testScope = kosmos.testScope
+ private val sceneTransitions =
+ MutableStateFlow<ObservableTransitionState>(
+ ObservableTransitionState.Idle(Scenes.Lockscreen)
+ )
+
+ private val lsToGone =
+ ObservableTransitionState.Transition(
+ Scenes.Lockscreen,
+ Scenes.Gone,
+ flowOf(Scenes.Lockscreen),
+ flowOf(0f),
+ false,
+ flowOf(false)
+ )
+
+ private val goneToLs =
+ ObservableTransitionState.Transition(
+ Scenes.Gone,
+ Scenes.Lockscreen,
+ flowOf(Scenes.Lockscreen),
+ flowOf(0f),
+ false,
+ flowOf(false)
+ )
+
+ @Before
+ fun setUp() {
+ kosmos.sceneContainerRepository.setTransitionState(sceneTransitions)
+ }
+
@Test
fun transitionCollectorsReceivesOnlyAppropriateEvents() =
testScope.runTest {
- val lockscreenToAodSteps by collectValues(underTest.transition(LOCKSCREEN, AOD))
- val aodToLockscreenSteps by collectValues(underTest.transition(AOD, LOCKSCREEN))
+ val lockscreenToAodSteps by
+ collectValues(underTest.transition(Edge.create(LOCKSCREEN, AOD)))
+ val aodToLockscreenSteps by
+ collectValues(underTest.transition(Edge.create(AOD, LOCKSCREEN)))
val steps = mutableListOf<TransitionStep>()
steps.add(TransitionStep(AOD, GONE, 0f, STARTED))
@@ -482,6 +524,7 @@
}
@Test
+ @DisableSceneContainer
fun isInTransitionToState() =
testScope.runTest {
val results by collectValues(underTest.isInTransitionToState(GONE))
@@ -586,7 +629,7 @@
)
sendSteps(
- TransitionStep(DOZING, GONE, 0f, STARTED),
+ TransitionStep(DOZING, LOCKSCREEN, 0f, STARTED),
)
assertThat(results)
@@ -598,7 +641,7 @@
)
sendSteps(
- TransitionStep(DOZING, GONE, 0f, RUNNING),
+ TransitionStep(DOZING, LOCKSCREEN, 0f, RUNNING),
)
assertThat(results)
@@ -610,7 +653,7 @@
)
sendSteps(
- TransitionStep(DOZING, GONE, 0f, FINISHED),
+ TransitionStep(DOZING, LOCKSCREEN, 0f, FINISHED),
)
assertThat(results)
@@ -623,9 +666,9 @@
)
sendSteps(
- TransitionStep(GONE, DOZING, 0f, STARTED),
- TransitionStep(GONE, DOZING, 0f, RUNNING),
- TransitionStep(GONE, DOZING, 1f, FINISHED),
+ TransitionStep(LOCKSCREEN, DOZING, 0f, STARTED),
+ TransitionStep(LOCKSCREEN, DOZING, 0f, RUNNING),
+ TransitionStep(LOCKSCREEN, DOZING, 1f, FINISHED),
)
assertThat(results)
@@ -638,8 +681,8 @@
)
sendSteps(
- TransitionStep(DOZING, GONE, 0f, STARTED),
- TransitionStep(DOZING, GONE, 0f, RUNNING),
+ TransitionStep(DOZING, LOCKSCREEN, 0f, STARTED),
+ TransitionStep(DOZING, LOCKSCREEN, 0f, RUNNING),
)
assertThat(results)
@@ -1404,6 +1447,143 @@
)
}
+ @Test
+ @DisableSceneContainer
+ fun transition_no_conversion_with_flag_off() =
+ testScope.runTest {
+ val currentStates by
+ collectValues(underTest.transition(Edge.create(PRIMARY_BOUNCER, GONE)))
+
+ val sendStep1 = TransitionStep(PRIMARY_BOUNCER, GONE, 0f, STARTED)
+ sendSteps(sendStep1)
+
+ assertEquals(listOf(sendStep1), currentStates)
+ }
+
+ @Test
+ @EnableSceneContainer
+ fun transition_conversion_with_flag_on() =
+ testScope.runTest {
+ val currentStates by
+ collectValues(underTest.transition(Edge.create(PRIMARY_BOUNCER, GONE)))
+
+ val sendStep1 = TransitionStep(PRIMARY_BOUNCER, GONE, 0f, STARTED)
+ sendSteps(sendStep1)
+
+ assertEquals(listOf<TransitionStep>(), currentStates)
+ }
+
+ @Test
+ @EnableSceneContainer
+ fun transition_conversion_emits_values_with_sceneContainer_in_correct_state() =
+ testScope.runTest {
+ val currentStates by collectValues(underTest.transition(Edge.create(LOCKSCREEN, GONE)))
+ val currentStatesConverted by
+ collectValues(underTest.transition(Edge.create(LOCKSCREEN, UNDEFINED)))
+
+ sceneTransitions.value = lsToGone
+ val sendStep1 = TransitionStep(LOCKSCREEN, UNDEFINED, 0f, STARTED)
+ val sendStep2 = TransitionStep(LOCKSCREEN, UNDEFINED, 1f, FINISHED)
+ val sendStep3 = TransitionStep(LOCKSCREEN, AOD, 0f, STARTED)
+ sendSteps(sendStep1, sendStep2, sendStep3)
+
+ assertEquals(listOf(sendStep1, sendStep2), currentStates)
+ assertEquals(listOf(sendStep1, sendStep2), currentStatesConverted)
+ }
+
+ @Test
+ @EnableSceneContainer
+ fun transition_conversion_emits_nothing_with_sceneContainer_in_wrong_state() =
+ testScope.runTest {
+ val currentStates by collectValues(underTest.transition(Edge.create(LOCKSCREEN, GONE)))
+
+ sceneTransitions.value = goneToLs
+ val sendStep1 = TransitionStep(LOCKSCREEN, UNDEFINED, 0f, STARTED)
+ val sendStep2 = TransitionStep(LOCKSCREEN, UNDEFINED, 1f, FINISHED)
+ val sendStep3 = TransitionStep(LOCKSCREEN, AOD, 0f, STARTED)
+ sendSteps(sendStep1, sendStep2, sendStep3)
+
+ assertEquals(listOf<TransitionStep>(), currentStates)
+ }
+
+ @Test
+ @EnableSceneContainer
+ fun transition_conversion_emits_values_when_edge_within_lockscreen_scene() =
+ testScope.runTest {
+ val currentStates by
+ collectValues(underTest.transition(Edge.create(LOCKSCREEN, DOZING)))
+
+ sceneTransitions.value = goneToLs
+ val sendStep1 = TransitionStep(LOCKSCREEN, DOZING, 0f, STARTED)
+ val sendStep2 = TransitionStep(LOCKSCREEN, DOZING, 1f, FINISHED)
+ val sendStep3 = TransitionStep(LOCKSCREEN, AOD, 0f, STARTED)
+ sendSteps(sendStep1, sendStep2, sendStep3)
+
+ assertEquals(listOf(sendStep1, sendStep2), currentStates)
+ }
+
+ @Test
+ @EnableSceneContainer
+ fun transition_conversion_emits_values_with_null_edge_within_lockscreen_scene() =
+ testScope.runTest {
+ val currentStates by collectValues(underTest.transition(Edge.create(LOCKSCREEN, null)))
+ val currentStatesReversed by
+ collectValues(underTest.transition(Edge.create(null, LOCKSCREEN)))
+
+ sceneTransitions.value = goneToLs
+ val sendStep1 = TransitionStep(LOCKSCREEN, DOZING, 0f, STARTED)
+ val sendStep2 = TransitionStep(LOCKSCREEN, DOZING, 1f, FINISHED)
+ val sendStep3 = TransitionStep(LOCKSCREEN, AOD, 0f, STARTED)
+ val sendStep4 = TransitionStep(AOD, LOCKSCREEN, 0f, STARTED)
+ sendSteps(sendStep1, sendStep2, sendStep3, sendStep4)
+
+ assertEquals(listOf(sendStep1, sendStep2, sendStep3), currentStates)
+ assertEquals(listOf(sendStep4), currentStatesReversed)
+ }
+
+ @Test
+ @EnableSceneContainer
+ fun transition_conversion_emits_values_with_null_edge_out_of_lockscreen_scene() =
+ testScope.runTest {
+ val currentStates by collectValues(underTest.transition(Edge.create(null, UNDEFINED)))
+ val currentStatesMapped by collectValues(underTest.transition(Edge.create(null, GONE)))
+
+ sceneTransitions.value = lsToGone
+ val sendStep1 = TransitionStep(LOCKSCREEN, UNDEFINED, 0f, STARTED)
+ val sendStep2 = TransitionStep(LOCKSCREEN, UNDEFINED, 1f, FINISHED)
+ val sendStep3 = TransitionStep(UNDEFINED, AOD, 0f, STARTED)
+ val sendStep4 = TransitionStep(AOD, LOCKSCREEN, 0f, STARTED)
+ sendSteps(sendStep1, sendStep2, sendStep3, sendStep4)
+
+ assertEquals(listOf(sendStep1, sendStep2), currentStates)
+ assertEquals(listOf(sendStep1, sendStep2), currentStatesMapped)
+ }
+
+ @Test
+ @EnableSceneContainer
+ fun transition_conversion_does_not_emit_with_null_edge_with_wrong_stl_state() =
+ testScope.runTest {
+ val currentStatesMapped by collectValues(underTest.transition(Edge.create(null, GONE)))
+
+ sceneTransitions.value = goneToLs
+ val sendStep1 = TransitionStep(LOCKSCREEN, UNDEFINED, 0f, STARTED)
+ val sendStep2 = TransitionStep(LOCKSCREEN, UNDEFINED, 1f, FINISHED)
+ val sendStep3 = TransitionStep(UNDEFINED, AOD, 0f, STARTED)
+ val sendStep4 = TransitionStep(AOD, LOCKSCREEN, 0f, STARTED)
+ sendSteps(sendStep1, sendStep2, sendStep3, sendStep4)
+
+ assertEquals(listOf<TransitionStep>(), currentStatesMapped)
+ }
+
+ @Test
+ @EnableSceneContainer
+ fun transition_null_edges_throw() =
+ testScope.runTest {
+ assertThrows(IllegalStateException::class.java) {
+ underTest.transition(Edge.create(null, null))
+ }
+ }
+
private suspend fun sendSteps(vararg steps: TransitionStep) {
steps.forEach {
repository.sendTransitionStep(it)
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/KeyguardTransitionAnimationFlowTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/KeyguardTransitionAnimationFlowTest.kt
index 0ac7ff5..a0fed6b 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/KeyguardTransitionAnimationFlowTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/KeyguardTransitionAnimationFlowTest.kt
@@ -23,11 +23,13 @@
import com.android.systemui.coroutines.collectLastValue
import com.android.systemui.coroutines.collectValues
import com.android.systemui.keyguard.data.repository.fakeKeyguardTransitionRepository
+import com.android.systemui.keyguard.shared.model.Edge
import com.android.systemui.keyguard.shared.model.KeyguardState.DREAMING
import com.android.systemui.keyguard.shared.model.KeyguardState.GONE
import com.android.systemui.keyguard.shared.model.TransitionState
import com.android.systemui.keyguard.shared.model.TransitionStep
import com.android.systemui.kosmos.testScope
+import com.android.systemui.scene.shared.model.Scenes
import com.android.systemui.testKosmos
import com.google.common.truth.Truth.assertThat
import kotlin.time.Duration.Companion.milliseconds
@@ -50,11 +52,14 @@
@Before
fun setUp() {
underTest =
- animationFlow.setup(
- duration = 1000.milliseconds,
- from = GONE,
- to = DREAMING,
- )
+ animationFlow
+ .setup(
+ duration = 1000.milliseconds,
+ edge = Edge.create(from = Scenes.Gone, to = DREAMING),
+ )
+ .setupWithoutSceneContainer(
+ edge = Edge.create(from = GONE, to = DREAMING),
+ )
}
@Test(expected = IllegalArgumentException::class)
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/BouncerToGoneFlowsTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/BouncerToGoneFlowsTest.kt
index d632936..7a9bd92 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/BouncerToGoneFlowsTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/BouncerToGoneFlowsTest.kt
@@ -87,6 +87,7 @@
}
@Test
+ @BrokenWithSceneContainer(339465026)
fun scrimAlpha_runDimissFromKeyguard_shadeExpanded() =
testScope.runTest {
val values by collectValues(underTest.scrimAlpha(500.milliseconds, PRIMARY_BOUNCER))
@@ -137,6 +138,7 @@
}
@Test
+ @BrokenWithSceneContainer(339465026)
fun scrimBehindAlpha_leaveShadeOpen() =
testScope.runTest {
val values by collectValues(underTest.scrimAlpha(500.milliseconds, PRIMARY_BOUNCER))
@@ -161,6 +163,7 @@
}
@Test
+ @BrokenWithSceneContainer(339465026)
fun showAllNotifications_isTrue_whenLeaveShadeOpen() =
testScope.runTest {
val showAllNotifications by
@@ -177,6 +180,7 @@
}
@Test
+ @BrokenWithSceneContainer(339465026)
fun showAllNotifications_isFalse_whenLeaveShadeIsNotOpen() =
testScope.runTest {
val showAllNotifications by
@@ -193,6 +197,7 @@
}
@Test
+ @BrokenWithSceneContainer(330311871)
fun scrimBehindAlpha_doNotLeaveShadeOpen() =
testScope.runTest {
val values by collectValues(underTest.scrimAlpha(500.milliseconds, PRIMARY_BOUNCER))
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardRootViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardRootViewModelTest.kt
index 838b2a7..20ffa33 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardRootViewModelTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardRootViewModelTest.kt
@@ -30,6 +30,8 @@
import com.android.systemui.communal.shared.model.CommunalScenes
import com.android.systemui.coroutines.collectLastValue
import com.android.systemui.deviceentry.data.repository.fakeDeviceEntryRepository
+import com.android.systemui.flags.DisableSceneContainer
+import com.android.systemui.flags.EnableSceneContainer
import com.android.systemui.flags.parameterizeSceneContainerFlag
import com.android.systemui.keyguard.data.repository.fakeKeyguardRepository
import com.android.systemui.keyguard.data.repository.fakeKeyguardTransitionRepository
@@ -37,7 +39,9 @@
import com.android.systemui.keyguard.shared.model.TransitionState
import com.android.systemui.keyguard.shared.model.TransitionStep
import com.android.systemui.kosmos.testScope
+import com.android.systemui.scene.data.repository.sceneContainerRepository
import com.android.systemui.scene.shared.flag.SceneContainerFlag
+import com.android.systemui.scene.shared.model.Scenes
import com.android.systemui.shade.shadeTestUtil
import com.android.systemui.statusbar.notification.stack.domain.interactor.notificationsKeyguardInteractor
import com.android.systemui.statusbar.phone.dozeParameters
@@ -49,6 +53,8 @@
import com.android.systemui.util.ui.value
import com.google.common.truth.Truth.assertThat
import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.flow.MutableStateFlow
+import kotlinx.coroutines.flow.emptyFlow
import kotlinx.coroutines.flow.flowOf
import kotlinx.coroutines.test.runCurrent
import kotlinx.coroutines.test.runTest
@@ -75,6 +81,11 @@
private val viewState = ViewStateAccessor()
+ private val transitionState =
+ MutableStateFlow<ObservableTransitionState>(
+ ObservableTransitionState.Idle(Scenes.Lockscreen)
+ )
+
companion object {
@JvmStatic
@Parameters(name = "{0}")
@@ -96,6 +107,7 @@
AConfigFlags.FLAG_MIGRATE_CLOCKS_TO_BLUEPRINT,
)
}
+ kosmos.sceneContainerRepository.setTransitionState(transitionState)
}
@Test
@@ -309,6 +321,32 @@
}
@Test
+ @EnableSceneContainer
+ fun alpha_transitionToHub_isZero_scene_container() =
+ testScope.runTest {
+ val alpha by collectLastValue(underTest.alpha(viewState))
+
+ transitionState.value =
+ ObservableTransitionState.Transition(
+ fromScene = Scenes.Lockscreen,
+ toScene = Scenes.Communal,
+ emptyFlow(),
+ emptyFlow(),
+ false,
+ emptyFlow()
+ )
+
+ keyguardTransitionRepository.sendTransitionSteps(
+ from = KeyguardState.LOCKSCREEN,
+ to = KeyguardState.UNDEFINED,
+ testScope,
+ )
+
+ assertThat(alpha).isEqualTo(0f)
+ }
+
+ @Test
+ @DisableSceneContainer
fun alpha_transitionToHub_isZero() =
testScope.runTest {
val alpha by collectLastValue(underTest.alpha(viewState))
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToPrimaryBouncerTransitionViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToPrimaryBouncerTransitionViewModelTest.kt
index 58c6817..1c1fcc4 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToPrimaryBouncerTransitionViewModelTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToPrimaryBouncerTransitionViewModelTest.kt
@@ -18,8 +18,10 @@
import android.platform.test.flag.junit.FlagsParameterization
import androidx.test.filters.SmallTest
+import com.android.compose.animation.scene.ObservableTransitionState
import com.android.systemui.SysuiTestCase
import com.android.systemui.coroutines.collectLastValue
+import com.android.systemui.flags.BrokenWithSceneContainer
import com.android.systemui.flags.Flags
import com.android.systemui.flags.andSceneContainer
import com.android.systemui.flags.fakeFeatureFlagsClassic
@@ -30,11 +32,16 @@
import com.android.systemui.keyguard.shared.model.TransitionState
import com.android.systemui.keyguard.shared.model.TransitionStep
import com.android.systemui.kosmos.testScope
+import com.android.systemui.scene.data.repository.sceneContainerRepository
+import com.android.systemui.scene.shared.flag.SceneContainerFlag
+import com.android.systemui.scene.shared.model.Scenes
import com.android.systemui.shade.shadeTestUtil
import com.android.systemui.testKosmos
import com.google.common.collect.Range
import com.google.common.truth.Truth
import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.flow.MutableStateFlow
+import kotlinx.coroutines.flow.emptyFlow
import kotlinx.coroutines.test.runCurrent
import kotlinx.coroutines.test.runTest
import org.junit.Before
@@ -58,6 +65,11 @@
private val keyguardRepository = kosmos.fakeKeyguardRepository
private lateinit var underTest: LockscreenToPrimaryBouncerTransitionViewModel
+ private val transitionState =
+ MutableStateFlow<ObservableTransitionState>(
+ ObservableTransitionState.Idle(Scenes.Lockscreen)
+ )
+
companion object {
@JvmStatic
@Parameters(name = "{0}")
@@ -76,6 +88,7 @@
}
@Test
+ @BrokenWithSceneContainer(330311871)
fun deviceEntryParentViewAlpha_shadeExpanded() =
testScope.runTest {
val actual by collectLastValue(underTest.deviceEntryParentViewAlpha)
@@ -107,6 +120,17 @@
shadeExpanded(false)
runCurrent()
+ kosmos.sceneContainerRepository.setTransitionState(transitionState)
+ transitionState.value =
+ ObservableTransitionState.Transition(
+ fromScene = Scenes.Lockscreen,
+ toScene = Scenes.Bouncer,
+ emptyFlow(),
+ emptyFlow(),
+ false,
+ emptyFlow()
+ )
+ runCurrent()
// fade out
repository.sendTransitionStep(step(0f, TransitionState.STARTED))
runCurrent()
@@ -132,7 +156,9 @@
): TransitionStep {
return TransitionStep(
from = KeyguardState.LOCKSCREEN,
- to = KeyguardState.PRIMARY_BOUNCER,
+ to =
+ if (SceneContainerFlag.isEnabled) KeyguardState.UNDEFINED
+ else KeyguardState.PRIMARY_BOUNCER,
value = value,
transitionState = state,
ownerName = "LockscreenToPrimaryBouncerTransitionViewModelTest"
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/media/controls/domain/interactor/MediaControlInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/media/controls/domain/interactor/MediaControlInteractorTest.kt
index d9224d7..365a7c3 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/media/controls/domain/interactor/MediaControlInteractorTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/media/controls/domain/interactor/MediaControlInteractorTest.kt
@@ -27,12 +27,16 @@
import com.android.systemui.animation.DialogTransitionAnimator
import com.android.systemui.animation.Expandable
import com.android.systemui.bluetooth.mockBroadcastDialogController
+import com.android.systemui.concurrency.fakeExecutor
import com.android.systemui.coroutines.collectLastValue
import com.android.systemui.kosmos.testScope
+import com.android.systemui.media.controls.data.repository.mediaDataRepository
import com.android.systemui.media.controls.domain.pipeline.MediaDataFilterImpl
+import com.android.systemui.media.controls.domain.pipeline.MediaDataProcessor
import com.android.systemui.media.controls.domain.pipeline.interactor.MediaControlInteractor
import com.android.systemui.media.controls.domain.pipeline.interactor.mediaControlInteractor
import com.android.systemui.media.controls.domain.pipeline.mediaDataFilter
+import com.android.systemui.media.controls.domain.pipeline.mediaDataProcessor
import com.android.systemui.media.controls.shared.model.MediaData
import com.android.systemui.media.controls.util.mediaInstanceId
import com.android.systemui.media.mediaOutputDialogManager
@@ -41,16 +45,16 @@
import com.android.systemui.statusbar.notificationLockscreenUserManager
import com.android.systemui.statusbar.policy.keyguardStateController
import com.android.systemui.testKosmos
-import com.android.systemui.util.mockito.any
-import com.android.systemui.util.mockito.eq
-import com.android.systemui.util.mockito.mock
-import com.android.systemui.util.mockito.whenever
import com.google.common.truth.Truth.assertThat
import kotlinx.coroutines.test.runTest
import org.junit.Test
import org.junit.runner.RunWith
import org.mockito.Mockito.never
import org.mockito.Mockito.verify
+import org.mockito.kotlin.any
+import org.mockito.kotlin.eq
+import org.mockito.kotlin.mock
+import org.mockito.kotlin.whenever
@SmallTest
@RunWith(AndroidJUnit4::class)
@@ -117,7 +121,7 @@
whenever(kosmos.activityIntentHelper.wouldPendingShowOverLockscreen(any(), any()))
.thenReturn(true)
- val clickIntent = mock<PendingIntent> { whenever(isActivity).thenReturn(true) }
+ val clickIntent = mock<PendingIntent> { whenever(it.isActivity).thenReturn(true) }
val expandable = mock<Expandable>()
underTest.startClickIntent(expandable, clickIntent)
@@ -129,7 +133,7 @@
fun startClickIntent_hideOverLockscreen() {
whenever(keyguardStateController.isShowing).thenReturn(false)
- val clickIntent = mock<PendingIntent> { whenever(isActivity).thenReturn(true) }
+ val clickIntent = mock<PendingIntent> { whenever(it.isActivity).thenReturn(true) }
val expandable = mock<Expandable>()
val activityController = mock<ActivityTransitionAnimator.Controller>()
whenever(expandable.activityTransitionController(any())).thenReturn(activityController)
@@ -146,7 +150,7 @@
whenever(kosmos.activityIntentHelper.wouldPendingShowOverLockscreen(any(), any()))
.thenReturn(true)
- val deviceIntent = mock<PendingIntent> { whenever(isActivity).thenReturn(true) }
+ val deviceIntent = mock<PendingIntent> { whenever(it.isActivity).thenReturn(true) }
underTest.startDeviceIntent(deviceIntent)
@@ -159,7 +163,7 @@
whenever(kosmos.activityIntentHelper.wouldPendingShowOverLockscreen(any(), any()))
.thenReturn(true)
- val deviceIntent = mock<PendingIntent> { whenever(isActivity).thenReturn(false) }
+ val deviceIntent = mock<PendingIntent> { whenever(it.isActivity).thenReturn(false) }
underTest.startDeviceIntent(deviceIntent)
@@ -170,7 +174,7 @@
fun startDeviceIntent_hideOverLockscreen() {
whenever(keyguardStateController.isShowing).thenReturn(false)
- val deviceIntent = mock<PendingIntent> { whenever(isActivity).thenReturn(true) }
+ val deviceIntent = mock<PendingIntent> { whenever(it.isActivity).thenReturn(true) }
underTest.startDeviceIntent(deviceIntent)
@@ -211,6 +215,21 @@
)
}
+ @Test
+ fun removeMediaControl() {
+ val listener = mock<MediaDataProcessor.Listener>()
+ kosmos.mediaDataProcessor.addInternalListener(listener)
+
+ var mediaData = MediaData(userId = USER_ID, instanceId = instanceId, artist = ARTIST)
+ kosmos.mediaDataRepository.addMediaEntry(KEY, mediaData)
+
+ underTest.removeMediaControl(null, instanceId, 0L)
+ kosmos.fakeExecutor.advanceClockToNext()
+ kosmos.fakeExecutor.runAllReady()
+
+ verify(listener).onMediaDataRemoved(eq(KEY), eq(true))
+ }
+
companion object {
private const val USER_ID = 0
private const val KEY = "key"
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/media/controls/ui/viewmodel/MediaCarouselViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/media/controls/ui/viewmodel/MediaCarouselViewModelTest.kt
index 4226a9d..0551bfb 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/media/controls/ui/viewmodel/MediaCarouselViewModelTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/media/controls/ui/viewmodel/MediaCarouselViewModelTest.kt
@@ -109,7 +109,7 @@
assertThat(mediaControl2.instanceId).isEqualTo(instanceId2)
assertThat(mediaControl1.instanceId).isEqualTo(instanceId1)
- underTest.onAttached()
+ underTest.onReorderingAllowed()
mediaControl1 = sortedMedia?.get(0) as MediaCommonViewModel.MediaControl
mediaControl2 = sortedMedia?.get(1) as MediaCommonViewModel.MediaControl
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/notifications/ui/viewmodel/NotificationsShadeSceneViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/notifications/ui/viewmodel/NotificationsShadeSceneViewModelTest.kt
index 5661bd3..9d8ec95 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/notifications/ui/viewmodel/NotificationsShadeSceneViewModelTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/notifications/ui/viewmodel/NotificationsShadeSceneViewModelTest.kt
@@ -51,8 +51,8 @@
private val kosmos = testKosmos()
private val testScope = kosmos.testScope
- private val sceneInteractor = kosmos.sceneInteractor
- private val deviceUnlockedInteractor = kosmos.deviceUnlockedInteractor
+ private val sceneInteractor by lazy { kosmos.sceneInteractor }
+ private val deviceUnlockedInteractor by lazy { kosmos.deviceUnlockedInteractor }
private val underTest = kosmos.notificationsShadeSceneViewModel
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/base/actions/QSTileIntentUserInputHandlerTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/base/actions/QSTileIntentUserInputHandlerTest.kt
index bf48784..02a8141 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/base/actions/QSTileIntentUserInputHandlerTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/base/actions/QSTileIntentUserInputHandlerTest.kt
@@ -69,6 +69,15 @@
}
@Test
+ fun testPassesIntentToStarter_dismissShadeAndShowOverLockScreenWhenLocked() {
+ val intent = Intent("test.ACTION")
+
+ underTest.handle(null, intent, true)
+
+ verify(activityStarter).startActivity(eq(intent), eq(true), any(), eq(true))
+ }
+
+ @Test
fun testPassesActivityPendingIntentToStarterAsPendingIntent() {
val pendingIntent = mock<PendingIntent> { whenever(isActivity).thenReturn(true) }
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/qr/domain/interactor/QRCodeScannerTileDataInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/qr/domain/interactor/QRCodeScannerTileDataInteractorTest.kt
new file mode 100644
index 0000000..c41ce2f
--- /dev/null
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/qr/domain/interactor/QRCodeScannerTileDataInteractorTest.kt
@@ -0,0 +1,112 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.qs.tiles.impl.qr.domain.interactor
+
+import android.content.Intent
+import android.os.UserHandle
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.SmallTest
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.coroutines.collectLastValue
+import com.android.systemui.qrcodescanner.controller.QRCodeScannerController
+import com.android.systemui.qrcodescanner.controller.QRCodeScannerController.Callback
+import com.android.systemui.qs.tiles.base.interactor.DataUpdateTrigger
+import com.android.systemui.qs.tiles.impl.qr.domain.model.QRCodeScannerTileModel
+import com.android.systemui.util.mockito.argumentCaptor
+import com.android.systemui.util.mockito.mock
+import com.android.systemui.util.mockito.whenever
+import com.google.common.truth.Truth.assertThat
+import kotlinx.coroutines.flow.flowOf
+import kotlinx.coroutines.test.StandardTestDispatcher
+import kotlinx.coroutines.test.TestScope
+import kotlinx.coroutines.test.runCurrent
+import kotlinx.coroutines.test.runTest
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.Mockito.verify
+
+@SmallTest
+@RunWith(AndroidJUnit4::class)
+class QRCodeScannerTileDataInteractorTest : SysuiTestCase() {
+
+ private val testUser = UserHandle.of(1)!!
+ private val testDispatcher = StandardTestDispatcher()
+ private val scope = TestScope(testDispatcher)
+ private val testIntent = mock<Intent>()
+ private val qrCodeScannerController =
+ mock<QRCodeScannerController> {
+ whenever(intent).thenReturn(testIntent)
+ whenever(isAbleToLaunchScannerActivity).thenReturn(false)
+ }
+ private val testAvailableModel = QRCodeScannerTileModel.Available(testIntent)
+ private val testUnavailableModel = QRCodeScannerTileModel.TemporarilyUnavailable
+
+ private val underTest: QRCodeScannerTileDataInteractor =
+ QRCodeScannerTileDataInteractor(
+ testDispatcher,
+ scope.backgroundScope,
+ qrCodeScannerController,
+ )
+
+ @Test
+ fun availability_matchesController_cameraNotAvailable() =
+ scope.runTest {
+ val expectedAvailability = false
+ whenever(qrCodeScannerController.isCameraAvailable).thenReturn(false)
+
+ val availability by collectLastValue(underTest.availability(testUser))
+
+ assertThat(availability).isEqualTo(expectedAvailability)
+ }
+
+ @Test
+ fun availability_matchesController_cameraIsAvailable() =
+ scope.runTest {
+ val expectedAvailability = true
+ whenever(qrCodeScannerController.isCameraAvailable).thenReturn(true)
+
+ val availability by collectLastValue(underTest.availability(testUser))
+
+ assertThat(availability).isEqualTo(expectedAvailability)
+ }
+
+ @Test
+ fun data_matchesController() =
+ scope.runTest {
+ val captor = argumentCaptor<Callback>()
+ val lastData by
+ collectLastValue(
+ underTest.tileData(testUser, flowOf(DataUpdateTrigger.InitialRequest))
+ )
+ runCurrent()
+
+ verify(qrCodeScannerController).addCallback(captor.capture())
+ val callback = captor.value
+
+ assertThat(lastData!!).isEqualTo(testUnavailableModel)
+
+ whenever(qrCodeScannerController.isAbleToLaunchScannerActivity).thenReturn(true)
+ callback.onQRCodeScannerActivityChanged()
+ runCurrent()
+ assertThat(lastData!!).isEqualTo(testAvailableModel)
+
+ whenever(qrCodeScannerController.isAbleToLaunchScannerActivity).thenReturn(false)
+ callback.onQRCodeScannerActivityChanged()
+ runCurrent()
+ assertThat(lastData!!).isEqualTo(testUnavailableModel)
+ }
+}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/qr/domain/interactor/QRCodeScannerTileUserActionInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/qr/domain/interactor/QRCodeScannerTileUserActionInteractorTest.kt
new file mode 100644
index 0000000..312f180
--- /dev/null
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/qr/domain/interactor/QRCodeScannerTileUserActionInteractorTest.kt
@@ -0,0 +1,81 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.qs.tiles.impl.qr.domain.interactor
+
+import android.content.Intent
+import android.platform.test.annotations.EnabledOnRavenwood
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.SmallTest
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.kosmos.Kosmos
+import com.android.systemui.qs.tiles.base.actions.QSTileIntentUserInputHandlerSubject
+import com.android.systemui.qs.tiles.base.actions.qsTileIntentUserInputHandler
+import com.android.systemui.qs.tiles.base.interactor.QSTileInputTestKtx
+import com.android.systemui.qs.tiles.impl.qr.domain.model.QRCodeScannerTileModel
+import com.android.systemui.qs.tiles.impl.qr.qrCodeScannerTileUserActionInteractor
+import com.android.systemui.util.mockito.mock
+import kotlinx.coroutines.test.runTest
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@SmallTest
+@EnabledOnRavenwood
+@RunWith(AndroidJUnit4::class)
+class QRCodeScannerTileUserActionInteractorTest : SysuiTestCase() {
+ val kosmos = Kosmos()
+ private val inputHandler = kosmos.qsTileIntentUserInputHandler
+ private val underTest = kosmos.qrCodeScannerTileUserActionInteractor
+ private val intent = mock<Intent>()
+
+ @Test
+ fun handleClick_available() = runTest {
+ val inputModel = QRCodeScannerTileModel.Available(intent)
+
+ underTest.handleInput(QSTileInputTestKtx.click(inputModel))
+
+ QSTileIntentUserInputHandlerSubject.assertThat(inputHandler).handledOneIntentInput {
+ intent
+ }
+ }
+
+ @Test
+ fun handleClick_temporarilyUnavailable() = runTest {
+ val inputModel = QRCodeScannerTileModel.TemporarilyUnavailable
+
+ underTest.handleInput(QSTileInputTestKtx.click(inputModel))
+
+ QSTileIntentUserInputHandlerSubject.assertThat(inputHandler).handledNoInputs()
+ }
+
+ @Test
+ fun handleLongClick_available() = runTest {
+ val inputModel = QRCodeScannerTileModel.Available(intent)
+
+ underTest.handleInput(QSTileInputTestKtx.longClick(inputModel))
+
+ QSTileIntentUserInputHandlerSubject.assertThat(inputHandler).handledNoInputs()
+ }
+
+ @Test
+ fun handleLongClick_temporarilyUnavailable() = runTest {
+ val inputModel = QRCodeScannerTileModel.TemporarilyUnavailable
+
+ underTest.handleInput(QSTileInputTestKtx.longClick(inputModel))
+
+ QSTileIntentUserInputHandlerSubject.assertThat(inputHandler).handledNoInputs()
+ }
+}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/qr/ui/QRCodeScannerTileMapperTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/qr/ui/QRCodeScannerTileMapperTest.kt
new file mode 100644
index 0000000..d26a213
--- /dev/null
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/qr/ui/QRCodeScannerTileMapperTest.kt
@@ -0,0 +1,114 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.qs.tiles.impl.qr.ui
+
+import android.content.Intent
+import android.graphics.drawable.TestStubDrawable
+import android.widget.Switch
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.SmallTest
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.common.shared.model.Icon
+import com.android.systemui.kosmos.Kosmos
+import com.android.systemui.qs.tiles.impl.custom.QSTileStateSubject
+import com.android.systemui.qs.tiles.impl.qr.domain.model.QRCodeScannerTileModel
+import com.android.systemui.qs.tiles.impl.qr.qsQRCodeScannerTileConfig
+import com.android.systemui.qs.tiles.viewmodel.QSTileState
+import com.android.systemui.util.mockito.mock
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@SmallTest
+@RunWith(AndroidJUnit4::class)
+class QRCodeScannerTileMapperTest : SysuiTestCase() {
+ private val kosmos = Kosmos()
+ private val config = kosmos.qsQRCodeScannerTileConfig
+
+ private lateinit var mapper: QRCodeScannerTileMapper
+
+ @Before
+ fun setup() {
+ mapper =
+ QRCodeScannerTileMapper(
+ context.orCreateTestableResources
+ .apply {
+ addOverride(
+ com.android.systemui.res.R.drawable.ic_qr_code_scanner,
+ TestStubDrawable()
+ )
+ }
+ .resources,
+ context.theme
+ )
+ }
+
+ @Test
+ fun availableModel() {
+ val mockIntent = mock<Intent>()
+ val inputModel = QRCodeScannerTileModel.Available(mockIntent)
+
+ val outputState = mapper.map(config, inputModel)
+
+ val expectedState =
+ createQRCodeScannerTileState(
+ QSTileState.ActivationState.INACTIVE,
+ null,
+ )
+ QSTileStateSubject.assertThat(outputState).isEqualTo(expectedState)
+ }
+
+ @Test
+ fun temporarilyUnavailableModel() {
+ val inputModel = QRCodeScannerTileModel.TemporarilyUnavailable
+
+ val outputState = mapper.map(config, inputModel)
+
+ val expectedState =
+ createQRCodeScannerTileState(
+ QSTileState.ActivationState.UNAVAILABLE,
+ context.getString(
+ com.android.systemui.res.R.string.qr_code_scanner_updating_secondary_label
+ )
+ )
+ QSTileStateSubject.assertThat(outputState).isEqualTo(expectedState)
+ }
+
+ private fun createQRCodeScannerTileState(
+ activationState: QSTileState.ActivationState,
+ secondaryLabel: String?,
+ ): QSTileState {
+ val label = context.getString(com.android.systemui.res.R.string.qr_code_scanner_title)
+ return QSTileState(
+ {
+ Icon.Loaded(
+ context.getDrawable(com.android.systemui.res.R.drawable.ic_qr_code_scanner)!!,
+ null
+ )
+ },
+ label,
+ activationState,
+ secondaryLabel,
+ setOf(QSTileState.UserAction.CLICK),
+ label,
+ null,
+ QSTileState.SideViewIcon.Chevron,
+ QSTileState.EnabledState.ENABLED,
+ Switch::class.qualifiedName
+ )
+ }
+}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/ui/adapter/QSSceneAdapterImplTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/ui/adapter/QSSceneAdapterImplTest.kt
index c660ff3..afe7b8f 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/ui/adapter/QSSceneAdapterImplTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/ui/adapter/QSSceneAdapterImplTest.kt
@@ -257,7 +257,7 @@
runCurrent()
clearInvocations(qsImpl!!)
- underTest.setState(QSSceneAdapter.State.UnsquishingQQS(squishiness))
+ underTest.setState(QSSceneAdapter.State.UnsquishingQQS { squishiness })
with(qsImpl!!) {
verify(this).setQsVisible(true)
verify(this)
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/ui/adapter/QSSceneAdapterTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/ui/adapter/QSSceneAdapterTest.kt
index ebd65fd..63ce67c 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/ui/adapter/QSSceneAdapterTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/ui/adapter/QSSceneAdapterTest.kt
@@ -32,7 +32,7 @@
@Test
fun expanding_squishiness1() {
- assertThat(QSSceneAdapter.State.Expanding(0.3f).squishiness).isEqualTo(1f)
+ assertThat(QSSceneAdapter.State.Expanding(0.3f).squishiness()).isEqualTo(1f)
}
@Test
@@ -51,14 +51,14 @@
@Test
fun unsquishingQQS_expansionSameAsQQS() {
val squishiness = 0.6f
- assertThat(QSSceneAdapter.State.UnsquishingQQS(squishiness).expansion)
+ assertThat(QSSceneAdapter.State.UnsquishingQQS { squishiness }.expansion)
.isEqualTo(QSSceneAdapter.State.QQS.expansion)
}
@Test
fun unsquishingQS_expansionSameAsQS() {
val squishiness = 0.6f
- assertThat(QSSceneAdapter.State.UnsquishingQS(squishiness).expansion)
+ assertThat(QSSceneAdapter.State.UnsquishingQS { squishiness }.expansion)
.isEqualTo(QSSceneAdapter.State.QS.expansion)
}
}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/scene/domain/interactor/SceneBackInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/scene/domain/interactor/SceneBackInteractorTest.kt
index c75e297..e3108ad 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/scene/domain/interactor/SceneBackInteractorTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/scene/domain/interactor/SceneBackInteractorTest.kt
@@ -45,11 +45,11 @@
private val kosmos = testKosmos()
private val testScope = kosmos.testScope
- private val sceneInteractor = kosmos.sceneInteractor
- private val sceneContainerStartable = kosmos.sceneContainerStartable
- private val authenticationInteractor = kosmos.authenticationInteractor
+ private val sceneInteractor by lazy { kosmos.sceneInteractor }
+ private val sceneContainerStartable by lazy { kosmos.sceneContainerStartable }
+ private val authenticationInteractor by lazy { kosmos.authenticationInteractor }
- private val underTest = kosmos.sceneBackInteractor
+ private val underTest by lazy { kosmos.sceneBackInteractor }
@Test
@EnableSceneContainer
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/scene/domain/startable/SceneContainerStartableTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/scene/domain/startable/SceneContainerStartableTest.kt
index 93465c8..677477d 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/scene/domain/startable/SceneContainerStartableTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/scene/domain/startable/SceneContainerStartableTest.kt
@@ -437,12 +437,12 @@
runCurrent()
assertThat(
sysUiState.flags and
- QuickStepContract.SYSUI_STATE_STATUS_BAR_KEYGUARD_SHOWING_OCCLUDED != 0
+ QuickStepContract.SYSUI_STATE_STATUS_BAR_KEYGUARD_SHOWING_OCCLUDED != 0L
)
.isTrue()
assertThat(
sysUiState.flags and
- QuickStepContract.SYSUI_STATE_STATUS_BAR_KEYGUARD_SHOWING != 0
+ QuickStepContract.SYSUI_STATE_STATUS_BAR_KEYGUARD_SHOWING != 0L
)
.isFalse()
@@ -450,12 +450,12 @@
runCurrent()
assertThat(
sysUiState.flags and
- QuickStepContract.SYSUI_STATE_STATUS_BAR_KEYGUARD_SHOWING_OCCLUDED != 0
+ QuickStepContract.SYSUI_STATE_STATUS_BAR_KEYGUARD_SHOWING_OCCLUDED != 0L
)
.isFalse()
assertThat(
sysUiState.flags and
- QuickStepContract.SYSUI_STATE_STATUS_BAR_KEYGUARD_SHOWING != 0
+ QuickStepContract.SYSUI_STATE_STATUS_BAR_KEYGUARD_SHOWING != 0L
)
.isTrue()
}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/domain/interactor/PanelExpansionInteractorImplTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/domain/interactor/PanelExpansionInteractorImplTest.kt
index e11a8f1..851b7b9 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/domain/interactor/PanelExpansionInteractorImplTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/domain/interactor/PanelExpansionInteractorImplTest.kt
@@ -52,9 +52,9 @@
private val kosmos = testKosmos()
private val testScope = kosmos.testScope
- private val deviceUnlockedInteractor = kosmos.deviceUnlockedInteractor
- private val sceneInteractor = kosmos.sceneInteractor
- private val shadeAnimationInteractor = kosmos.shadeAnimationInteractor
+ private val deviceUnlockedInteractor by lazy { kosmos.deviceUnlockedInteractor }
+ private val sceneInteractor by lazy { kosmos.sceneInteractor }
+ private val shadeAnimationInteractor by lazy { kosmos.shadeAnimationInteractor }
private val transitionState =
MutableStateFlow<ObservableTransitionState>(
ObservableTransitionState.Idle(Scenes.Lockscreen)
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/NotificationMediaManagerTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/NotificationMediaManagerTest.kt
new file mode 100644
index 0000000..8cb811d
--- /dev/null
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/NotificationMediaManagerTest.kt
@@ -0,0 +1,117 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.statusbar
+
+import android.platform.test.annotations.DisableFlags
+import android.platform.test.annotations.EnableFlags
+import android.service.notification.NotificationListenerService
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.SmallTest
+import com.android.systemui.Flags
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.dump.dumpManager
+import com.android.systemui.media.controls.domain.pipeline.MediaDataManager
+import com.android.systemui.statusbar.notification.collection.NotificationEntry
+import com.android.systemui.statusbar.notification.collection.mockNotifCollection
+import com.android.systemui.statusbar.notification.collection.notifPipeline
+import com.android.systemui.statusbar.notification.collection.render.notificationVisibilityProvider
+import com.android.systemui.testKosmos
+import com.android.systemui.util.concurrency.FakeExecutor
+import com.android.systemui.util.time.FakeSystemClock
+import com.google.common.truth.Truth.assertThat
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.Mockito.never
+import org.mockito.Mockito.verify
+import org.mockito.kotlin.any
+import org.mockito.kotlin.argumentCaptor
+import org.mockito.kotlin.mock
+import org.mockito.kotlin.whenever
+
+@SmallTest
+@RunWith(AndroidJUnit4::class)
+class NotificationMediaManagerTest : SysuiTestCase() {
+
+ private val KEY = "KEY"
+
+ private val kosmos = testKosmos()
+ private val visibilityProvider = kosmos.notificationVisibilityProvider
+ private val notifPipeline = kosmos.notifPipeline
+ private val notifCollection = kosmos.mockNotifCollection
+ private val dumpManager = kosmos.dumpManager
+ private val mediaDataManager = mock<MediaDataManager>()
+ private val backgroundExecutor = FakeExecutor(FakeSystemClock())
+
+ private var listenerCaptor = argumentCaptor<MediaDataManager.Listener>()
+
+ private lateinit var notificationMediaManager: NotificationMediaManager
+
+ @Before
+ fun setup() {
+ notificationMediaManager =
+ NotificationMediaManager(
+ context,
+ visibilityProvider,
+ notifPipeline,
+ notifCollection,
+ mediaDataManager,
+ dumpManager,
+ backgroundExecutor,
+ )
+
+ verify(mediaDataManager).addListener(listenerCaptor.capture())
+ }
+
+ @Test
+ @EnableFlags(Flags.FLAG_MEDIA_CONTROLS_USER_INITIATED_DISMISS)
+ fun mediaDataRemoved_userInitiated_dismissNotif() {
+ val notifEntryCaptor = argumentCaptor<NotificationEntry>()
+ val notifEntry = mock<NotificationEntry>()
+ whenever(notifEntry.key).thenReturn(KEY)
+ whenever(notifEntry.ranking).thenReturn(NotificationListenerService.Ranking())
+ whenever(notifPipeline.allNotifs).thenReturn(listOf(notifEntry))
+
+ listenerCaptor.lastValue.onMediaDataRemoved(KEY, true)
+
+ verify(notifCollection).dismissNotification(notifEntryCaptor.capture(), any())
+ assertThat(notifEntryCaptor.lastValue.key).isEqualTo(KEY)
+ }
+
+ @Test
+ @EnableFlags(Flags.FLAG_MEDIA_CONTROLS_USER_INITIATED_DISMISS)
+ fun mediaDataRemoved_notUserInitiated_doesNotDismissNotif() {
+ listenerCaptor.lastValue.onMediaDataRemoved(KEY, false)
+
+ verify(notifCollection, never()).dismissNotification(any(), any())
+ }
+
+ @Test
+ @DisableFlags(Flags.FLAG_MEDIA_CONTROLS_USER_INITIATED_DISMISS)
+ fun mediaDataRemoved_notUserInitiated_flagOff_dismissNotif() {
+ val notifEntryCaptor = argumentCaptor<NotificationEntry>()
+ val notifEntry = mock<NotificationEntry>()
+ whenever(notifEntry.key).thenReturn(KEY)
+ whenever(notifEntry.ranking).thenReturn(NotificationListenerService.Ranking())
+ whenever(notifPipeline.allNotifs).thenReturn(listOf(notifEntry))
+
+ listenerCaptor.lastValue.onMediaDataRemoved(KEY, false)
+
+ verify(notifCollection).dismissNotification(notifEntryCaptor.capture(), any())
+ assertThat(notifEntryCaptor.lastValue.key).isEqualTo(KEY)
+ }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/ConversationCoordinatorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/collection/coordinator/ConversationCoordinatorTest.kt
similarity index 62%
rename from packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/ConversationCoordinatorTest.kt
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/collection/coordinator/ConversationCoordinatorTest.kt
index c5d7e1f..2853264 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/ConversationCoordinatorTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/collection/coordinator/ConversationCoordinatorTest.kt
@@ -23,9 +23,8 @@
import android.app.NotificationManager.IMPORTANCE_LOW
import android.platform.test.annotations.DisableFlags
import android.platform.test.annotations.EnableFlags
-import android.platform.test.flag.junit.SetFlagsRule
-import android.testing.AndroidTestingRunner
import android.testing.TestableLooper
+import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.statusbar.notification.collection.GroupEntry
@@ -44,25 +43,29 @@
import com.android.systemui.statusbar.notification.collection.render.NodeController
import com.android.systemui.statusbar.notification.icon.ConversationIconManager
import com.android.systemui.statusbar.notification.people.PeopleNotificationIdentifier
+import com.android.systemui.statusbar.notification.people.PeopleNotificationIdentifier.Companion.PeopleNotificationType
+import com.android.systemui.statusbar.notification.people.PeopleNotificationIdentifier.Companion.TYPE_FULL_PERSON
import com.android.systemui.statusbar.notification.people.PeopleNotificationIdentifier.Companion.TYPE_IMPORTANT_PERSON
import com.android.systemui.statusbar.notification.people.PeopleNotificationIdentifier.Companion.TYPE_NON_PERSON
import com.android.systemui.statusbar.notification.people.PeopleNotificationIdentifier.Companion.TYPE_PERSON
+import com.android.systemui.statusbar.notification.people.PeopleNotificationIdentifierImpl
import com.android.systemui.util.mockito.eq
import com.android.systemui.util.mockito.withArgCaptor
import com.google.common.truth.Truth.assertThat
+import org.junit.Assert.assertEquals
import org.junit.Assert.assertFalse
import org.junit.Assert.assertTrue
import org.junit.Before
-import org.junit.Rule
import org.junit.Test
import org.junit.runner.RunWith
import org.mockito.Mock
import org.mockito.Mockito.verify
-import org.mockito.MockitoAnnotations
import org.mockito.Mockito.`when` as whenever
+import org.mockito.MockitoAnnotations
+import org.mockito.kotlin.mock
@SmallTest
-@RunWith(AndroidTestingRunner::class)
+@RunWith(AndroidJUnit4::class)
@TestableLooper.RunWithLooper
class ConversationCoordinatorTest : SysuiTestCase() {
// captured listeners and pluggables:
@@ -72,22 +75,20 @@
private lateinit var peopleComparator: NotifComparator
private lateinit var beforeRenderListListener: OnBeforeRenderListListener
+ private lateinit var peopleNotificationIdentifier: PeopleNotificationIdentifier
+ private lateinit var peopleAlertingSection: NotifSection
+
@Mock private lateinit var pipeline: NotifPipeline
@Mock private lateinit var conversationIconManager: ConversationIconManager
- @Mock private lateinit var peopleNotificationIdentifier: PeopleNotificationIdentifier
- @Mock private lateinit var channel: NotificationChannel
@Mock private lateinit var headerController: NodeController
- private lateinit var entry: NotificationEntry
- private lateinit var entryA: NotificationEntry
- private lateinit var entryB: NotificationEntry
private lateinit var coordinator: ConversationCoordinator
- @Rule @JvmField public val setFlagsRule = SetFlagsRule()
-
@Before
fun setUp() {
MockitoAnnotations.initMocks(this)
+ peopleNotificationIdentifier =
+ PeopleNotificationIdentifierImpl(mock(), GroupMembershipManagerImpl())
coordinator =
ConversationCoordinator(
peopleNotificationIdentifier,
@@ -95,7 +96,6 @@
HighPriorityProvider(peopleNotificationIdentifier, GroupMembershipManagerImpl()),
headerController
)
- whenever(channel.isImportantConversation).thenReturn(true)
coordinator.attach(pipeline)
@@ -110,49 +110,65 @@
if (!SortBySectionTimeFlag.isEnabled)
peopleComparator = peopleAlertingSectioner.comparator!!
- entry = NotificationEntryBuilder().setChannel(channel).build()
+ peopleAlertingSection = NotifSection(peopleAlertingSectioner, 0)
+ }
- val section = NotifSection(peopleAlertingSectioner, 0)
- entryA =
- NotificationEntryBuilder().setChannel(channel).setSection(section).setTag("A").build()
- entryB =
- NotificationEntryBuilder().setChannel(channel).setSection(section).setTag("B").build()
+ @Test
+ fun priorityPeopleSectionerClaimsOnlyImportantConversations() {
+ val sectioner = coordinator.priorityPeopleSectioner
+ assertTrue(sectioner.isInSection(makeEntryOfPeopleType(TYPE_IMPORTANT_PERSON)))
+ assertFalse(sectioner.isInSection(makeEntryOfPeopleType(TYPE_FULL_PERSON)))
+ assertFalse(sectioner.isInSection(makeEntryOfPeopleType(TYPE_PERSON)))
+ assertFalse(sectioner.isInSection(makeEntryOfPeopleType(TYPE_NON_PERSON)))
+ assertFalse(sectioner.isInSection(NotificationEntryBuilder().build()))
}
@Test
fun testPromotesImportantConversations() {
- // only promote important conversations
- assertTrue(promoter.shouldPromoteToTopLevel(entry))
+ assertTrue(promoter.shouldPromoteToTopLevel(makeEntryOfPeopleType(TYPE_IMPORTANT_PERSON)))
+ assertFalse(promoter.shouldPromoteToTopLevel(makeEntryOfPeopleType(TYPE_FULL_PERSON)))
+ assertFalse(promoter.shouldPromoteToTopLevel(makeEntryOfPeopleType(TYPE_PERSON)))
+ assertFalse(promoter.shouldPromoteToTopLevel(makeEntryOfPeopleType(TYPE_NON_PERSON)))
assertFalse(promoter.shouldPromoteToTopLevel(NotificationEntryBuilder().build()))
}
@Test
fun testPromotedImportantConversationsMakesSummaryUnimportant() {
- val altChildA = NotificationEntryBuilder().setTag("A").build()
- val altChildB = NotificationEntryBuilder().setTag("B").build()
- val summary = NotificationEntryBuilder().setId(2).setChannel(channel).build()
+ val importantChannel =
+ mock<NotificationChannel>().also {
+ whenever(it.isImportantConversation).thenReturn(true)
+ }
+ val otherChannel =
+ mock<NotificationChannel>().also {
+ whenever(it.isImportantConversation).thenReturn(false)
+ }
+ val importantChild =
+ makeEntryOfPeopleType(TYPE_IMPORTANT_PERSON) { setChannel(importantChannel) }
+ val altChildA =
+ makeEntryOfPeopleType(TYPE_FULL_PERSON) { setChannel(otherChannel).setTag("A") }
+ val altChildB =
+ makeEntryOfPeopleType(TYPE_FULL_PERSON) { setChannel(otherChannel).setTag("B") }
+ val summary =
+ makeEntryOfPeopleType(TYPE_IMPORTANT_PERSON) { setChannel(importantChannel).setId(2) }
val groupEntry =
GroupEntryBuilder()
.setParent(GroupEntry.ROOT_ENTRY)
.setSummary(summary)
- .setChildren(listOf(entry, altChildA, altChildB))
+ .setChildren(listOf(importantChild, altChildA, altChildB))
.build()
- assertTrue(promoter.shouldPromoteToTopLevel(entry))
+ assertTrue(promoter.shouldPromoteToTopLevel(importantChild))
assertFalse(promoter.shouldPromoteToTopLevel(altChildA))
assertFalse(promoter.shouldPromoteToTopLevel(altChildB))
- NotificationEntryBuilder.setNewParent(entry, GroupEntry.ROOT_ENTRY)
- GroupEntryBuilder.getRawChildren(groupEntry).remove(entry)
- beforeRenderListListener.onBeforeRenderList(listOf(entry, groupEntry))
+ NotificationEntryBuilder.setNewParent(importantChild, GroupEntry.ROOT_ENTRY)
+ GroupEntryBuilder.getRawChildren(groupEntry).remove(importantChild)
+ beforeRenderListListener.onBeforeRenderList(listOf(importantChild, groupEntry))
verify(conversationIconManager).setUnimportantConversations(eq(listOf(summary.key)))
}
@Test
fun testInAlertingPeopleSectionWhenTheImportanceIsAtLeastDefault() {
// GIVEN
- val alertingEntry =
- NotificationEntryBuilder().setChannel(channel).setImportance(IMPORTANCE_DEFAULT).build()
- whenever(peopleNotificationIdentifier.getPeopleNotificationType(alertingEntry))
- .thenReturn(TYPE_PERSON)
+ val alertingEntry = makeEntryOfPeopleType(TYPE_PERSON) { setImportance(IMPORTANCE_DEFAULT) }
// put alerting people notifications in this section
assertThat(peopleAlertingSectioner.isInSection(alertingEntry)).isTrue()
@@ -162,10 +178,7 @@
@EnableFlags(Flags.FLAG_SORT_SECTION_BY_TIME)
fun testInAlertingPeopleSectionWhenTheImportanceIsLowerThanDefault() {
// GIVEN
- val silentEntry =
- NotificationEntryBuilder().setChannel(channel).setImportance(IMPORTANCE_LOW).build()
- whenever(peopleNotificationIdentifier.getPeopleNotificationType(silentEntry))
- .thenReturn(TYPE_PERSON)
+ val silentEntry = makeEntryOfPeopleType(TYPE_PERSON) { setImportance(IMPORTANCE_LOW) }
// THEN put silent people notifications in alerting section
assertThat(peopleAlertingSectioner.isInSection(silentEntry)).isTrue()
@@ -175,10 +188,7 @@
@DisableFlags(Flags.FLAG_SORT_SECTION_BY_TIME)
fun testInSilentPeopleSectionWhenTheImportanceIsLowerThanDefault() {
// GIVEN
- val silentEntry =
- NotificationEntryBuilder().setChannel(channel).setImportance(IMPORTANCE_LOW).build()
- whenever(peopleNotificationIdentifier.getPeopleNotificationType(silentEntry))
- .thenReturn(TYPE_PERSON)
+ val silentEntry = makeEntryOfPeopleType(TYPE_PERSON) { setImportance(IMPORTANCE_LOW) }
// THEN put silent people notifications in this section
assertThat(peopleSilentSectioner.isInSection(silentEntry)).isTrue()
@@ -191,18 +201,14 @@
@Test
fun testNotInPeopleSection() {
// GIVEN
- val entry =
- NotificationEntryBuilder().setChannel(channel).setImportance(IMPORTANCE_LOW).build()
+ val entry = makeEntryOfPeopleType(TYPE_NON_PERSON) { setImportance(IMPORTANCE_LOW) }
val importantEntry =
- NotificationEntryBuilder().setChannel(channel).setImportance(IMPORTANCE_HIGH).build()
- whenever(peopleNotificationIdentifier.getPeopleNotificationType(entry))
- .thenReturn(TYPE_NON_PERSON)
- whenever(peopleNotificationIdentifier.getPeopleNotificationType(importantEntry))
- .thenReturn(TYPE_NON_PERSON)
+ makeEntryOfPeopleType(TYPE_NON_PERSON) { setImportance(IMPORTANCE_HIGH) }
// THEN - only put people notification either silent or alerting
- if (!SortBySectionTimeFlag.isEnabled)
+ if (!SortBySectionTimeFlag.isEnabled) {
assertThat(peopleSilentSectioner.isInSection(entry)).isFalse()
+ }
assertThat(peopleAlertingSectioner.isInSection(importantEntry)).isFalse()
}
@@ -210,22 +216,16 @@
fun testInAlertingPeopleSectionWhenThereIsAnImportantChild() {
// GIVEN
val altChildA =
- NotificationEntryBuilder().setTag("A").setImportance(IMPORTANCE_DEFAULT).build()
- val altChildB = NotificationEntryBuilder().setTag("B").setImportance(IMPORTANCE_LOW).build()
- val summary =
- NotificationEntryBuilder()
- .setId(2)
- .setImportance(IMPORTANCE_LOW)
- .setChannel(channel)
- .build()
+ makeEntryOfPeopleType(TYPE_NON_PERSON) { setTag("A").setImportance(IMPORTANCE_DEFAULT) }
+ val altChildB =
+ makeEntryOfPeopleType(TYPE_NON_PERSON) { setTag("B").setImportance(IMPORTANCE_LOW) }
+ val summary = makeEntryOfPeopleType(TYPE_PERSON) { setId(2).setImportance(IMPORTANCE_LOW) }
val groupEntry =
GroupEntryBuilder()
.setParent(GroupEntry.ROOT_ENTRY)
.setSummary(summary)
.setChildren(listOf(altChildA, altChildB))
.build()
- whenever(peopleNotificationIdentifier.getPeopleNotificationType(summary))
- .thenReturn(TYPE_PERSON)
// THEN
assertThat(peopleAlertingSectioner.isInSection(groupEntry)).isTrue()
}
@@ -233,10 +233,12 @@
@Test
@DisableFlags(Flags.FLAG_SORT_SECTION_BY_TIME)
fun testComparatorPutsImportantPeopleFirst() {
- whenever(peopleNotificationIdentifier.getPeopleNotificationType(entryA))
- .thenReturn(TYPE_IMPORTANT_PERSON)
- whenever(peopleNotificationIdentifier.getPeopleNotificationType(entryB))
- .thenReturn(TYPE_PERSON)
+ val entryA =
+ makeEntryOfPeopleType(TYPE_IMPORTANT_PERSON) {
+ setSection(peopleAlertingSection).setTag("A")
+ }
+ val entryB =
+ makeEntryOfPeopleType(TYPE_PERSON) { setSection(peopleAlertingSection).setTag("B") }
// only put people notifications in this section
assertThat(peopleComparator.compare(entryA, entryB)).isEqualTo(-1)
@@ -245,10 +247,10 @@
@Test
@DisableFlags(Flags.FLAG_SORT_SECTION_BY_TIME)
fun testComparatorEquatesPeopleWithSameType() {
- whenever(peopleNotificationIdentifier.getPeopleNotificationType(entryA))
- .thenReturn(TYPE_PERSON)
- whenever(peopleNotificationIdentifier.getPeopleNotificationType(entryB))
- .thenReturn(TYPE_PERSON)
+ val entryA =
+ makeEntryOfPeopleType(TYPE_PERSON) { setSection(peopleAlertingSection).setTag("A") }
+ val entryB =
+ makeEntryOfPeopleType(TYPE_PERSON) { setSection(peopleAlertingSection).setTag("B") }
// only put people notifications in this section
assertThat(peopleComparator.compare(entryA, entryB)).isEqualTo(0)
@@ -259,4 +261,23 @@
fun testNoSecondarySortForConversations() {
assertThat(peopleAlertingSectioner.comparator).isNull()
}
+
+ private fun makeEntryOfPeopleType(
+ @PeopleNotificationType type: Int,
+ buildBlock: NotificationEntryBuilder.() -> Unit = {}
+ ): NotificationEntry {
+ val channel: NotificationChannel = mock()
+ whenever(channel.isImportantConversation).thenReturn(type == TYPE_IMPORTANT_PERSON)
+ val entry =
+ NotificationEntryBuilder()
+ .updateRanking {
+ it.setIsConversation(type != TYPE_NON_PERSON)
+ it.setShortcutInfo(if (type >= TYPE_FULL_PERSON) mock() else null)
+ it.setChannel(channel)
+ }
+ .also(buildBlock)
+ .build()
+ assertEquals(type, peopleNotificationIdentifier.getPeopleNotificationType(entry))
+ return entry
+ }
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/VisualStabilityCoordinatorTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/collection/coordinator/VisualStabilityCoordinatorTest.java
similarity index 98%
rename from packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/VisualStabilityCoordinatorTest.java
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/collection/coordinator/VisualStabilityCoordinatorTest.java
index 50ae985..ce134e6 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/VisualStabilityCoordinatorTest.java
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/collection/coordinator/VisualStabilityCoordinatorTest.java
@@ -31,9 +31,9 @@
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
-import android.testing.AndroidTestingRunner;
import android.testing.TestableLooper;
+import androidx.test.ext.junit.runners.AndroidJUnit4;
import androidx.test.filters.SmallTest;
import com.android.compose.animation.scene.ObservableTransitionState;
@@ -57,6 +57,7 @@
import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.NotifStabilityManager;
import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.Pluggable;
import com.android.systemui.statusbar.notification.collection.provider.VisualStabilityProvider;
+import com.android.systemui.statusbar.notification.domain.interactor.SeenNotificationsInteractor;
import com.android.systemui.statusbar.policy.HeadsUpManager;
import com.android.systemui.util.concurrency.FakeExecutor;
import com.android.systemui.util.kotlin.JavaAdapter;
@@ -75,7 +76,7 @@
import org.mockito.verification.VerificationMode;
@SmallTest
-@RunWith(AndroidTestingRunner.class)
+@RunWith(AndroidJUnit4.class)
@TestableLooper.RunWithLooper
public class VisualStabilityCoordinatorTest extends SysuiTestCase {
@@ -86,6 +87,7 @@
@Mock private WakefulnessLifecycle mWakefulnessLifecycle;
@Mock private StatusBarStateController mStatusBarStateController;
@Mock private Pluggable.PluggableListener<NotifStabilityManager> mInvalidateListener;
+ @Mock private SeenNotificationsInteractor mSeenNotificationsInteractor;
@Mock private HeadsUpManager mHeadsUpManager;
@Mock private VisibilityLocationProvider mVisibilityLocationProvider;
@Mock private VisualStabilityProvider mVisualStabilityProvider;
@@ -121,6 +123,7 @@
mHeadsUpManager,
mShadeAnimationInteractor,
mJavaAdapter,
+ mSeenNotificationsInteractor,
mStatusBarStateController,
mVisibilityLocationProvider,
mVisualStabilityProvider,
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/NotificationsPlaceholderViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/NotificationsPlaceholderViewModelTest.kt
index 1f0812d..ee9fd349 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/NotificationsPlaceholderViewModelTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/NotificationsPlaceholderViewModelTest.kt
@@ -44,13 +44,4 @@
collectLastValue(kosmos.notificationStackAppearanceInteractor.shadeScrimBounds)
assertThat(stackBounds).isEqualTo(bounds)
}
-
- @Test
- fun onStackBoundsChanged() =
- kosmos.testScope.runTest {
- underTest.onStackBoundsChanged(top = 5f, bottom = 500f)
- assertThat(kosmos.notificationStackAppearanceInteractor.stackTop.value).isEqualTo(5f)
- assertThat(kosmos.notificationStackAppearanceInteractor.stackBottom.value)
- .isEqualTo(500f)
- }
}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/SharedNotificationContainerViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/SharedNotificationContainerViewModelTest.kt
index f2ce745..82e2bb7 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/SharedNotificationContainerViewModelTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/SharedNotificationContainerViewModelTest.kt
@@ -30,6 +30,7 @@
import com.android.systemui.common.ui.data.repository.fakeConfigurationRepository
import com.android.systemui.coroutines.collectLastValue
import com.android.systemui.coroutines.collectValues
+import com.android.systemui.flags.BrokenWithSceneContainer
import com.android.systemui.flags.DisableSceneContainer
import com.android.systemui.flags.EnableSceneContainer
import com.android.systemui.flags.Flags
@@ -107,18 +108,25 @@
val testScope = kosmos.testScope
val configurationRepository
get() = kosmos.fakeConfigurationRepository
+
val keyguardRepository
get() = kosmos.fakeKeyguardRepository
+
val keyguardInteractor
get() = kosmos.keyguardInteractor
+
val keyguardRootViewModel
get() = kosmos.keyguardRootViewModel
+
val keyguardTransitionRepository
get() = kosmos.fakeKeyguardTransitionRepository
+
val shadeTestUtil
get() = kosmos.shadeTestUtil
+
val sharedNotificationContainerInteractor
get() = kosmos.sharedNotificationContainerInteractor
+
val largeScreenHeaderHelper
get() = kosmos.mockLargeScreenHeaderHelper
@@ -660,9 +668,6 @@
overrideResource(R.bool.config_use_split_notification_shade, false)
configurationRepository.onAnyConfigurationChange()
- keyguardInteractor.setNotificationContainerBounds(
- NotificationContainerBounds(top = 1f, bottom = 2f)
- )
assertThat(maxNotifications).isEqualTo(10)
@@ -691,9 +696,6 @@
overrideResource(R.bool.config_use_split_notification_shade, false)
configurationRepository.onAnyConfigurationChange()
- keyguardInteractor.setNotificationContainerBounds(
- NotificationContainerBounds(top = 1f, bottom = 2f)
- )
assertThat(maxNotifications).isEqualTo(10)
@@ -728,9 +730,6 @@
overrideResource(R.bool.config_use_split_notification_shade, false)
configurationRepository.onAnyConfigurationChange()
- keyguardInteractor.setNotificationContainerBounds(
- NotificationContainerBounds(top = 1f, bottom = 2f)
- )
// -1 means No Limit
assertThat(maxNotifications).isEqualTo(-1)
@@ -823,6 +822,7 @@
}
@Test
+ @BrokenWithSceneContainer(330311871)
fun alphaDoesNotUpdateWhileGoneTransitionIsRunning() =
testScope.runTest {
val viewState = ViewStateAccessor()
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/phone/DozeServiceHostCoroutinesTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/phone/DozeServiceHostCoroutinesTest.kt
index 1cd12f0..7bc6948 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/phone/DozeServiceHostCoroutinesTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/phone/DozeServiceHostCoroutinesTest.kt
@@ -35,6 +35,7 @@
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.test.runCurrent
import kotlinx.coroutines.test.runTest
+import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
import org.mockito.Mockito.verify
@@ -46,30 +47,32 @@
private val kosmos = testKosmos()
private val testScope = kosmos.testScope
- private val sceneContainerRepository = kosmos.sceneContainerRepository
- private val keyguardInteractor = kosmos.keyguardInteractor
+ lateinit var underTest: DozeServiceHost
- val underTest =
- kosmos.dozeServiceHost.apply {
- initialize(
- /* centralSurfaces = */ mock(),
- /* statusBarKeyguardViewManager = */ mock(),
- /* notificationShadeWindowViewController = */ mock(),
- /* ambientIndicationContainer = */ mock(),
- )
- }
+ @Before
+ fun setup() {
+ underTest =
+ kosmos.dozeServiceHost.apply {
+ initialize(
+ /* centralSurfaces = */ mock(),
+ /* statusBarKeyguardViewManager = */ mock(),
+ /* notificationShadeWindowViewController = */ mock(),
+ /* ambientIndicationContainer = */ mock(),
+ )
+ }
+ }
@Test
@EnableSceneContainer
fun startStopDozing() =
testScope.runTest {
- val isDozing by collectLastValue(keyguardInteractor.isDozing)
+ val isDozing by collectLastValue(kosmos.keyguardInteractor.isDozing)
// GIVEN a callback is set
val callback: DozeHost.Callback = mock()
underTest.addCallback(callback)
// AND we are on the lock screen
- sceneContainerRepository.changeScene(Scenes.Lockscreen)
+ kosmos.sceneContainerRepository.changeScene(Scenes.Lockscreen)
// AND dozing is not requested yet
assertThat(underTest.dozingRequested).isFalse()
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/volume/domain/interactor/AudioVolumeInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/volume/domain/interactor/AudioVolumeInteractorTest.kt
index 675136c..a163ca0 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/volume/domain/interactor/AudioVolumeInteractorTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/volume/domain/interactor/AudioVolumeInteractorTest.kt
@@ -36,7 +36,6 @@
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.test.runCurrent
import kotlinx.coroutines.test.runTest
-import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
@@ -47,19 +46,8 @@
private val kosmos = testKosmos()
- private lateinit var underTest: AudioVolumeInteractor
-
- @Before
- fun setup() {
- with(kosmos) {
- underTest = AudioVolumeInteractor(audioRepository, notificationsSoundPolicyInteractor)
-
- audioRepository.setRingerMode(RingerMode(AudioManager.RINGER_MODE_NORMAL))
-
- notificationsSoundPolicyRepository.updateNotificationPolicy()
- notificationsSoundPolicyRepository.updateZenMode(ZenMode(Settings.Global.ZEN_MODE_OFF))
- }
- }
+ private val underTest: AudioVolumeInteractor =
+ with(kosmos) { AudioVolumeInteractor(audioRepository, notificationsSoundPolicyInteractor) }
@Test
fun setMuted_mutesStream() {
@@ -236,6 +224,55 @@
}
}
+ @Test
+ fun testReducingVolumeToMin_mutes() =
+ with(kosmos) {
+ testScope.runTest {
+ val audioStreamModel by
+ collectLastValue(audioRepository.getAudioStream(audioStream))
+ runCurrent()
+
+ underTest.setVolume(audioStream, audioStreamModel!!.minVolume)
+ runCurrent()
+
+ assertThat(audioStreamModel!!.isMuted).isTrue()
+ }
+ }
+
+ @Test
+ fun testIncreasingVolumeFromMin_unmutes() =
+ with(kosmos) {
+ testScope.runTest {
+ val audioStreamModel by
+ collectLastValue(audioRepository.getAudioStream(audioStream))
+ audioRepository.setMuted(audioStream, true)
+ audioRepository.setVolume(audioStream, audioStreamModel!!.minVolume)
+ runCurrent()
+
+ underTest.setVolume(audioStream, audioStreamModel!!.maxVolume)
+ runCurrent()
+
+ assertThat(audioStreamModel!!.isMuted).isFalse()
+ }
+ }
+
+ @Test
+ fun testUnmutingMinVolume_increasesVolume() =
+ with(kosmos) {
+ testScope.runTest {
+ val audioStreamModel by
+ collectLastValue(audioRepository.getAudioStream(audioStream))
+ audioRepository.setMuted(audioStream, true)
+ audioRepository.setVolume(audioStream, audioStreamModel!!.minVolume)
+ runCurrent()
+
+ underTest.setMuted(audioStream, false)
+ runCurrent()
+
+ assertThat(audioStreamModel!!.volume).isGreaterThan(audioStreamModel!!.minVolume)
+ }
+ }
+
private companion object {
val audioStream = AudioStream(AudioManager.STREAM_SYSTEM)
}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/volume/panel/component/anc/data/repository/AncSliceRepositoryTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/volume/panel/component/anc/data/repository/AncSliceRepositoryTest.kt
index dddf582..9a95274 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/volume/panel/component/anc/data/repository/AncSliceRepositoryTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/volume/panel/component/anc/data/repository/AncSliceRepositoryTest.kt
@@ -18,20 +18,13 @@
import android.bluetooth.BluetoothDevice
import android.net.Uri
+import android.testing.TestableLooper
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
-import com.android.settingslib.bluetooth.CachedBluetoothDevice
-import com.android.settingslib.media.BluetoothMediaDevice
-import com.android.settingslib.media.MediaDevice
import com.android.systemui.SysuiTestCase
import com.android.systemui.coroutines.collectLastValue
import com.android.systemui.kosmos.testScope
import com.android.systemui.testKosmos
-import com.android.systemui.util.mockito.any
-import com.android.systemui.util.mockito.mock
-import com.android.systemui.util.mockito.whenever
-import com.android.systemui.volume.localMediaRepository
-import com.android.systemui.volume.localMediaRepositoryFactory
import com.android.systemui.volume.panel.component.anc.FakeSliceFactory
import com.android.systemui.volume.panel.component.anc.sliceViewManager
import com.google.common.truth.Truth.assertThat
@@ -41,10 +34,14 @@
import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
+import org.mockito.kotlin.any
+import org.mockito.kotlin.mock
+import org.mockito.kotlin.whenever
@OptIn(ExperimentalCoroutinesApi::class)
@SmallTest
@RunWith(AndroidJUnit4::class)
+@TestableLooper.RunWithLooper(setAsMainLooper = true)
class AncSliceRepositoryTest : SysuiTestCase() {
private val kosmos = testKosmos()
@@ -57,23 +54,23 @@
val slice = FakeSliceFactory.createSlice(hasError = false, hasSliceItem = true)
whenever(sliceViewManager.bindSlice(any<Uri>())).thenReturn(slice)
- underTest =
- AncSliceRepositoryImpl(
- localMediaRepositoryFactory,
- testScope.testScheduler,
- testScope.testScheduler,
- sliceViewManager,
- )
+ underTest = AncSliceRepositoryImpl(testScope.testScheduler, sliceViewManager)
}
}
@Test
- fun noConnectedDevice_noSlice() {
+ fun connectedDevice_noUri_noSlice() {
with(kosmos) {
testScope.runTest {
- localMediaRepository.updateCurrentConnectedDevice(null)
-
- val slice by collectLastValue(underTest.ancSlice(1, false, false))
+ val slice by
+ collectLastValue(
+ underTest.ancSlice(
+ device = createMediaDevice(""),
+ width = 1,
+ isCollapsed = false,
+ hideLabel = false,
+ )
+ )
runCurrent()
assertThat(slice).isNull()
@@ -82,12 +79,18 @@
}
@Test
- fun connectedDevice_sliceReturned() {
+ fun connectedDevice_hasUri_sliceReturned() {
with(kosmos) {
testScope.runTest {
- localMediaRepository.updateCurrentConnectedDevice(createMediaDevice())
-
- val slice by collectLastValue(underTest.ancSlice(1, false, false))
+ val slice by
+ collectLastValue(
+ underTest.ancSlice(
+ device = createMediaDevice("content://test.slice"),
+ width = 1,
+ isCollapsed = false,
+ hideLabel = false,
+ )
+ )
runCurrent()
assertThat(slice).isNotNull()
@@ -95,21 +98,13 @@
}
}
- private fun createMediaDevice(sliceUri: String = "content://test.slice"): MediaDevice {
- val bluetoothDevice: BluetoothDevice = mock {
- whenever(getMetadata(any()))
- .thenReturn(
- ("<HEARABLE_CONTROL_SLICE_WITH_WIDTH>" +
- sliceUri +
- "</HEARABLE_CONTROL_SLICE_WITH_WIDTH>")
- .toByteArray()
- )
- }
- val cachedBluetoothDevice: CachedBluetoothDevice = mock {
- whenever(device).thenReturn(bluetoothDevice)
- }
- return mock<BluetoothMediaDevice> {
- whenever(cachedDevice).thenReturn(cachedBluetoothDevice)
- }
+ private fun createMediaDevice(sliceUri: String): BluetoothDevice = mock {
+ on { getMetadata(any()) }
+ .thenReturn(
+ ("<HEARABLE_CONTROL_SLICE_WITH_WIDTH>" +
+ sliceUri +
+ "</HEARABLE_CONTROL_SLICE_WITH_WIDTH>")
+ .toByteArray()
+ )
}
}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/volume/panel/component/anc/domain/AncAvailabilityCriteriaTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/volume/panel/component/anc/domain/AncAvailabilityCriteriaTest.kt
index 553aed8..8d052fe 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/volume/panel/component/anc/domain/AncAvailabilityCriteriaTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/volume/panel/component/anc/domain/AncAvailabilityCriteriaTest.kt
@@ -16,7 +16,9 @@
package com.android.systemui.volume.panel.component.anc.domain
+import android.media.AudioManager
import android.net.Uri
+import android.testing.TestableLooper
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
@@ -26,10 +28,13 @@
import com.android.systemui.util.mockito.any
import com.android.systemui.util.mockito.mock
import com.android.systemui.util.mockito.whenever
+import com.android.systemui.volume.data.repository.audioRepository
+import com.android.systemui.volume.localMediaRepository
import com.android.systemui.volume.panel.component.anc.FakeSliceFactory
import com.android.systemui.volume.panel.component.anc.ancSliceInteractor
import com.android.systemui.volume.panel.component.anc.ancSliceRepository
import com.android.systemui.volume.panel.component.anc.sliceViewManager
+import com.android.systemui.volume.panel.component.mediaoutput.domain.interactor.TestMediaDevicesFactory
import com.google.common.truth.Truth.assertThat
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.test.runCurrent
@@ -41,6 +46,7 @@
@OptIn(ExperimentalCoroutinesApi::class)
@SmallTest
@RunWith(AndroidJUnit4::class)
+@TestableLooper.RunWithLooper(setAsMainLooper = true)
class AncAvailabilityCriteriaTest : SysuiTestCase() {
private val kosmos = testKosmos()
@@ -74,6 +80,10 @@
fun hasSlice_available() {
with(kosmos) {
testScope.runTest {
+ audioRepository.setMode(AudioManager.MODE_NORMAL)
+ localMediaRepository.updateCurrentConnectedDevice(
+ TestMediaDevicesFactory.bluetoothMediaDevice()
+ )
ancSliceRepository.putSlice(
1,
FakeSliceFactory.createSlice(hasError = false, hasSliceItem = true)
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/volume/panel/component/anc/domain/interactor/AncSliceInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/volume/panel/component/anc/domain/interactor/AncSliceInteractorTest.kt
index 81e6ac4..741671e 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/volume/panel/component/anc/domain/interactor/AncSliceInteractorTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/volume/panel/component/anc/domain/interactor/AncSliceInteractorTest.kt
@@ -16,15 +16,21 @@
package com.android.systemui.volume.panel.component.anc.domain.interactor
+import android.media.AudioManager
+import android.testing.TestableLooper
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.coroutines.collectLastValue
import com.android.systemui.kosmos.testScope
import com.android.systemui.testKosmos
+import com.android.systemui.volume.data.repository.audioRepository
+import com.android.systemui.volume.localMediaRepository
import com.android.systemui.volume.panel.component.anc.FakeSliceFactory
+import com.android.systemui.volume.panel.component.anc.ancSliceInteractor
import com.android.systemui.volume.panel.component.anc.ancSliceRepository
import com.android.systemui.volume.panel.component.anc.domain.model.AncSlices
+import com.android.systemui.volume.panel.component.mediaoutput.domain.interactor.TestMediaDevicesFactory
import com.google.common.truth.Truth.assertThat
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.test.runCurrent
@@ -36,6 +42,7 @@
@OptIn(ExperimentalCoroutinesApi::class)
@SmallTest
@RunWith(AndroidJUnit4::class)
+@TestableLooper.RunWithLooper(setAsMainLooper = true)
class AncSliceInteractorTest : SysuiTestCase() {
private val kosmos = testKosmos()
@@ -43,14 +50,12 @@
private lateinit var underTest: AncSliceInteractor
@Before
- fun setup() {
- with(kosmos) {
- underTest = AncSliceInteractor(ancSliceRepository, testScope.backgroundScope)
- }
+ fun setUp() {
+ underTest = kosmos.ancSliceInteractor
}
@Test
- fun errorSlice_returnsNull() {
+ fun errorSlice_returnsUnavailable() {
with(kosmos) {
testScope.runTest {
ancSliceRepository.putSlice(
@@ -67,7 +72,7 @@
}
@Test
- fun noSliceItem_returnsNull() {
+ fun noSliceItem_returnsUnavailable() {
with(kosmos) {
testScope.runTest {
ancSliceRepository.putSlice(
@@ -84,9 +89,31 @@
}
@Test
+ fun sliceItem_noError_noDevice_returnsUnavailable() {
+ with(kosmos) {
+ testScope.runTest {
+ ancSliceRepository.putSlice(
+ 1,
+ FakeSliceFactory.createSlice(hasError = false, hasSliceItem = true)
+ )
+
+ val slice by collectLastValue(underTest.ancSlices)
+ runCurrent()
+
+ assertThat(slice).isInstanceOf(AncSlices.Unavailable::class.java)
+ }
+ }
+ }
+
+ @Test
fun sliceItem_noError_returnsSlice() {
with(kosmos) {
testScope.runTest {
+ audioRepository.setMode(AudioManager.MODE_NORMAL)
+ localMediaRepository.updateCurrentConnectedDevice(
+ TestMediaDevicesFactory.bluetoothMediaDevice()
+ )
+
ancSliceRepository.putSlice(
1,
FakeSliceFactory.createSlice(hasError = false, hasSliceItem = true)
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/volume/panel/component/mediaoutput/domain/interactor/MediaDeviceSessionInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/volume/panel/component/mediaoutput/domain/interactor/MediaDeviceSessionInteractorTest.kt
index 64c9429..46df0c2 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/volume/panel/component/mediaoutput/domain/interactor/MediaDeviceSessionInteractorTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/volume/panel/component/mediaoutput/domain/interactor/MediaDeviceSessionInteractorTest.kt
@@ -16,17 +16,16 @@
package com.android.systemui.volume.panel.component.mediaoutput.domain.interactor
-import android.os.Handler
import android.testing.TestableLooper
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.coroutines.collectLastValue
-import com.android.systemui.kosmos.testCase
import com.android.systemui.kosmos.testScope
import com.android.systemui.testKosmos
import com.android.systemui.volume.localMediaController
import com.android.systemui.volume.mediaControllerRepository
+import com.android.systemui.volume.mediaDeviceSessionInteractor
import com.android.systemui.volume.mediaOutputInteractor
import com.android.systemui.volume.panel.shared.model.filterData
import com.android.systemui.volume.remoteMediaController
@@ -55,12 +54,7 @@
listOf(localMediaController, remoteMediaController)
)
- underTest =
- MediaDeviceSessionInteractor(
- testScope.testScheduler,
- Handler(TestableLooper.get(kosmos.testCase).looper),
- mediaControllerRepository,
- )
+ underTest = mediaDeviceSessionInteractor
}
}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/volume/panel/component/spatial/SpatialAudioComponentKosmos.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/volume/panel/component/spatial/SpatialAudioComponentKosmos.kt
index 737b7f3..777240c 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/volume/panel/component/spatial/SpatialAudioComponentKosmos.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/volume/panel/component/spatial/SpatialAudioComponentKosmos.kt
@@ -19,13 +19,13 @@
import com.android.systemui.kosmos.Kosmos
import com.android.systemui.kosmos.testScope
import com.android.systemui.media.spatializerInteractor
-import com.android.systemui.volume.mediaOutputInteractor
+import com.android.systemui.volume.domain.interactor.audioOutputInteractor
import com.android.systemui.volume.panel.component.spatial.domain.interactor.SpatialAudioComponentInteractor
val Kosmos.spatialAudioComponentInteractor by
Kosmos.Fixture {
SpatialAudioComponentInteractor(
- mediaOutputInteractor,
+ audioOutputInteractor,
spatializerInteractor,
testScope.backgroundScope
)
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/volume/panel/component/spatial/domain/interactor/SpatialAudioComponentInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/volume/panel/component/spatial/domain/interactor/SpatialAudioComponentInteractorTest.kt
index e36ae60..c6c46fa 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/volume/panel/component/spatial/domain/interactor/SpatialAudioComponentInteractorTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/volume/panel/component/spatial/domain/interactor/SpatialAudioComponentInteractorTest.kt
@@ -29,7 +29,6 @@
import com.android.systemui.coroutines.collectLastValue
import com.android.systemui.coroutines.collectValues
import com.android.systemui.kosmos.testScope
-import com.android.systemui.media.spatializerInteractor
import com.android.systemui.media.spatializerRepository
import com.android.systemui.testKosmos
import com.android.systemui.util.mockito.mock
@@ -37,9 +36,9 @@
import com.android.systemui.volume.localMediaController
import com.android.systemui.volume.localMediaRepository
import com.android.systemui.volume.mediaControllerRepository
-import com.android.systemui.volume.mediaOutputInteractor
import com.android.systemui.volume.panel.component.spatial.domain.model.SpatialAudioAvailabilityModel
import com.android.systemui.volume.panel.component.spatial.domain.model.SpatialAudioEnabledModel
+import com.android.systemui.volume.panel.component.spatial.spatialAudioComponentInteractor
import com.google.common.truth.Truth.assertThat
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.test.runCurrent
@@ -76,12 +75,7 @@
mediaControllerRepository.setActiveSessions(listOf(localMediaController))
- underTest =
- SpatialAudioComponentInteractor(
- mediaOutputInteractor,
- spatializerInteractor,
- testScope.backgroundScope,
- )
+ underTest = spatialAudioComponentInteractor
}
}
diff --git a/packages/SystemUI/res-keyguard/drawable/status_bar_user_chip_bg.xml b/packages/SystemUI/res-keyguard/drawable/status_bar_user_chip_bg.xml
index a751f58..370677ac 100644
--- a/packages/SystemUI/res-keyguard/drawable/status_bar_user_chip_bg.xml
+++ b/packages/SystemUI/res-keyguard/drawable/status_bar_user_chip_bg.xml
@@ -16,5 +16,5 @@
<shape xmlns:android="http://schemas.android.com/apk/res/android">
<solid android:color="@color/material_dynamic_neutral20" />
- <corners android:radius="@dimen/ongoing_call_chip_corner_radius" />
+ <corners android:radius="@dimen/ongoing_activity_chip_corner_radius" />
</shape>
diff --git a/packages/SystemUI/res/drawable/contrast_dialog_button_background.xml b/packages/SystemUI/res/drawable/contrast_dialog_button_background.xml
deleted file mode 100644
index 4181220..0000000
--- a/packages/SystemUI/res/drawable/contrast_dialog_button_background.xml
+++ /dev/null
@@ -1,46 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-/*
-* Copyright 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.
-*/
--->
-<selector
- xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:androidprv="http://schemas.android.com/apk/prv/res/android">
-
- <item android:state_selected="true">
- <shape android:shape="rectangle">
- <solid android:color="?androidprv:attr/colorSurfaceHighlight" />
- <stroke
- android:color="?androidprv:attr/colorAccentPrimary"
- android:width="@dimen/contrast_dialog_button_stroke_width" />
- <corners android:radius="@dimen/contrast_dialog_button_radius"/>
- </shape>
- </item>
-
- <item>
- <layer-list>
- <item android:top="@dimen/contrast_dialog_button_stroke_width"
- android:bottom="@dimen/contrast_dialog_button_stroke_width"
- android:left="@dimen/contrast_dialog_button_stroke_width"
- android:right="@dimen/contrast_dialog_button_stroke_width">
- <shape android:shape="rectangle">
- <solid android:color="?androidprv:attr/colorSurfaceHighlight" />
- <corners android:radius="@dimen/contrast_dialog_button_radius"/>
- </shape>
- </item>
- </layer-list>
- </item>
-</selector>
diff --git a/packages/SystemUI/res/drawable/ongoing_call_chip_bg.xml b/packages/SystemUI/res/drawable/hub_handle.xml
similarity index 77%
copy from packages/SystemUI/res/drawable/ongoing_call_chip_bg.xml
copy to packages/SystemUI/res/drawable/hub_handle.xml
index bdd6270..8bc276f 100644
--- a/packages/SystemUI/res/drawable/ongoing_call_chip_bg.xml
+++ b/packages/SystemUI/res/drawable/hub_handle.xml
@@ -1,5 +1,5 @@
-<!--
- ~ Copyright (C) 2021 The Android Open Source Project
+<?xml version="1.0" encoding="utf-8"?><!--
+ ~ Copyright (C) 2024 The Android Open Source Project
~
~ Licensed under the Apache License, Version 2.0 (the "License");
~ you may not use this file except in compliance with the License.
@@ -15,6 +15,6 @@
-->
<shape xmlns:android="http://schemas.android.com/apk/res/android">
- <solid android:color="?android:attr/colorAccent" />
- <corners android:radius="@dimen/ongoing_call_chip_corner_radius" />
+ <corners android:radius="4dp" />
+ <solid android:color="#FFFFFF" />
</shape>
\ No newline at end of file
diff --git a/packages/SystemUI/res/drawable/ic_contrast_high.xml b/packages/SystemUI/res/drawable/ic_contrast_high.xml
deleted file mode 100644
index aa5b5ab..0000000
--- a/packages/SystemUI/res/drawable/ic_contrast_high.xml
+++ /dev/null
@@ -1,25 +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.
- -->
-<vector android:autoMirrored="true" android:height="20dp"
- android:viewportHeight="20" android:viewportWidth="66"
- android:width="66dp" xmlns:android="http://schemas.android.com/apk/res/android">
- <path android:fillColor="#F2F1E8"
- android:pathData="M0.5,8C0.5,3.858 3.858,0.5 8,0.5H58C62.142,0.5 65.5,3.858 65.5,8V12C65.5,16.142 62.142,19.5 58,19.5H8C3.858,19.5 0.5,16.142 0.5,12V8Z"
- android:strokeColor="#1B1C17" android:strokeWidth="1"/>
- <path android:fillColor="#1B1C17" android:pathData="M11,10m-6,0a6,6 0,1 1,12 0a6,6 0,1 1,-12 0"/>
- <path android:fillColor="#1B1C17" android:pathData="M23,5L43,5A2,2 0,0 1,45 7L45,7A2,2 0,0 1,43 9L23,9A2,2 0,0 1,21 7L21,7A2,2 0,0 1,23 5z"/>
- <path android:fillColor="#1B1C17" android:pathData="M23,11L55,11A2,2 0,0 1,57 13L57,13A2,2 0,0 1,55 15L23,15A2,2 0,0 1,21 13L21,13A2,2 0,0 1,23 11z"/>
-</vector>
diff --git a/packages/SystemUI/res/drawable/ic_contrast_medium.xml b/packages/SystemUI/res/drawable/ic_contrast_medium.xml
deleted file mode 100644
index 89519b8..0000000
--- a/packages/SystemUI/res/drawable/ic_contrast_medium.xml
+++ /dev/null
@@ -1,23 +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.
- -->
-<vector android:autoMirrored="true" android:height="20dp"
- android:viewportHeight="20" android:viewportWidth="66"
- android:width="66dp" xmlns:android="http://schemas.android.com/apk/res/android">
- <path android:fillColor="#F2F1E8" android:pathData="M0,8C0,3.582 3.582,0 8,0H58C62.418,0 66,3.582 66,8V12C66,16.418 62.418,20 58,20H8C3.582,20 0,16.418 0,12V8Z"/>
- <path android:fillColor="#919283" android:pathData="M11,10m-6,0a6,6 0,1 1,12 0a6,6 0,1 1,-12 0"/>
- <path android:fillColor="#919283" android:pathData="M23,5L43,5A2,2 0,0 1,45 7L45,7A2,2 0,0 1,43 9L23,9A2,2 0,0 1,21 7L21,7A2,2 0,0 1,23 5z"/>
- <path android:fillColor="#919283" android:pathData="M23,11L55,11A2,2 0,0 1,57 13L57,13A2,2 0,0 1,55 15L23,15A2,2 0,0 1,21 13L21,13A2,2 0,0 1,23 11z"/>
-</vector>
diff --git a/packages/SystemUI/res/drawable/ic_contrast_standard.xml b/packages/SystemUI/res/drawable/ic_contrast_standard.xml
deleted file mode 100644
index f914975..0000000
--- a/packages/SystemUI/res/drawable/ic_contrast_standard.xml
+++ /dev/null
@@ -1,23 +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.
- -->
-<vector android:autoMirrored="true" android:height="20dp"
- android:viewportHeight="20" android:viewportWidth="66"
- android:width="66dp" xmlns:android="http://schemas.android.com/apk/res/android">
- <path android:fillColor="#C7C8B7" android:pathData="M0,8C0,3.582 3.582,0 8,0H58C62.418,0 66,3.582 66,8V12C66,16.418 62.418,20 58,20H8C3.582,20 0,16.418 0,12V8Z"/>
- <path android:fillColor="#919283" android:pathData="M11,10m-6,0a6,6 0,1 1,12 0a6,6 0,1 1,-12 0"/>
- <path android:fillColor="#919283" android:pathData="M23,5L43,5A2,2 0,0 1,45 7L45,7A2,2 0,0 1,43 9L23,9A2,2 0,0 1,21 7L21,7A2,2 0,0 1,23 5z"/>
- <path android:fillColor="#919283" android:pathData="M23,11L55,11A2,2 0,0 1,57 13L57,13A2,2 0,0 1,55 15L23,15A2,2 0,0 1,21 13L21,13A2,2 0,0 1,23 11z"/>
-</vector>
diff --git a/packages/SystemUI/res/drawable/ongoing_call_chip_bg.xml b/packages/SystemUI/res/drawable/ongoing_activity_chip_bg.xml
similarity index 90%
rename from packages/SystemUI/res/drawable/ongoing_call_chip_bg.xml
rename to packages/SystemUI/res/drawable/ongoing_activity_chip_bg.xml
index bdd6270..b9a4cbf 100644
--- a/packages/SystemUI/res/drawable/ongoing_call_chip_bg.xml
+++ b/packages/SystemUI/res/drawable/ongoing_activity_chip_bg.xml
@@ -16,5 +16,5 @@
<shape xmlns:android="http://schemas.android.com/apk/res/android">
<solid android:color="?android:attr/colorAccent" />
- <corners android:radius="@dimen/ongoing_call_chip_corner_radius" />
+ <corners android:radius="@dimen/ongoing_activity_chip_corner_radius" />
</shape>
\ No newline at end of file
diff --git a/packages/SystemUI/res/drawable/qs_tile_background_flagged.xml b/packages/SystemUI/res/drawable/qs_tile_background_flagged.xml
index a30a122..c32acf2 100644
--- a/packages/SystemUI/res/drawable/qs_tile_background_flagged.xml
+++ b/packages/SystemUI/res/drawable/qs_tile_background_flagged.xml
@@ -15,16 +15,10 @@
-->
<ripple xmlns:android="http://schemas.android.com/apk/res/android"
android:color="@color/qs_tile_ripple_color">
- <!-- We don't really use the ripple effect here, but changing it to LayerDrawable causes
- performance regression, see: b/339412453.
- Since this ripple has just one layer inside, we can try to remove that extra "background"
- layer. However this should only be done when the flag
- com.android.systemui.qs_tile_focus_state has completed all its stages and this drawable
- fully replaces the previous one to ensure consistency with code sections searching for
- specific ids in drawable hierarchy
- -->
<item
- android:id="@id/background">
+ android:id="@android:id/mask"
+ android:drawable="@drawable/qs_tile_background_shape" />
+ <item android:id="@id/background">
<layer-list>
<item
android:id="@+id/qs_tile_background_base"
@@ -32,22 +26,8 @@
<item android:id="@+id/qs_tile_background_overlay">
<selector>
<item
- android:state_hovered="true"
- android:drawable="@drawable/qs_tile_background_shape" />
- </selector>
- </item>
- <!-- In the layer below we have negative insets because we need the focus outline
- to draw outside the bounds, around the main background. We use 5dp because
- the outline stroke is 3dp and the required padding is 2dp.-->
- <item
- android:top="-5dp"
- android:right="-5dp"
- android:left="-5dp"
- android:bottom="-5dp">
- <selector>
- <item
- android:state_focused="true"
- android:drawable="@drawable/qs_tile_focused_background"/>
+ android:drawable="@drawable/qs_tile_background_shape"
+ android:state_hovered="true" />
</selector>
</item>
</layer-list>
diff --git a/packages/SystemUI/res/drawable/qs_tile_focused_background.xml b/packages/SystemUI/res/drawable/qs_tile_focused_background.xml
index fd456df..33f0d02 100644
--- a/packages/SystemUI/res/drawable/qs_tile_focused_background.xml
+++ b/packages/SystemUI/res/drawable/qs_tile_focused_background.xml
@@ -13,10 +13,14 @@
~ See the License for the specific language governing permissions and
~ limitations under the License.
-->
-<shape
- xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:androidprv="http://schemas.android.com/apk/prv/res/android"
- android:shape="rectangle">
- <corners android:radius="30dp"/>
- <stroke android:width="3dp" android:color="?androidprv:attr/materialColorSecondaryFixed"/>
-</shape>
\ No newline at end of file
+
+<inset xmlns:android="http://schemas.android.com/apk/res/android"
+ android:inset="-5dp">
+ <shape xmlns:androidprv="http://schemas.android.com/apk/prv/res/android"
+ android:shape="rectangle">
+ <corners android:radius="30dp" />
+ <stroke
+ android:width="3dp"
+ android:color="?androidprv:attr/materialColorSecondaryFixed" />
+ </shape>
+</inset>
\ No newline at end of file
diff --git a/packages/SystemUI/res/layout-land/biometric_prompt_constraint_layout.xml b/packages/SystemUI/res/layout-land/biometric_prompt_constraint_layout.xml
index f644584f..01b9f7e 100644
--- a/packages/SystemUI/res/layout-land/biometric_prompt_constraint_layout.xml
+++ b/packages/SystemUI/res/layout-land/biometric_prompt_constraint_layout.xml
@@ -26,8 +26,8 @@
android:paddingVertical="16dp"
android:visibility="visible"
app:layout_constraintBottom_toBottomOf="parent"
- app:layout_constraintEnd_toStartOf="@+id/rightGuideline"
- app:layout_constraintStart_toStartOf="@+id/leftGuideline"
+ app:layout_constraintRight_toLeftOf="@+id/rightGuideline"
+ app:layout_constraintLeft_toLeftOf="@+id/leftGuideline"
app:layout_constraintTop_toTopOf="@+id/topGuideline" />
<com.android.systemui.biometrics.BiometricPromptLottieViewWrapper
@@ -35,8 +35,8 @@
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:layout_constraintBottom_toBottomOf="parent"
- app:layout_constraintEnd_toEndOf="parent"
- app:layout_constraintStart_toStartOf="parent"
+ app:layout_constraintRight_toRightOf="parent"
+ app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintTop_toTopOf="parent"
tools:srcCompat="@tools:sample/avatars" />
@@ -47,6 +47,7 @@
android:layout_gravity="center"
android:contentDescription="@null"
android:scaleType="fitXY"
+ android:importantForAccessibility="no"
app:layout_constraintBottom_toBottomOf="@+id/biometric_icon"
app:layout_constraintEnd_toEndOf="@+id/biometric_icon"
app:layout_constraintStart_toStartOf="@+id/biometric_icon"
@@ -62,8 +63,8 @@
android:paddingTop="24dp"
android:fadeScrollbars="false"
app:layout_constraintBottom_toTopOf="@+id/button_bar"
- app:layout_constraintEnd_toStartOf="@+id/midGuideline"
- app:layout_constraintStart_toStartOf="@id/leftGuideline"
+ app:layout_constraintRight_toLeftOf="@+id/midGuideline"
+ app:layout_constraintLeft_toLeftOf="@id/leftGuideline"
app:layout_constraintTop_toTopOf="@+id/topGuideline">
<androidx.constraintlayout.widget.ConstraintLayout
@@ -88,7 +89,7 @@
android:layout_width="0dp"
android:layout_height="wrap_content"
android:textAlignment="viewStart"
- android:paddingLeft="16dp"
+ android:paddingStart="16dp"
app:layout_constraintBottom_toBottomOf="@+id/logo"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toEndOf="@+id/logo"
@@ -208,6 +209,7 @@
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="vertical"
+ app:guidelineUseRtl="false"
app:layout_constraintGuide_begin="@dimen/biometric_dialog_border_padding" />
<androidx.constraintlayout.widget.Guideline
@@ -215,6 +217,7 @@
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="vertical"
+ app:guidelineUseRtl="false"
app:layout_constraintGuide_end="@dimen/biometric_dialog_border_padding" />
<androidx.constraintlayout.widget.Guideline
@@ -222,6 +225,7 @@
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="vertical"
+ app:guidelineUseRtl="false"
app:layout_constraintGuide_begin="406dp" />
<androidx.constraintlayout.widget.Guideline
diff --git a/packages/SystemUI/res/layout-sw600dp/biometric_prompt_constraint_layout.xml b/packages/SystemUI/res/layout-sw600dp/biometric_prompt_constraint_layout.xml
index 46b8e46..8b9eabc 100644
--- a/packages/SystemUI/res/layout-sw600dp/biometric_prompt_constraint_layout.xml
+++ b/packages/SystemUI/res/layout-sw600dp/biometric_prompt_constraint_layout.xml
@@ -33,8 +33,7 @@
layout="@layout/biometric_prompt_button_bar"
android:layout_width="0dp"
android:layout_height="wrap_content"
- android:layout_marginBottom="40dp"
- app:layout_constraintBottom_toBottomOf="parent"
+ app:layout_constraintBottom_toBottomOf="@id/bottomGuideline"
app:layout_constraintEnd_toEndOf="@id/panel"
app:layout_constraintStart_toStartOf="@id/panel" />
@@ -229,6 +228,7 @@
android:layout_gravity="center"
android:contentDescription="@null"
android:scaleType="fitXY"
+ android:importantForAccessibility="no"
app:layout_constraintBottom_toBottomOf="@+id/biometric_icon"
app:layout_constraintEnd_toEndOf="@+id/biometric_icon"
app:layout_constraintStart_toStartOf="@+id/biometric_icon"
diff --git a/packages/SystemUI/res/layout/biometric_prompt_button_bar.xml b/packages/SystemUI/res/layout/biometric_prompt_button_bar.xml
index 4d2310a..9f4ad0e 100644
--- a/packages/SystemUI/res/layout/biometric_prompt_button_bar.xml
+++ b/packages/SystemUI/res/layout/biometric_prompt_button_bar.xml
@@ -23,11 +23,12 @@
<!-- Negative Button, reserved for app -->
<Button
android:id="@+id/button_negative"
- style="@style/Widget.Dialog.Button.BorderButton"
+ style="@style/AuthCredentialNegativeButtonStyle"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_vertical"
- android:layout_marginLeft="24dp"
+ android:layout_marginStart="24dp"
+ android:layout_marginBottom="8dp"
android:ellipsize="end"
android:maxLines="2"
android:visibility="invisible"
@@ -37,11 +38,12 @@
<!-- Cancel Button, replaces negative button when biometric is accepted -->
<Button
android:id="@+id/button_cancel"
- style="@style/Widget.Dialog.Button.BorderButton"
+ style="@style/AuthCredentialNegativeButtonStyle"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_vertical"
- android:layout_marginLeft="24dp"
+ android:layout_marginStart="24dp"
+ android:layout_marginBottom="8dp"
android:text="@string/cancel"
android:visibility="invisible"
app:layout_constraintBottom_toBottomOf="parent"
@@ -50,11 +52,12 @@
<!-- "Use Credential" Button, replaces if device credential is allowed -->
<Button
android:id="@+id/button_use_credential"
- style="@style/Widget.Dialog.Button.BorderButton"
+ style="@style/AuthCredentialNegativeButtonStyle"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_vertical"
- android:layout_marginLeft="24dp"
+ android:layout_marginStart="24dp"
+ android:layout_marginBottom="8dp"
android:visibility="invisible"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintStart_toStartOf="parent" />
@@ -66,7 +69,8 @@
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_vertical"
- android:layout_marginRight="24dp"
+ android:layout_marginEnd="24dp"
+ android:layout_marginBottom="8dp"
android:ellipsize="end"
android:maxLines="2"
android:text="@string/biometric_dialog_confirm"
@@ -81,7 +85,8 @@
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_vertical"
- android:layout_marginRight="24dp"
+ android:layout_marginEnd="24dp"
+ android:layout_marginBottom="8dp"
android:ellipsize="end"
android:maxLines="2"
android:text="@string/biometric_dialog_try_again"
diff --git a/packages/SystemUI/res/layout/biometric_prompt_constraint_layout.xml b/packages/SystemUI/res/layout/biometric_prompt_constraint_layout.xml
index d51fe58..9b5b59f 100644
--- a/packages/SystemUI/res/layout/biometric_prompt_constraint_layout.xml
+++ b/packages/SystemUI/res/layout/biometric_prompt_constraint_layout.xml
@@ -31,8 +31,7 @@
layout="@layout/biometric_prompt_button_bar"
android:layout_width="0dp"
android:layout_height="wrap_content"
- android:layout_marginBottom="40dp"
- app:layout_constraintBottom_toBottomOf="parent"
+ app:layout_constraintBottom_toBottomOf="@id/bottomGuideline"
app:layout_constraintEnd_toEndOf="@id/panel"
app:layout_constraintStart_toStartOf="@id/panel" />
@@ -222,6 +221,7 @@
android:layout_gravity="center"
android:contentDescription="@null"
android:scaleType="fitXY"
+ android:importantForAccessibility="no"
app:layout_constraintBottom_toBottomOf="@+id/biometric_icon"
app:layout_constraintEnd_toEndOf="@+id/biometric_icon"
app:layout_constraintStart_toStartOf="@+id/biometric_icon"
diff --git a/packages/SystemUI/res/layout/contrast_dialog.xml b/packages/SystemUI/res/layout/contrast_dialog.xml
deleted file mode 100644
index 8e885cf..0000000
--- a/packages/SystemUI/res/layout/contrast_dialog.xml
+++ /dev/null
@@ -1,127 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
- 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.
- -->
-
-<LinearLayout
- xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:androidprv="http://schemas.android.com/apk/prv/res/android"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:orientation="horizontal">
-
- <Space
- android:layout_width="0dp"
- android:layout_height="match_parent"
- android:layout_weight="1"/>
-
- <LinearLayout
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:orientation="vertical">
-
- <FrameLayout
- android:id="@+id/contrast_button_standard"
- android:layout_width="@dimen/contrast_dialog_button_total_size"
- android:layout_height="@dimen/contrast_dialog_button_total_size"
- android:background="@drawable/contrast_dialog_button_background">
-
- <ImageView
- android:layout_gravity="center"
- android:layout_height="wrap_content"
- android:layout_width="wrap_content"
- android:src="@drawable/ic_contrast_standard"/>
- </FrameLayout>
-
- <TextView
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:layout_marginTop="@dimen/contrast_dialog_button_text_spacing"
- android:gravity="center_horizontal|top"
- android:textSize="@dimen/contrast_dialog_button_text_size"
- android:text="@string/quick_settings_contrast_standard"
- android:textColor="?androidprv:attr/textColorPrimary"/>
- </LinearLayout>
-
- <Space
- android:layout_width="@dimen/contrast_dialog_button_horizontal_spacing"
- android:layout_height="match_parent" />
-
- <LinearLayout
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:orientation="vertical">
-
- <FrameLayout
- android:id="@+id/contrast_button_medium"
- android:layout_width="@dimen/contrast_dialog_button_total_size"
- android:layout_height="@dimen/contrast_dialog_button_total_size"
- android:background="@drawable/contrast_dialog_button_background">
-
- <ImageView
- android:layout_gravity="center"
- android:layout_height="wrap_content"
- android:layout_width="wrap_content"
- android:src="@drawable/ic_contrast_medium"/>
- </FrameLayout>
-
- <TextView
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:layout_marginTop="@dimen/contrast_dialog_button_text_spacing"
- android:gravity="center_horizontal|top"
- android:textSize="@dimen/contrast_dialog_button_text_size"
- android:text="@string/quick_settings_contrast_medium"
- android:textColor="?androidprv:attr/textColorPrimary"/>
- </LinearLayout>
-
- <Space
- android:layout_width="@dimen/contrast_dialog_button_horizontal_spacing"
- android:layout_height="match_parent" />
-
- <LinearLayout
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:orientation="vertical">
-
- <FrameLayout
- android:id="@+id/contrast_button_high"
- android:layout_width="@dimen/contrast_dialog_button_total_size"
- android:layout_height="@dimen/contrast_dialog_button_total_size"
- android:background="@drawable/contrast_dialog_button_background">
-
- <ImageView
- android:layout_gravity="center"
- android:layout_height="wrap_content"
- android:layout_width="wrap_content"
- android:src="@drawable/ic_contrast_high"/>
-
- </FrameLayout>
-
- <TextView
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:layout_marginTop="@dimen/contrast_dialog_button_text_spacing"
- android:gravity="center_horizontal|top"
- android:textSize="@dimen/contrast_dialog_button_text_size"
- android:text="@string/quick_settings_contrast_high"
- android:textColor="?androidprv:attr/textColorPrimary"/>
- </LinearLayout>
-
- <Space
- android:layout_width="0dp"
- android:layout_height="match_parent"
- android:layout_weight="1"/>
-</LinearLayout>
diff --git a/packages/SystemUI/res/layout/dream_overlay_container.xml b/packages/SystemUI/res/layout/dream_overlay_container.xml
index 19fb874..4234fca5 100644
--- a/packages/SystemUI/res/layout/dream_overlay_container.xml
+++ b/packages/SystemUI/res/layout/dream_overlay_container.xml
@@ -21,6 +21,19 @@
android:layout_width="match_parent"
android:layout_height="match_parent">
+ <ImageView
+ android:id="@+id/glanceable_hub_handle"
+ android:layout_width="4dp"
+ android:layout_height="220dp"
+ android:layout_centerVertical="true"
+ android:layout_marginEnd="12dp"
+ android:background="@drawable/hub_handle"
+ android:visibility="gone"
+ android:contentDescription="UI indicator for swiping open the glanceable hub"
+ app:layout_constraintBottom_toBottomOf="parent"
+ app:layout_constraintEnd_toEndOf="parent"
+ app:layout_constraintTop_toTopOf="parent" />
+
<androidx.constraintlayout.widget.ConstraintLayout
android:id="@+id/dream_overlay_content"
android:layout_width="match_parent"
diff --git a/packages/SystemUI/res/layout/ongoing_call_chip.xml b/packages/SystemUI/res/layout/ongoing_activity_chip.xml
similarity index 66%
rename from packages/SystemUI/res/layout/ongoing_call_chip.xml
rename to packages/SystemUI/res/layout/ongoing_activity_chip.xml
index 6a0217ec..a33be12 100644
--- a/packages/SystemUI/res/layout/ongoing_call_chip.xml
+++ b/packages/SystemUI/res/layout/ongoing_activity_chip.xml
@@ -17,43 +17,45 @@
the chip. -->
<FrameLayout
xmlns:android="http://schemas.android.com/apk/res/android"
- android:id="@+id/ongoing_call_chip"
+ android:id="@+id/ongoing_activity_chip"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:layout_gravity="center_vertical|start"
android:layout_marginStart="5dp"
>
- <com.android.systemui.statusbar.phone.ongoingcall.OngoingCallBackgroundContainer
- android:id="@+id/ongoing_call_chip_background"
+ <!-- TODO(b/332662551): Update this content description when this supports more than just
+ phone calls. -->
+ <com.android.systemui.statusbar.chips.ui.view.ChipBackgroundContainer
+ android:id="@+id/ongoing_activity_chip_background"
android:layout_width="wrap_content"
android:layout_height="@dimen/ongoing_appops_chip_height"
android:layout_gravity="center_vertical"
android:gravity="center"
- android:background="@drawable/ongoing_call_chip_bg"
- android:paddingStart="@dimen/ongoing_call_chip_side_padding"
- android:paddingEnd="@dimen/ongoing_call_chip_side_padding"
+ android:background="@drawable/ongoing_activity_chip_bg"
+ android:paddingStart="@dimen/ongoing_activity_chip_side_padding"
+ android:paddingEnd="@dimen/ongoing_activity_chip_side_padding"
android:contentDescription="@string/ongoing_phone_call_content_description"
android:minWidth="@dimen/min_clickable_item_size"
>
<ImageView
android:src="@*android:drawable/ic_phone"
- android:layout_width="@dimen/ongoing_call_chip_icon_size"
- android:layout_height="@dimen/ongoing_call_chip_icon_size"
+ android:layout_width="@dimen/ongoing_activity_chip_icon_size"
+ android:layout_height="@dimen/ongoing_activity_chip_icon_size"
android:tint="?android:attr/colorPrimary"
/>
- <com.android.systemui.statusbar.phone.ongoingcall.OngoingCallChronometer
- android:id="@+id/ongoing_call_chip_time"
+ <com.android.systemui.statusbar.chips.ui.view.ChipChronometer
+ android:id="@+id/ongoing_activity_chip_time"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:singleLine="true"
android:gravity="center|start"
- android:paddingStart="@dimen/ongoing_call_chip_icon_text_padding"
+ android:paddingStart="@dimen/ongoing_activity_chip_icon_text_padding"
android:textAppearance="@android:style/TextAppearance.Material.Small"
android:fontFamily="@*android:string/config_headlineFontFamily"
android:textColor="?android:attr/colorPrimary"
/>
- </com.android.systemui.statusbar.phone.ongoingcall.OngoingCallBackgroundContainer>
+ </com.android.systemui.statusbar.chips.ui.view.ChipBackgroundContainer>
</FrameLayout>
diff --git a/packages/SystemUI/res/layout/screenshot_shelf.xml b/packages/SystemUI/res/layout/screenshot_shelf.xml
index 2cb4b02..6b65e9c 100644
--- a/packages/SystemUI/res/layout/screenshot_shelf.xml
+++ b/packages/SystemUI/res/layout/screenshot_shelf.xml
@@ -26,36 +26,6 @@
android:layout_height="match_parent"
android:clipChildren="false"
android:clipToPadding="false">
- <FrameLayout
- android:id="@+id/actions_container_background"
- android:visibility="gone"
- android:layout_height="wrap_content"
- android:layout_width="wrap_content"
- android:elevation="4dp"
- android:background="@drawable/shelf_action_chip_container_background"
- android:layout_marginHorizontal="@dimen/overlay_action_container_minimum_edge_spacing"
- app:layout_constraintStart_toStartOf="parent"
- app:layout_constraintBottom_toTopOf="@id/guideline"
- >
- <HorizontalScrollView
- android:id="@+id/actions_container"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_marginVertical="@dimen/overlay_action_container_padding_vertical"
- android:layout_marginHorizontal="@dimen/overlay_action_chip_margin_start"
- android:background="@drawable/shelf_action_container_clipping_shape"
- android:clipToOutline="true"
- android:scrollbars="none">
- <LinearLayout
- android:id="@+id/screenshot_actions"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:showDividers="middle"
- android:divider="@drawable/shelf_action_chip_divider"
- android:animateLayoutChanges="true"
- />
- </HorizontalScrollView>
- </FrameLayout>
<View
android:id="@+id/screenshot_preview_border"
android:layout_width="0dp"
@@ -101,6 +71,37 @@
android:visibility="invisible"
app:layout_constraintStart_toStartOf="@id/screenshot_preview_border"
app:layout_constraintBottom_toBottomOf="@id/screenshot_preview_border"/>
+ <!-- Action bar should be drawn on top of the thumbnail -->
+ <FrameLayout
+ android:id="@+id/actions_container_background"
+ android:visibility="gone"
+ android:layout_height="wrap_content"
+ android:layout_width="wrap_content"
+ android:elevation="4dp"
+ android:background="@drawable/shelf_action_chip_container_background"
+ android:layout_marginHorizontal="@dimen/overlay_action_container_minimum_edge_spacing"
+ app:layout_constraintStart_toStartOf="parent"
+ app:layout_constraintBottom_toTopOf="@id/guideline"
+ >
+ <HorizontalScrollView
+ android:id="@+id/actions_container"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_marginVertical="@dimen/overlay_action_container_padding_vertical"
+ android:layout_marginHorizontal="@dimen/overlay_action_chip_margin_start"
+ android:background="@drawable/shelf_action_container_clipping_shape"
+ android:clipToOutline="true"
+ android:scrollbars="none">
+ <LinearLayout
+ android:id="@+id/screenshot_actions"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:showDividers="middle"
+ android:divider="@drawable/shelf_action_chip_divider"
+ android:animateLayoutChanges="true"
+ android:orientation="horizontal" />
+ </HorizontalScrollView>
+ </FrameLayout>
<ImageView
android:id="@+id/screenshot_badge"
android:layout_width="56dp"
@@ -135,11 +136,12 @@
android:id="@+id/screenshot_scrollable_preview"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
+ android:clipToOutline="true"
android:scaleType="matrix"
android:visibility="gone"
app:layout_constraintStart_toStartOf="@id/screenshot_preview"
app:layout_constraintTop_toTopOf="@id/screenshot_preview"
- android:elevation="7dp"/>
+ android:elevation="3dp"/>
<androidx.constraintlayout.widget.Guideline
android:id="@+id/guideline"
@@ -170,6 +172,13 @@
</FrameLayout>
</androidx.constraintlayout.widget.ConstraintLayout>
<ImageView
+ android:id="@+id/screenshot_scrolling_scrim"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:visibility="gone"
+ android:clickable="true"
+ android:importantForAccessibility="no"/>
+ <ImageView
android:id="@+id/screenshot_flash"
android:layout_width="match_parent"
android:layout_height="match_parent"
diff --git a/packages/SystemUI/res/layout/status_bar.xml b/packages/SystemUI/res/layout/status_bar.xml
index 452bc31..4247c7e 100644
--- a/packages/SystemUI/res/layout/status_bar.xml
+++ b/packages/SystemUI/res/layout/status_bar.xml
@@ -99,7 +99,7 @@
android:gravity="center_vertical|start"
/>
- <include layout="@layout/ongoing_call_chip" />
+ <include layout="@layout/ongoing_activity_chip" />
<com.android.systemui.statusbar.AlphaOptimizedFrameLayout
android:id="@+id/notification_icon_area"
diff --git a/packages/SystemUI/res/values/dimens.xml b/packages/SystemUI/res/values/dimens.xml
index 517b44f..b960813 100644
--- a/packages/SystemUI/res/values/dimens.xml
+++ b/packages/SystemUI/res/values/dimens.xml
@@ -913,10 +913,6 @@
obvious when corner radii differ.-->
<dimen name="communal_enforced_rounded_corner_max_radius">28dp</dimen>
- <!-- Width and height used to filter widgets displayed in the communal widget picker -->
- <dimen name="communal_widget_picker_desired_width">424dp</dimen>
- <dimen name="communal_widget_picker_desired_height">282dp</dimen>
-
<!-- The width/height of the unlock icon view on keyguard. -->
<dimen name="keyguard_lock_height">42dp</dimen>
<dimen name="keyguard_lock_padding">20dp</dimen>
@@ -1713,12 +1709,12 @@
<dimen name="wallet_button_horizontal_padding">24dp</dimen>
<dimen name="wallet_button_vertical_padding">8dp</dimen>
- <!-- Ongoing call chip -->
- <dimen name="ongoing_call_chip_side_padding">12dp</dimen>
- <dimen name="ongoing_call_chip_icon_size">16dp</dimen>
+ <!-- Ongoing activity chip -->
+ <dimen name="ongoing_activity_chip_side_padding">12dp</dimen>
+ <dimen name="ongoing_activity_chip_icon_size">16dp</dimen>
<!-- The padding between the icon and the text. -->
- <dimen name="ongoing_call_chip_icon_text_padding">4dp</dimen>
- <dimen name="ongoing_call_chip_corner_radius">28dp</dimen>
+ <dimen name="ongoing_activity_chip_icon_text_padding">4dp</dimen>
+ <dimen name="ongoing_activity_chip_corner_radius">28dp</dimen>
<!-- Status bar user chip -->
<dimen name="status_bar_user_chip_avatar_size">16dp</dimen>
@@ -1963,15 +1959,6 @@
<dimen name="broadcast_dialog_btn_minHeight">44dp</dimen>
<dimen name="broadcast_dialog_margin">16dp</dimen>
- <!-- Contrast dialog -->
- <dimen name="contrast_dialog_button_total_size">90dp</dimen>
- <dimen name="contrast_dialog_button_inner_size">82dp</dimen>
- <dimen name="contrast_dialog_button_radius">20dp</dimen>
- <dimen name="contrast_dialog_button_stroke_width">4dp</dimen>
- <dimen name="contrast_dialog_button_text_size">14sp</dimen>
- <dimen name="contrast_dialog_button_text_spacing">4dp</dimen>
- <dimen name="contrast_dialog_button_horizontal_spacing">16dp</dimen>
-
<!-- Shadow for dream overlay clock complication -->
<dimen name="dream_overlay_clock_key_text_shadow_dx">0dp</dimen>
<dimen name="dream_overlay_clock_key_text_shadow_dy">0dp</dimen>
diff --git a/packages/SystemUI/res/values/strings.xml b/packages/SystemUI/res/values/strings.xml
index aecc906..6df48a0 100644
--- a/packages/SystemUI/res/values/strings.xml
+++ b/packages/SystemUI/res/values/strings.xml
@@ -765,7 +765,7 @@
<!-- QuickSettings: Wifi secondary label shown when the wifi is being enabled [CHAR LIMIT=NONE] -->
<string name="quick_settings_wifi_secondary_label_transient">Turning on…</string>
<!-- QuickSettings: Cast title [CHAR LIMIT=NONE] -->
- <string name="quick_settings_cast_title">Screen Cast</string>
+ <string name="quick_settings_cast_title">Cast</string>
<!-- QuickSettings: Cast detail panel, status text when casting [CHAR LIMIT=NONE] -->
<string name="quick_settings_casting">Casting</string>
<!-- QuickSettings: Cast detail panel, default device name [CHAR LIMIT=NONE] -->
@@ -902,15 +902,6 @@
<!-- QuickSettings: Label for the toggle that controls whether One-handed mode is enabled. [CHAR LIMIT=NONE] -->
<string name="quick_settings_onehanded_label">One-handed mode</string>
- <!-- QuickSettings: Contrast tile [CHAR LIMIT=NONE] -->
- <string name="quick_settings_contrast_label">Contrast</string>
- <!-- QuickSettings: Contrast tile description: standard [CHAR LIMIT=NONE] -->
- <string name="quick_settings_contrast_standard">Standard</string>
- <!-- QuickSettings: Contrast tile description: medium [CHAR LIMIT=NONE] -->
- <string name="quick_settings_contrast_medium">Medium</string>
- <!-- QuickSettings: Contrast tile description: high [CHAR LIMIT=NONE] -->
- <string name="quick_settings_contrast_high">High</string>
-
<!-- Hearing devices -->
<!-- QuickSettings: Hearing devices [CHAR LIMIT=NONE] -->
<string name="quick_settings_hearing_devices_label">Hearing devices</string>
diff --git a/packages/SystemUI/res/values/styles.xml b/packages/SystemUI/res/values/styles.xml
index 2c4cdb9..8813588 100644
--- a/packages/SystemUI/res/values/styles.xml
+++ b/packages/SystemUI/res/values/styles.xml
@@ -375,6 +375,12 @@
<item name="android:textColor">?androidprv:attr/materialColorPrimary</item>
</style>
+ <style name="AuthCredentialNegativeButtonStyle" parent="TextAppearance.Material3.LabelLarge">
+ <item name="android:fontFamily">@*android:string/config_bodyFontFamilyMedium</item>
+ <item name="android:background">@color/transparent</item>
+ <item name="android:textColor">?androidprv:attr/materialColorPrimary</item>
+ </style>
+
<style name="DeviceManagementDialogTitle">
<item name="android:gravity">center</item>
<item name="android:textAppearance">@style/TextAppearance.Dialog.Title</item>
@@ -523,10 +529,6 @@
<item name="android:windowExitAnimation">@anim/instant_fade_out</item>
</style>
- <style name="Theme.SystemUI.ContrastDialog" parent="@android:style/Theme.DeviceDefault.Dialog">
- <item name="android:windowBackground">@android:color/transparent</item>
- </style>
-
<style name="Theme.SystemUI.QuickSettings.Dialog" parent="@style/Theme.SystemUI.Dialog.QuickSettings">
</style>
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/pip/PipSurfaceTransactionHelper.java b/packages/SystemUI/shared/src/com/android/systemui/shared/pip/PipSurfaceTransactionHelper.java
index 8ac1de8..c33b7ce 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/pip/PipSurfaceTransactionHelper.java
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/pip/PipSurfaceTransactionHelper.java
@@ -99,7 +99,7 @@
final float startScale = sourceRectHint.width() <= sourceRectHint.height()
? (float) destinationBounds.width() / sourceBounds.width()
: (float) destinationBounds.height() / sourceBounds.height();
- scale = (1 - progress) * startScale + progress * endScale;
+ scale = Math.min((1 - progress) * startScale + progress * endScale, 1.0f);
}
final float left = destinationBounds.left - (insets.left + sourceBounds.left) * scale;
final float top = destinationBounds.top - (insets.top + sourceBounds.top) * scale;
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/recents/IOverviewProxy.aidl b/packages/SystemUI/shared/src/com/android/systemui/shared/recents/IOverviewProxy.aidl
index d191a3c..d5bc10a 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/recents/IOverviewProxy.aidl
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/recents/IOverviewProxy.aidl
@@ -65,7 +65,7 @@
/**
* Sent when some system ui state changes.
*/
- void onSystemUiStateChanged(int stateFlags) = 16;
+ void onSystemUiStateChanged(long stateFlags) = 16;
/**
* Sent when suggested rotation button could be shown
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/recents/model/ThumbnailData.java b/packages/SystemUI/shared/src/com/android/systemui/shared/recents/model/ThumbnailData.java
deleted file mode 100644
index 0b0df83..0000000
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/recents/model/ThumbnailData.java
+++ /dev/null
@@ -1,120 +0,0 @@
-/*
- * Copyright (C) 2016 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.recents.model;
-
-import static android.app.WindowConfiguration.ROTATION_UNDEFINED;
-import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED;
-import static android.content.res.Configuration.ORIENTATION_UNDEFINED;
-import static android.graphics.Bitmap.Config.ARGB_8888;
-
-import android.graphics.Bitmap;
-import android.graphics.Color;
-import android.graphics.Point;
-import android.graphics.Rect;
-import android.hardware.HardwareBuffer;
-import android.util.Log;
-import android.view.WindowInsetsController.Appearance;
-import android.window.TaskSnapshot;
-
-import java.util.HashMap;
-
-/**
- * Data for a single thumbnail.
- */
-public class ThumbnailData {
-
- public final Bitmap thumbnail;
- public int orientation;
- public int rotation;
- public Rect insets;
- public Rect letterboxInsets;
- public boolean reducedResolution;
- public boolean isRealSnapshot;
- public boolean isTranslucent;
- public int windowingMode;
- public @Appearance int appearance;
- public float scale;
- public long snapshotId;
-
- public ThumbnailData() {
- thumbnail = null;
- orientation = ORIENTATION_UNDEFINED;
- rotation = ROTATION_UNDEFINED;
- insets = new Rect();
- letterboxInsets = new Rect();
- reducedResolution = false;
- scale = 1f;
- isRealSnapshot = true;
- isTranslucent = false;
- windowingMode = WINDOWING_MODE_UNDEFINED;
- snapshotId = 0;
- }
-
- public void recycleBitmap() {
- if (thumbnail != null) {
- thumbnail.recycle();
- }
- }
-
- private static Bitmap makeThumbnail(TaskSnapshot snapshot) {
- Bitmap thumbnail = null;
- try (final HardwareBuffer buffer = snapshot.getHardwareBuffer()) {
- if (buffer != null) {
- thumbnail = Bitmap.wrapHardwareBuffer(buffer, snapshot.getColorSpace());
- }
- } catch (IllegalArgumentException ex) {
- // TODO(b/157562905): Workaround for a crash when we get a snapshot without this state
- Log.e("ThumbnailData", "Unexpected snapshot without USAGE_GPU_SAMPLED_IMAGE: "
- + snapshot.getHardwareBuffer(), ex);
- }
- if (thumbnail == null) {
- Point taskSize = snapshot.getTaskSize();
- thumbnail = Bitmap.createBitmap(taskSize.x, taskSize.y, ARGB_8888);
- thumbnail.eraseColor(Color.BLACK);
- }
- return thumbnail;
- }
-
- public static HashMap<Integer, ThumbnailData> wrap(int[] taskIds, TaskSnapshot[] snapshots) {
- HashMap<Integer, ThumbnailData> temp = new HashMap<>();
- if (taskIds == null || snapshots == null || taskIds.length != snapshots.length) {
- return temp;
- }
-
- for (int i = snapshots.length - 1; i >= 0; i--) {
- temp.put(taskIds[i], new ThumbnailData(snapshots[i]));
- }
- return temp;
- }
-
- public ThumbnailData(TaskSnapshot snapshot) {
- thumbnail = makeThumbnail(snapshot);
- insets = new Rect(snapshot.getContentInsets());
- letterboxInsets = new Rect(snapshot.getLetterboxInsets());
- orientation = snapshot.getOrientation();
- rotation = snapshot.getRotation();
- reducedResolution = snapshot.isLowResolution();
- // TODO(b/149579527): Pass task size instead of computing scale.
- // Assume width and height were scaled the same; compute scale only for width
- scale = (float) thumbnail.getWidth() / snapshot.getTaskSize().x;
- isRealSnapshot = snapshot.isRealSnapshot();
- isTranslucent = snapshot.isTranslucent();
- windowingMode = snapshot.getWindowingMode();
- appearance = snapshot.getAppearance();
- snapshotId = snapshot.getId();
- }
-}
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/recents/model/ThumbnailData.kt b/packages/SystemUI/shared/src/com/android/systemui/shared/recents/model/ThumbnailData.kt
new file mode 100644
index 0000000..dcf7754
--- /dev/null
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/recents/model/ThumbnailData.kt
@@ -0,0 +1,101 @@
+/*
+ * Copyright (C) 2016 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.recents.model
+
+import android.app.WindowConfiguration
+import android.content.res.Configuration
+import android.graphics.Bitmap
+import android.graphics.Bitmap.Config.ARGB_8888
+import android.graphics.Color
+import android.graphics.Rect
+import android.util.Log
+import android.view.WindowInsetsController.Appearance
+import android.window.TaskSnapshot
+
+/** Data for a single thumbnail. */
+data class ThumbnailData(
+ val thumbnail: Bitmap? = null,
+ var orientation: Int = Configuration.ORIENTATION_UNDEFINED,
+ @JvmField var rotation: Int = WindowConfiguration.ROTATION_UNDEFINED,
+ @JvmField var insets: Rect = Rect(),
+ @JvmField var letterboxInsets: Rect = Rect(),
+ @JvmField var reducedResolution: Boolean = false,
+ @JvmField var isRealSnapshot: Boolean = true,
+ var isTranslucent: Boolean = false,
+ @JvmField var windowingMode: Int = WindowConfiguration.WINDOWING_MODE_UNDEFINED,
+ @JvmField @Appearance var appearance: Int = 0,
+ @JvmField var scale: Float = 1f,
+ var snapshotId: Long = 0,
+) {
+ fun recycleBitmap() {
+ thumbnail?.recycle()
+ }
+
+ companion object {
+ private fun makeThumbnail(snapshot: TaskSnapshot): Bitmap {
+ var thumbnail: Bitmap? = null
+ try {
+ snapshot.hardwareBuffer?.use { buffer ->
+ thumbnail = Bitmap.wrapHardwareBuffer(buffer, snapshot.colorSpace)
+ }
+ } catch (ex: IllegalArgumentException) {
+ // TODO(b/157562905): Workaround for a crash when we get a snapshot without this
+ // state
+ Log.e(
+ "ThumbnailData",
+ "Unexpected snapshot without USAGE_GPU_SAMPLED_IMAGE: " +
+ "${snapshot.hardwareBuffer}",
+ ex
+ )
+ }
+
+ return thumbnail
+ ?: Bitmap.createBitmap(snapshot.taskSize.x, snapshot.taskSize.y, ARGB_8888).apply {
+ eraseColor(Color.BLACK)
+ }
+ }
+
+ @JvmStatic
+ fun wrap(taskIds: IntArray?, snapshots: Array<TaskSnapshot>?): HashMap<Int, ThumbnailData> {
+ return if (taskIds == null || snapshots == null || taskIds.size != snapshots.size) {
+ HashMap()
+ } else {
+ HashMap(taskIds.associateWith { taskId -> fromSnapshot(snapshots[taskId]) })
+ }
+ }
+
+ @JvmStatic
+ fun fromSnapshot(snapshot: TaskSnapshot): ThumbnailData {
+ val thumbnail = makeThumbnail(snapshot)
+ return ThumbnailData(
+ thumbnail = thumbnail,
+ insets = Rect(snapshot.contentInsets),
+ letterboxInsets = Rect(snapshot.letterboxInsets),
+ orientation = snapshot.orientation,
+ rotation = snapshot.rotation,
+ reducedResolution = snapshot.isLowResolution,
+ // TODO(b/149579527): Pass task size instead of computing scale.
+ // Assume width and height were scaled the same; compute scale only for width
+ scale = thumbnail.width.toFloat() / snapshot.taskSize.x,
+ isRealSnapshot = snapshot.isRealSnapshot,
+ isTranslucent = snapshot.isTranslucent,
+ windowingMode = snapshot.windowingMode,
+ appearance = snapshot.appearance,
+ snapshotId = snapshot.id,
+ )
+ }
+ }
+}
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/system/ActivityManagerWrapper.java b/packages/SystemUI/shared/src/com/android/systemui/shared/system/ActivityManagerWrapper.java
index ca63483..845ca5e 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/system/ActivityManagerWrapper.java
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/system/ActivityManagerWrapper.java
@@ -147,7 +147,7 @@
Log.w(TAG, "Failed to retrieve task snapshot", e);
}
if (snapshot != null) {
- return new ThumbnailData(snapshot);
+ return ThumbnailData.fromSnapshot(snapshot);
} else {
return new ThumbnailData();
}
@@ -167,7 +167,7 @@
Log.w(TAG, "Failed to take task snapshot", e);
}
if (snapshot != null) {
- return new ThumbnailData(snapshot);
+ return ThumbnailData.fromSnapshot(snapshot);
} else {
return new ThumbnailData();
}
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/system/QuickStepContract.java b/packages/SystemUI/shared/src/com/android/systemui/shared/system/QuickStepContract.java
index 69aa909..b4377ea 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/system/QuickStepContract.java
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/system/QuickStepContract.java
@@ -22,7 +22,7 @@
import static com.android.systemui.shared.Flags.shadeAllowBackGesture;
-import android.annotation.IntDef;
+import android.annotation.LongDef;
import android.content.Context;
import android.content.res.Resources;
import android.view.ViewConfiguration;
@@ -51,92 +51,94 @@
// Overview is disabled, either because the device is in lock task mode, or because the device
// policy has disabled the feature
- public static final int SYSUI_STATE_SCREEN_PINNING = 1 << 0;
+ public static final long SYSUI_STATE_SCREEN_PINNING = 1L << 0;
// The navigation bar is hidden due to immersive mode
- public static final int SYSUI_STATE_NAV_BAR_HIDDEN = 1 << 1;
+ public static final long SYSUI_STATE_NAV_BAR_HIDDEN = 1L << 1;
// The notification panel is expanded and interactive (either locked or unlocked), and the
// quick settings is not expanded
- public static final int SYSUI_STATE_NOTIFICATION_PANEL_EXPANDED = 1 << 2;
+ public static final long SYSUI_STATE_NOTIFICATION_PANEL_EXPANDED = 1L << 2;
// The keyguard bouncer is showing
- public static final int SYSUI_STATE_BOUNCER_SHOWING = 1 << 3;
+ public static final long SYSUI_STATE_BOUNCER_SHOWING = 1L << 3;
// The navigation bar a11y button should be shown
- public static final int SYSUI_STATE_A11Y_BUTTON_CLICKABLE = 1 << 4;
+ public static final long SYSUI_STATE_A11Y_BUTTON_CLICKABLE = 1L << 4;
// The navigation bar a11y button shortcut is available
- public static final int SYSUI_STATE_A11Y_BUTTON_LONG_CLICKABLE = 1 << 5;
+ public static final long SYSUI_STATE_A11Y_BUTTON_LONG_CLICKABLE = 1L << 5;
// The keyguard is showing and not occluded
- public static final int SYSUI_STATE_STATUS_BAR_KEYGUARD_SHOWING = 1 << 6;
+ public static final long SYSUI_STATE_STATUS_BAR_KEYGUARD_SHOWING = 1L << 6;
// The recents feature is disabled (either by SUW/SysUI/device policy)
- public static final int SYSUI_STATE_OVERVIEW_DISABLED = 1 << 7;
+ public static final long SYSUI_STATE_OVERVIEW_DISABLED = 1L << 7;
// The home feature is disabled (either by SUW/SysUI/device policy)
- public static final int SYSUI_STATE_HOME_DISABLED = 1 << 8;
+ public static final long SYSUI_STATE_HOME_DISABLED = 1L << 8;
// The keyguard is showing, but occluded
- public static final int SYSUI_STATE_STATUS_BAR_KEYGUARD_SHOWING_OCCLUDED = 1 << 9;
+ public static final long SYSUI_STATE_STATUS_BAR_KEYGUARD_SHOWING_OCCLUDED = 1L << 9;
// The search feature is disabled (either by SUW/SysUI/device policy)
- public static final int SYSUI_STATE_SEARCH_DISABLED = 1 << 10;
+ public static final long SYSUI_STATE_SEARCH_DISABLED = 1L << 10;
// The notification panel is expanded and interactive (either locked or unlocked), and quick
// settings is expanded.
- public static final int SYSUI_STATE_QUICK_SETTINGS_EXPANDED = 1 << 11;
+ public static final long SYSUI_STATE_QUICK_SETTINGS_EXPANDED = 1L << 11;
// Winscope tracing is enabled
- public static final int SYSUI_STATE_DISABLE_GESTURE_SPLIT_INVOCATION = 1 << 12;
+ public static final long SYSUI_STATE_DISABLE_GESTURE_SPLIT_INVOCATION = 1L << 12;
// The Assistant gesture should be constrained. It is up to the launcher implementation to
// decide how to constrain it
- public static final int SYSUI_STATE_ASSIST_GESTURE_CONSTRAINED = 1 << 13;
+ public static final long SYSUI_STATE_ASSIST_GESTURE_CONSTRAINED = 1L << 13;
// The bubble stack is expanded. This means that the home gesture should be ignored, since a
// swipe up is an attempt to close the bubble stack, but that the back gesture should remain
// enabled (since it's used to navigate back within the bubbled app, or to collapse the bubble
// stack.
- public static final int SYSUI_STATE_BUBBLES_EXPANDED = 1 << 14;
+ public static final long SYSUI_STATE_BUBBLES_EXPANDED = 1L << 14;
// A SysUI dialog is showing.
- public static final int SYSUI_STATE_DIALOG_SHOWING = 1 << 15;
+ public static final long SYSUI_STATE_DIALOG_SHOWING = 1L << 15;
// The one-handed mode is active
- public static final int SYSUI_STATE_ONE_HANDED_ACTIVE = 1 << 16;
+ public static final long SYSUI_STATE_ONE_HANDED_ACTIVE = 1L << 16;
// Allow system gesture no matter the system bar(s) is visible or not
- public static final int SYSUI_STATE_ALLOW_GESTURE_IGNORING_BAR_VISIBILITY = 1 << 17;
+ public static final long SYSUI_STATE_ALLOW_GESTURE_IGNORING_BAR_VISIBILITY = 1L << 17;
// The IME is showing
- public static final int SYSUI_STATE_IME_SHOWING = 1 << 18;
+ public static final long SYSUI_STATE_IME_SHOWING = 1L << 18;
// The window magnification is overlapped with system gesture insets at the bottom.
- public static final int SYSUI_STATE_MAGNIFICATION_OVERLAP = 1 << 19;
+ public static final long SYSUI_STATE_MAGNIFICATION_OVERLAP = 1L << 19;
// ImeSwitcher is showing
- public static final int SYSUI_STATE_IME_SWITCHER_SHOWING = 1 << 20;
+ public static final long SYSUI_STATE_IME_SWITCHER_SHOWING = 1L << 20;
// Device dozing/AOD state
- public static final int SYSUI_STATE_DEVICE_DOZING = 1 << 21;
+ public static final long SYSUI_STATE_DEVICE_DOZING = 1L << 21;
// The home feature is disabled (either by SUW/SysUI/device policy)
- public static final int SYSUI_STATE_BACK_DISABLED = 1 << 22;
+ public static final long SYSUI_STATE_BACK_DISABLED = 1L << 22;
// The bubble stack is expanded AND the mange menu for bubbles is expanded on top of it.
- public static final int SYSUI_STATE_BUBBLES_MANAGE_MENU_EXPANDED = 1 << 23;
+ public static final long SYSUI_STATE_BUBBLES_MANAGE_MENU_EXPANDED = 1L << 23;
// The voice interaction session window is showing
- public static final int SYSUI_STATE_VOICE_INTERACTION_WINDOW_SHOWING = 1 << 25;
+ public static final long SYSUI_STATE_VOICE_INTERACTION_WINDOW_SHOWING = 1L << 25;
// Freeform windows are showing in desktop mode
- public static final int SYSUI_STATE_FREEFORM_ACTIVE_IN_DESKTOP_MODE = 1 << 26;
+ public static final long SYSUI_STATE_FREEFORM_ACTIVE_IN_DESKTOP_MODE = 1L << 26;
// Device dreaming state
- public static final int SYSUI_STATE_DEVICE_DREAMING = 1 << 27;
+ public static final long SYSUI_STATE_DEVICE_DREAMING = 1L << 27;
// Whether the device is currently awake (as opposed to asleep, see WakefulnessLifecycle).
// Note that the device is awake on while waking up on, but not while going to sleep.
- public static final int SYSUI_STATE_AWAKE = 1 << 28;
+ public static final long SYSUI_STATE_AWAKE = 1L << 28;
// Whether the device is currently transitioning between awake/asleep indicated by
// SYSUI_STATE_AWAKE.
- public static final int SYSUI_STATE_WAKEFULNESS_TRANSITION = 1 << 29;
+ public static final long SYSUI_STATE_WAKEFULNESS_TRANSITION = 1L << 29;
// The notification panel expansion fraction is > 0
- public static final int SYSUI_STATE_NOTIFICATION_PANEL_VISIBLE = 1 << 30;
+ public static final long SYSUI_STATE_NOTIFICATION_PANEL_VISIBLE = 1L << 30;
// When keyguard will be dismissed but didn't start animation yet
- public static final int SYSUI_STATE_STATUS_BAR_KEYGUARD_GOING_AWAY = 1 << 31;
+ public static final long SYSUI_STATE_STATUS_BAR_KEYGUARD_GOING_AWAY = 1L << 31;
+ // Physical keyboard shortcuts helper is showing
+ public static final long SYSUI_STATE_SHORTCUT_HELPER_SHOWING = 1L << 32;
// Mask for SystemUiStateFlags to isolate SYSUI_STATE_AWAKE and
// SYSUI_STATE_WAKEFULNESS_TRANSITION, to match WAKEFULNESS_* constants
- public static final int SYSUI_STATE_WAKEFULNESS_MASK =
+ public static final long SYSUI_STATE_WAKEFULNESS_MASK =
SYSUI_STATE_AWAKE | SYSUI_STATE_WAKEFULNESS_TRANSITION;
// Mirroring the WakefulnessLifecycle#Wakefulness states
- public static final int WAKEFULNESS_ASLEEP = 0;
- public static final int WAKEFULNESS_AWAKE = SYSUI_STATE_AWAKE;
- public static final int WAKEFULNESS_GOING_TO_SLEEP = SYSUI_STATE_WAKEFULNESS_TRANSITION;
- public static final int WAKEFULNESS_WAKING =
+ public static final long WAKEFULNESS_ASLEEP = 0;
+ public static final long WAKEFULNESS_AWAKE = SYSUI_STATE_AWAKE;
+ public static final long WAKEFULNESS_GOING_TO_SLEEP = SYSUI_STATE_WAKEFULNESS_TRANSITION;
+ public static final long WAKEFULNESS_WAKING =
SYSUI_STATE_WAKEFULNESS_TRANSITION | SYSUI_STATE_AWAKE;
// Whether the back gesture is allowed (or ignored) by the Shade
public static final boolean ALLOW_BACK_GESTURE_IN_SHADE = shadeAllowBackGesture();
@Retention(RetentionPolicy.SOURCE)
- @IntDef({SYSUI_STATE_SCREEN_PINNING,
+ @LongDef({SYSUI_STATE_SCREEN_PINNING,
SYSUI_STATE_NAV_BAR_HIDDEN,
SYSUI_STATE_NOTIFICATION_PANEL_EXPANDED,
SYSUI_STATE_QUICK_SETTINGS_EXPANDED,
@@ -167,10 +169,11 @@
SYSUI_STATE_WAKEFULNESS_TRANSITION,
SYSUI_STATE_NOTIFICATION_PANEL_VISIBLE,
SYSUI_STATE_STATUS_BAR_KEYGUARD_GOING_AWAY,
+ SYSUI_STATE_SHORTCUT_HELPER_SHOWING,
})
public @interface SystemUiStateFlags {}
- public static String getSystemUiStateString(int flags) {
+ public static String getSystemUiStateString(long flags) {
StringJoiner str = new StringJoiner("|");
if ((flags & SYSUI_STATE_SCREEN_PINNING) != 0) {
str.add("screen_pinned");
@@ -265,6 +268,9 @@
if ((flags & SYSUI_STATE_STATUS_BAR_KEYGUARD_GOING_AWAY) != 0) {
str.add("keygrd_going_away");
}
+ if ((flags & SYSUI_STATE_SHORTCUT_HELPER_SHOWING) != 0) {
+ str.add("shortcut_helper_showing");
+ }
return str.toString();
}
@@ -285,13 +291,13 @@
* Returns whether the specified sysui state is such that the assistant gesture should be
* disabled.
*/
- public static boolean isAssistantGestureDisabled(int sysuiStateFlags) {
+ public static boolean isAssistantGestureDisabled(long sysuiStateFlags) {
if ((sysuiStateFlags & SYSUI_STATE_ALLOW_GESTURE_IGNORING_BAR_VISIBILITY) != 0) {
sysuiStateFlags &= ~SYSUI_STATE_NAV_BAR_HIDDEN;
}
// Disable when in quick settings, screen pinning, immersive, the bouncer is showing,
// or search is disabled
- int disableFlags = SYSUI_STATE_SCREEN_PINNING
+ long disableFlags = SYSUI_STATE_SCREEN_PINNING
| SYSUI_STATE_NAV_BAR_HIDDEN
| SYSUI_STATE_BOUNCER_SHOWING
| SYSUI_STATE_SEARCH_DISABLED
@@ -313,7 +319,7 @@
* Returns whether the specified sysui state is such that the back gesture should be
* disabled.
*/
- public static boolean isBackGestureDisabled(int sysuiStateFlags, boolean forTrackpad) {
+ public static boolean isBackGestureDisabled(long sysuiStateFlags, boolean forTrackpad) {
// Always allow when the bouncer/global actions/voice session is showing (even on top of
// the keyguard)
if ((sysuiStateFlags & SYSUI_STATE_BOUNCER_SHOWING) != 0
@@ -328,9 +334,9 @@
return (sysuiStateFlags & getBackGestureDisabledMask(forTrackpad)) != 0;
}
- private static int getBackGestureDisabledMask(boolean forTrackpad) {
+ private static long getBackGestureDisabledMask(boolean forTrackpad) {
// Disable when in immersive, or the notifications are interactive
- int disableFlags = SYSUI_STATE_STATUS_BAR_KEYGUARD_SHOWING;
+ long disableFlags = SYSUI_STATE_STATUS_BAR_KEYGUARD_SHOWING;
if (!forTrackpad) {
disableFlags |= SYSUI_STATE_NAV_BAR_HIDDEN;
}
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/system/RecentsAnimationControllerCompat.java b/packages/SystemUI/shared/src/com/android/systemui/shared/system/RecentsAnimationControllerCompat.java
index a6e04ce..bbf4698 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/system/RecentsAnimationControllerCompat.java
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/system/RecentsAnimationControllerCompat.java
@@ -42,7 +42,7 @@
try {
final TaskSnapshot snapshot = mAnimationController.screenshotTask(taskId);
if (snapshot != null) {
- return new ThumbnailData(snapshot);
+ return ThumbnailData.fromSnapshot(snapshot);
}
} catch (RemoteException e) {
Log.e(TAG, "Failed to screenshot task", e);
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/system/TaskStackChangeListeners.java b/packages/SystemUI/shared/src/com/android/systemui/shared/system/TaskStackChangeListeners.java
index 473719fa..cf8ec62 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/system/TaskStackChangeListeners.java
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/system/TaskStackChangeListeners.java
@@ -351,7 +351,7 @@
case ON_TASK_SNAPSHOT_CHANGED: {
Trace.beginSection("onTaskSnapshotChanged");
final TaskSnapshot snapshot = (TaskSnapshot) msg.obj;
- final ThumbnailData thumbnail = new ThumbnailData(snapshot);
+ final ThumbnailData thumbnail = ThumbnailData.fromSnapshot(snapshot);
boolean snapshotConsumed = false;
for (int i = mTaskStackListeners.size() - 1; i >= 0; i--) {
boolean consumed = mTaskStackListeners.get(i).onTaskSnapshotChanged(
diff --git a/packages/SystemUI/src/com/android/keyguard/ClockEventController.kt b/packages/SystemUI/src/com/android/keyguard/ClockEventController.kt
index f33acf2..3f3bb0b 100644
--- a/packages/SystemUI/src/com/android/keyguard/ClockEventController.kt
+++ b/packages/SystemUI/src/com/android/keyguard/ClockEventController.kt
@@ -42,6 +42,7 @@
import com.android.systemui.keyguard.MigrateClocksToBlueprint
import com.android.systemui.keyguard.domain.interactor.KeyguardInteractor
import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor
+import com.android.systemui.keyguard.shared.model.Edge
import com.android.systemui.keyguard.shared.model.KeyguardState.AOD
import com.android.systemui.keyguard.shared.model.KeyguardState.DOZING
import com.android.systemui.keyguard.shared.model.KeyguardState.LOCKSCREEN
@@ -544,10 +545,10 @@
internal fun listenForDozeAmountTransition(scope: CoroutineScope): Job {
return scope.launch {
merge(
- keyguardTransitionInteractor.transition(AOD, LOCKSCREEN).map { step ->
- step.copy(value = 1f - step.value)
+ keyguardTransitionInteractor.transition(Edge.create(AOD, LOCKSCREEN)).map {
+ it.copy(value = 1f - it.value)
},
- keyguardTransitionInteractor.transition(LOCKSCREEN, AOD),
+ keyguardTransitionInteractor.transition(Edge.create(LOCKSCREEN, AOD)),
).filter {
it.transitionState != TransitionState.FINISHED
}
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainerController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainerController.java
index 905a98c..42838ae 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainerController.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainerController.java
@@ -326,7 +326,8 @@
}
if (KeyguardWmStateRefactor.isEnabled()) {
- mKeyguardTransitionInteractor.startDismissKeyguardTransition();
+ mKeyguardTransitionInteractor.startDismissKeyguardTransition(
+ "KeyguardSecurityContainerController#finish");
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnificationController.java b/packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnificationController.java
index e66261c..5458ab1 100644
--- a/packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnificationController.java
+++ b/packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnificationController.java
@@ -240,7 +240,7 @@
private boolean mEditSizeEnable = false;
private boolean mSettingsPanelVisibility = false;
@VisibleForTesting
- WindowMagnificationSizePrefs mWindowMagnificationSizePrefs;
+ WindowMagnificationFrameSizePrefs mWindowMagnificationFrameSizePrefs;
@Nullable
private final MirrorWindowControl mMirrorWindowControl;
@@ -270,7 +270,7 @@
mSysUiState = sysUiState;
mScvhSupplier = scvhSupplier;
mConfiguration = new Configuration(context.getResources().getConfiguration());
- mWindowMagnificationSizePrefs = new WindowMagnificationSizePrefs(mContext);
+ mWindowMagnificationFrameSizePrefs = new WindowMagnificationFrameSizePrefs(mContext);
final Display display = mContext.getDisplay();
mDisplayId = mContext.getDisplayId();
@@ -457,7 +457,7 @@
if (!enable) {
// Keep the magnifier size when exiting edit mode
- mWindowMagnificationSizePrefs.saveSizeForCurrentDensity(
+ mWindowMagnificationFrameSizePrefs.saveSizeForCurrentDensity(
new Size(mMagnificationFrame.width(), mMagnificationFrame.height()));
}
}
@@ -944,7 +944,7 @@
}
private void setMagnificationFrame(int width, int height, int centerX, int centerY) {
- mWindowMagnificationSizePrefs.saveSizeForCurrentDensity(new Size(width, height));
+ mWindowMagnificationFrameSizePrefs.saveSizeForCurrentDensity(new Size(width, height));
// Sets the initial frame area for the mirror and place it to the given center on the
// display.
@@ -954,11 +954,11 @@
}
private Size restoreMagnificationWindowFrameSizeIfPossible() {
- if (!mWindowMagnificationSizePrefs.isPreferenceSavedForCurrentDensity()) {
+ if (!mWindowMagnificationFrameSizePrefs.isPreferenceSavedForCurrentDensity()) {
return getDefaultMagnificationWindowFrameSize();
}
- return mWindowMagnificationSizePrefs.getSizeForCurrentDensity();
+ return mWindowMagnificationFrameSizePrefs.getSizeForCurrentDensity();
}
private Size getDefaultMagnificationWindowFrameSize() {
diff --git a/packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnificationSizePrefs.java b/packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnificationFrameSizePrefs.java
similarity index 95%
rename from packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnificationSizePrefs.java
rename to packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnificationFrameSizePrefs.java
index a401f2a..e83e85e 100644
--- a/packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnificationSizePrefs.java
+++ b/packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnificationFrameSizePrefs.java
@@ -23,14 +23,14 @@
/**
* Class to handle SharedPreference for window magnification size.
*/
-final class WindowMagnificationSizePrefs {
+final class WindowMagnificationFrameSizePrefs {
private static final String WINDOW_MAGNIFICATION_PREFERENCES =
"window_magnification_preferences";
Context mContext;
SharedPreferences mWindowMagnificationSizePreferences;
- public WindowMagnificationSizePrefs(Context context) {
+ WindowMagnificationFrameSizePrefs(Context context) {
mContext = context;
mWindowMagnificationSizePreferences = mContext
.getSharedPreferences(WINDOW_MAGNIFICATION_PREFERENCES, Context.MODE_PRIVATE);
diff --git a/packages/SystemUI/src/com/android/systemui/accessibility/data/repository/NightDisplayRepository.kt b/packages/SystemUI/src/com/android/systemui/accessibility/data/repository/NightDisplayRepository.kt
index bf44fab..b33746c 100644
--- a/packages/SystemUI/src/com/android/systemui/accessibility/data/repository/NightDisplayRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/accessibility/data/repository/NightDisplayRepository.kt
@@ -149,12 +149,7 @@
secureSettings
.observerFlow(userHandle.identifier, DISPLAY_AUTO_MODE_RAW_SETTING_NAME)
.onStart { emit(Unit) }
- .map {
- secureSettings.getIntForUser(
- DISPLAY_AUTO_MODE_RAW_SETTING_NAME,
- userHandle.identifier
- ) == NIGHT_DISPLAY_AUTO_MODE_RAW_NOT_SET
- }
+ .map { isNightDisplayAutoModeRawSettingNotSet(userHandle.identifier) }
}
.distinctUntilChanged()
@@ -179,12 +174,19 @@
colorDisplayManager.nightDisplayCustomEndTime,
globalSettings.getString(IS_FORCE_AUTO_MODE_AVAILABLE_SETTING_NAME) ==
NIGHT_DISPLAY_FORCED_AUTO_MODE_AVAILABLE &&
- secureSettings.getIntForUser(DISPLAY_AUTO_MODE_RAW_SETTING_NAME, user.identifier) ==
- NIGHT_DISPLAY_AUTO_MODE_RAW_NOT_SET,
+ isNightDisplayAutoModeRawSettingNotSet(user.identifier),
locationController.isLocationEnabled,
)
}
+ private fun isNightDisplayAutoModeRawSettingNotSet(userId: Int): Boolean {
+ return secureSettings.getIntForUser(
+ DISPLAY_AUTO_MODE_RAW_SETTING_NAME,
+ NIGHT_DISPLAY_AUTO_MODE_RAW_NOT_SET,
+ userId
+ ) == NIGHT_DISPLAY_AUTO_MODE_RAW_NOT_SET
+ }
+
private companion object {
const val NIGHT_DISPLAY_AUTO_MODE_RAW_NOT_SET = -1
const val NIGHT_DISPLAY_FORCED_AUTO_MODE_AVAILABLE = "1"
diff --git a/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/MenuAnimationController.java b/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/MenuAnimationController.java
index 1f04599..d5e911e 100644
--- a/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/MenuAnimationController.java
+++ b/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/MenuAnimationController.java
@@ -37,7 +37,6 @@
import androidx.recyclerview.widget.RecyclerView;
import com.android.internal.annotations.VisibleForTesting;
-import com.android.systemui.Flags;
import java.util.HashMap;
@@ -339,15 +338,11 @@
mMenuView.updateMenuMoveToTucked(/* isMoveToTucked= */ true);
final PointF position = mMenuView.getMenuPosition();
final PointF tuckedPosition = getTuckedMenuPosition();
- if (Flags.floatingMenuAnimatedTuck()) {
- flingThenSpringMenuWith(DynamicAnimation.TRANSLATION_X,
- Math.signum(tuckedPosition.x - position.x) * ESCAPE_VELOCITY,
- FLING_FRICTION_SCALAR,
- createDefaultSpringForce(),
- tuckedPosition.x);
- } else {
- moveToPosition(tuckedPosition);
- }
+ flingThenSpringMenuWith(DynamicAnimation.TRANSLATION_X,
+ Math.signum(tuckedPosition.x - position.x) * ESCAPE_VELOCITY,
+ FLING_FRICTION_SCALAR,
+ createDefaultSpringForce(),
+ tuckedPosition.x);
// Keep the touch region let users could click extra space to pop up the menu view
// from the screen edge
@@ -359,23 +354,19 @@
void moveOutEdgeAndShow() {
mMenuView.updateMenuMoveToTucked(/* isMoveToTucked= */ false);
- if (Flags.floatingMenuAnimatedTuck()) {
- PointF position = mMenuView.getMenuPosition();
- springMenuWith(DynamicAnimation.TRANSLATION_X,
- createDefaultSpringForce(),
- 0,
- position.x,
- true
- );
- springMenuWith(DynamicAnimation.TRANSLATION_Y,
- createDefaultSpringForce(),
- 0,
- position.y,
- true
- );
- } else {
- mMenuView.onPositionChanged();
- }
+ PointF position = mMenuView.getMenuPosition();
+ springMenuWith(DynamicAnimation.TRANSLATION_X,
+ createDefaultSpringForce(),
+ 0,
+ position.x,
+ true
+ );
+ springMenuWith(DynamicAnimation.TRANSLATION_Y,
+ createDefaultSpringForce(),
+ 0,
+ position.y,
+ true
+ );
mMenuView.onEdgeChangedIfNeeded();
}
diff --git a/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/MenuView.java b/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/MenuView.java
index be75e10..9d9e7df 100644
--- a/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/MenuView.java
+++ b/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/MenuView.java
@@ -321,22 +321,6 @@
if (mMoveToTuckedListener != null) {
mMoveToTuckedListener.onMoveToTuckedChanged(isMoveToTucked);
}
-
- if (!Flags.floatingMenuAnimatedTuck()) {
- if (isMoveToTucked) {
- final float halfWidth = getMenuWidth() / 2.0f;
- final boolean isOnLeftSide = mMenuAnimationController.isOnLeftSide();
- final Rect clipBounds = new Rect(
- (int) (!isOnLeftSide ? 0 : halfWidth),
- 0,
- (int) (!isOnLeftSide ? halfWidth : getMenuWidth()),
- getMenuHeight()
- );
- setClipBounds(clipBounds);
- } else {
- setClipBounds(null);
- }
- }
}
diff --git a/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/MenuViewLayer.java b/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/MenuViewLayer.java
index 6dce1bb..0c67c50 100644
--- a/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/MenuViewLayer.java
+++ b/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/MenuViewLayer.java
@@ -322,9 +322,8 @@
}
addView(mMessageView, LayerIndex.MESSAGE_VIEW);
- if (Flags.floatingMenuAnimatedTuck()) {
- setClipChildren(true);
- }
+ setClipChildren(true);
+
setClickable(false);
setFocusable(false);
setImportantForAccessibility(IMPORTANT_FOR_ACCESSIBILITY_NO);
@@ -476,10 +475,8 @@
mMenuAnimationController.startTuckedAnimationPreview();
}
- if (Flags.floatingMenuAnimatedTuck()) {
- if (!mMenuView.isMoveToTucked()) {
- setClipBounds(null);
- }
+ if (!mMenuView.isMoveToTucked()) {
+ setClipBounds(null);
}
mMenuView.onArrivalAtPosition(false);
}
diff --git a/packages/SystemUI/src/com/android/systemui/ambient/touch/ShadeTouchHandler.java b/packages/SystemUI/src/com/android/systemui/ambient/touch/ShadeTouchHandler.java
index 9c7fc9d..9ef9938 100644
--- a/packages/SystemUI/src/com/android/systemui/ambient/touch/ShadeTouchHandler.java
+++ b/packages/SystemUI/src/com/android/systemui/ambient/touch/ShadeTouchHandler.java
@@ -23,8 +23,7 @@
import android.view.GestureDetector;
import android.view.MotionEvent;
-import androidx.annotation.NonNull;
-
+import com.android.systemui.shade.ShadeViewController;
import com.android.systemui.statusbar.phone.CentralSurfaces;
import java.util.Optional;
@@ -38,34 +37,29 @@
*/
public class ShadeTouchHandler implements TouchHandler {
private final Optional<CentralSurfaces> mSurfaces;
+ private final ShadeViewController mShadeViewController;
private final int mInitiationHeight;
- /**
- * Tracks whether or not we are capturing a given touch. Will be null before and after a touch.
- */
- private Boolean mCapture;
-
@Inject
ShadeTouchHandler(Optional<CentralSurfaces> centralSurfaces,
+ ShadeViewController shadeViewController,
@Named(NOTIFICATION_SHADE_GESTURE_INITIATION_HEIGHT) int initiationHeight) {
mSurfaces = centralSurfaces;
+ mShadeViewController = shadeViewController;
mInitiationHeight = initiationHeight;
}
@Override
public void onSessionStart(TouchSession session) {
- if (mSurfaces.isEmpty()) {
+ if (mSurfaces.map(CentralSurfaces::isBouncerShowing).orElse(false)) {
session.pop();
return;
}
- session.registerCallback(() -> mCapture = null);
-
session.registerInputListener(ev -> {
+ mShadeViewController.handleExternalTouch((MotionEvent) ev);
+
if (ev instanceof MotionEvent) {
- if (mCapture != null && mCapture) {
- mSurfaces.get().handleExternalShadeWindowTouch((MotionEvent) ev);
- }
if (((MotionEvent) ev).getAction() == MotionEvent.ACTION_UP) {
session.pop();
}
@@ -74,25 +68,15 @@
session.registerGestureListener(new GestureDetector.SimpleOnGestureListener() {
@Override
- public boolean onScroll(MotionEvent e1, @NonNull MotionEvent e2, float distanceX,
+ public boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX,
float distanceY) {
- if (mCapture == null) {
- // Only capture swipes that are going downwards.
- mCapture = Math.abs(distanceY) > Math.abs(distanceX) && distanceY < 0;
- if (mCapture) {
- // Send the initial touches over, as the input listener has already
- // processed these touches.
- mSurfaces.get().handleExternalShadeWindowTouch(e1);
- mSurfaces.get().handleExternalShadeWindowTouch(e2);
- }
- }
- return mCapture;
+ return true;
}
@Override
- public boolean onFling(MotionEvent e1, @NonNull MotionEvent e2, float velocityX,
+ public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX,
float velocityY) {
- return mCapture;
+ return true;
}
});
}
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/AuthContainerView.java b/packages/SystemUI/src/com/android/systemui/biometrics/AuthContainerView.java
index 298c0f7..b75b292 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/AuthContainerView.java
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/AuthContainerView.java
@@ -362,7 +362,7 @@
mPromptSelectorInteractorProvider = promptSelectorInteractorProvider;
mPromptSelectorInteractorProvider.get().setPrompt(mConfig.mPromptInfo, mEffectiveUserId,
- biometricModalities, mConfig.mOperationId, mConfig.mOpPackageName,
+ getRequestId(), biometricModalities, mConfig.mOperationId, mConfig.mOpPackageName,
false /*onSwitchToCredential*/);
final LayoutInflater layoutInflater = LayoutInflater.from(mContext);
@@ -436,7 +436,7 @@
addCredentialView(true, false);
}
} else {
- mPromptSelectorInteractorProvider.get().resetPrompt();
+ mPromptSelectorInteractorProvider.get().resetPrompt(getRequestId());
}
}
@@ -884,7 +884,8 @@
final Runnable endActionRunnable = () -> {
setVisibility(View.INVISIBLE);
if (Flags.customBiometricPrompt() && constraintBp()) {
- mPromptSelectorInteractorProvider.get().resetPrompt();
+ // TODO(b/288175645): resetPrompt calls should be lifecycle aware
+ mPromptSelectorInteractorProvider.get().resetPrompt(getRequestId());
}
removeWindowIfAttached();
};
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsKeyguardViewControllerLegacy.kt b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsKeyguardViewControllerLegacy.kt
index 9816896..298b87d 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsKeyguardViewControllerLegacy.kt
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsKeyguardViewControllerLegacy.kt
@@ -32,11 +32,18 @@
import com.android.systemui.bouncer.domain.interactor.PrimaryBouncerInteractor
import com.android.systemui.dump.DumpManager
import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor
-import com.android.systemui.keyguard.shared.model.KeyguardState
+import com.android.systemui.keyguard.shared.model.Edge
+import com.android.systemui.keyguard.shared.model.KeyguardState.ALTERNATE_BOUNCER
+import com.android.systemui.keyguard.shared.model.KeyguardState.AOD
+import com.android.systemui.keyguard.shared.model.KeyguardState.DREAMING
+import com.android.systemui.keyguard.shared.model.KeyguardState.GONE
+import com.android.systemui.keyguard.shared.model.KeyguardState.OCCLUDED
+import com.android.systemui.keyguard.shared.model.KeyguardState.PRIMARY_BOUNCER
import com.android.systemui.keyguard.shared.model.TransitionState
import com.android.systemui.lifecycle.repeatWhenAttached
import com.android.systemui.plugins.statusbar.StatusBarStateController
import com.android.systemui.res.R
+import com.android.systemui.scene.shared.model.Scenes
import com.android.systemui.shade.domain.interactor.ShadeInteractor
import com.android.systemui.statusbar.LockscreenShadeTransitionController
import com.android.systemui.statusbar.StatusBarState
@@ -131,6 +138,7 @@
override fun onUnlockedChanged() {
updatePauseAuth()
}
+
override fun onLaunchTransitionFadingAwayChanged() {
launchTransitionFadingAway = keyguardStateController.isLaunchTransitionFadingAway
updatePauseAuth()
@@ -211,7 +219,10 @@
suspend fun listenForPrimaryBouncerToAodTransitions(scope: CoroutineScope): Job {
return scope.launch {
transitionInteractor
- .transition(KeyguardState.PRIMARY_BOUNCER, KeyguardState.AOD)
+ .transition(
+ edge = Edge.create(Scenes.Bouncer, AOD),
+ edgeWithoutSceneContainer = Edge.create(PRIMARY_BOUNCER, AOD)
+ )
.collect { transitionStep ->
view.onDozeAmountChanged(
transitionStep.value,
@@ -225,8 +236,7 @@
@VisibleForTesting
suspend fun listenForDreamingToAodTransitions(scope: CoroutineScope): Job {
return scope.launch {
- transitionInteractor.transition(KeyguardState.DREAMING, KeyguardState.AOD).collect {
- transitionStep ->
+ transitionInteractor.transition(Edge.create(DREAMING, AOD)).collect { transitionStep ->
view.onDozeAmountChanged(
transitionStep.value,
transitionStep.value,
@@ -239,23 +249,21 @@
@VisibleForTesting
suspend fun listenForAlternateBouncerToAodTransitions(scope: CoroutineScope): Job {
return scope.launch {
- transitionInteractor
- .transition(KeyguardState.ALTERNATE_BOUNCER, KeyguardState.AOD)
- .collect { transitionStep ->
- view.onDozeAmountChanged(
- transitionStep.value,
- transitionStep.value,
- UdfpsKeyguardViewLegacy.ANIMATION_BETWEEN_AOD_AND_LOCKSCREEN,
- )
- }
+ transitionInteractor.transition(Edge.create(ALTERNATE_BOUNCER, AOD)).collect {
+ transitionStep ->
+ view.onDozeAmountChanged(
+ transitionStep.value,
+ transitionStep.value,
+ UdfpsKeyguardViewLegacy.ANIMATION_BETWEEN_AOD_AND_LOCKSCREEN,
+ )
+ }
}
}
@VisibleForTesting
suspend fun listenForAodToOccludedTransitions(scope: CoroutineScope): Job {
return scope.launch {
- transitionInteractor.transition(KeyguardState.AOD, KeyguardState.OCCLUDED).collect {
- transitionStep ->
+ transitionInteractor.transition(Edge.create(AOD, OCCLUDED)).collect { transitionStep ->
view.onDozeAmountChanged(
1f - transitionStep.value,
1f - transitionStep.value,
@@ -268,8 +276,7 @@
@VisibleForTesting
suspend fun listenForOccludedToAodTransition(scope: CoroutineScope): Job {
return scope.launch {
- transitionInteractor.transition(KeyguardState.OCCLUDED, KeyguardState.AOD).collect {
- transitionStep ->
+ transitionInteractor.transition(Edge.create(OCCLUDED, AOD)).collect { transitionStep ->
view.onDozeAmountChanged(
transitionStep.value,
transitionStep.value,
@@ -282,14 +289,18 @@
@VisibleForTesting
suspend fun listenForGoneToAodTransition(scope: CoroutineScope): Job {
return scope.launch {
- transitionInteractor.transition(KeyguardState.GONE, KeyguardState.AOD).collect {
- transitionStep ->
- view.onDozeAmountChanged(
- transitionStep.value,
- transitionStep.value,
- ANIMATE_APPEAR_ON_SCREEN_OFF,
+ transitionInteractor
+ .transition(
+ edge = Edge.create(Scenes.Gone, AOD),
+ edgeWithoutSceneContainer = Edge.create(GONE, AOD)
)
- }
+ .collect { transitionStep ->
+ view.onDozeAmountChanged(
+ transitionStep.value,
+ transitionStep.value,
+ ANIMATE_APPEAR_ON_SCREEN_OFF,
+ )
+ }
}
}
@@ -298,13 +309,10 @@
return scope.launch {
transitionInteractor.dozeAmountTransition.collect { transitionStep ->
if (
- transitionStep.from == KeyguardState.AOD &&
+ transitionStep.from == AOD &&
transitionStep.transitionState == TransitionState.CANCELED
) {
- if (
- transitionInteractor.startedKeyguardTransitionStep.first().to !=
- KeyguardState.AOD
- ) {
+ if (transitionInteractor.startedKeyguardTransitionStep.first().to != AOD) {
// If the next started transition isn't transitioning back to AOD, force
// doze amount to be 0f (as if the transition to the lockscreen completed).
view.onDozeAmountChanged(
@@ -557,6 +565,7 @@
private fun updateScaleFactor() {
udfpsController.mOverlayParams?.scaleFactor?.let { view.setScaleFactor(it) }
}
+
companion object {
const val TAG = "UdfpsKeyguardViewController"
}
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/data/repository/BiometricStatusRepository.kt b/packages/SystemUI/src/com/android/systemui/biometrics/data/repository/BiometricStatusRepository.kt
index cc52484..ca03a00 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/data/repository/BiometricStatusRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/data/repository/BiometricStatusRepository.kt
@@ -34,6 +34,7 @@
import android.hardware.biometrics.events.AuthenticationSucceededInfo
import android.hardware.face.FaceManager
import android.hardware.fingerprint.FingerprintManager
+import android.util.Log
import com.android.systemui.biometrics.shared.model.AuthenticationReason
import com.android.systemui.biometrics.shared.model.AuthenticationReason.SettingsOperations
import com.android.systemui.biometrics.shared.model.AuthenticationState
@@ -52,6 +53,7 @@
import kotlinx.coroutines.flow.filter
import kotlinx.coroutines.flow.filterIsInstance
import kotlinx.coroutines.flow.map
+import kotlinx.coroutines.flow.onEach
import kotlinx.coroutines.flow.shareIn
/** A repository for the state of biometric authentication. */
@@ -85,6 +87,7 @@
private val authenticationState: Flow<AuthenticationState> =
conflatedCallbackFlow {
val updateAuthenticationState = { state: AuthenticationState ->
+ Log.d(TAG, "authenticationState updated: $state")
trySendWithFailureLogging(state, TAG, "Error sending AuthenticationState state")
}
@@ -187,6 +190,7 @@
it.biometricSourceType == BiometricSourceType.FINGERPRINT)
}
.map { it.requestReason }
+ .onEach { Log.d(TAG, "fingerprintAuthenticationReason updated: $it") }
override val fingerprintAcquiredStatus: Flow<FingerprintAuthenticationStatus> =
authenticationState
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/data/repository/FingerprintPropertyRepository.kt b/packages/SystemUI/src/com/android/systemui/biometrics/data/repository/FingerprintPropertyRepository.kt
index 40d38dd..ba51d02 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/data/repository/FingerprintPropertyRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/data/repository/FingerprintPropertyRepository.kt
@@ -41,7 +41,10 @@
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.SharingStarted
import kotlinx.coroutines.flow.StateFlow
+import kotlinx.coroutines.flow.combine
+import kotlinx.coroutines.flow.distinctUntilChanged
import kotlinx.coroutines.flow.map
+import kotlinx.coroutines.flow.onStart
import kotlinx.coroutines.flow.stateIn
import kotlinx.coroutines.withContext
@@ -52,7 +55,7 @@
*/
interface FingerprintPropertyRepository {
/** Whether the fingerprint properties have been initialized yet. */
- val propertiesInitialized: StateFlow<Boolean>
+ val propertiesInitialized: Flow<Boolean>
/** The id of fingerprint sensor. */
val sensorId: Flow<Int>
@@ -110,15 +113,6 @@
initialValue = UNINITIALIZED_PROPS,
)
- override val propertiesInitialized: StateFlow<Boolean> =
- props
- .map { it != UNINITIALIZED_PROPS }
- .stateIn(
- applicationScope,
- started = SharingStarted.WhileSubscribed(),
- initialValue = props.value != UNINITIALIZED_PROPS,
- )
-
override val sensorId: Flow<Int> = props.map { it.sensorId }
override val strength: Flow<SensorStrength> = props.map { it.sensorStrength.toSensorStrength() }
@@ -139,6 +133,22 @@
}
}
+ override val propertiesInitialized: Flow<Boolean> =
+ combine(
+ props
+ .map { it != UNINITIALIZED_PROPS }
+ .onStart { emit(props.value != UNINITIALIZED_PROPS) },
+ sensorId.map {}.onStart { if (props.value != UNINITIALIZED_PROPS) emit(Unit) },
+ sensorLocations
+ .map {}
+ .onStart { if (props.value != UNINITIALIZED_PROPS) emit(Unit) },
+ sensorType.map {}.onStart { if (props.value != UNINITIALIZED_PROPS) emit(Unit) },
+ strength.map {}.onStart { if (props.value != UNINITIALIZED_PROPS) emit(Unit) },
+ ) { initialized, _, _, _, _ ->
+ initialized
+ }
+ .distinctUntilChanged()
+
companion object {
private const val TAG = "FingerprintPropertyRepositoryImpl"
private val UNINITIALIZED_PROPS =
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/data/repository/PromptRepository.kt b/packages/SystemUI/src/com/android/systemui/biometrics/data/repository/PromptRepository.kt
index 58b238b..230b30b 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/data/repository/PromptRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/data/repository/PromptRepository.kt
@@ -17,6 +17,7 @@
package com.android.systemui.biometrics.data.repository
import android.hardware.biometrics.PromptInfo
+import android.util.Log
import com.android.systemui.biometrics.AuthController
import com.android.systemui.biometrics.shared.model.PromptKind
import com.android.systemui.common.coroutine.ChannelExt.trySendWithFailureLogging
@@ -49,6 +50,9 @@
/** The user that the prompt is for. */
val userId: StateFlow<Int?>
+ /** The request that the prompt is for. */
+ val requestId: StateFlow<Long?>
+
/** The gatekeeper challenge, if one is associated with this prompt. */
val challenge: StateFlow<Long?>
@@ -69,13 +73,14 @@
fun setPrompt(
promptInfo: PromptInfo,
userId: Int,
+ requestId: Long,
gatekeeperChallenge: Long?,
kind: PromptKind,
opPackageName: String,
)
/** Unset the prompt info. */
- fun unsetPrompt()
+ fun unsetPrompt(requestId: Long)
}
@SysUISingleton
@@ -109,6 +114,9 @@
private val _userId: MutableStateFlow<Int?> = MutableStateFlow(null)
override val userId = _userId.asStateFlow()
+ private val _requestId: MutableStateFlow<Long?> = MutableStateFlow(null)
+ override val requestId = _requestId.asStateFlow()
+
private val _promptKind: MutableStateFlow<PromptKind> = MutableStateFlow(PromptKind.None)
override val promptKind = _promptKind.asStateFlow()
@@ -132,23 +140,30 @@
override fun setPrompt(
promptInfo: PromptInfo,
userId: Int,
+ requestId: Long,
gatekeeperChallenge: Long?,
kind: PromptKind,
opPackageName: String,
) {
_promptKind.value = kind
_userId.value = userId
+ _requestId.value = requestId
_challenge.value = gatekeeperChallenge
_promptInfo.value = promptInfo
_opPackageName.value = opPackageName
}
- override fun unsetPrompt() {
- _promptInfo.value = null
- _userId.value = null
- _challenge.value = null
- _promptKind.value = PromptKind.None
- _opPackageName.value = null
+ override fun unsetPrompt(requestId: Long) {
+ if (requestId == _requestId.value) {
+ _promptInfo.value = null
+ _userId.value = null
+ _requestId.value = null
+ _challenge.value = null
+ _promptKind.value = PromptKind.None
+ _opPackageName.value = null
+ } else {
+ Log.w(TAG, "Ignoring unsetPrompt - requestId mismatch")
+ }
}
companion object {
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/domain/interactor/BiometricStatusInteractor.kt b/packages/SystemUI/src/com/android/systemui/biometrics/domain/interactor/BiometricStatusInteractor.kt
index 6e79e46..83aefca 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/domain/interactor/BiometricStatusInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/domain/interactor/BiometricStatusInteractor.kt
@@ -17,6 +17,7 @@
package com.android.systemui.biometrics.domain.interactor
import android.app.ActivityTaskManager
+import android.util.Log
import com.android.systemui.biometrics.data.repository.BiometricStatusRepository
import com.android.systemui.biometrics.data.repository.FingerprintPropertyRepository
import com.android.systemui.biometrics.shared.model.AuthenticationReason
@@ -26,6 +27,7 @@
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.combine
import kotlinx.coroutines.flow.distinctUntilChanged
+import kotlinx.coroutines.flow.onEach
/** Encapsulates business logic for interacting with biometric authentication state. */
interface BiometricStatusInteractor {
@@ -49,15 +51,20 @@
override val sfpsAuthenticationReason: Flow<AuthenticationReason> =
combine(
- biometricStatusRepository.fingerprintAuthenticationReason,
- fingerprintPropertyRepository.sensorType
- ) { reason: AuthenticationReason, sensorType ->
- if (sensorType.isPowerButton() && reason.isReasonToAlwaysUpdateSfpsOverlay(activityTaskManager)) {
- reason
- } else {
- AuthenticationReason.NotRunning
+ biometricStatusRepository.fingerprintAuthenticationReason,
+ fingerprintPropertyRepository.sensorType
+ ) { reason: AuthenticationReason, sensorType ->
+ if (
+ sensorType.isPowerButton() &&
+ reason.isReasonToAlwaysUpdateSfpsOverlay(activityTaskManager)
+ ) {
+ reason
+ } else {
+ AuthenticationReason.NotRunning
+ }
}
- }.distinctUntilChanged()
+ .distinctUntilChanged()
+ .onEach { Log.d(TAG, "sfpsAuthenticationReason updated: $it") }
override val fingerprintAcquiredStatus: Flow<FingerprintAuthenticationStatus> =
biometricStatusRepository.fingerprintAcquiredStatus
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/domain/interactor/FingerprintPropertyInteractor.kt b/packages/SystemUI/src/com/android/systemui/biometrics/domain/interactor/FingerprintPropertyInteractor.kt
index 3112b67..a74b0b0 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/domain/interactor/FingerprintPropertyInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/domain/interactor/FingerprintPropertyInteractor.kt
@@ -46,13 +46,13 @@
displayStateInteractor: DisplayStateInteractor,
udfpsOverlayInteractor: UdfpsOverlayInteractor,
) {
- val propertiesInitialized: StateFlow<Boolean> = repository.propertiesInitialized
+ val propertiesInitialized: Flow<Boolean> = repository.propertiesInitialized
val isUdfps: StateFlow<Boolean> =
repository.sensorType
.map { it.isUdfps() }
.stateIn(
scope = applicationScope,
- started = SharingStarted.WhileSubscribed(),
+ started = SharingStarted.Eagerly,
initialValue = repository.sensorType.value.isUdfps(),
)
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/domain/interactor/PromptSelectorInteractor.kt b/packages/SystemUI/src/com/android/systemui/biometrics/domain/interactor/PromptSelectorInteractor.kt
index 4ba780f..dc338d0 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/domain/interactor/PromptSelectorInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/domain/interactor/PromptSelectorInteractor.kt
@@ -86,6 +86,7 @@
fun setPrompt(
promptInfo: PromptInfo,
effectiveUserId: Int,
+ requestId: Long,
modalities: BiometricModalities,
challenge: Long,
opPackageName: String,
@@ -93,7 +94,7 @@
)
/** Unset the current authentication request. */
- fun resetPrompt()
+ fun resetPrompt(requestId: Long)
}
@SysUISingleton
@@ -161,6 +162,7 @@
setPrompt(
promptRepository.promptInfo.value!!,
promptRepository.userId.value!!,
+ promptRepository.requestId.value!!,
modalities,
promptRepository.challenge.value!!,
promptRepository.opPackageName.value!!,
@@ -171,6 +173,7 @@
override fun setPrompt(
promptInfo: PromptInfo,
effectiveUserId: Int,
+ requestId: Long,
modalities: BiometricModalities,
challenge: Long,
opPackageName: String,
@@ -198,13 +201,14 @@
promptRepository.setPrompt(
promptInfo = promptInfo,
userId = effectiveUserId,
+ requestId = requestId,
gatekeeperChallenge = challenge,
kind = kind,
opPackageName = opPackageName,
)
}
- override fun resetPrompt() {
- promptRepository.unsetPrompt()
+ override fun resetPrompt(requestId: Long) {
+ promptRepository.unsetPrompt(requestId)
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/ui/binder/BiometricViewSizeBinder.kt b/packages/SystemUI/src/com/android/systemui/biometrics/ui/binder/BiometricViewSizeBinder.kt
index f0969ed..47174c0 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/ui/binder/BiometricViewSizeBinder.kt
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/ui/binder/BiometricViewSizeBinder.kt
@@ -199,29 +199,32 @@
iconParams.leftMargin = position.left
mediumConstraintSet.clear(
R.id.biometric_icon,
- ConstraintSet.END
+ ConstraintSet.RIGHT
)
mediumConstraintSet.connect(
R.id.biometric_icon,
- ConstraintSet.START,
+ ConstraintSet.LEFT,
ConstraintSet.PARENT_ID,
- ConstraintSet.START
+ ConstraintSet.LEFT
)
mediumConstraintSet.setMargin(
R.id.biometric_icon,
- ConstraintSet.START,
+ ConstraintSet.LEFT,
position.left
)
- smallConstraintSet.clear(R.id.biometric_icon, ConstraintSet.END)
+ smallConstraintSet.clear(
+ R.id.biometric_icon,
+ ConstraintSet.RIGHT
+ )
smallConstraintSet.connect(
R.id.biometric_icon,
- ConstraintSet.START,
+ ConstraintSet.LEFT,
ConstraintSet.PARENT_ID,
- ConstraintSet.START
+ ConstraintSet.LEFT
)
smallConstraintSet.setMargin(
R.id.biometric_icon,
- ConstraintSet.START,
+ ConstraintSet.LEFT,
position.left
)
}
@@ -252,32 +255,32 @@
iconParams.rightMargin = position.right
mediumConstraintSet.clear(
R.id.biometric_icon,
- ConstraintSet.START
+ ConstraintSet.LEFT
)
mediumConstraintSet.connect(
R.id.biometric_icon,
- ConstraintSet.END,
+ ConstraintSet.RIGHT,
ConstraintSet.PARENT_ID,
- ConstraintSet.END
+ ConstraintSet.RIGHT
)
mediumConstraintSet.setMargin(
R.id.biometric_icon,
- ConstraintSet.END,
+ ConstraintSet.RIGHT,
position.right
)
smallConstraintSet.clear(
R.id.biometric_icon,
- ConstraintSet.START
+ ConstraintSet.LEFT
)
smallConstraintSet.connect(
R.id.biometric_icon,
- ConstraintSet.END,
+ ConstraintSet.RIGHT,
ConstraintSet.PARENT_ID,
- ConstraintSet.END
+ ConstraintSet.RIGHT
)
smallConstraintSet.setMargin(
R.id.biometric_icon,
- ConstraintSet.END,
+ ConstraintSet.RIGHT,
position.right
)
}
@@ -318,6 +321,12 @@
lifecycleScope.launch {
viewModel.guidelineBounds.collect { bounds ->
+ val bottomInset =
+ windowManager.maximumWindowMetrics.windowInsets
+ .getInsets(WindowInsets.Type.navigationBars())
+ .bottom
+ mediumConstraintSet.setGuidelineEnd(R.id.bottomGuideline, bottomInset)
+
if (bounds.left >= 0) {
mediumConstraintSet.setGuidelineBegin(leftGuideline.id, bounds.left)
smallConstraintSet.setGuidelineBegin(leftGuideline.id, bounds.left)
@@ -383,15 +392,15 @@
// Move all content to other panel
flipConstraintSet.connect(
R.id.scrollView,
- ConstraintSet.START,
+ ConstraintSet.LEFT,
R.id.midGuideline,
- ConstraintSet.START
+ ConstraintSet.LEFT
)
flipConstraintSet.connect(
R.id.scrollView,
- ConstraintSet.END,
+ ConstraintSet.RIGHT,
R.id.rightGuideline,
- ConstraintSet.END
+ ConstraintSet.RIGHT
)
}
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/ui/binder/SideFpsOverlayViewBinder.kt b/packages/SystemUI/src/com/android/systemui/biometrics/ui/binder/SideFpsOverlayViewBinder.kt
index 4bdbfa2..ff7ac35 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/ui/binder/SideFpsOverlayViewBinder.kt
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/ui/binder/SideFpsOverlayViewBinder.kt
@@ -20,6 +20,7 @@
import android.content.Context
import android.graphics.PorterDuff
import android.graphics.PorterDuffColorFilter
+import android.util.Log
import android.view.LayoutInflater
import android.view.View
import android.view.WindowManager
@@ -91,6 +92,13 @@
showIndicatorForDeviceEntry,
progressBarIsVisible) =
combinedFlows
+ Log.d(
+ TAG,
+ "systemServerAuthReason = $systemServerAuthReason, " +
+ "showIndicatorForDeviceEntry = " +
+ "$showIndicatorForDeviceEntry, " +
+ "progressBarIsVisible = $progressBarIsVisible"
+ )
if (!isInRearDisplayMode) {
if (progressBarIsVisible) {
hide()
@@ -114,6 +122,10 @@
/** Show the side fingerprint sensor indicator */
private fun show() {
if (overlayView?.isAttachedToWindow == true) {
+ Log.d(
+ TAG,
+ "show(): overlayView $overlayView isAttachedToWindow already, ignoring show request"
+ )
return
}
@@ -128,6 +140,7 @@
)
bind(overlayView!!, overlayViewModel, fpsUnlockTracker.get(), windowManager.get())
overlayView!!.visibility = View.INVISIBLE
+ Log.d(TAG, "show(): adding overlayView $overlayView")
windowManager.get().addView(overlayView, overlayViewModel.defaultOverlayViewParams)
}
@@ -137,6 +150,7 @@
val lottie = overlayView!!.requireViewById<LottieAnimationView>(R.id.sidefps_animation)
lottie.pauseAnimation()
lottie.removeAllLottieOnCompositionLoadedListener()
+ Log.d(TAG, "hide(): removing overlayView $overlayView, setting to null")
windowManager.get().removeView(overlayView)
overlayView = null
}
diff --git a/packages/SystemUI/src/com/android/systemui/bluetooth/qsdialog/BluetoothTileDialogDelegate.kt b/packages/SystemUI/src/com/android/systemui/bluetooth/qsdialog/BluetoothTileDialogDelegate.kt
index f0230be..911145b 100644
--- a/packages/SystemUI/src/com/android/systemui/bluetooth/qsdialog/BluetoothTileDialogDelegate.kt
+++ b/packages/SystemUI/src/com/android/systemui/bluetooth/qsdialog/BluetoothTileDialogDelegate.kt
@@ -419,8 +419,7 @@
const val ACTION_PREVIOUSLY_CONNECTED_DEVICE =
"com.android.settings.PREVIOUSLY_CONNECTED_DEVICE"
const val ACTION_PAIR_NEW_DEVICE = "android.settings.BLUETOOTH_PAIRING_SETTINGS"
- const val ACTION_AUDIO_SHARING =
- "com.google.android.settings.BLUETOOTH_AUDIO_SHARING_SETTINGS"
+ const val ACTION_AUDIO_SHARING = "com.android.settings.BLUETOOTH_AUDIO_SHARING_SETTINGS"
const val DISABLED_ALPHA = 0.3f
const val ENABLED_ALPHA = 1f
const val PROGRESS_BAR_ANIMATION_DURATION_MS = 1500L
diff --git a/packages/SystemUI/src/com/android/systemui/brightness/data/repository/BrightnessPolicyRepository.kt b/packages/SystemUI/src/com/android/systemui/brightness/data/repository/BrightnessPolicyRepository.kt
index c018ecb..0544a4f 100644
--- a/packages/SystemUI/src/com/android/systemui/brightness/data/repository/BrightnessPolicyRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/brightness/data/repository/BrightnessPolicyRepository.kt
@@ -18,6 +18,8 @@
import android.content.Context
import android.os.UserManager
+import com.android.settingslib.RestrictedLockUtils
+import com.android.systemui.Flags.enforceBrightnessBaseUserRestriction
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Application
import com.android.systemui.dagger.qualifiers.Background
@@ -66,7 +68,18 @@
user.id
)
?.let { PolicyRestriction.Restricted(it) }
- ?: PolicyRestriction.NoRestriction
+ ?: if (
+ enforceBrightnessBaseUserRestriction() &&
+ userRestrictionChecker.hasBaseUserRestriction(
+ applicationContext,
+ UserManager.DISALLOW_CONFIG_BRIGHTNESS,
+ user.id
+ )
+ ) {
+ PolicyRestriction.Restricted(RestrictedLockUtils.EnforcedAdmin())
+ } else {
+ PolicyRestriction.NoRestriction
+ }
}
.flowOn(backgroundDispatcher)
}
diff --git a/packages/SystemUI/src/com/android/systemui/brightness/ui/compose/BrightnessSlider.kt b/packages/SystemUI/src/com/android/systemui/brightness/ui/compose/BrightnessSlider.kt
index c1be37a..a51d8ff 100644
--- a/packages/SystemUI/src/com/android/systemui/brightness/ui/compose/BrightnessSlider.kt
+++ b/packages/SystemUI/src/com/android/systemui/brightness/ui/compose/BrightnessSlider.kt
@@ -23,7 +23,6 @@
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
-import androidx.compose.runtime.collectAsState
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableIntStateOf
import androidx.compose.runtime.remember
@@ -32,6 +31,7 @@
import androidx.compose.ui.Modifier
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.unit.dp
+import androidx.lifecycle.compose.collectAsStateWithLifecycle
import com.android.compose.PlatformSlider
import com.android.systemui.brightness.shared.GammaBrightness
import com.android.systemui.brightness.ui.viewmodel.BrightnessSliderViewModel
@@ -107,10 +107,13 @@
viewModel: BrightnessSliderViewModel,
modifier: Modifier = Modifier,
) {
- val gamma: Int by viewModel.currentBrightness.map { it.value }.collectAsState(initial = 0)
+ val gamma: Int by
+ viewModel.currentBrightness.map { it.value }.collectAsStateWithLifecycle(initialValue = 0)
val coroutineScope = rememberCoroutineScope()
val restriction by
- viewModel.policyRestriction.collectAsState(initial = PolicyRestriction.NoRestriction)
+ viewModel.policyRestriction.collectAsStateWithLifecycle(
+ initialValue = PolicyRestriction.NoRestriction
+ )
BrightnessSlider(
gammaValue = gamma,
diff --git a/packages/SystemUI/src/com/android/systemui/classifier/FalsingCollectorImpl.java b/packages/SystemUI/src/com/android/systemui/classifier/FalsingCollectorImpl.java
index 5653bc2..2eca02c 100644
--- a/packages/SystemUI/src/com/android/systemui/classifier/FalsingCollectorImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/classifier/FalsingCollectorImpl.java
@@ -31,9 +31,12 @@
import com.android.systemui.communal.domain.interactor.CommunalInteractor;
import com.android.systemui.dagger.SysUISingleton;
import com.android.systemui.dagger.qualifiers.Main;
+import com.android.systemui.deviceentry.domain.interactor.DeviceEntryInteractor;
import com.android.systemui.dock.DockManager;
import com.android.systemui.plugins.FalsingManager;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
+import com.android.systemui.scene.domain.interactor.SceneContainerOcclusionInteractor;
+import com.android.systemui.scene.shared.flag.SceneContainerFlag;
import com.android.systemui.shade.domain.interactor.ShadeInteractor;
import com.android.systemui.statusbar.StatusBarState;
import com.android.systemui.statusbar.policy.BatteryController;
@@ -88,6 +91,8 @@
private final JavaAdapter mJavaAdapter;
private final SystemClock mSystemClock;
private final Lazy<SelectedUserInteractor> mUserInteractor;
+ private final Lazy<DeviceEntryInteractor> mDeviceEntryInteractor;
+ private final Lazy<SceneContainerOcclusionInteractor> mSceneContainerOcclusionInteractor;
private int mState;
private boolean mShowingAod;
@@ -170,7 +175,9 @@
JavaAdapter javaAdapter,
SystemClock systemClock,
Lazy<SelectedUserInteractor> userInteractor,
- Lazy<CommunalInteractor> communalInteractorLazy) {
+ Lazy<CommunalInteractor> communalInteractorLazy,
+ Lazy<DeviceEntryInteractor> deviceEntryInteractor,
+ Lazy<SceneContainerOcclusionInteractor> sceneContainerOcclusionInteractor) {
mFalsingDataProvider = falsingDataProvider;
mFalsingManager = falsingManager;
mKeyguardUpdateMonitor = keyguardUpdateMonitor;
@@ -186,6 +193,8 @@
mSystemClock = systemClock;
mUserInteractor = userInteractor;
mCommunalInteractorLazy = communalInteractorLazy;
+ mDeviceEntryInteractor = deviceEntryInteractor;
+ mSceneContainerOcclusionInteractor = sceneContainerOcclusionInteractor;
}
@Override
@@ -196,7 +205,18 @@
mStatusBarStateController.addCallback(mStatusBarStateListener);
mState = mStatusBarStateController.getState();
- mKeyguardStateController.addCallback(mKeyguardStateControllerCallback);
+ if (SceneContainerFlag.isEnabled()) {
+ mJavaAdapter.alwaysCollectFlow(
+ mDeviceEntryInteractor.get().isDeviceEntered(),
+ this::isDeviceEnteredChanged
+ );
+ mJavaAdapter.alwaysCollectFlow(
+ mSceneContainerOcclusionInteractor.get().getInvisibleDueToOcclusion(),
+ this::isInvisibleDueToOcclusionChanged
+ );
+ } else {
+ mKeyguardStateController.addCallback(mKeyguardStateControllerCallback);
+ }
mKeyguardUpdateMonitor.registerCallback(mKeyguardUpdateCallback);
@@ -216,6 +236,14 @@
mDockManager.addListener(mDockEventListener);
}
+ public void isDeviceEnteredChanged(boolean unused) {
+ updateSensorRegistration();
+ }
+
+ public void isInvisibleDueToOcclusionChanged(boolean unused) {
+ updateSensorRegistration();
+ }
+
@Override
public void onSuccessfulUnlock() {
logDebug("REAL: onSuccessfulUnlock");
@@ -302,7 +330,7 @@
@Override
public void onTouchEvent(MotionEvent ev) {
logDebug("REAL: onTouchEvent(" + MotionEvent.actionToString(ev.getActionMasked()) + ")");
- if (!mKeyguardStateController.isShowing()) {
+ if (!isKeyguardShowing()) {
avoidGesture();
return;
}
@@ -402,8 +430,8 @@
final boolean isKeyguard = mState == StatusBarState.KEYGUARD;
final boolean isShadeOverOccludedKeyguard = mState == StatusBarState.SHADE
- && mKeyguardStateController.isShowing()
- && mKeyguardStateController.isOccluded();
+ && isKeyguardShowing()
+ && isKeyguardOccluded();
return mScreenOn && !mShowingAod && (isKeyguard || isShadeOverOccludedKeyguard);
}
@@ -447,6 +475,32 @@
mFalsingManager.onProximityEvent(new ProximityEventImpl(proximityEvent));
}
+ /**
+ * Returns {@code true} if the keyguard is showing (whether or not the screen is on, whether or
+ * not an activity is occluding the keyguard, and whether or not the shade is open on top of the
+ * keyguard), or {@code false} if the user has dismissed the keyguard by authenticating or
+ * swiping up.
+ */
+ private boolean isKeyguardShowing() {
+ if (SceneContainerFlag.isEnabled()) {
+ return !mDeviceEntryInteractor.get().isDeviceEntered().getValue();
+ } else {
+ return mKeyguardStateController.isShowing();
+ }
+ }
+
+ /**
+ * Returns {@code true} if there is an activity display on top of ("occluding") the keyguard, or
+ * {@code false} if an activity is not occluding the keyguard (including if the keyguard is not
+ * showing at all).
+ */
+ private boolean isKeyguardOccluded() {
+ if (SceneContainerFlag.isEnabled()) {
+ return mSceneContainerOcclusionInteractor.get().getInvisibleDueToOcclusion().getValue();
+ } else {
+ return mKeyguardStateController.isOccluded();
+ }
+ }
static void logDebug(String msg) {
if (DEBUG) {
diff --git a/packages/SystemUI/src/com/android/systemui/clipboardoverlay/ClipboardOverlayView.java b/packages/SystemUI/src/com/android/systemui/clipboardoverlay/ClipboardOverlayView.java
index 8efc66d..ba236ba 100644
--- a/packages/SystemUI/src/com/android/systemui/clipboardoverlay/ClipboardOverlayView.java
+++ b/packages/SystemUI/src/com/android/systemui/clipboardoverlay/ClipboardOverlayView.java
@@ -66,11 +66,11 @@
import com.android.systemui.screenshot.ui.viewmodel.ActionButtonAppearance;
import com.android.systemui.screenshot.ui.viewmodel.ActionButtonViewModel;
-import java.util.ArrayList;
-
import kotlin.Unit;
import kotlin.jvm.functions.Function0;
+import java.util.ArrayList;
+
/**
* Handles the visual elements and animations for the clipboard overlay.
*/
@@ -109,6 +109,7 @@
private View mDismissButton;
private LinearLayout mActionContainer;
private ClipboardOverlayCallbacks mClipboardCallbacks;
+ private ActionButtonViewBinder mActionButtonViewBinder = new ActionButtonViewBinder();
public ClipboardOverlayView(Context context) {
this(context, null);
@@ -152,14 +153,15 @@
private void bindDefaultActionChips() {
if (screenshotShelfUi2()) {
- ActionButtonViewBinder.INSTANCE.bind(mRemoteCopyChip,
+ mActionButtonViewBinder.bind(mRemoteCopyChip,
ActionButtonViewModel.Companion.withNextId(
new ActionButtonAppearance(
Icon.createWithResource(mContext,
R.drawable.ic_baseline_devices_24).loadDrawable(
mContext),
null,
- mContext.getString(R.string.clipboard_send_nearby_description)),
+ mContext.getString(R.string.clipboard_send_nearby_description),
+ true),
new Function0<>() {
@Override
public Unit invoke() {
@@ -169,12 +171,14 @@
return null;
}
}));
- ActionButtonViewBinder.INSTANCE.bind(mShareChip,
+ mActionButtonViewBinder.bind(mShareChip,
ActionButtonViewModel.Companion.withNextId(
new ActionButtonAppearance(
Icon.createWithResource(mContext,
R.drawable.ic_screenshot_share).loadDrawable(mContext),
- null, mContext.getString(com.android.internal.R.string.share)),
+ null,
+ mContext.getString(com.android.internal.R.string.share),
+ true),
new Function0<>() {
@Override
public Unit invoke() {
@@ -512,9 +516,9 @@
private View constructShelfActionChip(RemoteAction action, Runnable onFinish) {
View chip = LayoutInflater.from(mContext).inflate(
R.layout.shelf_action_chip, mActionContainer, false);
- ActionButtonViewBinder.INSTANCE.bind(chip, ActionButtonViewModel.Companion.withNextId(
+ mActionButtonViewBinder.bind(chip, ActionButtonViewModel.Companion.withNextId(
new ActionButtonAppearance(action.getIcon().loadDrawable(mContext),
- action.getTitle(), action.getTitle()), new Function0<>() {
+ action.getTitle(), action.getTitle(), false), new Function0<>() {
@Override
public Unit invoke() {
try {
diff --git a/packages/SystemUI/src/com/android/systemui/communal/CommunalSceneStartable.kt b/packages/SystemUI/src/com/android/systemui/communal/CommunalSceneStartable.kt
index f437032..6f20a8d 100644
--- a/packages/SystemUI/src/com/android/systemui/communal/CommunalSceneStartable.kt
+++ b/packages/SystemUI/src/com/android/systemui/communal/CommunalSceneStartable.kt
@@ -17,7 +17,6 @@
package com.android.systemui.communal
import android.provider.Settings
-import android.service.dreams.Flags.dreamTracksFocus
import com.android.compose.animation.scene.SceneKey
import com.android.systemui.CoreStartable
import com.android.systemui.communal.domain.interactor.CommunalInteractor
@@ -43,6 +42,7 @@
import kotlinx.coroutines.CoroutineDispatcher
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.Job
import kotlinx.coroutines.delay
import kotlinx.coroutines.flow.collectLatest
import kotlinx.coroutines.flow.combine
@@ -74,6 +74,10 @@
) : CoreStartable {
private var screenTimeout: Int = DEFAULT_SCREEN_TIMEOUT
+ private var timeoutJob: Job? = null
+
+ private var isDreaming: Boolean = false
+
override fun start() {
// Handle automatically switching based on keyguard state.
keyguardTransitionInteractor.startedKeyguardTransitionStep
@@ -113,47 +117,67 @@
}
.launchIn(bgScope)
- // Handle timing out back to the dream.
+ // The hub mode timeout should start as soon as the user enters hub mode. At the end of the
+ // timer, if the device is dreaming, hub mode should closed and reveal the dream. If the
+ // dream is not running, nothing will happen. However if the dream starts again underneath
+ // hub mode after the initial timeout expires, such as if the device is docked or the dream
+ // app is updated by the Play store, a new timeout should be started.
bgScope.launch {
combine(
communalInteractor.desiredScene,
// Emit a value on start so the combine starts.
communalInteractor.userActivity.emitOnStart()
) { scene, _ ->
- // Time out should run whenever we're dreaming and the hub is open, even if not
- // docked.
+ // Only timeout if we're on the hub is open.
scene == CommunalScenes.Communal
}
- // mapLatest cancels the previous action block when new values arrive, so any
- // already running timeout gets cancelled when conditions change or user interaction
- // is detected.
- .mapLatest { shouldTimeout ->
- if (!shouldTimeout) {
- return@mapLatest false
+ .collectLatest { shouldTimeout ->
+ cancelHubTimeout()
+ if (shouldTimeout) {
+ startHubTimeout()
}
-
- delay(screenTimeout.milliseconds)
- true
}
- .sample(keyguardInteractor.isDreaming, ::Pair)
- .collect { (shouldTimeout, isDreaming) ->
- if (isDreaming && shouldTimeout) {
+ }
+ bgScope.launch {
+ keyguardInteractor.isDreaming
+ .sample(communalInteractor.desiredScene, ::Pair)
+ .collectLatest { (isDreaming, scene) ->
+ this@CommunalSceneStartable.isDreaming = isDreaming
+ if (scene == CommunalScenes.Communal && isDreaming && timeoutJob == null) {
+ // If dreaming starts after timeout has expired, ex. if dream restarts under
+ // the hub, just close the hub immediately.
communalInteractor.changeScene(CommunalScenes.Blank)
}
}
}
- if (dreamTracksFocus()) {
- bgScope.launch {
- communalInteractor.isIdleOnCommunal.collectLatest {
- withContext(mainDispatcher) {
- notificationShadeWindowController.setGlanceableHubShowing(it)
- }
+ bgScope.launch {
+ communalInteractor.isIdleOnCommunal.collectLatest {
+ withContext(mainDispatcher) {
+ notificationShadeWindowController.setGlanceableHubShowing(it)
}
}
}
}
+ private fun cancelHubTimeout() {
+ timeoutJob?.cancel()
+ timeoutJob = null
+ }
+
+ private fun startHubTimeout() {
+ if (timeoutJob == null) {
+ timeoutJob =
+ bgScope.launch {
+ delay(screenTimeout.milliseconds)
+ if (isDreaming) {
+ communalInteractor.changeScene(CommunalScenes.Blank)
+ }
+ timeoutJob = null
+ }
+ }
+ }
+
private suspend fun determineSceneAfterTransition(
lastStartedTransition: TransitionStep,
): SceneKey? {
diff --git a/packages/SystemUI/src/com/android/systemui/communal/data/model/CommunalWidgetCategories.kt b/packages/SystemUI/src/com/android/systemui/communal/data/model/CommunalWidgetCategories.kt
index 03f54c8..5cd15f2 100644
--- a/packages/SystemUI/src/com/android/systemui/communal/data/model/CommunalWidgetCategories.kt
+++ b/packages/SystemUI/src/com/android/systemui/communal/data/model/CommunalWidgetCategories.kt
@@ -17,15 +17,23 @@
package com.android.systemui.communal.data.model
import android.appwidget.AppWidgetProviderInfo
+import com.android.settingslib.flags.Flags.allowAllWidgetsOnLockscreenByDefault
/**
* The widget categories to display on communal hub (where categories is a bitfield with values that
* match those in {@link AppWidgetProviderInfo}).
*/
@JvmInline
-value class CommunalWidgetCategories(
- // The default is keyguard widgets.
- val categories: Int = AppWidgetProviderInfo.WIDGET_CATEGORY_KEYGUARD
-) {
+value class CommunalWidgetCategories(val categories: Int = defaultCategories) {
fun contains(category: Int) = (categories and category) == category
+
+ companion object {
+ val defaultCategories: Int
+ get() {
+ return AppWidgetProviderInfo.WIDGET_CATEGORY_KEYGUARD or
+ if (allowAllWidgetsOnLockscreenByDefault())
+ AppWidgetProviderInfo.WIDGET_CATEGORY_HOME_SCREEN
+ else 0
+ }
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/communal/data/repository/CommunalMediaRepository.kt b/packages/SystemUI/src/com/android/systemui/communal/data/repository/CommunalMediaRepository.kt
index e2fed6d..e5a0e50 100644
--- a/packages/SystemUI/src/com/android/systemui/communal/data/repository/CommunalMediaRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/communal/data/repository/CommunalMediaRepository.kt
@@ -53,7 +53,7 @@
updateMediaModel(data)
}
- override fun onMediaDataRemoved(key: String) {
+ override fun onMediaDataRemoved(key: String, userInitiated: Boolean) {
updateMediaModel()
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/communal/data/repository/CommunalSettingsRepository.kt b/packages/SystemUI/src/com/android/systemui/communal/data/repository/CommunalSettingsRepository.kt
index 9debe0e..88cb64c 100644
--- a/packages/SystemUI/src/com/android/systemui/communal/data/repository/CommunalSettingsRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/communal/data/repository/CommunalSettingsRepository.kt
@@ -18,7 +18,6 @@
import android.app.admin.DevicePolicyManager
import android.app.admin.DevicePolicyManager.KEYGUARD_DISABLE_WIDGETS_ALL
-import android.appwidget.AppWidgetProviderInfo
import android.content.IntentFilter
import android.content.pm.UserInfo
import android.provider.Settings
@@ -108,10 +107,9 @@
.onStart { emit(Unit) }
.map {
CommunalWidgetCategories(
- // The default is to show only keyguard widgets.
secureSettings.getIntForUser(
GLANCEABLE_HUB_CONTENT_SETTING,
- AppWidgetProviderInfo.WIDGET_CATEGORY_KEYGUARD,
+ CommunalWidgetCategories.defaultCategories,
user.id
)
)
diff --git a/packages/SystemUI/src/com/android/systemui/communal/domain/interactor/CommunalInteractor.kt b/packages/SystemUI/src/com/android/systemui/communal/domain/interactor/CommunalInteractor.kt
index 06c8396..9599a88 100644
--- a/packages/SystemUI/src/com/android/systemui/communal/domain/interactor/CommunalInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/communal/domain/interactor/CommunalInteractor.kt
@@ -61,7 +61,6 @@
import com.android.systemui.settings.UserTracker
import com.android.systemui.smartspace.data.repository.SmartspaceRepository
import com.android.systemui.util.kotlin.BooleanFlowOperators.allOf
-import com.android.systemui.util.kotlin.BooleanFlowOperators.anyOf
import com.android.systemui.util.kotlin.BooleanFlowOperators.not
import com.android.systemui.util.kotlin.emitOnStart
import javax.inject.Inject
@@ -130,7 +129,7 @@
allOf(
communalSettingsInteractor.isCommunalEnabled,
not(keyguardInteractor.isEncryptedOrLockdown),
- anyOf(keyguardInteractor.isKeyguardShowing, keyguardInteractor.isDreaming)
+ keyguardInteractor.isKeyguardShowing
)
.distinctUntilChanged()
.onEach { available ->
diff --git a/packages/SystemUI/src/com/android/systemui/communal/domain/interactor/CommunalSettingsInteractor.kt b/packages/SystemUI/src/com/android/systemui/communal/domain/interactor/CommunalSettingsInteractor.kt
index f9de609..3e5126a 100644
--- a/packages/SystemUI/src/com/android/systemui/communal/domain/interactor/CommunalSettingsInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/communal/domain/interactor/CommunalSettingsInteractor.kt
@@ -75,7 +75,7 @@
scope = bgScope,
// Start this eagerly since the value can be accessed synchronously.
started = SharingStarted.Eagerly,
- initialValue = CommunalWidgetCategories().categories
+ initialValue = CommunalWidgetCategories.defaultCategories
)
private val workProfileUserInfoCallbackFlow: Flow<UserInfo?> = conflatedCallbackFlow {
diff --git a/packages/SystemUI/src/com/android/systemui/communal/ui/viewmodel/CommunalEditModeViewModel.kt b/packages/SystemUI/src/com/android/systemui/communal/ui/viewmodel/CommunalEditModeViewModel.kt
index f6122ad..650852c 100644
--- a/packages/SystemUI/src/com/android/systemui/communal/ui/viewmodel/CommunalEditModeViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/communal/ui/viewmodel/CommunalEditModeViewModel.kt
@@ -35,7 +35,6 @@
import com.android.systemui.log.dagger.CommunalLog
import com.android.systemui.media.controls.ui.view.MediaHost
import com.android.systemui.media.dagger.MediaModule
-import com.android.systemui.res.R
import javax.inject.Inject
import javax.inject.Named
import kotlinx.coroutines.CoroutineDispatcher
@@ -96,6 +95,8 @@
uiEventLogger.log(CommunalUiEvent.COMMUNAL_HUB_REORDER_WIDGET_CANCEL)
}
+ val isIdleOnCommunal: StateFlow<Boolean> = communalInteractor.isIdleOnCommunal
+
/** Launch the widget picker activity using the given {@link ActivityResultLauncher}. */
suspend fun onOpenWidgetPicker(
resources: Resources,
@@ -136,14 +137,6 @@
return Intent(Intent.ACTION_PICK).apply {
setPackage(packageName)
putExtra(
- EXTRA_DESIRED_WIDGET_WIDTH,
- resources.getDimensionPixelSize(R.dimen.communal_widget_picker_desired_width)
- )
- putExtra(
- EXTRA_DESIRED_WIDGET_HEIGHT,
- resources.getDimensionPixelSize(R.dimen.communal_widget_picker_desired_height)
- )
- putExtra(
AppWidgetManager.EXTRA_CATEGORY_FILTER,
communalSettingsInteractor.communalWidgetCategories.value
)
@@ -168,8 +161,6 @@
companion object {
private const val TAG = "CommunalEditModeViewModel"
- private const val EXTRA_DESIRED_WIDGET_WIDTH = "desired_widget_width"
- private const val EXTRA_DESIRED_WIDGET_HEIGHT = "desired_widget_height"
private const val EXTRA_UI_SURFACE_KEY = "ui_surface"
private const val EXTRA_UI_SURFACE_VALUE = "widgets_hub"
const val EXTRA_ADDED_APP_WIDGETS_KEY = "added_app_widgets"
diff --git a/packages/SystemUI/src/com/android/systemui/communal/ui/viewmodel/CommunalViewModel.kt b/packages/SystemUI/src/com/android/systemui/communal/ui/viewmodel/CommunalViewModel.kt
index 656e5cb..97db43b 100644
--- a/packages/SystemUI/src/com/android/systemui/communal/ui/viewmodel/CommunalViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/communal/ui/viewmodel/CommunalViewModel.kt
@@ -25,6 +25,7 @@
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Application
import com.android.systemui.dagger.qualifiers.Main
+import com.android.systemui.keyguard.domain.interactor.KeyguardInteractor
import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor
import com.android.systemui.keyguard.shared.model.KeyguardState
import com.android.systemui.log.LogBuffer
@@ -63,6 +64,7 @@
@Application private val scope: CoroutineScope,
@Main private val resources: Resources,
keyguardTransitionInteractor: KeyguardTransitionInteractor,
+ keyguardInteractor: KeyguardInteractor,
private val communalInteractor: CommunalInteractor,
tutorialInteractor: CommunalTutorialInteractor,
private val shadeInteractor: ShadeInteractor,
@@ -236,6 +238,14 @@
*/
val touchesAllowed: Flow<Boolean> = not(shadeInteractor.isAnyFullyExpanded)
+ // TODO(b/339667383): remove this temporary swipe gesture handle
+ /**
+ * The dream overlay has its own gesture handle as the SysUI window is not visible above the
+ * dream. This flow will be false when dreaming so that we don't show a duplicate handle when
+ * opening the hub over the dream.
+ */
+ val showGestureIndicator: Flow<Boolean> = not(keyguardInteractor.isDreaming)
+
companion object {
const val POPUP_AUTO_HIDE_TIMEOUT_MS = 12000L
}
diff --git a/packages/SystemUI/src/com/android/systemui/communal/widgets/CommunalAppWidgetHostView.kt b/packages/SystemUI/src/com/android/systemui/communal/widgets/CommunalAppWidgetHostView.kt
index 840c3a8..2559137 100644
--- a/packages/SystemUI/src/com/android/systemui/communal/widgets/CommunalAppWidgetHostView.kt
+++ b/packages/SystemUI/src/com/android/systemui/communal/widgets/CommunalAppWidgetHostView.kt
@@ -17,6 +17,7 @@
package com.android.systemui.communal.widgets
import android.appwidget.AppWidgetHostView
+import android.appwidget.AppWidgetProviderInfo
import android.content.Context
import android.graphics.Outline
import android.graphics.Rect
@@ -50,6 +51,11 @@
enforceRoundedCorners()
}
+ override fun setAppWidget(appWidgetId: Int, info: AppWidgetProviderInfo?) {
+ super.setAppWidget(appWidgetId, info)
+ setPadding(0, 0, 0, 0)
+ }
+
private val cornerRadiusEnforcementOutline: ViewOutlineProvider =
object : ViewOutlineProvider() {
override fun getOutline(view: View?, outline: Outline) {
diff --git a/packages/SystemUI/src/com/android/systemui/communal/widgets/EditWidgetsActivity.kt b/packages/SystemUI/src/com/android/systemui/communal/widgets/EditWidgetsActivity.kt
index f20fafc..426f484 100644
--- a/packages/SystemUI/src/com/android/systemui/communal/widgets/EditWidgetsActivity.kt
+++ b/packages/SystemUI/src/com/android/systemui/communal/widgets/EditWidgetsActivity.kt
@@ -44,6 +44,7 @@
import com.android.systemui.log.core.Logger
import com.android.systemui.log.dagger.CommunalLog
import javax.inject.Inject
+import kotlinx.coroutines.flow.first
import kotlinx.coroutines.launch
/** An Activity for editing the widgets that appear in hub mode. */
@@ -69,6 +70,8 @@
private var shouldOpenWidgetPickerOnStart = false
+ private var lockOnDestroy = false
+
private val addWidgetActivityLauncher: ActivityResultLauncher<Intent> =
registerForActivityResult(StartActivityForResult()) { result ->
when (result.resultCode) {
@@ -149,15 +152,18 @@
}
private fun onEditDone() {
- try {
+ lifecycleScope.launch {
communalViewModel.changeScene(
CommunalScenes.Communal,
CommunalTransitionKeys.SimpleFade
)
- checkNotNull(windowManagerService).lockNow(/* options */ null)
+
+ // Wait for the current scene to be idle on communal.
+ communalViewModel.isIdleOnCommunal.first { it }
+ // Then finish the activity (this helps to avoid a flash of lockscreen when locking
+ // in onDestroy()).
+ lockOnDestroy = true
finish()
- } catch (e: RemoteException) {
- Log.e(TAG, "Couldn't lock the device as WindowManager is dead.")
}
}
@@ -190,5 +196,15 @@
override fun onDestroy() {
super.onDestroy()
communalViewModel.setEditModeOpen(false)
+
+ if (lockOnDestroy) lockNow()
+ }
+
+ private fun lockNow() {
+ try {
+ checkNotNull(windowManagerService).lockNow(/* options */ null)
+ } catch (e: RemoteException) {
+ Log.e(TAG, "Couldn't lock the device as WindowManager is dead.")
+ }
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/contrast/ContrastDialogActivity.kt b/packages/SystemUI/src/com/android/systemui/contrast/ContrastDialogActivity.kt
deleted file mode 100644
index 4e40042..0000000
--- a/packages/SystemUI/src/com/android/systemui/contrast/ContrastDialogActivity.kt
+++ /dev/null
@@ -1,34 +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.contrast
-
-import android.app.Activity
-import android.os.Bundle
-import javax.inject.Inject
-
-/** Trampoline activity responsible for creating a [ContrastDialogDelegate] */
-class ContrastDialogActivity
-@Inject
-constructor(
- private val contrastDialogDelegate : ContrastDialogDelegate
-) : Activity() {
-
- override fun onCreate(savedInstanceState: Bundle?) {
- super.onCreate(savedInstanceState)
- contrastDialogDelegate.createDialog().show()
- finish()
- }
-}
diff --git a/packages/SystemUI/src/com/android/systemui/contrast/ContrastDialogDelegate.kt b/packages/SystemUI/src/com/android/systemui/contrast/ContrastDialogDelegate.kt
deleted file mode 100644
index 0daa058..0000000
--- a/packages/SystemUI/src/com/android/systemui/contrast/ContrastDialogDelegate.kt
+++ /dev/null
@@ -1,111 +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.contrast
-
-import android.app.UiModeManager
-import android.app.UiModeManager.ContrastUtils.CONTRAST_LEVEL_HIGH
-import android.app.UiModeManager.ContrastUtils.CONTRAST_LEVEL_MEDIUM
-import android.app.UiModeManager.ContrastUtils.CONTRAST_LEVEL_STANDARD
-import android.app.UiModeManager.ContrastUtils.fromContrastLevel
-import android.app.UiModeManager.ContrastUtils.toContrastLevel
-import android.os.Bundle
-import android.provider.Settings
-import android.view.View
-import android.widget.FrameLayout
-import com.android.internal.annotations.VisibleForTesting
-import com.android.systemui.dagger.qualifiers.Main
-import com.android.systemui.res.R
-import com.android.systemui.settings.UserTracker
-import com.android.systemui.statusbar.phone.SystemUIDialog
-import com.android.systemui.util.settings.SecureSettings
-import java.util.concurrent.Executor
-import javax.inject.Inject
-
-/** Dialog to select contrast options */
-class ContrastDialogDelegate
-@Inject
-constructor(
- private val sysuiDialogFactory: SystemUIDialog.Factory,
- @Main private val mainExecutor: Executor,
- private val uiModeManager: UiModeManager,
- private val userTracker: UserTracker,
- private val secureSettings: SecureSettings,
-) : SystemUIDialog.Delegate, UiModeManager.ContrastChangeListener {
-
- @VisibleForTesting lateinit var contrastButtons: Map<Int, FrameLayout>
- lateinit var dialogView: View
- @VisibleForTesting var initialContrast: Float = fromContrastLevel(CONTRAST_LEVEL_STANDARD)
-
- override fun createDialog(): SystemUIDialog {
- val dialog = sysuiDialogFactory.create(this)
- dialogView = dialog.layoutInflater.inflate(R.layout.contrast_dialog, null)
- with(dialog) {
- setView(dialogView)
-
- setTitle(R.string.quick_settings_contrast_label)
- setNeutralButton(R.string.cancel) { _, _ ->
- secureSettings.putFloatForUser(
- Settings.Secure.CONTRAST_LEVEL,
- initialContrast,
- userTracker.userId
- )
- dialog.dismiss()
- }
- setPositiveButton(com.android.settingslib.R.string.done) { _, _ -> dialog.dismiss() }
- }
-
- return dialog
- }
-
- override fun onCreate(dialog: SystemUIDialog, savedInstanceState: Bundle?) {
- contrastButtons =
- mapOf(
- CONTRAST_LEVEL_STANDARD to dialog.requireViewById(R.id.contrast_button_standard),
- CONTRAST_LEVEL_MEDIUM to dialog.requireViewById(R.id.contrast_button_medium),
- CONTRAST_LEVEL_HIGH to dialog.requireViewById(R.id.contrast_button_high)
- )
-
- contrastButtons.forEach { (contrastLevel, contrastButton) ->
- contrastButton.setOnClickListener {
- val contrastValue = fromContrastLevel(contrastLevel)
- secureSettings.putFloatForUser(
- Settings.Secure.CONTRAST_LEVEL,
- contrastValue,
- userTracker.userId
- )
- }
- }
-
- initialContrast = uiModeManager.contrast
- highlightContrast(toContrastLevel(initialContrast))
- }
-
- override fun onStart(dialog: SystemUIDialog) {
- uiModeManager.addContrastChangeListener(mainExecutor, this)
- }
-
- override fun onStop(dialog: SystemUIDialog) {
- uiModeManager.removeContrastChangeListener(this)
- }
-
- override fun onContrastChanged(contrast: Float) {
- highlightContrast(toContrastLevel(contrast))
- }
-
- private fun highlightContrast(contrast: Int) {
- contrastButtons.forEach { (level, button) -> button.isSelected = level == contrast }
- }
-}
diff --git a/packages/SystemUI/src/com/android/systemui/dagger/DefaultActivityBinder.java b/packages/SystemUI/src/com/android/systemui/dagger/DefaultActivityBinder.java
index d2df276..c2e1e33 100644
--- a/packages/SystemUI/src/com/android/systemui/dagger/DefaultActivityBinder.java
+++ b/packages/SystemUI/src/com/android/systemui/dagger/DefaultActivityBinder.java
@@ -20,7 +20,6 @@
import com.android.systemui.ForegroundServicesDialog;
import com.android.systemui.communal.widgets.EditWidgetsActivity;
-import com.android.systemui.contrast.ContrastDialogActivity;
import com.android.systemui.keyguard.WorkLockActivity;
import com.android.systemui.people.PeopleSpaceActivity;
import com.android.systemui.people.widget.LaunchConversationActivity;
@@ -72,12 +71,6 @@
@ClassKey(BrightnessDialog.class)
public abstract Activity bindBrightnessDialog(BrightnessDialog activity);
- /** Inject into ContrastDialogActivity. */
- @Binds
- @IntoMap
- @ClassKey(ContrastDialogActivity.class)
- public abstract Activity bindContrastDialogActivity(ContrastDialogActivity activity);
-
/** Inject into UsbDebuggingActivity. */
@Binds
@IntoMap
diff --git a/packages/SystemUI/src/com/android/systemui/deviceentry/data/repository/DeviceEntryFaceAuthRepository.kt b/packages/SystemUI/src/com/android/systemui/deviceentry/data/repository/DeviceEntryFaceAuthRepository.kt
index 30a56a2..813fccf 100644
--- a/packages/SystemUI/src/com/android/systemui/deviceentry/data/repository/DeviceEntryFaceAuthRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/deviceentry/data/repository/DeviceEntryFaceAuthRepository.kt
@@ -48,6 +48,7 @@
import com.android.systemui.keyguard.data.repository.KeyguardRepository
import com.android.systemui.keyguard.domain.interactor.KeyguardInteractor
import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor
+import com.android.systemui.keyguard.shared.model.Edge
import com.android.systemui.keyguard.shared.model.KeyguardState
import com.android.systemui.keyguard.shared.model.StatusBarState
import com.android.systemui.keyguard.shared.model.SysUiFaceAuthenticateOptions
@@ -302,7 +303,7 @@
private fun listenForSchedulingWatchdog() {
keyguardTransitionInteractor
- .transition(to = KeyguardState.GONE)
+ .transition(Edge.create(to = KeyguardState.GONE))
.filter { it.transitionState == TransitionState.FINISHED }
.onEach {
// We deliberately want to run this in background because scheduleWatchdog does
diff --git a/packages/SystemUI/src/com/android/systemui/deviceentry/domain/interactor/DeviceEntryUdfpsInteractor.kt b/packages/SystemUI/src/com/android/systemui/deviceentry/domain/interactor/DeviceEntryUdfpsInteractor.kt
index 80b52ed..6c6d730 100644
--- a/packages/SystemUI/src/com/android/systemui/deviceentry/domain/interactor/DeviceEntryUdfpsInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/deviceentry/domain/interactor/DeviceEntryUdfpsInteractor.kt
@@ -17,6 +17,7 @@
package com.android.systemui.deviceentry.domain.interactor
import com.android.systemui.biometrics.domain.interactor.FingerprintPropertyInteractor
+import com.android.systemui.biometrics.shared.model.SensorLocation
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.keyguard.data.repository.BiometricSettingsRepository
import com.android.systemui.keyguard.data.repository.DeviceEntryFingerprintAuthRepository
@@ -58,4 +59,17 @@
flowOf(false)
}
}
+
+ /**
+ * Location of the under-display fingerprint sensor on the display. Null if the device does not
+ * support UDFPS.
+ */
+ val udfpsLocation: Flow<SensorLocation?> =
+ isUdfpsSupported.flatMapLatest {
+ if (it) {
+ fingerprintPropertyInteractor.sensorLocation
+ } else {
+ flowOf(null)
+ }
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/deviceentry/domain/interactor/SystemUIDeviceEntryFaceAuthInteractor.kt b/packages/SystemUI/src/com/android/systemui/deviceentry/domain/interactor/SystemUIDeviceEntryFaceAuthInteractor.kt
index 6c6683a..669cd94 100644
--- a/packages/SystemUI/src/com/android/systemui/deviceentry/domain/interactor/SystemUIDeviceEntryFaceAuthInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/deviceentry/domain/interactor/SystemUIDeviceEntryFaceAuthInteractor.kt
@@ -38,6 +38,7 @@
import com.android.systemui.keyguard.data.repository.BiometricSettingsRepository
import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor
import com.android.systemui.keyguard.shared.model.DevicePosture
+import com.android.systemui.keyguard.shared.model.Edge
import com.android.systemui.keyguard.shared.model.KeyguardState.AOD
import com.android.systemui.keyguard.shared.model.KeyguardState.DOZING
import com.android.systemui.keyguard.shared.model.KeyguardState.LOCKSCREEN
@@ -126,9 +127,9 @@
.launchIn(applicationScope)
merge(
- keyguardTransitionInteractor.transition(AOD, LOCKSCREEN),
- keyguardTransitionInteractor.transition(OFF, LOCKSCREEN),
- keyguardTransitionInteractor.transition(DOZING, LOCKSCREEN),
+ keyguardTransitionInteractor.transition(Edge.create(AOD, LOCKSCREEN)),
+ keyguardTransitionInteractor.transition(Edge.create(OFF, LOCKSCREEN)),
+ keyguardTransitionInteractor.transition(Edge.create(DOZING, LOCKSCREEN)),
)
.filter { it.transitionState == TransitionState.STARTED }
.sample(powerInteractor.detailedWakefulness)
diff --git a/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayContainerViewController.java b/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayContainerViewController.java
index 6e04339..1e725eb 100644
--- a/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayContainerViewController.java
+++ b/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayContainerViewController.java
@@ -16,15 +16,21 @@
package com.android.systemui.dreams;
+import static android.service.dreams.Flags.dreamHandlesBeingObscured;
+
import static com.android.keyguard.BouncerPanelExpansionCalculator.aboutToShowBouncerProgress;
import static com.android.keyguard.BouncerPanelExpansionCalculator.getDreamAlphaScaledExpansion;
import static com.android.keyguard.BouncerPanelExpansionCalculator.getDreamYPositionScaledExpansion;
+import static com.android.systemui.Flags.communalHub;
+import static com.android.systemui.Flags.glanceableHubGestureHandle;
import static com.android.systemui.complication.ComplicationLayoutParams.POSITION_BOTTOM;
import static com.android.systemui.complication.ComplicationLayoutParams.POSITION_TOP;
import static com.android.systemui.doze.util.BurnInHelperKt.getBurnInOffset;
import static com.android.systemui.util.kotlin.JavaAdapterKt.collectFlow;
+import static com.android.systemui.util.kotlin.JavaAdapterKt.combineFlows;
import android.animation.Animator;
+import android.app.DreamManager;
import android.content.res.Resources;
import android.graphics.Region;
import android.os.Handler;
@@ -37,7 +43,9 @@
import com.android.systemui.ambient.touch.scrim.BouncerlessScrimController;
import com.android.systemui.bouncer.domain.interactor.PrimaryBouncerCallbackInteractor;
import com.android.systemui.bouncer.domain.interactor.PrimaryBouncerCallbackInteractor.PrimaryBouncerExpansionCallback;
+import com.android.systemui.communal.domain.interactor.CommunalInteractor;
import com.android.systemui.complication.ComplicationHostViewController;
+import com.android.systemui.dagger.qualifiers.Background;
import com.android.systemui.dagger.qualifiers.Main;
import com.android.systemui.dreams.dagger.DreamOverlayComponent;
import com.android.systemui.dreams.dagger.DreamOverlayModule;
@@ -45,10 +53,12 @@
import com.android.systemui.keyguard.shared.model.KeyguardState;
import com.android.systemui.res.R;
import com.android.systemui.shade.ShadeExpansionChangeEvent;
+import com.android.systemui.shade.domain.interactor.ShadeInteractor;
import com.android.systemui.statusbar.BlurUtils;
import com.android.systemui.util.ViewController;
import kotlinx.coroutines.CoroutineDispatcher;
+import kotlinx.coroutines.flow.FlowKt;
import java.util.Arrays;
@@ -68,6 +78,8 @@
private final DreamOverlayStateController mStateController;
private final LowLightTransitionCoordinator mLowLightTransitionCoordinator;
private final KeyguardTransitionInteractor mKeyguardTransitionInteractor;
+ private final ShadeInteractor mShadeInteractor;
+ private final CommunalInteractor mCommunalInteractor;
private final ComplicationHostViewController mComplicationHostViewController;
@@ -87,9 +99,10 @@
// Main thread handler used to schedule periodic tasks (e.g. burn-in protection updates).
private final Handler mHandler;
- private final CoroutineDispatcher mMainDispatcher;
+ private final CoroutineDispatcher mBackgroundDispatcher;
private final int mDreamOverlayMaxTranslationY;
private final PrimaryBouncerCallbackInteractor mPrimaryBouncerCallbackInteractor;
+ private final DreamManager mDreamManager;
private long mJitterStartTimeMillis;
@@ -174,11 +187,12 @@
DreamOverlayContainerView containerView,
ComplicationHostViewController complicationHostViewController,
@Named(DreamOverlayModule.DREAM_OVERLAY_CONTENT_VIEW) ViewGroup contentView,
+ @Named(DreamOverlayModule.HUB_GESTURE_INDICATOR_VIEW) View hubGestureIndicatorView,
DreamOverlayStatusBarViewController statusBarViewController,
LowLightTransitionCoordinator lowLightTransitionCoordinator,
BlurUtils blurUtils,
@Main Handler handler,
- @Main CoroutineDispatcher mainDispatcher,
+ @Background CoroutineDispatcher backgroundDispatcher,
@Main Resources resources,
@Named(DreamOverlayModule.MAX_BURN_IN_OFFSET) int maxBurnInOffset,
@Named(DreamOverlayModule.BURN_IN_PROTECTION_UPDATE_INTERVAL) long
@@ -188,22 +202,33 @@
DreamOverlayAnimationsController animationsController,
DreamOverlayStateController stateController,
BouncerlessScrimController bouncerlessScrimController,
- KeyguardTransitionInteractor keyguardTransitionInteractor) {
+ KeyguardTransitionInteractor keyguardTransitionInteractor,
+ ShadeInteractor shadeInteractor,
+ CommunalInteractor communalInteractor,
+ DreamManager dreamManager) {
super(containerView);
mDreamOverlayContentView = contentView;
mStatusBarViewController = statusBarViewController;
mBlurUtils = blurUtils;
mDreamOverlayAnimationsController = animationsController;
mStateController = stateController;
+ mCommunalInteractor = communalInteractor;
mLowLightTransitionCoordinator = lowLightTransitionCoordinator;
mBouncerlessScrimController = bouncerlessScrimController;
mKeyguardTransitionInteractor = keyguardTransitionInteractor;
+ mShadeInteractor = shadeInteractor;
mComplicationHostViewController = complicationHostViewController;
mDreamOverlayMaxTranslationY = resources.getDimensionPixelSize(
R.dimen.dream_overlay_y_offset);
+
+ if (communalHub() && glanceableHubGestureHandle()) {
+ // TODO(b/339667383): remove this temporary swipe gesture handle
+ hubGestureIndicatorView.setVisibility(View.VISIBLE);
+ }
+
final View view = mComplicationHostViewController.getView();
mDreamOverlayContentView.addView(view,
@@ -211,11 +236,12 @@
ViewGroup.LayoutParams.MATCH_PARENT));
mHandler = handler;
- mMainDispatcher = mainDispatcher;
+ mBackgroundDispatcher = backgroundDispatcher;
mMaxBurnInOffset = maxBurnInOffset;
mBurnInProtectionUpdateInterval = burnInProtectionUpdateInterval;
mMillisUntilFullJitter = millisUntilFullJitter;
mPrimaryBouncerCallbackInteractor = primaryBouncerCallbackInteractor;
+ mDreamManager = dreamManager;
}
@Override
@@ -238,11 +264,21 @@
mView.getRootSurfaceControl().setTouchableRegion(emptyRegion);
emptyRegion.recycle();
- collectFlow(
- mView,
- mKeyguardTransitionInteractor.isFinishedInStateWhere(KeyguardState::isBouncerState),
- isFinished -> mAnyBouncerShowing = isFinished,
- mMainDispatcher);
+ if (dreamHandlesBeingObscured()) {
+ collectFlow(
+ mView,
+ FlowKt.distinctUntilChanged(combineFlows(
+ mKeyguardTransitionInteractor.isFinishedInStateWhere(
+ KeyguardState::isBouncerState),
+ mShadeInteractor.isAnyExpanded(),
+ mCommunalInteractor.isCommunalShowing(),
+ (anyBouncerShowing, shadeExpanded, communalShowing) -> {
+ mAnyBouncerShowing = anyBouncerShowing;
+ return anyBouncerShowing || shadeExpanded || communalShowing;
+ })),
+ mDreamManager::setDreamIsObscured,
+ mBackgroundDispatcher);
+ }
// Start dream entry animations. Skip animations for low light clock.
if (!mStateController.isLowLightActive()) {
diff --git a/packages/SystemUI/src/com/android/systemui/dreams/dagger/DreamOverlayModule.java b/packages/SystemUI/src/com/android/systemui/dreams/dagger/DreamOverlayModule.java
index 999e681..789b7f8 100644
--- a/packages/SystemUI/src/com/android/systemui/dreams/dagger/DreamOverlayModule.java
+++ b/packages/SystemUI/src/com/android/systemui/dreams/dagger/DreamOverlayModule.java
@@ -18,6 +18,7 @@
import android.content.res.Resources;
import android.view.LayoutInflater;
+import android.view.View;
import android.view.ViewGroup;
import androidx.lifecycle.Lifecycle;
@@ -39,6 +40,7 @@
@Module
public abstract class DreamOverlayModule {
public static final String DREAM_OVERLAY_CONTENT_VIEW = "dream_overlay_content_view";
+ public static final String HUB_GESTURE_INDICATOR_VIEW = "hub_gesture_indicator_view";
public static final String MAX_BURN_IN_OFFSET = "max_burn_in_offset";
public static final String BURN_IN_PROTECTION_UPDATE_INTERVAL =
"burn_in_protection_update_interval";
@@ -71,6 +73,18 @@
"R.id.dream_overlay_content must not be null");
}
+ /**
+ * Gesture indicator bar on the right edge of the screen to indicate to users that they can
+ * swipe to see their widgets on lock screen.
+ */
+ @Provides
+ @DreamOverlayComponent.DreamOverlayScope
+ @Named(HUB_GESTURE_INDICATOR_VIEW)
+ public static View providesHubGestureIndicatorView(DreamOverlayContainerView view) {
+ return Preconditions.checkNotNull(view.findViewById(R.id.glanceable_hub_handle),
+ "R.id.glanceable_hub_handle must not be null");
+ }
+
/** */
@Provides
public static TouchInsetManager.TouchInsetSession providesTouchInsetSession(
diff --git a/packages/SystemUI/src/com/android/systemui/dreams/touch/CommunalTouchHandler.java b/packages/SystemUI/src/com/android/systemui/dreams/touch/CommunalTouchHandler.java
index fff0c58..1c047dd 100644
--- a/packages/SystemUI/src/com/android/systemui/dreams/touch/CommunalTouchHandler.java
+++ b/packages/SystemUI/src/com/android/systemui/dreams/touch/CommunalTouchHandler.java
@@ -98,7 +98,7 @@
// Notification shade window has its own logic to be visible if the hub is open, no need to
// do anything here other than send touch events over.
session.registerInputListener(ev -> {
- surfaces.handleExternalShadeWindowTouch((MotionEvent) ev);
+ surfaces.handleDreamTouch((MotionEvent) ev);
if (ev != null && ((MotionEvent) ev).getAction() == MotionEvent.ACTION_UP) {
var unused = session.pop();
}
diff --git a/packages/SystemUI/src/com/android/systemui/dreams/ui/viewmodel/DreamViewModel.kt b/packages/SystemUI/src/com/android/systemui/dreams/ui/viewmodel/DreamViewModel.kt
index 221f790..c5b3c53 100644
--- a/packages/SystemUI/src/com/android/systemui/dreams/ui/viewmodel/DreamViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/dreams/ui/viewmodel/DreamViewModel.kt
@@ -23,6 +23,7 @@
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dump.DumpManager
import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor
+import com.android.systemui.keyguard.shared.model.Edge
import com.android.systemui.keyguard.shared.model.KeyguardState.DREAMING
import com.android.systemui.keyguard.shared.model.TransitionState
import com.android.systemui.keyguard.ui.viewmodel.DreamingToGlanceableHubTransitionViewModel
@@ -97,7 +98,7 @@
.distinctUntilChanged()
val transitionEnded =
- keyguardTransitionInteractor.transition(from = DREAMING).filter { step ->
+ keyguardTransitionInteractor.transition(Edge.create(from = DREAMING)).filter { step ->
step.transitionState == TransitionState.FINISHED ||
step.transitionState == TransitionState.CANCELED
}
diff --git a/packages/SystemUI/src/com/android/systemui/dump/DumpHandler.kt b/packages/SystemUI/src/com/android/systemui/dump/DumpHandler.kt
index 9876fe4..f04cbb8 100644
--- a/packages/SystemUI/src/com/android/systemui/dump/DumpHandler.kt
+++ b/packages/SystemUI/src/com/android/systemui/dump/DumpHandler.kt
@@ -477,7 +477,7 @@
}
private inline fun PrintWriter.wrapSection(entry: DumpsysEntry, block: () -> Unit) {
- Trace.beginSection(entry.name)
+ Trace.beginSection(entry.name.take(Trace.MAX_SECTION_NAME_LEN))
preamble(entry)
val dumpTime = measureTimeMillis(block)
footer(entry, dumpTime)
diff --git a/packages/SystemUI/src/com/android/systemui/flags/FlagDependencies.kt b/packages/SystemUI/src/com/android/systemui/flags/FlagDependencies.kt
index 67c5564..1404340 100644
--- a/packages/SystemUI/src/com/android/systemui/flags/FlagDependencies.kt
+++ b/packages/SystemUI/src/com/android/systemui/flags/FlagDependencies.kt
@@ -29,11 +29,13 @@
import com.android.systemui.keyguard.MigrateClocksToBlueprint
import com.android.systemui.keyguard.shared.ComposeLockscreen
import com.android.systemui.scene.shared.flag.SceneContainerFlag
+import com.android.systemui.statusbar.notification.collection.SortBySectionTimeFlag
import com.android.systemui.statusbar.notification.footer.shared.FooterViewRefactor
import com.android.systemui.statusbar.notification.interruption.VisualInterruptionRefactor
import com.android.systemui.statusbar.notification.shared.NotificationAvalancheSuppression
import com.android.systemui.statusbar.notification.shared.NotificationIconContainerRefactor
import com.android.systemui.statusbar.notification.shared.NotificationsLiveDataStoreRefactor
+import com.android.systemui.statusbar.notification.shared.PriorityPeopleSection
import javax.inject.Inject
/** A class in which engineers can define flag dependencies */
@@ -49,6 +51,7 @@
NotificationsLiveDataStoreRefactor.token dependsOn NotificationIconContainerRefactor.token
FooterViewRefactor.token dependsOn NotificationIconContainerRefactor.token
NotificationAvalancheSuppression.token dependsOn VisualInterruptionRefactor.token
+ PriorityPeopleSection.token dependsOn SortBySectionTimeFlag.token
// SceneContainer dependencies
SceneContainerFlag.getFlagDependencies().forEach { (alpha, beta) -> alpha dependsOn beta }
diff --git a/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/domain/interactor/ShortcutHelperInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/domain/interactor/ShortcutHelperInteractor.kt
index d3f7e24..44f1c1e 100644
--- a/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/domain/interactor/ShortcutHelperInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/domain/interactor/ShortcutHelperInteractor.kt
@@ -17,19 +17,43 @@
package com.android.systemui.keyboard.shortcut.domain.interactor
import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.dagger.qualifiers.Background
import com.android.systemui.keyboard.shortcut.data.repository.ShortcutHelperRepository
import com.android.systemui.keyboard.shortcut.shared.model.ShortcutHelperState
+import com.android.systemui.model.SysUiState
+import com.android.systemui.settings.DisplayTracker
+import com.android.systemui.shared.system.QuickStepContract
import javax.inject.Inject
+import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.launch
@SysUISingleton
class ShortcutHelperInteractor
@Inject
-constructor(private val repository: ShortcutHelperRepository) {
+constructor(
+ private val displayTracker: DisplayTracker,
+ @Background private val backgroundScope: CoroutineScope,
+ private val sysUiState: SysUiState,
+ private val repository: ShortcutHelperRepository
+) {
val state: Flow<ShortcutHelperState> = repository.state
- fun onUserLeave() {
+ fun onViewClosed() {
repository.hide()
+ setSysUiStateFlagEnabled(false)
+ }
+
+ fun onViewOpened() {
+ setSysUiStateFlagEnabled(true)
+ }
+
+ private fun setSysUiStateFlagEnabled(enabled: Boolean) {
+ backgroundScope.launch {
+ sysUiState
+ .setFlag(QuickStepContract.SYSUI_STATE_SHORTCUT_HELPER_SHOWING, enabled)
+ .commitUpdate(displayTracker.defaultDisplayId)
+ }
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/ui/view/ShortcutHelperActivity.kt b/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/ui/view/ShortcutHelperActivity.kt
index 934f9ee..ef4156d 100644
--- a/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/ui/view/ShortcutHelperActivity.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/ui/view/ShortcutHelperActivity.kt
@@ -63,12 +63,13 @@
setUpSheetDismissListener()
setUpDismissOnTouchOutside()
observeFinishRequired()
+ viewModel.onViewOpened()
}
override fun onDestroy() {
super.onDestroy()
if (isFinishing) {
- viewModel.onUserLeave()
+ viewModel.onViewClosed()
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/ui/viewmodel/ShortcutHelperViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/ui/viewmodel/ShortcutHelperViewModel.kt
index 7e48c65..c623f5c 100644
--- a/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/ui/viewmodel/ShortcutHelperViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/ui/viewmodel/ShortcutHelperViewModel.kt
@@ -38,7 +38,11 @@
.distinctUntilChanged()
.flowOn(backgroundDispatcher)
- fun onUserLeave() {
- interactor.onUserLeave()
+ fun onViewClosed() {
+ interactor.onViewClosed()
+ }
+
+ fun onViewOpened() {
+ interactor.onViewOpened()
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardIndication.java b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardIndication.java
index ee3706a..a0b25b9 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardIndication.java
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardIndication.java
@@ -32,6 +32,8 @@
public class KeyguardIndication {
@Nullable
private final CharSequence mMessage;
+ @Nullable
+ private final boolean mForceAccessibilityLiveRegionAssertive;
@NonNull
private final ColorStateList mTextColor;
@Nullable
@@ -49,13 +51,15 @@
Drawable icon,
View.OnClickListener onClickListener,
Drawable background,
- Long minVisibilityMillis) {
+ Long minVisibilityMillis,
+ Boolean foceAssertive) {
mMessage = message;
mTextColor = textColor;
mIcon = icon;
mOnClickListener = onClickListener;
mBackground = background;
mMinVisibilityMillis = minVisibilityMillis;
+ mForceAccessibilityLiveRegionAssertive = foceAssertive;
}
/**
@@ -101,6 +105,15 @@
return mMinVisibilityMillis;
}
+
+ /**
+ * Whether to force the accessibility live region to be assertive.
+ */
+ public boolean getForceAssertiveAccessibilityLiveRegion() {
+ return mForceAccessibilityLiveRegionAssertive;
+ }
+
+
@Override
public String toString() {
String str = "KeyguardIndication{";
@@ -109,6 +122,7 @@
if (mOnClickListener != null) str += " mOnClickListener=" + mOnClickListener;
if (mBackground != null) str += " mBackground=" + mBackground;
if (mMinVisibilityMillis != null) str += " mMinVisibilityMillis=" + mMinVisibilityMillis;
+ if (mForceAccessibilityLiveRegionAssertive) str += "mForceAccessibilityLiveRegionAssertive";
str += "}";
return str;
}
@@ -123,6 +137,7 @@
private ColorStateList mTextColor;
private Drawable mBackground;
private Long mMinVisibilityMillis;
+ private boolean mForceAccessibilityLiveRegionAssertive;
public Builder() { }
@@ -178,6 +193,14 @@
}
/**
+ * Optional. Can force the accessibility live region to be assertive for this message.
+ */
+ public Builder setForceAccessibilityLiveRegionAssertive() {
+ this.mForceAccessibilityLiveRegionAssertive = true;
+ return this;
+ }
+
+ /**
* Build the KeyguardIndication.
*/
public KeyguardIndication build() {
@@ -190,7 +213,7 @@
return new KeyguardIndication(
mMessage, mTextColor, mIcon, mOnClickListener, mBackground,
- mMinVisibilityMillis);
+ mMinVisibilityMillis, mForceAccessibilityLiveRegionAssertive);
}
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardService.java b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardService.java
index 674c128..f9adc47 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardService.java
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardService.java
@@ -84,20 +84,25 @@
import com.android.systemui.keyguard.ui.viewmodel.WindowManagerLockscreenVisibilityViewModel;
import com.android.systemui.power.domain.interactor.PowerInteractor;
import com.android.systemui.power.shared.model.ScreenPowerState;
+import com.android.systemui.scene.domain.interactor.SceneInteractor;
+import com.android.systemui.scene.shared.flag.SceneContainerFlag;
+import com.android.systemui.scene.shared.model.Scenes;
import com.android.systemui.settings.DisplayTracker;
import com.android.wm.shell.shared.CounterRotator;
import com.android.wm.shell.shared.ShellTransitions;
import com.android.wm.shell.shared.TransitionUtil;
import com.android.wm.shell.transition.Transitions;
+import dagger.Lazy;
+
+import kotlinx.coroutines.CoroutineScope;
+
import java.util.ArrayList;
import java.util.Map;
import java.util.WeakHashMap;
import javax.inject.Inject;
-import kotlinx.coroutines.CoroutineScope;
-
public class KeyguardService extends Service {
static final String TAG = "KeyguardService";
static final String PERMISSION = android.Manifest.permission.CONTROL_KEYGUARD;
@@ -109,6 +114,7 @@
private final ShellTransitions mShellTransitions;
private final DisplayTracker mDisplayTracker;
private final PowerInteractor mPowerInteractor;
+ private final Lazy<SceneInteractor> mSceneInteractorLazy;
private static RemoteAnimationTarget[] wrap(TransitionInfo info, boolean wallpapers,
SurfaceControl.Transaction t, ArrayMap<SurfaceControl, SurfaceControl> leashMap,
@@ -316,7 +322,8 @@
@Application CoroutineScope scope,
FeatureFlags featureFlags,
PowerInteractor powerInteractor,
- WindowManagerOcclusionManager windowManagerOcclusionManager) {
+ WindowManagerOcclusionManager windowManagerOcclusionManager,
+ Lazy<SceneInteractor> sceneInteractorLazy) {
super();
mKeyguardViewMediator = keyguardViewMediator;
mKeyguardLifecyclesDispatcher = keyguardLifecyclesDispatcher;
@@ -325,6 +332,7 @@
mDisplayTracker = displayTracker;
mFlags = featureFlags;
mPowerInteractor = powerInteractor;
+ mSceneInteractorLazy = sceneInteractorLazy;
if (KeyguardWmStateRefactor.isEnabled()) {
WindowManagerLockscreenVisibilityViewBinder.bind(
@@ -601,6 +609,10 @@
trace("showDismissibleKeyguard");
checkPermission();
mKeyguardViewMediator.showDismissibleKeyguard();
+ if (SceneContainerFlag.isEnabled()) {
+ mSceneInteractorLazy.get().changeScene(
+ Scenes.Lockscreen, "KeyguardService.showDismissibleKeyguard");
+ }
}
@Override // Binder interface
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardSliceProvider.java b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardSliceProvider.java
index dbaa297..68a252b 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardSliceProvider.java
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardSliceProvider.java
@@ -37,6 +37,7 @@
import android.service.notification.ZenModeConfig;
import android.text.TextUtils;
import android.text.style.StyleSpan;
+import android.util.Log;
import androidx.core.graphics.drawable.IconCompat;
import androidx.slice.Slice;
@@ -212,21 +213,27 @@
@AnyThread
@Override
public Slice onBindSlice(Uri sliceUri) {
- Trace.beginSection("KeyguardSliceProvider#onBindSlice");
- Slice slice;
- synchronized (this) {
- ListBuilder builder = new ListBuilder(getContext(), mSliceUri, ListBuilder.INFINITY);
- if (needsMediaLocked()) {
- addMediaLocked(builder);
- } else {
- builder.addRow(new RowBuilder(mDateUri).setTitle(mLastText));
+ Slice slice = null;
+ try {
+ Trace.beginSection("KeyguardSliceProvider#onBindSlice");
+ synchronized (this) {
+ ListBuilder builder = new ListBuilder(getContext(), mSliceUri,
+ ListBuilder.INFINITY);
+ if (needsMediaLocked()) {
+ addMediaLocked(builder);
+ } else {
+ builder.addRow(new RowBuilder(mDateUri).setTitle(mLastText));
+ }
+ addNextAlarmLocked(builder);
+ addZenModeLocked(builder);
+ addPrimaryActionLocked(builder);
+ slice = builder.build();
}
- addNextAlarmLocked(builder);
- addZenModeLocked(builder);
- addPrimaryActionLocked(builder);
- slice = builder.build();
+ } catch (IllegalStateException e) {
+ Log.w(TAG, "Could not initialize slice", e);
+ } finally {
+ Trace.endSection();
}
- Trace.endSection();
return slice;
}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
index 2cda728..81c2d92 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
@@ -1076,6 +1076,33 @@
}
};
+ /**
+ * For now, the keyguard-appearing animation is a no-op, because we assume that this is
+ * happening while the screen is already off or turning off.
+ *
+ * TODO(b/278086361): create an animation for keyguard appearing over a non-showWhenLocked
+ * activity.
+ */
+ private final IRemoteAnimationRunner.Stub mAppearAnimationRunner =
+ new IRemoteAnimationRunner.Stub() {
+ @Override
+ public void onAnimationStart(@WindowManager.TransitionOldType int transit,
+ RemoteAnimationTarget[] apps,
+ RemoteAnimationTarget[] wallpapers,
+ RemoteAnimationTarget[] nonApps,
+ IRemoteAnimationFinishedCallback finishedCallback) {
+ try {
+ finishedCallback.onAnimationFinished();
+ } catch (RemoteException e) {
+ Log.e(TAG, "Failed to finish transition", e);
+ }
+ }
+
+ @Override
+ public void onAnimationCancelled() {
+ }
+ };
+
private final IRemoteAnimationRunner mOccludeAnimationRunner =
new OccludeActivityLaunchRemoteAnimationRunner(mOccludeAnimationController);
@@ -1164,7 +1191,7 @@
finishedCallback.onAnimationFinished();
mOccludeByDreamAnimator = null;
} catch (RemoteException e) {
- e.printStackTrace();
+ Log.e(TAG, "Failed to finish transition", e);
}
}
});
@@ -1279,7 +1306,7 @@
mInteractionJankMonitor.end(CUJ_LOCKSCREEN_OCCLUSION);
} catch (RemoteException e) {
- e.printStackTrace();
+ Log.e(TAG, "Failed to finish transition", e);
}
}
});
@@ -1545,6 +1572,7 @@
mKeyguardTransitions.register(
KeyguardService.wrap(this, getExitAnimationRunner()),
+ KeyguardService.wrap(this, getAppearAnimationRunner()),
KeyguardService.wrap(this, getOccludeAnimationRunner()),
KeyguardService.wrap(this, getOccludeByDreamAnimationRunner()),
KeyguardService.wrap(this, getUnoccludeAnimationRunner()));
@@ -2123,6 +2151,10 @@
return validatingRemoteAnimationRunner(mExitAnimationRunner);
}
+ public IRemoteAnimationRunner getAppearAnimationRunner() {
+ return validatingRemoteAnimationRunner(mAppearAnimationRunner);
+ }
+
public IRemoteAnimationRunner getOccludeAnimationRunner() {
if (KeyguardWmStateRefactor.isEnabled()) {
return validatingRemoteAnimationRunner(mWmOcclusionManager.getOccludeAnimationRunner());
@@ -3356,7 +3388,7 @@
}
} catch (RemoteException e) {
mSurfaceBehindRemoteAnimationRequested = false;
- e.printStackTrace();
+ Log.e(TAG, "Failed to report keyguardGoingAway", e);
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ResourceTrimmer.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ResourceTrimmer.kt
index a65a882..3cbcb2c 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ResourceTrimmer.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ResourceTrimmer.kt
@@ -29,15 +29,20 @@
import com.android.systemui.flags.Flags
import com.android.systemui.keyguard.domain.interactor.KeyguardInteractor
import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor
+import com.android.systemui.keyguard.shared.model.Edge
import com.android.systemui.keyguard.shared.model.KeyguardState.GONE
import com.android.systemui.keyguard.shared.model.TransitionState
import com.android.systemui.power.domain.interactor.PowerInteractor
+import com.android.systemui.scene.domain.interactor.SceneInteractor
+import com.android.systemui.scene.shared.flag.SceneContainerFlag
+import com.android.systemui.scene.shared.model.Scenes
import com.android.systemui.utils.GlobalWindowManager
import javax.inject.Inject
import kotlinx.coroutines.CoroutineDispatcher
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.flow.combine
import kotlinx.coroutines.flow.distinctUntilChanged
+import kotlinx.coroutines.flow.filter
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.launch
@@ -59,6 +64,7 @@
@Application private val applicationScope: CoroutineScope,
@Background private val bgDispatcher: CoroutineDispatcher,
private val featureFlags: FeatureFlags,
+ private val sceneInteractor: SceneInteractor,
) : CoreStartable, WakefulnessLifecycle.Observer {
override fun start() {
@@ -84,9 +90,15 @@
applicationScope.launch(bgDispatcher) {
// We drop 1 to avoid triggering on initial collect().
- keyguardTransitionInteractor.transition(to = GONE).collect { transition ->
- if (transition.transitionState == TransitionState.FINISHED) {
- onKeyguardGone()
+ if (SceneContainerFlag.isEnabled) {
+ sceneInteractor.transitionState
+ .filter { it.isIdle(Scenes.Gone) }
+ .collect { onKeyguardGone() }
+ } else {
+ keyguardTransitionInteractor.transition(Edge.create(to = GONE)).collect {
+ if (it.transitionState == TransitionState.FINISHED) {
+ onKeyguardGone()
+ }
}
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/WindowManagerLockscreenVisibilityManager.kt b/packages/SystemUI/src/com/android/systemui/keyguard/WindowManagerLockscreenVisibilityManager.kt
index 00f5002..1b342ed 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/WindowManagerLockscreenVisibilityManager.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/WindowManagerLockscreenVisibilityManager.kt
@@ -23,6 +23,7 @@
import android.view.WindowManager
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Main
+import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor
import com.android.systemui.keyguard.ui.binder.KeyguardSurfaceBehindParamsApplier
import com.android.systemui.statusbar.policy.KeyguardStateController
import java.util.concurrent.Executor
@@ -40,6 +41,7 @@
private val activityTaskManagerService: IActivityTaskManager,
private val keyguardStateController: KeyguardStateController,
private val keyguardSurfaceBehindAnimator: KeyguardSurfaceBehindParamsApplier,
+ private val keyguardTransitionInteractor: KeyguardTransitionInteractor,
) {
/**
@@ -141,6 +143,14 @@
finishedCallback: IRemoteAnimationFinishedCallback
) {
if (apps.isNotEmpty()) {
+ // Ensure that we've started a dismiss keyguard transition. WindowManager can start the
+ // going away animation on its own, if an activity launches and then requests dismissing
+ // the keyguard. In this case, this is the first and only signal we'll receive to start
+ // a transition to GONE.
+ keyguardTransitionInteractor.startDismissKeyguardTransition(
+ reason = "Going away remote animation started"
+ )
+
goingAwayRemoteAnimationFinishedCallback = finishedCallback
keyguardSurfaceBehindAnimator.applyParamsToSurface(apps[0])
} else {
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/KeyguardSmartspaceRepository.kt b/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/KeyguardSmartspaceRepository.kt
index 956125c..a1e4af5 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/KeyguardSmartspaceRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/KeyguardSmartspaceRepository.kt
@@ -51,10 +51,6 @@
) : KeyguardSmartspaceRepository {
private val _bcSmartspaceVisibility: MutableStateFlow<Int> = MutableStateFlow(View.GONE)
override val bcSmartspaceVisibility: StateFlow<Int> = _bcSmartspaceVisibility.asStateFlow()
- val defaultValue =
- context.resources.getBoolean(
- com.android.internal.R.bool.config_lockscreenWeatherEnabledByDefault
- )
override val isWeatherEnabled: StateFlow<Boolean> =
secureSettings
.observerFlow(
@@ -76,7 +72,7 @@
private fun getLockscreenWeatherEnabled(): Boolean {
return secureSettings.getIntForUser(
Settings.Secure.LOCK_SCREEN_WEATHER_ENABLED,
- if (defaultValue) 1 else 0,
+ 1,
userTracker.userId
) == 1
}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/KeyguardTransitionRepository.kt b/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/KeyguardTransitionRepository.kt
index 7f3274c..f488d3b 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/KeyguardTransitionRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/KeyguardTransitionRepository.kt
@@ -34,6 +34,7 @@
import javax.inject.Inject
import kotlinx.coroutines.CoroutineDispatcher
import kotlinx.coroutines.channels.BufferOverflow
+import kotlinx.coroutines.delay
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.MutableSharedFlow
import kotlinx.coroutines.flow.MutableStateFlow
@@ -42,6 +43,7 @@
import kotlinx.coroutines.flow.asStateFlow
import kotlinx.coroutines.flow.distinctUntilChanged
import kotlinx.coroutines.flow.filter
+import kotlinx.coroutines.sync.Mutex
/**
* The source of truth for all keyguard transitions.
@@ -129,6 +131,7 @@
private var lastStep: TransitionStep = TransitionStep()
private var lastAnimator: ValueAnimator? = null
+ private val _currentTransitionMutex = Mutex()
private val _currentTransitionInfo: MutableStateFlow<TransitionInfo> =
MutableStateFlow(
TransitionInfo(
@@ -146,6 +149,9 @@
*/
private var updateTransitionId: UUID? = null
+ // Only used in a test environment
+ var forceDelayForRaceConditionTest = false
+
init {
// Start with a FINISHED transition in OFF. KeyguardBootInteractor will transition from OFF
// to either GONE or LOCKSCREEN once we're booted up and can determine which state we should
@@ -162,9 +168,21 @@
override suspend fun startTransition(info: TransitionInfo): UUID? {
_currentTransitionInfo.value = info
+ Log.d(TAG, "(Internal) Setting current transition info: $info")
+
+ // There is no fairness guarantee with 'withContext', which means that transitions could
+ // be processed out of order. Use a Mutex to guarantee ordering.
+ _currentTransitionMutex.lock()
+
+ // Only used in a test environment
+ if (forceDelayForRaceConditionTest) {
+ delay(50L)
+ }
// Animators must be started on the main thread.
return withContext("$TAG#startTransition", mainDispatcher) {
+ _currentTransitionMutex.unlock()
+
if (lastStep.from == info.from && lastStep.to == info.to) {
Log.i(TAG, "Duplicate call to start the transition, rejecting: $info")
return@withContext null
@@ -247,7 +265,7 @@
state: TransitionState
) {
if (updateTransitionId != transitionId) {
- Log.wtf(TAG, "Attempting to update with old/invalid transitionId: $transitionId")
+ Log.w(TAG, "Attempting to update with old/invalid transitionId: $transitionId")
return
}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/LockscreenSceneTransitionRepository.kt b/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/LockscreenSceneTransitionRepository.kt
new file mode 100644
index 0000000..80bdc65
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/LockscreenSceneTransitionRepository.kt
@@ -0,0 +1,37 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.keyguard.data.repository
+
+import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.keyguard.shared.model.KeyguardState
+import javax.inject.Inject
+import kotlinx.coroutines.flow.MutableStateFlow
+
+@SysUISingleton
+class LockscreenSceneTransitionRepository @Inject constructor() {
+
+ /**
+ * This [KeyguardState] will indicate which sub state within KTF should be navigated to when the
+ * next transition into the Lockscreen scene is started. It will be consumed exactly once and
+ * after that the state will be set back to [DEFAULT_STATE].
+ */
+ val nextLockscreenTargetState: MutableStateFlow<KeyguardState> = MutableStateFlow(DEFAULT_STATE)
+
+ companion object {
+ val DEFAULT_STATE = KeyguardState.LOCKSCREEN
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/DeviceEntrySideFpsOverlayInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/DeviceEntrySideFpsOverlayInteractor.kt
index eef4b97..9626077 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/DeviceEntrySideFpsOverlayInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/DeviceEntrySideFpsOverlayInteractor.kt
@@ -17,6 +17,7 @@
package com.android.systemui.keyguard.domain.interactor
import android.content.Context
+import android.util.Log
import com.android.keyguard.KeyguardUpdateMonitor
import com.android.systemui.bouncer.domain.interactor.AlternateBouncerInteractor
import com.android.systemui.bouncer.domain.interactor.PrimaryBouncerInteractor
@@ -39,6 +40,7 @@
import kotlinx.coroutines.flow.flowOf
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.flow.merge
+import kotlinx.coroutines.flow.onEach
import kotlinx.coroutines.launch
/**
@@ -96,10 +98,13 @@
keyguardUpdateMonitor.isFingerprintDetectionRunning &&
keyguardUpdateMonitor.isUnlockingWithFingerprintAllowed
}
+ .onEach { Log.d(TAG, "showIndicatorForPrimaryBouncer updated: $it") }
private val showIndicatorForAlternateBouncer: Flow<Boolean> =
// Note: this interactor internally verifies that SideFPS is enabled and running.
- alternateBouncerInteractor.isVisible
+ alternateBouncerInteractor.isVisible.onEach {
+ Log.d(TAG, "showIndicatorForAlternateBouncer updated: $it")
+ }
/**
* Indicates whether the primary or alternate bouncers request showing the side fingerprint
@@ -112,6 +117,7 @@
showForPrimaryBouncer || showForAlternateBouncer
}
.distinctUntilChanged()
+ .onEach { Log.d(TAG, "showIndicatorForDeviceEntry updated: $it") }
private fun isBouncerActive(): Boolean {
if (SceneContainerFlag.isEnabled) {
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromLockscreenTransitionInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromLockscreenTransitionInteractor.kt
index dad2d96..f1e98f3 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromLockscreenTransitionInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromLockscreenTransitionInteractor.kt
@@ -263,7 +263,9 @@
}
fun dismissKeyguard() {
- scope.launch("$TAG#dismissKeyguard") { startTransitionTo(KeyguardState.GONE) }
+ scope.launch("$TAG#dismissKeyguard") {
+ startTransitionTo(KeyguardState.GONE, ownerReason = "#dismissKeyguard()")
+ }
}
private fun listenForLockscreenToGone() {
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardBlueprintInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardBlueprintInteractor.kt
index 857096e..7cee258 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardBlueprintInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardBlueprintInteractor.kt
@@ -78,7 +78,7 @@
private val refreshEvents: Flow<Unit> =
merge(
configurationInteractor.onAnyConfigurationChange,
- fingerprintPropertyInteractor.propertiesInitialized.filter { it }.map { Unit },
+ fingerprintPropertyInteractor.propertiesInitialized.filter { it }.map {},
)
init {
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardDismissActionInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardDismissActionInteractor.kt
index 08d29d4..1aac1c5 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardDismissActionInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardDismissActionInteractor.kt
@@ -25,6 +25,9 @@
import com.android.systemui.keyguard.shared.model.KeyguardState.ALTERNATE_BOUNCER
import com.android.systemui.keyguard.shared.model.KeyguardState.GONE
import com.android.systemui.keyguard.shared.model.KeyguardState.PRIMARY_BOUNCER
+import com.android.systemui.scene.domain.interactor.SceneInteractor
+import com.android.systemui.scene.shared.flag.SceneContainerFlag
+import com.android.systemui.scene.shared.model.Scenes
import com.android.systemui.util.kotlin.sample
import javax.inject.Inject
import kotlinx.coroutines.CoroutineScope
@@ -48,6 +51,7 @@
transitionInteractor: KeyguardTransitionInteractor,
val dismissInteractor: KeyguardDismissInteractor,
@Application private val applicationScope: CoroutineScope,
+ sceneInteractor: SceneInteractor,
) {
val dismissAction: Flow<DismissAction> = repository.dismissAction
@@ -72,7 +76,12 @@
)
private val finishedTransitionToGone: Flow<Unit> =
- transitionInteractor.finishedKeyguardState.filter { it == GONE }.map {} // map to Unit
+ if (SceneContainerFlag.isEnabled) {
+ sceneInteractor.transitionState.filter { it.isIdle(Scenes.Gone) }.map {}
+ } else {
+ transitionInteractor.finishedKeyguardState.filter { it == GONE }.map {}
+ }
+
val executeDismissAction: Flow<() -> KeyguardDone> =
merge(
finishedTransitionToGone,
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardInteractor.kt
index 2d7b737..c44a40f 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardInteractor.kt
@@ -55,6 +55,7 @@
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.channels.awaitClose
+import kotlinx.coroutines.delay
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.SharingStarted
@@ -97,6 +98,7 @@
/** Bounds of the notification container. */
val notificationContainerBounds: StateFlow<NotificationContainerBounds> by lazy {
+ SceneContainerFlag.assertInLegacyMode()
combine(
_notificationPlaceholderBounds,
sharedNotificationContainerInteractor.get().configurationBasedDimensions,
@@ -115,6 +117,7 @@
}
fun setNotificationContainerBounds(position: NotificationContainerBounds) {
+ SceneContainerFlag.assertInLegacyMode()
_notificationPlaceholderBounds.value = position
}
@@ -179,7 +182,11 @@
}
.sample(powerInteractor.isAwake) { isAbleToDream, isAwake -> isAbleToDream && isAwake }
.debounce(50L)
- .distinctUntilChanged()
+ .stateIn(
+ scope = applicationScope,
+ started = SharingStarted.WhileSubscribed(),
+ initialValue = false,
+ )
/** Whether the keyguard is showing or not. */
@Deprecated("Use KeyguardTransitionInteractor + KeyguardState")
@@ -223,7 +230,19 @@
@JvmField val primaryBouncerShowing: Flow<Boolean> = bouncerRepository.primaryBouncerShow
/** Whether the alternate bouncer is showing or not. */
- val alternateBouncerShowing: Flow<Boolean> = bouncerRepository.alternateBouncerVisible
+ val alternateBouncerShowing: Flow<Boolean> =
+ bouncerRepository.alternateBouncerVisible.sample(isAbleToDream) {
+ alternateBouncerVisible,
+ isAbleToDream ->
+ if (isAbleToDream) {
+ // If the alternate bouncer will show over a dream, it is likely that the dream has
+ // requested a dismissal, which will stop the dream. By delaying this slightly, the
+ // DREAMING->LOCKSCREEN transition will now happen first, followed by
+ // LOCKSCREEN->ALTERNATE_BOUNCER.
+ delay(600L)
+ }
+ alternateBouncerVisible
+ }
/** Observable for the [StatusBarState] */
val statusBarState: Flow<StatusBarState> = repository.statusBarState
@@ -299,10 +318,12 @@
shadeRepository.legacyShadeExpansion.onStart { emit(0f) },
keyguardTransitionInteractor.transitionValue(GONE).onStart { emit(0f) },
) { legacyShadeExpansion, goneValue ->
- if (goneValue == 1f || (goneValue == 0f && legacyShadeExpansion == 0f)) {
+ val isLegacyShadeInResetPosition =
+ legacyShadeExpansion == 0f || legacyShadeExpansion == 1f
+ if (goneValue == 1f || (goneValue == 0f && isLegacyShadeInResetPosition)) {
// Reset the translation value
emit(0f)
- } else if (legacyShadeExpansion > 0f && legacyShadeExpansion < 1f) {
+ } else if (!isLegacyShadeInResetPosition) {
// On swipe up, translate the keyguard to reveal the bouncer, OR a GONE
// transition is running, which means this is a swipe to dismiss. Values of
// 0f and 1f need to be ignored in the legacy shade expansion. These can
@@ -320,7 +341,11 @@
}
}
}
- .distinctUntilChanged()
+ .stateIn(
+ scope = applicationScope,
+ started = SharingStarted.WhileSubscribed(),
+ initialValue = 0f,
+ )
val clockShouldBeCentered: Flow<Boolean> = repository.clockShouldBeCentered
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionAuditLogger.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionAuditLogger.kt
index e711edc..cf6942e 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionAuditLogger.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionAuditLogger.kt
@@ -19,6 +19,7 @@
import com.android.keyguard.logging.KeyguardLogger
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Background
+import com.android.systemui.keyguard.ui.viewmodel.KeyguardRootViewModel
import com.android.systemui.log.core.LogLevel.VERBOSE
import com.android.systemui.power.domain.interactor.PowerInteractor
import com.android.systemui.scene.shared.flag.SceneContainerFlag
@@ -26,6 +27,7 @@
import com.android.systemui.statusbar.notification.stack.ui.viewmodel.SharedNotificationContainerViewModel
import javax.inject.Inject
import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.flow.debounce
import kotlinx.coroutines.launch
private val TAG = KeyguardTransitionAuditLogger::class.simpleName!!
@@ -41,6 +43,7 @@
private val logger: KeyguardLogger,
private val powerInteractor: PowerInteractor,
private val sharedNotificationContainerViewModel: SharedNotificationContainerViewModel,
+ private val keyguardRootViewModel: KeyguardRootViewModel,
private val shadeInteractor: ShadeInteractor,
private val keyguardOcclusionInteractor: KeyguardOcclusionInteractor,
) {
@@ -53,12 +56,6 @@
}
scope.launch {
- sharedNotificationContainerViewModel
- .getMaxNotifications { height, useExtraShelfSpace -> height.toInt() }
- .collect { logger.log(TAG, VERBOSE, "Notif: max height in px", it) }
- }
-
- scope.launch {
sharedNotificationContainerViewModel.isOnLockscreen.collect {
logger.log(TAG, VERBOSE, "Notif: isOnLockscreen", it)
}
@@ -72,8 +69,8 @@
if (!SceneContainerFlag.isEnabled) {
scope.launch {
- sharedNotificationContainerViewModel.bounds.collect {
- logger.log(TAG, VERBOSE, "Notif: bounds", it)
+ sharedNotificationContainerViewModel.bounds.debounce(20L).collect {
+ logger.log(TAG, VERBOSE, "Notif: bounds (debounced)", it)
}
}
}
@@ -113,6 +110,18 @@
}
scope.launch {
+ keyguardInteractor.keyguardTranslationY.collect {
+ logger.log(TAG, VERBOSE, "keyguardTranslationY", it)
+ }
+ }
+
+ scope.launch {
+ keyguardRootViewModel.burnInModel.debounce(20L).collect {
+ logger.log(TAG, VERBOSE, "BurnInModel (debounced)", it)
+ }
+ }
+
+ scope.launch {
keyguardInteractor.isKeyguardDismissible.collect {
logger.log(TAG, VERBOSE, "isDismissible", it)
}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionInteractor.kt
index 2c05d49..c65dc30 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionInteractor.kt
@@ -17,7 +17,10 @@
package com.android.systemui.keyguard.domain.interactor
+import android.annotation.FloatRange
+import android.annotation.SuppressLint
import android.util.Log
+import com.android.compose.animation.scene.SceneKey
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Application
import com.android.systemui.keyguard.data.repository.KeyguardRepository
@@ -29,11 +32,15 @@
import com.android.systemui.keyguard.shared.model.KeyguardState.DOZING
import com.android.systemui.keyguard.shared.model.KeyguardState.LOCKSCREEN
import com.android.systemui.keyguard.shared.model.KeyguardState.PRIMARY_BOUNCER
+import com.android.systemui.keyguard.shared.model.KeyguardState.UNDEFINED
import com.android.systemui.keyguard.shared.model.TransitionInfo
import com.android.systemui.keyguard.shared.model.TransitionState
import com.android.systemui.keyguard.shared.model.TransitionStep
+import com.android.systemui.scene.domain.interactor.SceneInteractor
import com.android.systemui.scene.shared.flag.SceneContainerFlag
+import com.android.systemui.scene.shared.model.Scenes
import com.android.systemui.util.kotlin.pairwise
+import java.util.UUID
import javax.inject.Inject
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.ExperimentalCoroutinesApi
@@ -68,14 +75,17 @@
private val fromAlternateBouncerTransitionInteractor:
dagger.Lazy<FromAlternateBouncerTransitionInteractor>,
private val fromDozingTransitionInteractor: dagger.Lazy<FromDozingTransitionInteractor>,
+ private val sceneInteractor: dagger.Lazy<SceneInteractor>,
) {
- private val transitionMap = mutableMapOf<Edge, MutableSharedFlow<TransitionStep>>()
+ private val transitionMap = mutableMapOf<Edge.StateToState, MutableSharedFlow<TransitionStep>>()
/**
* Numerous flows are derived from, or care directly about, the transition value in and out of a
* single state. This prevent the redundant filters from running.
*/
private val transitionValueCache = mutableMapOf<KeyguardState, MutableSharedFlow<Float>>()
+
+ @SuppressLint("SharedFlowCreation")
private fun getTransitionValueFlow(state: KeyguardState): MutableSharedFlow<Float> {
return transitionValueCache.getOrPut(state) {
MutableSharedFlow<Float>(
@@ -90,6 +100,9 @@
@Deprecated("Not performant - Use something else in this class")
val transitions = repository.transitions
+ val transitionState: StateFlow<TransitionStep> =
+ transitions.stateIn(scope, SharingStarted.Eagerly, TransitionStep())
+
/**
* A pair of the most recent STARTED step, and the transition step immediately preceding it. The
* transition framework enforces that the previous step is either a CANCELED or FINISHED step,
@@ -99,6 +112,7 @@
* FINISHED. In the case of a CANCELED step, we can also figure out which state we were coming
* from when we were canceled.
*/
+ @SuppressLint("SharedFlowCreation")
val startedStepWithPrecedingStep =
repository.transitions
.pairwise()
@@ -119,11 +133,11 @@
scope.launch {
repository.transitions.collect {
// FROM->TO
- transitionMap[Edge(it.from, it.to)]?.emit(it)
+ transitionMap[Edge.create(it.from, it.to)]?.emit(it)
// FROM->(ANY)
- transitionMap[Edge(it.from, null)]?.emit(it)
+ transitionMap[Edge.create(it.from, null)]?.emit(it)
// (ANY)->TO
- transitionMap[Edge(null, it.to)]?.emit(it)
+ transitionMap[Edge.create(null, it.to)]?.emit(it)
}
}
@@ -143,25 +157,70 @@
}
}
- /** Given an [edge], return a SharedFlow to collect only relevant [TransitionStep]. */
- fun getOrCreateFlow(edge: Edge): MutableSharedFlow<TransitionStep> {
- return transitionMap.getOrPut(edge) {
- MutableSharedFlow<TransitionStep>(
- extraBufferCapacity = 10,
- onBufferOverflow = BufferOverflow.DROP_OLDEST
- )
+ fun transition(edge: Edge, edgeWithoutSceneContainer: Edge): Flow<TransitionStep> {
+ return transition(if (SceneContainerFlag.isEnabled) edge else edgeWithoutSceneContainer)
+ }
+
+ /** Given an [edge], return a Flow to collect only relevant [TransitionStep]s. */
+ @SuppressLint("SharedFlowCreation")
+ fun transition(edge: Edge): Flow<TransitionStep> {
+ edge.verifyValidKeyguardStates()
+ val mappedEdge = getMappedEdge(edge)
+
+ val flow: Flow<TransitionStep> =
+ transitionMap.getOrPut(mappedEdge) {
+ MutableSharedFlow(
+ extraBufferCapacity = 10,
+ onBufferOverflow = BufferOverflow.DROP_OLDEST
+ )
+ }
+
+ return if (SceneContainerFlag.isEnabled) {
+ flow.filter {
+ val fromScene =
+ when (edge) {
+ is Edge.StateToState -> edge.from?.mapToSceneContainerScene()
+ is Edge.StateToScene -> edge.from.mapToSceneContainerScene()
+ is Edge.SceneToState -> edge.from
+ }
+
+ val toScene =
+ when (edge) {
+ is Edge.StateToState -> edge.to?.mapToSceneContainerScene()
+ is Edge.StateToScene -> edge.to
+ is Edge.SceneToState -> edge.to.mapToSceneContainerScene()
+ }
+
+ fun SceneKey?.isLockscreenOrNull() = this == Scenes.Lockscreen || this == null
+
+ return@filter (fromScene.isLockscreenOrNull() && toScene.isLockscreenOrNull()) ||
+ sceneInteractor.get().transitionState.value.isTransitioning(fromScene, toScene)
+ }
+ } else {
+ flow
}
}
/**
- * Receive all [TransitionStep] matching a filter of [from]->[to]. Allow nulls in order to match
- * any transition, for instance (any)->GONE.
+ * Converts old KTF states to UNDEFINED when [SceneContainerFlag] is enabled.
+ *
+ * Does nothing otherwise.
+ *
+ * This method should eventually be removed when new code is only written for scene container.
+ * Even when all edges are ported today, there is still development on going in production that
+ * might utilize old states.
*/
- fun transition(from: KeyguardState? = null, to: KeyguardState? = null): Flow<TransitionStep> {
- if (from == null && to == null) {
- throw IllegalArgumentException("from and to cannot both be null")
+ private fun getMappedEdge(edge: Edge): Edge.StateToState {
+ if (!SceneContainerFlag.isEnabled) return edge as Edge.StateToState
+ return when (edge) {
+ is Edge.StateToState ->
+ Edge.create(
+ from = edge.from?.mapToSceneContainerState(),
+ to = edge.to?.mapToSceneContainerState()
+ )
+ is Edge.SceneToState -> Edge.create(UNDEFINED, edge.to)
+ is Edge.StateToScene -> Edge.create(edge.from, UNDEFINED)
}
- return getOrCreateFlow(Edge(from = from, to = to))
}
/**
@@ -180,6 +239,7 @@
* AOD<->* transition information, mapped to dozeAmount range of AOD (1f) <->
* * (0f).
*/
+ @SuppressLint("SharedFlowCreation")
val dozeAmountTransition: Flow<TransitionStep> =
repository.transitions
.filter { step -> step.from == AOD || step.to == AOD }
@@ -201,11 +261,22 @@
repository.transitions.filter { step -> step.transitionState == TransitionState.FINISHED }
/** The destination state of the last [TransitionState.STARTED] transition. */
+ @SuppressLint("SharedFlowCreation")
val startedKeyguardState: SharedFlow<KeyguardState> =
startedKeyguardTransitionStep
.map { step -> step.to }
.shareIn(scope, SharingStarted.Eagerly, replay = 1)
+ val currentTransitionInfo: StateFlow<TransitionInfo> = repository.currentTransitionInfoInternal
+
+ /** The from state of the last [TransitionState.STARTED] transition. */
+ // TODO: is it performant to have several SharedFlows side by side instead of one?
+ @SuppressLint("SharedFlowCreation")
+ val startedKeyguardFromState: SharedFlow<KeyguardState> =
+ startedKeyguardTransitionStep
+ .map { step -> step.from }
+ .shareIn(scope, SharingStarted.Eagerly, replay = 1)
+
/** Which keyguard state to use when the device goes to sleep. */
val asleepKeyguardState: StateFlow<KeyguardState> =
keyguardRepository.isAodAvailable
@@ -243,6 +314,7 @@
* sufficient. However, if you're having issues with state *during* transitions started after
* one or more canceled transitions, you probably need to use [currentKeyguardState].
*/
+ @SuppressLint("SharedFlowCreation")
val finishedKeyguardState: SharedFlow<KeyguardState> =
finishedKeyguardTransitionStep
.map { step -> step.to }
@@ -344,32 +416,37 @@
val isInTransitionToAnyState = isInTransitionWhere({ true }, { true })
fun transitionStepsFromState(fromState: KeyguardState): Flow<TransitionStep> {
- return getOrCreateFlow(Edge(from = fromState, to = null))
+ return transition(Edge.create(from = fromState, to = null))
}
fun transitionStepsToState(toState: KeyguardState): Flow<TransitionStep> {
- return getOrCreateFlow(Edge(from = null, to = toState))
+ return transition(Edge.create(from = null, to = toState))
}
/**
* Called to start a transition that will ultimately dismiss the keyguard from the current
* state.
+ *
+ * This is called exclusively by sources that can authoritatively say we should be unlocked,
+ * including KeyguardSecurityContainerController and WindowManager.
*/
- fun startDismissKeyguardTransition() {
+ fun startDismissKeyguardTransition(reason: String = "") {
// TODO(b/336576536): Check if adaptation for scene framework is needed
if (SceneContainerFlag.isEnabled) return
- when (val startedState = startedKeyguardState.replayCache.last()) {
+ Log.d(TAG, "#startDismissKeyguardTransition(reason=$reason)")
+ when (val startedState = currentTransitionInfoInternal.value.to) {
LOCKSCREEN -> fromLockscreenTransitionInteractor.get().dismissKeyguard()
PRIMARY_BOUNCER -> fromPrimaryBouncerTransitionInteractor.get().dismissPrimaryBouncer()
ALTERNATE_BOUNCER ->
fromAlternateBouncerTransitionInteractor.get().dismissAlternateBouncer()
AOD -> fromAodTransitionInteractor.get().dismissAod()
DOZING -> fromDozingTransitionInteractor.get().dismissFromDozing()
- else ->
- Log.e(
- "KeyguardTransitionInteractor",
- "We don't know how to dismiss keyguard from state $startedState."
+ KeyguardState.GONE ->
+ Log.i(
+ TAG,
+ "Already transitioning to GONE; ignoring startDismissKeyguardTransition."
)
+ else -> Log.e(TAG, "We don't know how to dismiss keyguard from state $startedState.")
}
}
@@ -377,7 +454,7 @@
fun isInTransitionToState(
state: KeyguardState,
): Flow<Boolean> {
- return getOrCreateFlow(Edge(from = null, to = state))
+ return transition(Edge.create(from = null, to = state))
.mapLatest { it.transitionState.isTransitioning() }
.onStart { emit(false) }
.distinctUntilChanged()
@@ -386,12 +463,16 @@
/**
* Whether we're in a transition to and from the given [KeyguardState]s, but haven't yet
* completed it.
+ *
+ * Provide [edgeWithoutSceneContainer] when the edge is different from what it is without it. If
+ * the edges are equal before and after the flag it is sufficient to provide just [edge].
*/
- fun isInTransition(
- from: KeyguardState,
- to: KeyguardState,
- ): Flow<Boolean> {
- return getOrCreateFlow(Edge(from = from, to = to))
+ fun isInTransition(edge: Edge, edgeWithoutSceneContainer: Edge? = null): Flow<Boolean> {
+ return if (SceneContainerFlag.isEnabled) {
+ transition(edge)
+ } else {
+ transition(edgeWithoutSceneContainer ?: edge)
+ }
.mapLatest { it.transitionState.isTransitioning() }
.onStart { emit(false) }
.distinctUntilChanged()
@@ -403,7 +484,7 @@
fun isInTransitionFromState(
state: KeyguardState,
): Flow<Boolean> {
- return getOrCreateFlow(Edge(from = state, to = null))
+ return transition(Edge.create(from = state, to = null))
.mapLatest { it.transitionState.isTransitioning() }
.onStart { emit(false) }
.distinctUntilChanged()
@@ -454,7 +535,7 @@
* If you only care about a single state for both from and to, instead use the optimized
* [isInTransition].
*/
- fun isInTransitionWhere(
+ private fun isInTransitionWhere(
fromToStatePredicate: (KeyguardState, KeyguardState) -> Boolean
): Flow<Boolean> {
return repository.transitions
@@ -491,7 +572,23 @@
return startedKeyguardState.replayCache.last()
}
+ fun getStartedFromState(): KeyguardState {
+ return startedKeyguardFromState.replayCache.last()
+ }
+
fun getFinishedState(): KeyguardState {
return finishedKeyguardState.replayCache.last()
}
+
+ suspend fun startTransition(info: TransitionInfo) = repository.startTransition(info)
+
+ fun updateTransition(
+ transitionId: UUID,
+ @FloatRange(from = 0.0, to = 1.0) value: Float,
+ state: TransitionState
+ ) = repository.updateTransition(transitionId, value, state)
+
+ companion object {
+ private val TAG = KeyguardTransitionInteractor::class.simpleName
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/LightRevealScrimInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/LightRevealScrimInteractor.kt
index 2d944c6..9443570 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/LightRevealScrimInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/LightRevealScrimInteractor.kt
@@ -103,6 +103,7 @@
KeyguardState.LOCKSCREEN -> true
KeyguardState.GONE -> true
KeyguardState.OCCLUDED -> true
+ KeyguardState.UNDEFINED -> true
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/StartKeyguardTransitionModule.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/StartKeyguardTransitionModule.kt
index d95c38e..3c66186 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/StartKeyguardTransitionModule.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/StartKeyguardTransitionModule.kt
@@ -16,6 +16,7 @@
package com.android.systemui.keyguard.domain.interactor
import com.android.systemui.CoreStartable
+import com.android.systemui.keyguard.domain.interactor.scenetransition.LockscreenSceneTransitionInteractor
import dagger.Binds
import dagger.Module
import dagger.multibindings.ClassKey
@@ -31,6 +32,13 @@
abstract fun bind(impl: KeyguardTransitionCoreStartable): CoreStartable
@Binds
+ @IntoMap
+ @ClassKey(LockscreenSceneTransitionInteractor::class)
+ abstract fun bindLockscreenSceneTransitionInteractor(
+ impl: LockscreenSceneTransitionInteractor
+ ): CoreStartable
+
+ @Binds
@IntoSet
abstract fun fromPrimaryBouncer(
impl: FromPrimaryBouncerTransitionInteractor
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/TransitionInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/TransitionInteractor.kt
index b2a24ca..323ceef 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/TransitionInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/TransitionInteractor.kt
@@ -229,6 +229,7 @@
startTransitionTo(
toState = KeyguardState.OCCLUDED,
modeOnCanceled = TransitionModeOnCanceled.RESET,
+ ownerReason = "keyguardInteractor.onCameraLaunchDetected",
)
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/WindowManagerLockscreenVisibilityInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/WindowManagerLockscreenVisibilityInteractor.kt
index dc35e43..350527a 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/WindowManagerLockscreenVisibilityInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/WindowManagerLockscreenVisibilityInteractor.kt
@@ -14,10 +14,12 @@
* limitations under the License.
*/
+@file:OptIn(ExperimentalCoroutinesApi::class)
+
package com.android.systemui.keyguard.domain.interactor
-import com.android.compose.animation.scene.ObservableTransitionState
import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.deviceentry.domain.interactor.DeviceEntryInteractor
import com.android.systemui.keyguard.shared.model.BiometricUnlockMode
import com.android.systemui.keyguard.shared.model.KeyguardState
import com.android.systemui.keyguard.shared.model.TransitionState
@@ -25,8 +27,8 @@
import com.android.systemui.scene.shared.flag.SceneContainerFlag
import com.android.systemui.scene.shared.model.Scenes
import com.android.systemui.statusbar.notification.domain.interactor.NotificationLaunchAnimationInteractor
-import com.android.systemui.util.kotlin.pairwise
import com.android.systemui.util.kotlin.sample
+import dagger.Lazy
import javax.inject.Inject
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.flow.Flow
@@ -47,7 +49,8 @@
fromBouncerInteractor: FromPrimaryBouncerTransitionInteractor,
fromAlternateBouncerInteractor: FromAlternateBouncerTransitionInteractor,
notificationLaunchAnimationInteractor: NotificationLaunchAnimationInteractor,
- sceneInteractor: SceneInteractor,
+ sceneInteractor: Lazy<SceneInteractor>,
+ deviceEntryInteractor: Lazy<DeviceEntryInteractor>,
) {
private val defaultSurfaceBehindVisibility =
transitionInteractor.finishedKeyguardState.map(::isSurfaceVisible)
@@ -111,7 +114,7 @@
val usingKeyguardGoingAwayAnimation: Flow<Boolean> =
if (SceneContainerFlag.isEnabled) {
combine(
- sceneInteractor.transitionState,
+ sceneInteractor.get().transitionState,
surfaceBehindInteractor.isAnimatingSurface,
notificationLaunchAnimationInteractor.isLaunchAnimationRunning,
) { transition, isAnimatingSurface, isLaunchAnimationRunning ->
@@ -155,19 +158,7 @@
*/
val lockscreenVisibility: Flow<Boolean> =
if (SceneContainerFlag.isEnabled) {
- sceneInteractor.transitionState
- .pairwise(ObservableTransitionState.Idle(Scenes.Lockscreen))
- .map { (prevTransitionState, transitionState) ->
- val isReturningToGoneAfterCancellation =
- prevTransitionState.isTransitioning(from = Scenes.Gone) &&
- transitionState.isTransitioning(to = Scenes.Gone)
- val isNotOnGone =
- !transitionState.isTransitioning(from = Scenes.Gone) &&
- !transitionState.isIdle(Scenes.Gone)
-
- isNotOnGone && !isReturningToGoneAfterCancellation
- }
- .distinctUntilChanged()
+ deviceEntryInteractor.get().isDeviceEntered.map { !it }
} else {
transitionInteractor.currentKeyguardState
.sample(transitionInteractor.startedStepWithPrecedingStep, ::Pair)
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/scenetransition/LockscreenSceneTransitionInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/scenetransition/LockscreenSceneTransitionInteractor.kt
new file mode 100644
index 0000000..3baeb76
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/scenetransition/LockscreenSceneTransitionInteractor.kt
@@ -0,0 +1,231 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.keyguard.domain.interactor.scenetransition
+
+import com.android.compose.animation.scene.ObservableTransitionState
+import com.android.compose.animation.scene.SceneKey
+import com.android.systemui.CoreStartable
+import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.dagger.qualifiers.Application
+import com.android.systemui.keyguard.data.repository.LockscreenSceneTransitionRepository
+import com.android.systemui.keyguard.data.repository.LockscreenSceneTransitionRepository.Companion.DEFAULT_STATE
+import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor
+import com.android.systemui.keyguard.shared.model.KeyguardState
+import com.android.systemui.keyguard.shared.model.KeyguardState.UNDEFINED
+import com.android.systemui.keyguard.shared.model.TransitionInfo
+import com.android.systemui.keyguard.shared.model.TransitionModeOnCanceled
+import com.android.systemui.keyguard.shared.model.TransitionState.FINISHED
+import com.android.systemui.keyguard.shared.model.TransitionState.RUNNING
+import com.android.systemui.scene.domain.interactor.SceneInteractor
+import com.android.systemui.scene.shared.model.Scenes
+import com.android.systemui.util.kotlin.pairwise
+import java.util.UUID
+import javax.inject.Inject
+import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.Job
+import kotlinx.coroutines.launch
+
+/**
+ * This class listens to scene framework scene transitions and manages keyguard transition framework
+ * (KTF) states accordingly.
+ *
+ * There are a few rules:
+ * - When scene framework is on a scene outside of Lockscreen, then KTF is in state UNDEFINED
+ * - When scene framework is on Lockscreen, KTF is allowed to change its scenes freely
+ * - When scene framework is transitioning away from Lockscreen, then KTF transitions to UNDEFINED
+ * and shares its progress.
+ * - When scene framework is transitioning to Lockscreen, then KTF starts a transition to LOCKSCREEN
+ * but it is allowed to interrupt this transition and transition to other internal KTF states
+ *
+ * There are a few notable differences between SceneTransitionLayout (STL) and KTF that require
+ * special treatment when synchronizing both state machines.
+ * - STL does not emit cancelations as KTF does
+ * - Both STL and KTF require state continuity, though the rules from where starting the next
+ * transition is allowed is different on each side:
+ * - STL has a concept of "currentScene" which can be chosen to be either A or B in a A -> B
+ * transition. The currentScene determines which transition can be started next. In KTF the
+ * currentScene is always the `to` state. Which means transitions can only be started from B.
+ * This also holds true when A -> B was canceled: the next transition needs to start from B.
+ * - KTF can not settle back in its from scene, instead it needs to cancel and start a reversed
+ * transition.
+ */
+@SysUISingleton
+class LockscreenSceneTransitionInteractor
+@Inject
+constructor(
+ val transitionInteractor: KeyguardTransitionInteractor,
+ @Application private val applicationScope: CoroutineScope,
+ private val sceneInteractor: SceneInteractor,
+ private val repository: LockscreenSceneTransitionRepository,
+) : CoreStartable, SceneInteractor.OnSceneAboutToChangeListener {
+
+ private var currentTransitionId: UUID? = null
+ private var progressJob: Job? = null
+
+ override fun start() {
+ sceneInteractor.registerSceneStateProcessor(this)
+ listenForSceneTransitionProgress()
+ }
+
+ override fun onSceneAboutToChange(toScene: SceneKey, sceneState: Any?) {
+ if (toScene != Scenes.Lockscreen || sceneState == null) return
+ if (sceneState !is KeyguardState) {
+ throw IllegalArgumentException("Lockscreen sceneState needs to be a KeyguardState.")
+ }
+ repository.nextLockscreenTargetState.value = sceneState
+ }
+
+ private fun listenForSceneTransitionProgress() {
+ applicationScope.launch {
+ sceneInteractor.transitionState
+ .pairwise(ObservableTransitionState.Idle(Scenes.Lockscreen))
+ .collect { (prevTransition, transition) ->
+ when (transition) {
+ is ObservableTransitionState.Idle -> handleIdle(prevTransition, transition)
+ is ObservableTransitionState.Transition -> handleTransition(transition)
+ }
+ }
+ }
+ }
+
+ private suspend fun handleIdle(
+ prevTransition: ObservableTransitionState,
+ idle: ObservableTransitionState.Idle
+ ) {
+ if (currentTransitionId == null) return
+ if (prevTransition !is ObservableTransitionState.Transition) return
+
+ if (idle.currentScene == prevTransition.toScene) {
+ finishCurrentTransition()
+ } else {
+ val targetState =
+ if (idle.currentScene == Scenes.Lockscreen) {
+ transitionInteractor.getStartedFromState()
+ } else {
+ UNDEFINED
+ }
+ finishReversedTransitionTo(targetState)
+ }
+ }
+
+ private fun finishCurrentTransition() {
+ transitionInteractor.updateTransition(currentTransitionId!!, 1f, FINISHED)
+ resetTransitionData()
+ }
+
+ private suspend fun finishReversedTransitionTo(state: KeyguardState) {
+ val newTransition =
+ TransitionInfo(
+ ownerName = this::class.java.simpleName,
+ from = transitionInteractor.currentTransitionInfo.value.to,
+ to = state,
+ animator = null,
+ modeOnCanceled = TransitionModeOnCanceled.REVERSE
+ )
+ currentTransitionId = transitionInteractor.startTransition(newTransition)
+ transitionInteractor.updateTransition(currentTransitionId!!, 1f, FINISHED)
+ resetTransitionData()
+ }
+
+ private fun resetTransitionData() {
+ progressJob?.cancel()
+ progressJob = null
+ currentTransitionId = null
+ }
+
+ private suspend fun handleTransition(transition: ObservableTransitionState.Transition) {
+ if (transition.fromScene == Scenes.Lockscreen) {
+ if (currentTransitionId != null) {
+ val currentToState = transitionInteractor.currentTransitionInfo.value.to
+ if (currentToState == UNDEFINED) {
+ transitionKtfTo(transitionInteractor.getStartedFromState())
+ }
+ }
+ startTransitionFromLockscreen()
+ collectProgress(transition)
+ } else if (transition.toScene == Scenes.Lockscreen) {
+ if (currentTransitionId != null) {
+ transitionKtfTo(UNDEFINED)
+ }
+ startTransitionToLockscreen()
+ collectProgress(transition)
+ } else {
+ transitionKtfTo(UNDEFINED)
+ }
+ }
+
+ private suspend fun transitionKtfTo(state: KeyguardState) {
+ // TODO(b/330311871): This is based on a sharedFlow and thus might not be up-to-date and
+ // cause a race condition. (There is no known scenario that is currently affected.)
+ val currentTransition = transitionInteractor.transitionState.value
+ if (currentTransition.isFinishedIn(state)) {
+ // This is already the state we want to be in
+ resetTransitionData()
+ } else if (currentTransition.isTransitioning(to = state)) {
+ finishCurrentTransition()
+ } else {
+ finishReversedTransitionTo(state)
+ }
+ }
+
+ private fun collectProgress(transition: ObservableTransitionState.Transition) {
+ progressJob?.cancel()
+ progressJob = applicationScope.launch { transition.progress.collect { updateProgress(it) } }
+ }
+
+ private suspend fun startTransitionToLockscreen() {
+ val newTransition =
+ TransitionInfo(
+ ownerName = this::class.java.simpleName,
+ from = UNDEFINED,
+ to = repository.nextLockscreenTargetState.value,
+ animator = null,
+ modeOnCanceled = TransitionModeOnCanceled.RESET
+ )
+ repository.nextLockscreenTargetState.value = DEFAULT_STATE
+ startTransition(newTransition)
+ }
+
+ private suspend fun startTransitionFromLockscreen() {
+ val currentState = transitionInteractor.currentTransitionInfo.value.to
+ val newTransition =
+ TransitionInfo(
+ ownerName = this::class.java.simpleName,
+ from = currentState,
+ to = UNDEFINED,
+ animator = null,
+ modeOnCanceled = TransitionModeOnCanceled.RESET
+ )
+ startTransition(newTransition)
+ }
+
+ private suspend fun startTransition(transitionInfo: TransitionInfo) {
+ if (currentTransitionId != null) {
+ resetTransitionData()
+ }
+ currentTransitionId = transitionInteractor.startTransition(transitionInfo)
+ }
+
+ private fun updateProgress(progress: Float) {
+ if (currentTransitionId == null) return
+ transitionInteractor.updateTransition(
+ currentTransitionId!!,
+ progress.coerceIn(0f, 1f),
+ RUNNING
+ )
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/shared/model/Edge.kt b/packages/SystemUI/src/com/android/systemui/keyguard/shared/model/Edge.kt
index a0f9be6..4f516f5 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/shared/model/Edge.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/shared/model/Edge.kt
@@ -15,8 +15,98 @@
*/
package com.android.systemui.keyguard.shared.model
-/** FROM -> TO keyguard transition. null values are allowed to signify FROM -> *, or * -> TO */
-data class Edge(
- val from: KeyguardState?,
- val to: KeyguardState?,
-)
+import android.util.Log
+import com.android.compose.animation.scene.SceneKey
+import com.android.systemui.keyguard.shared.model.KeyguardState.UNDEFINED
+import com.android.systemui.scene.shared.flag.SceneContainerFlag
+
+/**
+ * Represents an edge either between two Keyguard Transition Framework states (KTF) or a KTF state
+ * and a scene container scene. Passing [null] in either [from] or [to] indicates a wildcard.
+ *
+ * Wildcards are not allowed for transitions involving a scene. Use [sceneInteractor] directly
+ * instead. Reason: [TransitionStep]s are not emitted for every edge leading into/out of a scene.
+ * For example: Lockscreen -> Gone would be emitted as LOCKSCREEN -> UNDEFINED but Bouncer -> Gone
+ * would not emit anything.
+ */
+sealed class Edge {
+
+ fun verifyValidKeyguardStates() {
+ when (this) {
+ is StateToState -> verifyValidKeyguardStates(from, to)
+ is SceneToState -> verifyValidKeyguardStates(null, to)
+ is StateToScene -> verifyValidKeyguardStates(from, null)
+ }
+ }
+
+ private fun verifyValidKeyguardStates(from: KeyguardState?, to: KeyguardState?) {
+ val mappedFrom = from?.mapToSceneContainerState()
+ val mappedTo = to?.mapToSceneContainerState()
+
+ val fromChanged = from != mappedFrom
+ val toChanged = to != mappedTo
+
+ if (SceneContainerFlag.isEnabled) {
+ if (fromChanged && toChanged) {
+ // TODO:(b/330311871) As we come close to having all current edges converted these
+ // error messages can be converted to throw such that future developers fail early
+ // when they introduce invalid edges.
+ Log.e(
+ TAG,
+ """
+ The edge ${from?.name} => ${to?.name} was automatically converted to
+ ${mappedFrom?.name} => ${mappedTo?.name} but does not exist anymore in KTF.
+ Please remove or port this edge to scene container."""
+ .trimIndent(),
+ )
+ } else if ((fromChanged && to == null) || (toChanged && from == null)) {
+ Log.e(
+ TAG,
+ """
+ The edge ${from?.name} => ${to?.name} was automatically converted to
+ ${mappedFrom?.name} => ${mappedTo?.name}. Wildcards are not allowed together
+ with UNDEFINED because it will only be tracking edges leading in and out of
+ the Lockscreen scene but miss others. Please remove or port this edge."""
+ .trimIndent(),
+ Exception()
+ )
+ } else if (fromChanged || toChanged) {
+ Log.w(
+ TAG,
+ """
+ The edge ${from?.name} => ${to?.name} was automatically converted to
+ ${mappedFrom?.name} => ${mappedTo?.name} it probably exists but needs explicit
+ conversion. Please remove or port this edge to scene container."""
+ .trimIndent(),
+ )
+ }
+ } else {
+ if (from == UNDEFINED || to == UNDEFINED) {
+ Log.e(
+ TAG,
+ "UNDEFINED should not be used when scene container is disabled",
+ )
+ }
+ }
+ }
+
+ data class StateToState(val from: KeyguardState?, val to: KeyguardState?) : Edge() {
+ init {
+ check(!(from == null && to == null)) { "to and from can't both be null" }
+ }
+ }
+
+ data class StateToScene(val from: KeyguardState, val to: SceneKey) : Edge()
+
+ data class SceneToState(val from: SceneKey, val to: KeyguardState) : Edge()
+
+ companion object {
+ private const val TAG = "Edge"
+
+ fun create(from: KeyguardState? = null, to: KeyguardState? = null) = StateToState(from, to)
+
+ fun create(from: KeyguardState, to: SceneKey) = StateToScene(from, to)
+
+ fun create(from: SceneKey, to: KeyguardState) = SceneToState(from, to)
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/shared/model/KeyguardState.kt b/packages/SystemUI/src/com/android/systemui/keyguard/shared/model/KeyguardState.kt
index 7d05539..6a2bb5f 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/shared/model/KeyguardState.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/shared/model/KeyguardState.kt
@@ -15,9 +15,12 @@
*/
package com.android.systemui.keyguard.shared.model
+import com.android.compose.animation.scene.SceneKey
+import com.android.systemui.scene.shared.model.Scenes
+
/** List of all possible states to transition to/from */
enum class KeyguardState {
- /*
+ /**
* The display is completely off, as well as any sensors that would trigger the device to wake
* up.
*/
@@ -29,13 +32,13 @@
* notifications is enabled, allowing the device to quickly wake up.
*/
DOZING,
- /*
+ /**
* A device state after the device times out, which can be from both LOCKSCREEN or GONE states.
* DOZING is an example of special version of this state. Dreams may be implemented by third
* parties to present their own UI over keyguard, like a screensaver.
*/
DREAMING,
- /*
+ /**
* A device state after the device times out, which can be from both LOCKSCREEN or GONE states.
* It is a special version of DREAMING state but not DOZING. The active dream will be windowless
* and hosted in the lockscreen.
@@ -47,17 +50,17 @@
* low-power mode without a UI, then it is DOZING.
*/
AOD,
- /*
+ /**
* The security screen prompt containing UI to prompt the user to use a biometric credential
* (ie: fingerprint). When supported, this may show before showing the primary bouncer.
*/
ALTERNATE_BOUNCER,
- /*
+ /**
* The security screen prompt UI, containing PIN, Password, Pattern for the user to verify their
* credentials.
*/
PRIMARY_BOUNCER,
- /*
+ /**
* Device is actively displaying keyguard UI and is not in low-power mode. Device may be
* unlocked if SWIPE security method is used, or if face lockscreen bypass is false.
*/
@@ -68,17 +71,56 @@
* or dream, as well as swipe down for the notifications and up for the bouncer.
*/
GLANCEABLE_HUB,
- /*
- * Keyguard is no longer visible. In most cases the user has just authenticated and keyguard
- * is being removed, but there are other cases where the user is swiping away keyguard, such as
+ /**
+ * Keyguard is no longer visible. In most cases the user has just authenticated and keyguard is
+ * being removed, but there are other cases where the user is swiping away keyguard, such as
* with SWIPE security method or face unlock without bypass.
*/
GONE,
- /*
- * An activity is displaying over the keyguard.
+ /**
+ * Only used in scene framework. This means we are currently on any scene framework scene that
+ * is not Lockscreen. Transitions to and from UNDEFINED are always bound to the
+ * [SceneTransitionLayout] scene transition that either transitions to or from the Lockscreen
+ * scene. These transitions are automatically handled by [LockscreenSceneTransitionInteractor].
*/
+ UNDEFINED,
+ /** An activity is displaying over the keyguard. */
OCCLUDED;
+ fun mapToSceneContainerState(): KeyguardState {
+ return when (this) {
+ OFF,
+ DOZING,
+ DREAMING,
+ DREAMING_LOCKSCREEN_HOSTED,
+ AOD,
+ ALTERNATE_BOUNCER,
+ OCCLUDED,
+ LOCKSCREEN -> this
+ GLANCEABLE_HUB,
+ PRIMARY_BOUNCER,
+ GONE,
+ UNDEFINED -> UNDEFINED
+ }
+ }
+
+ fun mapToSceneContainerScene(): SceneKey? {
+ return when (this) {
+ OFF,
+ DOZING,
+ DREAMING,
+ DREAMING_LOCKSCREEN_HOSTED,
+ AOD,
+ ALTERNATE_BOUNCER,
+ OCCLUDED,
+ LOCKSCREEN -> Scenes.Lockscreen
+ GLANCEABLE_HUB -> Scenes.Communal
+ PRIMARY_BOUNCER -> Scenes.Bouncer
+ GONE -> Scenes.Gone
+ UNDEFINED -> null
+ }
+ }
+
companion object {
/** Whether the lockscreen is visible when we're FINISHED in the given state. */
@@ -109,6 +151,7 @@
LOCKSCREEN -> true
GONE -> true
OCCLUDED -> true
+ UNDEFINED -> true
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/shared/model/TransitionStep.kt b/packages/SystemUI/src/com/android/systemui/keyguard/shared/model/TransitionStep.kt
index 0fa6f4f..2b4c4af 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/shared/model/TransitionStep.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/shared/model/TransitionStep.kt
@@ -30,4 +30,12 @@
value: Float,
transitionState: TransitionState,
) : this(info.from, info.to, value, transitionState, info.ownerName)
+
+ fun isTransitioning(from: KeyguardState? = null, to: KeyguardState? = null): Boolean {
+ return (from == null || this.from == from) && (to == null || this.to == to)
+ }
+
+ fun isFinishedIn(state: KeyguardState): Boolean {
+ return to == state && transitionState == TransitionState.FINISHED
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/KeyguardTransitionAnimationFlow.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/KeyguardTransitionAnimationFlow.kt
index 735b109..23aa21c 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/KeyguardTransitionAnimationFlow.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/KeyguardTransitionAnimationFlow.kt
@@ -28,6 +28,7 @@
import com.android.systemui.keyguard.shared.model.TransitionState.RUNNING
import com.android.systemui.keyguard.shared.model.TransitionState.STARTED
import com.android.systemui.keyguard.shared.model.TransitionStep
+import com.android.systemui.scene.shared.flag.SceneContainerFlag
import javax.inject.Inject
import kotlin.math.max
import kotlin.math.min
@@ -52,20 +53,20 @@
/** Invoke once per transition between FROM->TO states to get access to a shared flow. */
fun setup(
duration: Duration,
- from: KeyguardState?,
- to: KeyguardState?,
+ edge: Edge,
): FlowBuilder {
- if (from == null && to == null) {
- throw IllegalArgumentException("from and to are both null")
- }
-
- return FlowBuilder(duration, Edge(from, to))
+ return FlowBuilder(duration, edge)
}
inner class FlowBuilder(
private val transitionDuration: Duration,
private val edge: Edge,
) {
+ fun setupWithoutSceneContainer(edge: Edge.StateToState): FlowBuilder {
+ if (SceneContainerFlag.isEnabled) return this
+ return setup(this.transitionDuration, edge)
+ }
+
/**
* Transitions will occur over a [transitionDuration] with [TransitionStep]s being emitted
* in the range of [0, 1]. View animations should begin and end within a subset of this
@@ -117,7 +118,7 @@
if (!duration.isPositive()) {
throw IllegalArgumentException("duration must be a positive number: $duration")
}
- if ((startTime + duration).compareTo(transitionDuration) > 0) {
+ if ((startTime + duration) > transitionDuration) {
throw IllegalArgumentException(
"startTime($startTime) + duration($duration) must be" +
" <= transitionDuration($transitionDuration)"
@@ -153,7 +154,7 @@
}
return transitionInteractor
- .getOrCreateFlow(edge)
+ .transition(edge)
.map { step ->
StateToValue(
from = step.from,
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardClockViewBinder.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardClockViewBinder.kt
index c846cbe..f2821a0 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardClockViewBinder.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardClockViewBinder.kt
@@ -37,7 +37,6 @@
import com.android.systemui.lifecycle.repeatWhenAttached
import com.android.systemui.plugins.clocks.AodClockBurnInModel
import com.android.systemui.plugins.clocks.ClockController
-import kotlinx.coroutines.flow.combine
import kotlinx.coroutines.launch
object KeyguardClockViewBinder {
@@ -113,24 +112,17 @@
launch {
if (!MigrateClocksToBlueprint.isEnabled) return@launch
- combine(
- rootViewModel.translationX,
- rootViewModel.translationY,
- rootViewModel.scale,
- ::Triple
- )
- .collect { (translationX, translationY, scale) ->
- viewModel.currentClock.value
- ?.largeClock
- ?.layout
- ?.applyAodBurnIn(
- AodClockBurnInModel(
- translationX = translationX.value!!,
- translationY = translationY,
- scale = scale.scale
- )
+ rootViewModel.burnInModel.collect { burnInModel ->
+ viewModel.currentClock.value?.let {
+ it.largeClock.layout.applyAodBurnIn(
+ AodClockBurnInModel(
+ translationX = burnInModel.translationX.toFloat(),
+ translationY = burnInModel.translationY.toFloat(),
+ scale = burnInModel.scale
)
+ )
}
+ }
}
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/DefaultDeviceEntrySection.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/DefaultDeviceEntrySection.kt
index 29041d1..0b8376a 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/DefaultDeviceEntrySection.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/DefaultDeviceEntrySection.kt
@@ -21,6 +21,7 @@
import android.graphics.Point
import android.graphics.Rect
import android.util.DisplayMetrics
+import android.util.Log
import android.view.View
import android.view.WindowManager
import androidx.annotation.VisibleForTesting
@@ -116,6 +117,10 @@
override fun applyConstraints(constraintSet: ConstraintSet) {
val isUdfpsSupported =
if (DeviceEntryUdfpsRefactor.isEnabled) {
+ Log.d(
+ "DefaultDeviceEntrySection",
+ "isUdfpsSupported=${deviceEntryIconViewModel.get().isUdfpsSupported.value}"
+ )
deviceEntryIconViewModel.get().isUdfpsSupported.value
} else {
authController.isUdfpsSupported
@@ -138,8 +143,24 @@
val iconRadiusPx = (defaultDensity * 36).toInt()
if (isUdfpsSupported) {
- authController.udfpsLocation?.let { udfpsLocation ->
- centerIcon(udfpsLocation, authController.udfpsRadius, constraintSet)
+ if (DeviceEntryUdfpsRefactor.isEnabled) {
+ deviceEntryIconViewModel.get().udfpsLocation.value?.let { udfpsLocation ->
+ Log.d(
+ "DeviceEntrySection",
+ "udfpsLocation=$udfpsLocation" +
+ " unusedAuthController=${authController.udfpsLocation}"
+ )
+ centerIcon(
+ Point(udfpsLocation.centerX.toInt(), udfpsLocation.centerY.toInt()),
+ udfpsLocation.radius,
+ constraintSet
+ )
+ }
+ } else {
+ authController.udfpsLocation?.let { udfpsLocation ->
+ Log.d("DeviceEntrySection", "udfpsLocation=$udfpsLocation")
+ centerIcon(udfpsLocation, authController.udfpsRadius, constraintSet)
+ }
}
} else {
centerIcon(
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/transitions/ClockSizeTransition.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/transitions/ClockSizeTransition.kt
index 218967c..7c745bc 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/transitions/ClockSizeTransition.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/transitions/ClockSizeTransition.kt
@@ -57,9 +57,9 @@
addTransition(SmartspaceMoveTransition(config, clockViewModel))
}
- open class VisibilityBoundsTransition() : Transition() {
- var captureSmartspace: Boolean = false
-
+ abstract class VisibilityBoundsTransition() : Transition() {
+ abstract val captureSmartspace: Boolean
+ protected val TAG = this::class.simpleName!!
override fun captureEndValues(transition: TransitionValues) = captureValues(transition)
override fun captureStartValues(transition: TransitionValues) = captureValues(transition)
override fun getTransitionProperties(): Array<String> = TRANSITION_PROPERTIES
@@ -76,7 +76,7 @@
parent.findViewById<View>(sharedR.id.bc_smartspace_view)
?: parent.findViewById<View>(R.id.keyguard_slice_view)
if (targetSSView == null) {
- Log.e(TAG, "Failed to find smartspace equivalent target for animation")
+ Log.e(TAG, "Failed to find smartspace equivalent target under $parent")
return
}
transition.values[SMARTSPACE_BOUNDS] = targetSSView.getRect()
@@ -109,14 +109,12 @@
var fromIsVis = fromVis == View.VISIBLE
var fromAlpha = startValues.values[PROP_ALPHA] as Float
val fromBounds = startValues.values[PROP_BOUNDS] as Rect
- val fromSSBounds =
- if (captureSmartspace) startValues.values[SMARTSPACE_BOUNDS] as Rect else null
+ val fromSSBounds = startValues.values[SMARTSPACE_BOUNDS] as Rect?
val toView = endValues.view
val toVis = endValues.values[PROP_VISIBILITY] as Int
val toBounds = endValues.values[PROP_BOUNDS] as Rect
- val toSSBounds =
- if (captureSmartspace) endValues.values[SMARTSPACE_BOUNDS] as Rect else null
+ val toSSBounds = endValues.values[SMARTSPACE_BOUNDS] as Rect?
val toIsVis = toVis == View.VISIBLE
val toAlpha = if (toIsVis) 1f else 0f
@@ -221,9 +219,6 @@
private const val SMARTSPACE_BOUNDS = "ClockSizeTransition:SSBounds"
private val TRANSITION_PROPERTIES =
arrayOf(PROP_VISIBILITY, PROP_ALPHA, PROP_BOUNDS, SMARTSPACE_BOUNDS)
-
- private val DEBUG = false
- private val TAG = VisibilityBoundsTransition::class.simpleName!!
}
}
@@ -232,18 +227,24 @@
val viewModel: KeyguardClockViewModel,
val smartspaceViewModel: KeyguardSmartspaceViewModel,
) : VisibilityBoundsTransition() {
+ override val captureSmartspace = !viewModel.isLargeClockVisible.value
+
init {
duration = CLOCK_IN_MILLIS
startDelay = CLOCK_IN_START_DELAY_MILLIS
interpolator = CLOCK_IN_INTERPOLATOR
- captureSmartspace =
- !viewModel.isLargeClockVisible.value && smartspaceViewModel.isSmartspaceEnabled
if (viewModel.isLargeClockVisible.value) {
viewModel.currentClock.value?.let {
+ if (DEBUG) Log.i(TAG, "Large Clock In: ${it.largeClock.layout.views}")
it.largeClock.layout.views.forEach { addTarget(it) }
}
+ ?: run {
+ Log.e(TAG, "No large clock set, falling back")
+ addTarget(R.id.lockscreen_clock_view_large)
+ }
} else {
+ if (DEBUG) Log.i(TAG, "Small Clock In")
addTarget(R.id.lockscreen_clock_view)
}
}
@@ -282,7 +283,6 @@
val CLOCK_IN_INTERPOLATOR = Interpolators.LINEAR_OUT_SLOW_IN
const val SMALL_CLOCK_IN_MOVE_SCALE =
CLOCK_IN_MILLIS / SmartspaceMoveTransition.STATUS_AREA_MOVE_DOWN_MILLIS.toFloat()
- private val TAG = ClockFaceInTransition::class.simpleName!!
}
}
@@ -291,18 +291,24 @@
val viewModel: KeyguardClockViewModel,
val smartspaceViewModel: KeyguardSmartspaceViewModel,
) : VisibilityBoundsTransition() {
+ override val captureSmartspace = viewModel.isLargeClockVisible.value
+
init {
duration = CLOCK_OUT_MILLIS
interpolator = CLOCK_OUT_INTERPOLATOR
- captureSmartspace =
- viewModel.isLargeClockVisible.value && smartspaceViewModel.isSmartspaceEnabled
if (viewModel.isLargeClockVisible.value) {
+ if (DEBUG) Log.i(TAG, "Small Clock Out")
addTarget(R.id.lockscreen_clock_view)
} else {
viewModel.currentClock.value?.let {
+ if (DEBUG) Log.i(TAG, "Large Clock Out: ${it.largeClock.layout.views}")
it.largeClock.layout.views.forEach { addTarget(it) }
}
+ ?: run {
+ Log.e(TAG, "No large clock set, falling back")
+ addTarget(R.id.lockscreen_clock_view_large)
+ }
}
}
@@ -339,7 +345,6 @@
val CLOCK_OUT_INTERPOLATOR = Interpolators.LINEAR
const val SMALL_CLOCK_OUT_MOVE_SCALE =
CLOCK_OUT_MILLIS / SmartspaceMoveTransition.STATUS_AREA_MOVE_UP_MILLIS.toFloat()
- private val TAG = ClockFaceOutTransition::class.simpleName!!
}
}
@@ -348,6 +353,8 @@
val config: IntraBlueprintTransition.Config,
viewModel: KeyguardClockViewModel,
) : VisibilityBoundsTransition() {
+ override val captureSmartspace = false
+
init {
duration =
if (viewModel.isLargeClockVisible.value) STATUS_AREA_MOVE_UP_MILLIS
@@ -367,4 +374,8 @@
const val STATUS_AREA_MOVE_DOWN_MILLIS = 467L
}
}
+
+ companion object {
+ val DEBUG = true
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/AlternateBouncerToAodTransitionViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/AlternateBouncerToAodTransitionViewModel.kt
index 4fd92d7..9da11ce 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/AlternateBouncerToAodTransitionViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/AlternateBouncerToAodTransitionViewModel.kt
@@ -19,7 +19,9 @@
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.deviceentry.domain.interactor.DeviceEntryUdfpsInteractor
import com.android.systemui.keyguard.domain.interactor.FromAlternateBouncerTransitionInteractor
-import com.android.systemui.keyguard.shared.model.KeyguardState
+import com.android.systemui.keyguard.shared.model.Edge
+import com.android.systemui.keyguard.shared.model.KeyguardState.ALTERNATE_BOUNCER
+import com.android.systemui.keyguard.shared.model.KeyguardState.AOD
import com.android.systemui.keyguard.ui.KeyguardTransitionAnimationFlow
import com.android.systemui.keyguard.ui.transitions.DeviceEntryIconTransition
import javax.inject.Inject
@@ -42,8 +44,7 @@
private val transitionAnimation =
animationFlow.setup(
duration = FromAlternateBouncerTransitionInteractor.TO_AOD_DURATION,
- from = KeyguardState.ALTERNATE_BOUNCER,
- to = KeyguardState.AOD,
+ edge = Edge.create(from = ALTERNATE_BOUNCER, to = AOD),
)
val deviceEntryBackgroundViewAlpha: Flow<Float> =
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/AlternateBouncerToDozingTransitionViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/AlternateBouncerToDozingTransitionViewModel.kt
index 9649af73..55a48b6 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/AlternateBouncerToDozingTransitionViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/AlternateBouncerToDozingTransitionViewModel.kt
@@ -19,7 +19,9 @@
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.deviceentry.domain.interactor.DeviceEntryUdfpsInteractor
import com.android.systemui.keyguard.domain.interactor.FromAlternateBouncerTransitionInteractor
-import com.android.systemui.keyguard.shared.model.KeyguardState
+import com.android.systemui.keyguard.shared.model.Edge
+import com.android.systemui.keyguard.shared.model.KeyguardState.ALTERNATE_BOUNCER
+import com.android.systemui.keyguard.shared.model.KeyguardState.DOZING
import com.android.systemui.keyguard.ui.KeyguardTransitionAnimationFlow
import com.android.systemui.keyguard.ui.transitions.DeviceEntryIconTransition
import javax.inject.Inject
@@ -42,8 +44,7 @@
private val transitionAnimation =
animationFlow.setup(
duration = FromAlternateBouncerTransitionInteractor.TO_DOZING_DURATION,
- from = KeyguardState.ALTERNATE_BOUNCER,
- to = KeyguardState.DOZING,
+ edge = Edge.create(from = ALTERNATE_BOUNCER, to = DOZING),
)
val deviceEntryBackgroundViewAlpha: Flow<Float> =
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/AlternateBouncerToGoneTransitionViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/AlternateBouncerToGoneTransitionViewModel.kt
index 8c6be98..bb4fb79 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/AlternateBouncerToGoneTransitionViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/AlternateBouncerToGoneTransitionViewModel.kt
@@ -19,11 +19,13 @@
import android.util.MathUtils
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.keyguard.domain.interactor.FromAlternateBouncerTransitionInteractor.Companion.TO_GONE_DURATION
-import com.android.systemui.keyguard.shared.model.KeyguardState
+import com.android.systemui.keyguard.shared.model.Edge
import com.android.systemui.keyguard.shared.model.KeyguardState.ALTERNATE_BOUNCER
+import com.android.systemui.keyguard.shared.model.KeyguardState.GONE
import com.android.systemui.keyguard.shared.model.ScrimAlpha
import com.android.systemui.keyguard.ui.KeyguardTransitionAnimationFlow
import com.android.systemui.keyguard.ui.transitions.DeviceEntryIconTransition
+import com.android.systemui.scene.shared.model.Scenes
import com.android.systemui.statusbar.SysuiStatusBarStateController
import javax.inject.Inject
import kotlin.time.Duration.Companion.milliseconds
@@ -44,11 +46,14 @@
private val statusBarStateController: SysuiStatusBarStateController,
) : DeviceEntryIconTransition {
private val transitionAnimation =
- animationFlow.setup(
- duration = TO_GONE_DURATION,
- from = ALTERNATE_BOUNCER,
- to = KeyguardState.GONE,
- )
+ animationFlow
+ .setup(
+ duration = TO_GONE_DURATION,
+ edge = Edge.create(from = ALTERNATE_BOUNCER, to = Scenes.Gone),
+ )
+ .setupWithoutSceneContainer(
+ edge = Edge.create(from = ALTERNATE_BOUNCER, to = GONE),
+ )
fun lockscreenAlpha(viewState: ViewStateAccessor): Flow<Float> {
var startAlpha = 1f
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/AlternateBouncerToOccludedTransitionViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/AlternateBouncerToOccludedTransitionViewModel.kt
index 27febd3..3f2ef29 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/AlternateBouncerToOccludedTransitionViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/AlternateBouncerToOccludedTransitionViewModel.kt
@@ -18,8 +18,9 @@
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.keyguard.domain.interactor.FromAlternateBouncerTransitionInteractor.Companion.TO_OCCLUDED_DURATION
-import com.android.systemui.keyguard.shared.model.KeyguardState
+import com.android.systemui.keyguard.shared.model.Edge
import com.android.systemui.keyguard.shared.model.KeyguardState.ALTERNATE_BOUNCER
+import com.android.systemui.keyguard.shared.model.KeyguardState.OCCLUDED
import com.android.systemui.keyguard.ui.KeyguardTransitionAnimationFlow
import com.android.systemui.keyguard.ui.transitions.DeviceEntryIconTransition
import javax.inject.Inject
@@ -40,8 +41,7 @@
private val transitionAnimation =
animationFlow.setup(
duration = TO_OCCLUDED_DURATION,
- from = ALTERNATE_BOUNCER,
- to = KeyguardState.OCCLUDED,
+ edge = Edge.create(from = ALTERNATE_BOUNCER, to = OCCLUDED),
)
override val deviceEntryParentViewAlpha: Flow<Float> =
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/AlternateBouncerToPrimaryBouncerTransitionViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/AlternateBouncerToPrimaryBouncerTransitionViewModel.kt
index 7592881..f0bccac 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/AlternateBouncerToPrimaryBouncerTransitionViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/AlternateBouncerToPrimaryBouncerTransitionViewModel.kt
@@ -18,9 +18,12 @@
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.keyguard.domain.interactor.FromAlternateBouncerTransitionInteractor
-import com.android.systemui.keyguard.shared.model.KeyguardState
+import com.android.systemui.keyguard.shared.model.Edge
+import com.android.systemui.keyguard.shared.model.KeyguardState.ALTERNATE_BOUNCER
+import com.android.systemui.keyguard.shared.model.KeyguardState.PRIMARY_BOUNCER
import com.android.systemui.keyguard.ui.KeyguardTransitionAnimationFlow
import com.android.systemui.keyguard.ui.transitions.DeviceEntryIconTransition
+import com.android.systemui.scene.shared.model.Scenes
import javax.inject.Inject
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.flow.Flow
@@ -37,11 +40,14 @@
animationFlow: KeyguardTransitionAnimationFlow,
) : DeviceEntryIconTransition {
private val transitionAnimation =
- animationFlow.setup(
- duration = FromAlternateBouncerTransitionInteractor.TO_PRIMARY_BOUNCER_DURATION,
- from = KeyguardState.ALTERNATE_BOUNCER,
- to = KeyguardState.PRIMARY_BOUNCER,
- )
+ animationFlow
+ .setup(
+ duration = FromAlternateBouncerTransitionInteractor.TO_PRIMARY_BOUNCER_DURATION,
+ edge = Edge.create(from = ALTERNATE_BOUNCER, to = Scenes.Bouncer),
+ )
+ .setupWithoutSceneContainer(
+ edge = Edge.create(from = ALTERNATE_BOUNCER, to = PRIMARY_BOUNCER),
+ )
override val deviceEntryParentViewAlpha: Flow<Float> =
transitionAnimation.immediatelyTransitionTo(0f)
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/AlternateBouncerWindowViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/AlternateBouncerWindowViewModel.kt
index 5cf100e..4128c52 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/AlternateBouncerWindowViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/AlternateBouncerWindowViewModel.kt
@@ -34,8 +34,8 @@
alternateBouncerInteractor: AlternateBouncerInteractor,
keyguardTransitionInteractor: KeyguardTransitionInteractor,
) {
- private val deviceSupportsAlternateBouncer: Flow<Boolean> =
- alternateBouncerInteractor.alternateBouncerSupported
+ val canShowAlternateBouncer: Flow<Boolean> = alternateBouncerInteractor.canShowAlternateBouncer
+
private val isTransitioningToOrFromOrShowingAlternateBouncer: Flow<Boolean> =
keyguardTransitionInteractor
.transitionValue(KeyguardState.ALTERNATE_BOUNCER)
@@ -43,8 +43,8 @@
.distinctUntilChanged()
val alternateBouncerWindowRequired: Flow<Boolean> =
- deviceSupportsAlternateBouncer.flatMapLatest { deviceSupportsAlternateBouncer ->
- if (deviceSupportsAlternateBouncer) {
+ canShowAlternateBouncer.flatMapLatest { canShowAlternateBouncer ->
+ if (canShowAlternateBouncer) {
isTransitioningToOrFromOrShowingAlternateBouncer
} else {
flowOf(false)
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/AodToGoneTransitionViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/AodToGoneTransitionViewModel.kt
index adc090d..8e8b09d 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/AodToGoneTransitionViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/AodToGoneTransitionViewModel.kt
@@ -19,9 +19,12 @@
import android.util.MathUtils
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.keyguard.domain.interactor.FromAodTransitionInteractor
-import com.android.systemui.keyguard.shared.model.KeyguardState
+import com.android.systemui.keyguard.shared.model.Edge
+import com.android.systemui.keyguard.shared.model.KeyguardState.AOD
+import com.android.systemui.keyguard.shared.model.KeyguardState.GONE
import com.android.systemui.keyguard.ui.KeyguardTransitionAnimationFlow
import com.android.systemui.keyguard.ui.transitions.DeviceEntryIconTransition
+import com.android.systemui.scene.shared.model.Scenes
import javax.inject.Inject
import kotlin.time.Duration.Companion.milliseconds
import kotlinx.coroutines.ExperimentalCoroutinesApi
@@ -37,11 +40,14 @@
) : DeviceEntryIconTransition {
private val transitionAnimation =
- animationFlow.setup(
- duration = FromAodTransitionInteractor.TO_GONE_DURATION,
- from = KeyguardState.AOD,
- to = KeyguardState.GONE,
- )
+ animationFlow
+ .setup(
+ duration = FromAodTransitionInteractor.TO_GONE_DURATION,
+ edge = Edge.create(from = AOD, to = Scenes.Gone),
+ )
+ .setupWithoutSceneContainer(
+ edge = Edge.create(from = AOD, to = GONE),
+ )
/**
* AOD -> GONE should fade out the lockscreen contents. This transition plays both during wake
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/AodToLockscreenTransitionViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/AodToLockscreenTransitionViewModel.kt
index cbbb820..b267ecb 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/AodToLockscreenTransitionViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/AodToLockscreenTransitionViewModel.kt
@@ -19,9 +19,10 @@
import android.util.MathUtils
import com.android.app.animation.Interpolators.FAST_OUT_SLOW_IN
import com.android.systemui.dagger.SysUISingleton
-import com.android.systemui.deviceentry.domain.interactor.DeviceEntryUdfpsInteractor
import com.android.systemui.keyguard.domain.interactor.FromAodTransitionInteractor.Companion.TO_LOCKSCREEN_DURATION
-import com.android.systemui.keyguard.shared.model.KeyguardState
+import com.android.systemui.keyguard.shared.model.Edge
+import com.android.systemui.keyguard.shared.model.KeyguardState.AOD
+import com.android.systemui.keyguard.shared.model.KeyguardState.LOCKSCREEN
import com.android.systemui.keyguard.ui.KeyguardTransitionAnimationFlow
import com.android.systemui.keyguard.ui.StateToValue
import com.android.systemui.keyguard.ui.transitions.DeviceEntryIconTransition
@@ -39,7 +40,6 @@
class AodToLockscreenTransitionViewModel
@Inject
constructor(
- deviceEntryUdfpsInteractor: DeviceEntryUdfpsInteractor,
shadeInteractor: ShadeInteractor,
animationFlow: KeyguardTransitionAnimationFlow,
) : DeviceEntryIconTransition {
@@ -47,8 +47,7 @@
private val transitionAnimation =
animationFlow.setup(
duration = TO_LOCKSCREEN_DURATION,
- from = KeyguardState.AOD,
- to = KeyguardState.LOCKSCREEN,
+ edge = Edge.create(from = AOD, to = LOCKSCREEN),
)
private var isShadeExpanded = false
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/AodToOccludedTransitionViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/AodToOccludedTransitionViewModel.kt
index 445575f..2497def 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/AodToOccludedTransitionViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/AodToOccludedTransitionViewModel.kt
@@ -19,7 +19,9 @@
import android.util.MathUtils
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.keyguard.domain.interactor.FromAodTransitionInteractor
-import com.android.systemui.keyguard.shared.model.KeyguardState
+import com.android.systemui.keyguard.shared.model.Edge
+import com.android.systemui.keyguard.shared.model.KeyguardState.AOD
+import com.android.systemui.keyguard.shared.model.KeyguardState.OCCLUDED
import com.android.systemui.keyguard.ui.KeyguardTransitionAnimationFlow
import com.android.systemui.keyguard.ui.transitions.DeviceEntryIconTransition
import javax.inject.Inject
@@ -36,8 +38,7 @@
private val transitionAnimation =
animationFlow.setup(
duration = FromAodTransitionInteractor.TO_OCCLUDED_DURATION,
- from = KeyguardState.AOD,
- to = KeyguardState.OCCLUDED,
+ edge = Edge.create(from = AOD, to = OCCLUDED),
)
/**
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/AodToPrimaryBouncerTransitionViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/AodToPrimaryBouncerTransitionViewModel.kt
index 9a23007..35f05f5 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/AodToPrimaryBouncerTransitionViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/AodToPrimaryBouncerTransitionViewModel.kt
@@ -18,9 +18,12 @@
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.keyguard.domain.interactor.FromAodTransitionInteractor
-import com.android.systemui.keyguard.shared.model.KeyguardState
+import com.android.systemui.keyguard.shared.model.Edge
+import com.android.systemui.keyguard.shared.model.KeyguardState.AOD
+import com.android.systemui.keyguard.shared.model.KeyguardState.PRIMARY_BOUNCER
import com.android.systemui.keyguard.ui.KeyguardTransitionAnimationFlow
import com.android.systemui.keyguard.ui.transitions.DeviceEntryIconTransition
+import com.android.systemui.scene.shared.model.Scenes
import javax.inject.Inject
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.flow.Flow
@@ -37,11 +40,14 @@
animationFlow: KeyguardTransitionAnimationFlow,
) : DeviceEntryIconTransition {
private val transitionAnimation =
- animationFlow.setup(
- duration = FromAodTransitionInteractor.TO_PRIMARY_BOUNCER_DURATION,
- from = KeyguardState.AOD,
- to = KeyguardState.PRIMARY_BOUNCER,
- )
+ animationFlow
+ .setup(
+ duration = FromAodTransitionInteractor.TO_PRIMARY_BOUNCER_DURATION,
+ edge = Edge.create(from = AOD, to = Scenes.Bouncer),
+ )
+ .setupWithoutSceneContainer(
+ edge = Edge.create(from = AOD, to = PRIMARY_BOUNCER),
+ )
override val deviceEntryParentViewAlpha: Flow<Float> =
transitionAnimation.immediatelyTransitionTo(0f)
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/BouncerToGoneFlows.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/BouncerToGoneFlows.kt
index 570f377..caa0436 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/BouncerToGoneFlows.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/BouncerToGoneFlows.kt
@@ -19,11 +19,13 @@
import com.android.app.animation.Interpolators.EMPHASIZED_ACCELERATE
import com.android.systemui.bouncer.domain.interactor.PrimaryBouncerInteractor
import com.android.systemui.keyguard.domain.interactor.KeyguardDismissActionInteractor
+import com.android.systemui.keyguard.shared.model.Edge
import com.android.systemui.keyguard.shared.model.KeyguardState
import com.android.systemui.keyguard.shared.model.KeyguardState.GONE
import com.android.systemui.keyguard.shared.model.ScrimAlpha
import com.android.systemui.keyguard.ui.KeyguardTransitionAnimationFlow
import com.android.systemui.scene.shared.flag.SceneContainerFlag
+import com.android.systemui.scene.shared.model.Scenes
import com.android.systemui.shade.domain.interactor.ShadeInteractor
import com.android.systemui.statusbar.SysuiStatusBarStateController
import dagger.Lazy
@@ -73,8 +75,12 @@
return animationFlow
.setup(
duration = duration,
- from = from,
- to = GONE,
+ // TODO(b/330311871): from can be PRIMARY_BOUNCER which would be a scene -> scene
+ // transition
+ edge = Edge.create(from = from, to = Scenes.Gone)
+ )
+ .setupWithoutSceneContainer(
+ edge = Edge.create(from = from, to = GONE),
)
.sharedFlow(
duration = duration,
@@ -96,11 +102,16 @@
var leaveShadeOpen: Boolean = false
var willRunDismissFromKeyguard: Boolean = false
val transitionAnimation =
- animationFlow.setup(
- duration = duration,
- from = fromState,
- to = GONE,
- )
+ animationFlow
+ .setup(
+ duration = duration,
+ // TODO(b/330311871): from can be PRIMARY_BOUNCER which would be a scene ->
+ // scene transition
+ edge = Edge.create(from = fromState, to = Scenes.Gone),
+ )
+ .setupWithoutSceneContainer(
+ edge = Edge.create(from = fromState, to = GONE),
+ )
return shadeInteractor.anyExpansion
.map { it > 0f }
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/DeviceEntryBackgroundViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/DeviceEntryBackgroundViewModel.kt
index 87324a2..6f8389f 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/DeviceEntryBackgroundViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/DeviceEntryBackgroundViewModel.kt
@@ -117,7 +117,8 @@
KeyguardState.DOZING,
KeyguardState.DREAMING,
KeyguardState.PRIMARY_BOUNCER,
- KeyguardState.AOD -> emit(0f)
+ KeyguardState.AOD,
+ KeyguardState.UNDEFINED -> emit(0f)
KeyguardState.ALTERNATE_BOUNCER,
KeyguardState.LOCKSCREEN -> emit(1f)
}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/DeviceEntryIconViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/DeviceEntryIconViewModel.kt
index 53b2697..8d90933 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/DeviceEntryIconViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/DeviceEntryIconViewModel.kt
@@ -20,6 +20,7 @@
import android.animation.IntEvaluator
import com.android.keyguard.KeyguardViewController
import com.android.systemui.accessibility.domain.interactor.AccessibilityInteractor
+import com.android.systemui.biometrics.shared.model.SensorLocation
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Application
import com.android.systemui.deviceentry.domain.interactor.DeviceEntryInteractor
@@ -73,6 +74,12 @@
@Application private val scope: CoroutineScope,
) {
val isUdfpsSupported: StateFlow<Boolean> = deviceEntryUdfpsInteractor.isUdfpsSupported
+ val udfpsLocation: StateFlow<SensorLocation?> =
+ deviceEntryUdfpsInteractor.udfpsLocation.stateIn(
+ scope = scope,
+ started = SharingStarted.Eagerly,
+ initialValue = null,
+ )
private val intEvaluator = IntEvaluator()
private val floatEvaluator = FloatEvaluator()
private val showingAlternateBouncer: Flow<Boolean> =
@@ -148,10 +155,11 @@
KeyguardState.GLANCEABLE_HUB,
KeyguardState.GONE,
KeyguardState.OCCLUDED,
- KeyguardState.DREAMING_LOCKSCREEN_HOSTED, -> 0f
+ KeyguardState.DREAMING_LOCKSCREEN_HOSTED,
+ KeyguardState.UNDEFINED, -> 0f
KeyguardState.AOD,
KeyguardState.ALTERNATE_BOUNCER,
- KeyguardState.LOCKSCREEN -> 1f
+ KeyguardState.LOCKSCREEN, -> 1f
}
}
val useBackgroundProtection: StateFlow<Boolean> = isUdfpsSupported
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/DozingToGoneTransitionViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/DozingToGoneTransitionViewModel.kt
index 8851a51..77ebfce 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/DozingToGoneTransitionViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/DozingToGoneTransitionViewModel.kt
@@ -19,9 +19,12 @@
import android.util.MathUtils
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.keyguard.domain.interactor.FromDozingTransitionInteractor.Companion.TO_GONE_DURATION
-import com.android.systemui.keyguard.shared.model.KeyguardState
+import com.android.systemui.keyguard.shared.model.Edge
+import com.android.systemui.keyguard.shared.model.KeyguardState.DOZING
+import com.android.systemui.keyguard.shared.model.KeyguardState.GONE
import com.android.systemui.keyguard.ui.KeyguardTransitionAnimationFlow
import com.android.systemui.keyguard.ui.transitions.DeviceEntryIconTransition
+import com.android.systemui.scene.shared.model.Scenes
import javax.inject.Inject
import kotlin.time.Duration.Companion.milliseconds
import kotlinx.coroutines.ExperimentalCoroutinesApi
@@ -37,11 +40,14 @@
) : DeviceEntryIconTransition {
private val transitionAnimation =
- animationFlow.setup(
- duration = TO_GONE_DURATION,
- from = KeyguardState.DOZING,
- to = KeyguardState.GONE,
- )
+ animationFlow
+ .setup(
+ duration = TO_GONE_DURATION,
+ edge = Edge.create(from = DOZING, to = Scenes.Gone),
+ )
+ .setupWithoutSceneContainer(
+ edge = Edge.create(from = DOZING, to = GONE),
+ )
fun lockscreenAlpha(viewState: ViewStateAccessor): Flow<Float> {
var startAlpha = 1f
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/DozingToLockscreenTransitionViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/DozingToLockscreenTransitionViewModel.kt
index 168d6e1..a460d51 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/DozingToLockscreenTransitionViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/DozingToLockscreenTransitionViewModel.kt
@@ -18,7 +18,9 @@
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.keyguard.domain.interactor.FromDozingTransitionInteractor
-import com.android.systemui.keyguard.shared.model.KeyguardState
+import com.android.systemui.keyguard.shared.model.Edge
+import com.android.systemui.keyguard.shared.model.KeyguardState.DOZING
+import com.android.systemui.keyguard.shared.model.KeyguardState.LOCKSCREEN
import com.android.systemui.keyguard.ui.KeyguardTransitionAnimationFlow
import com.android.systemui.keyguard.ui.transitions.DeviceEntryIconTransition
import javax.inject.Inject
@@ -39,8 +41,7 @@
private val transitionAnimation =
animationFlow.setup(
duration = FromDozingTransitionInteractor.TO_LOCKSCREEN_DURATION,
- from = KeyguardState.DOZING,
- to = KeyguardState.LOCKSCREEN,
+ edge = Edge.create(from = DOZING, to = LOCKSCREEN),
)
val shortcutsAlpha: Flow<Float> =
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/DozingToOccludedTransitionViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/DozingToOccludedTransitionViewModel.kt
index c0b1195..f33752f 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/DozingToOccludedTransitionViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/DozingToOccludedTransitionViewModel.kt
@@ -19,7 +19,9 @@
import android.util.MathUtils
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.keyguard.domain.interactor.FromAodTransitionInteractor
-import com.android.systemui.keyguard.shared.model.KeyguardState
+import com.android.systemui.keyguard.shared.model.Edge
+import com.android.systemui.keyguard.shared.model.KeyguardState.DOZING
+import com.android.systemui.keyguard.shared.model.KeyguardState.OCCLUDED
import com.android.systemui.keyguard.ui.KeyguardTransitionAnimationFlow
import com.android.systemui.keyguard.ui.transitions.DeviceEntryIconTransition
import javax.inject.Inject
@@ -38,8 +40,7 @@
private val transitionAnimation =
animationFlow.setup(
duration = FromAodTransitionInteractor.TO_OCCLUDED_DURATION,
- from = KeyguardState.DOZING,
- to = KeyguardState.OCCLUDED,
+ edge = Edge.create(from = DOZING, to = OCCLUDED),
)
/**
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/DozingToPrimaryBouncerTransitionViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/DozingToPrimaryBouncerTransitionViewModel.kt
index 4395c34..7ddf641 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/DozingToPrimaryBouncerTransitionViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/DozingToPrimaryBouncerTransitionViewModel.kt
@@ -18,9 +18,12 @@
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.keyguard.domain.interactor.FromDozingTransitionInteractor.Companion.TO_PRIMARY_BOUNCER_DURATION
-import com.android.systemui.keyguard.shared.model.KeyguardState
+import com.android.systemui.keyguard.shared.model.Edge
+import com.android.systemui.keyguard.shared.model.KeyguardState.DOZING
+import com.android.systemui.keyguard.shared.model.KeyguardState.PRIMARY_BOUNCER
import com.android.systemui.keyguard.ui.KeyguardTransitionAnimationFlow
import com.android.systemui.keyguard.ui.transitions.DeviceEntryIconTransition
+import com.android.systemui.scene.shared.model.Scenes
import javax.inject.Inject
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.flow.Flow
@@ -38,11 +41,14 @@
) : DeviceEntryIconTransition {
private val transitionAnimation =
- animationFlow.setup(
- duration = TO_PRIMARY_BOUNCER_DURATION,
- from = KeyguardState.DOZING,
- to = KeyguardState.PRIMARY_BOUNCER,
- )
+ animationFlow
+ .setup(
+ duration = TO_PRIMARY_BOUNCER_DURATION,
+ edge = Edge.create(from = DOZING, to = Scenes.Bouncer),
+ )
+ .setupWithoutSceneContainer(
+ edge = Edge.create(from = DOZING, to = PRIMARY_BOUNCER),
+ )
override val deviceEntryParentViewAlpha: Flow<Float> =
transitionAnimation.immediatelyTransitionTo(0f)
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/DreamingHostedToLockscreenTransitionViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/DreamingHostedToLockscreenTransitionViewModel.kt
index 67568e1..57ed455 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/DreamingHostedToLockscreenTransitionViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/DreamingHostedToLockscreenTransitionViewModel.kt
@@ -18,7 +18,9 @@
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.keyguard.domain.interactor.FromDreamingLockscreenHostedTransitionInteractor.Companion.TO_LOCKSCREEN_DURATION
-import com.android.systemui.keyguard.shared.model.KeyguardState
+import com.android.systemui.keyguard.shared.model.Edge
+import com.android.systemui.keyguard.shared.model.KeyguardState.DREAMING_LOCKSCREEN_HOSTED
+import com.android.systemui.keyguard.shared.model.KeyguardState.LOCKSCREEN
import com.android.systemui.keyguard.ui.KeyguardTransitionAnimationFlow
import javax.inject.Inject
import kotlin.time.Duration.Companion.milliseconds
@@ -34,8 +36,7 @@
private val transitionAnimation =
animationFlow.setup(
duration = TO_LOCKSCREEN_DURATION,
- from = KeyguardState.DREAMING_LOCKSCREEN_HOSTED,
- to = KeyguardState.LOCKSCREEN,
+ edge = Edge.create(from = DREAMING_LOCKSCREEN_HOSTED, to = LOCKSCREEN),
)
val shortcutsAlpha: Flow<Float> =
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/DreamingToAodTransitionViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/DreamingToAodTransitionViewModel.kt
index 0fa7475..754ed6c 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/DreamingToAodTransitionViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/DreamingToAodTransitionViewModel.kt
@@ -19,7 +19,9 @@
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.deviceentry.domain.interactor.DeviceEntryUdfpsInteractor
import com.android.systemui.keyguard.domain.interactor.FromDreamingTransitionInteractor
-import com.android.systemui.keyguard.shared.model.KeyguardState
+import com.android.systemui.keyguard.shared.model.Edge
+import com.android.systemui.keyguard.shared.model.KeyguardState.AOD
+import com.android.systemui.keyguard.shared.model.KeyguardState.DREAMING
import com.android.systemui.keyguard.ui.KeyguardTransitionAnimationFlow
import com.android.systemui.keyguard.ui.transitions.DeviceEntryIconTransition
import javax.inject.Inject
@@ -40,12 +42,12 @@
private val transitionAnimation =
animationFlow.setup(
duration = FromDreamingTransitionInteractor.TO_AOD_DURATION,
- from = KeyguardState.DREAMING,
- to = KeyguardState.AOD,
+ edge = Edge.create(from = DREAMING, to = AOD),
)
val deviceEntryBackgroundViewAlpha: Flow<Float> =
transitionAnimation.immediatelyTransitionTo(0f)
+
override val deviceEntryParentViewAlpha: Flow<Float> =
deviceEntryUdfpsInteractor.isUdfpsEnrolledAndEnabled.flatMapLatest { udfpsEnrolledAndEnabled
->
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/DreamingToGlanceableHubTransitionViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/DreamingToGlanceableHubTransitionViewModel.kt
index a083c24e..00aa102 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/DreamingToGlanceableHubTransitionViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/DreamingToGlanceableHubTransitionViewModel.kt
@@ -19,10 +19,13 @@
import com.android.app.animation.Interpolators.EMPHASIZED
import com.android.systemui.common.ui.domain.interactor.ConfigurationInteractor
import com.android.systemui.dagger.SysUISingleton
-import com.android.systemui.keyguard.shared.model.KeyguardState
+import com.android.systemui.keyguard.shared.model.Edge
+import com.android.systemui.keyguard.shared.model.KeyguardState.DREAMING
+import com.android.systemui.keyguard.shared.model.KeyguardState.GLANCEABLE_HUB
import com.android.systemui.keyguard.ui.KeyguardTransitionAnimationFlow
import com.android.systemui.keyguard.ui.transitions.DeviceEntryIconTransition
import com.android.systemui.res.R
+import com.android.systemui.scene.shared.model.Scenes
import javax.inject.Inject
import kotlin.time.Duration.Companion.milliseconds
import kotlin.time.Duration.Companion.seconds
@@ -40,11 +43,14 @@
configurationInteractor: ConfigurationInteractor,
) : DeviceEntryIconTransition {
private val transitionAnimation =
- animationFlow.setup(
- duration = TO_GLANCEABLE_HUB_DURATION,
- from = KeyguardState.DREAMING,
- to = KeyguardState.GLANCEABLE_HUB,
- )
+ animationFlow
+ .setup(
+ duration = TO_GLANCEABLE_HUB_DURATION,
+ edge = Edge.create(from = DREAMING, to = Scenes.Communal),
+ )
+ .setupWithoutSceneContainer(
+ edge = Edge.create(from = DREAMING, to = GLANCEABLE_HUB),
+ )
val dreamOverlayTranslationX: Flow<Float> =
configurationInteractor
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/DreamingToGoneTransitionViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/DreamingToGoneTransitionViewModel.kt
index ec7b931..1bdf6d29 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/DreamingToGoneTransitionViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/DreamingToGoneTransitionViewModel.kt
@@ -18,10 +18,13 @@
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.keyguard.domain.interactor.FromDreamingTransitionInteractor
-import com.android.systemui.keyguard.shared.model.KeyguardState
+import com.android.systemui.keyguard.shared.model.Edge
+import com.android.systemui.keyguard.shared.model.KeyguardState.DREAMING
+import com.android.systemui.keyguard.shared.model.KeyguardState.GONE
import com.android.systemui.keyguard.ui.KeyguardTransitionAnimationFlow
-import kotlinx.coroutines.flow.Flow
+import com.android.systemui.scene.shared.model.Scenes
import javax.inject.Inject
+import kotlinx.coroutines.flow.Flow
@SysUISingleton
class DreamingToGoneTransitionViewModel
@@ -31,13 +34,15 @@
) {
private val transitionAnimation =
- animationFlow.setup(
+ animationFlow
+ .setup(
duration = FromDreamingTransitionInteractor.TO_GONE_DURATION,
- from = KeyguardState.DREAMING,
- to = KeyguardState.GONE,
+ edge = Edge.create(from = DREAMING, to = Scenes.Gone),
+ )
+ .setupWithoutSceneContainer(
+ edge = Edge.create(from = DREAMING, to = GONE),
)
/** Lockscreen views alpha */
val lockscreenAlpha: Flow<Float> = transitionAnimation.immediatelyTransitionTo(0f)
-
-}
\ No newline at end of file
+}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/DreamingToLockscreenTransitionViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/DreamingToLockscreenTransitionViewModel.kt
index f191aa7..82381eb 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/DreamingToLockscreenTransitionViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/DreamingToLockscreenTransitionViewModel.kt
@@ -20,7 +20,9 @@
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.keyguard.domain.interactor.FromDreamingTransitionInteractor
import com.android.systemui.keyguard.domain.interactor.FromDreamingTransitionInteractor.Companion.TO_LOCKSCREEN_DURATION
-import com.android.systemui.keyguard.shared.model.KeyguardState
+import com.android.systemui.keyguard.shared.model.Edge
+import com.android.systemui.keyguard.shared.model.KeyguardState.DREAMING
+import com.android.systemui.keyguard.shared.model.KeyguardState.LOCKSCREEN
import com.android.systemui.keyguard.ui.KeyguardTransitionAnimationFlow
import com.android.systemui.keyguard.ui.transitions.DeviceEntryIconTransition
import javax.inject.Inject
@@ -45,8 +47,7 @@
private val transitionAnimation =
animationFlow.setup(
duration = TO_LOCKSCREEN_DURATION,
- from = KeyguardState.DREAMING,
- to = KeyguardState.LOCKSCREEN,
+ edge = Edge.create(from = DREAMING, to = LOCKSCREEN),
)
/** Dream overlay y-translation on exit */
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/GlanceableHubToDreamingTransitionViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/GlanceableHubToDreamingTransitionViewModel.kt
index 3716458..d594488 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/GlanceableHubToDreamingTransitionViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/GlanceableHubToDreamingTransitionViewModel.kt
@@ -19,10 +19,13 @@
import com.android.app.animation.Interpolators
import com.android.systemui.common.ui.domain.interactor.ConfigurationInteractor
import com.android.systemui.dagger.SysUISingleton
-import com.android.systemui.keyguard.shared.model.KeyguardState
+import com.android.systemui.keyguard.shared.model.Edge
+import com.android.systemui.keyguard.shared.model.KeyguardState.DREAMING
+import com.android.systemui.keyguard.shared.model.KeyguardState.GLANCEABLE_HUB
import com.android.systemui.keyguard.ui.KeyguardTransitionAnimationFlow
import com.android.systemui.keyguard.ui.transitions.DeviceEntryIconTransition
import com.android.systemui.res.R
+import com.android.systemui.scene.shared.model.Scenes
import javax.inject.Inject
import kotlin.time.Duration.Companion.milliseconds
import kotlin.time.Duration.Companion.seconds
@@ -41,11 +44,14 @@
) : DeviceEntryIconTransition {
private val transitionAnimation =
- animationFlow.setup(
- duration = FROM_GLANCEABLE_HUB_DURATION,
- from = KeyguardState.GLANCEABLE_HUB,
- to = KeyguardState.DREAMING,
- )
+ animationFlow
+ .setup(
+ duration = FROM_GLANCEABLE_HUB_DURATION,
+ edge = Edge.create(from = Scenes.Communal, to = DREAMING),
+ )
+ .setupWithoutSceneContainer(
+ edge = Edge.create(from = GLANCEABLE_HUB, to = DREAMING),
+ )
val dreamOverlayAlpha: Flow<Float> =
transitionAnimation.sharedFlow(
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/GlanceableHubToLockscreenTransitionViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/GlanceableHubToLockscreenTransitionViewModel.kt
index e05b500..046b95f 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/GlanceableHubToLockscreenTransitionViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/GlanceableHubToLockscreenTransitionViewModel.kt
@@ -20,10 +20,13 @@
import com.android.systemui.common.ui.domain.interactor.ConfigurationInteractor
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.keyguard.domain.interactor.FromGlanceableHubTransitionInteractor.Companion.TO_LOCKSCREEN_DURATION
-import com.android.systemui.keyguard.shared.model.KeyguardState
+import com.android.systemui.keyguard.shared.model.Edge
+import com.android.systemui.keyguard.shared.model.KeyguardState.GLANCEABLE_HUB
+import com.android.systemui.keyguard.shared.model.KeyguardState.LOCKSCREEN
import com.android.systemui.keyguard.ui.KeyguardTransitionAnimationFlow
import com.android.systemui.keyguard.ui.StateToValue
import com.android.systemui.res.R
+import com.android.systemui.scene.shared.model.Scenes
import javax.inject.Inject
import kotlin.time.Duration.Companion.milliseconds
import kotlinx.coroutines.ExperimentalCoroutinesApi
@@ -45,11 +48,14 @@
animationFlow: KeyguardTransitionAnimationFlow,
) {
private val transitionAnimation =
- animationFlow.setup(
- duration = TO_LOCKSCREEN_DURATION,
- from = KeyguardState.GLANCEABLE_HUB,
- to = KeyguardState.LOCKSCREEN,
- )
+ animationFlow
+ .setup(
+ duration = TO_LOCKSCREEN_DURATION,
+ edge = Edge.create(from = Scenes.Communal, to = LOCKSCREEN),
+ )
+ .setupWithoutSceneContainer(
+ edge = Edge.create(from = GLANCEABLE_HUB, to = LOCKSCREEN),
+ )
val keyguardAlpha: Flow<Float> =
transitionAnimation.sharedFlow(
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/GlanceableHubToOccludedTransitionViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/GlanceableHubToOccludedTransitionViewModel.kt
index 300121f..cd98bb0 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/GlanceableHubToOccludedTransitionViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/GlanceableHubToOccludedTransitionViewModel.kt
@@ -18,9 +18,12 @@
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.keyguard.domain.interactor.FromGlanceableHubTransitionInteractor.Companion.TO_OCCLUDED_DURATION
-import com.android.systemui.keyguard.shared.model.KeyguardState
+import com.android.systemui.keyguard.shared.model.Edge
+import com.android.systemui.keyguard.shared.model.KeyguardState.GLANCEABLE_HUB
+import com.android.systemui.keyguard.shared.model.KeyguardState.OCCLUDED
import com.android.systemui.keyguard.ui.KeyguardTransitionAnimationFlow
import com.android.systemui.keyguard.ui.transitions.DeviceEntryIconTransition
+import com.android.systemui.scene.shared.model.Scenes
import javax.inject.Inject
import kotlinx.coroutines.flow.Flow
@@ -32,11 +35,12 @@
) : DeviceEntryIconTransition {
private val transitionAnimation =
- animationFlow.setup(
- duration = TO_OCCLUDED_DURATION,
- from = KeyguardState.GLANCEABLE_HUB,
- to = KeyguardState.OCCLUDED,
- )
+ animationFlow
+ .setup(
+ duration = TO_OCCLUDED_DURATION,
+ edge = Edge.create(from = Scenes.Communal, to = OCCLUDED),
+ )
+ .setupWithoutSceneContainer(edge = Edge.create(from = GLANCEABLE_HUB, to = OCCLUDED))
override val deviceEntryParentViewAlpha: Flow<Float> =
transitionAnimation.immediatelyTransitionTo(0f)
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/GoneToAodTransitionViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/GoneToAodTransitionViewModel.kt
index 3540bec..74f7d75 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/GoneToAodTransitionViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/GoneToAodTransitionViewModel.kt
@@ -20,10 +20,13 @@
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.deviceentry.domain.interactor.DeviceEntryUdfpsInteractor
import com.android.systemui.keyguard.domain.interactor.FromGoneTransitionInteractor.Companion.TO_AOD_DURATION
-import com.android.systemui.keyguard.shared.model.KeyguardState
+import com.android.systemui.keyguard.shared.model.Edge
+import com.android.systemui.keyguard.shared.model.KeyguardState.AOD
+import com.android.systemui.keyguard.shared.model.KeyguardState.GONE
import com.android.systemui.keyguard.ui.KeyguardTransitionAnimationFlow
import com.android.systemui.keyguard.ui.StateToValue
import com.android.systemui.keyguard.ui.transitions.DeviceEntryIconTransition
+import com.android.systemui.scene.shared.model.Scenes
import javax.inject.Inject
import kotlin.time.Duration.Companion.milliseconds
import kotlinx.coroutines.ExperimentalCoroutinesApi
@@ -42,11 +45,14 @@
) : DeviceEntryIconTransition {
private val transitionAnimation =
- animationFlow.setup(
- duration = TO_AOD_DURATION,
- from = KeyguardState.GONE,
- to = KeyguardState.AOD,
- )
+ animationFlow
+ .setup(
+ duration = TO_AOD_DURATION,
+ edge = Edge.create(from = Scenes.Gone, to = AOD),
+ )
+ .setupWithoutSceneContainer(
+ edge = Edge.create(from = GONE, to = AOD),
+ )
/** y-translation from the top of the screen for AOD */
fun enterFromTopTranslationY(translatePx: Int): Flow<StateToValue> {
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/GoneToDozingTransitionViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/GoneToDozingTransitionViewModel.kt
index 80a6bda..70c0032 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/GoneToDozingTransitionViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/GoneToDozingTransitionViewModel.kt
@@ -19,9 +19,12 @@
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.deviceentry.domain.interactor.DeviceEntryUdfpsInteractor
import com.android.systemui.keyguard.domain.interactor.FromGoneTransitionInteractor.Companion.TO_DOZING_DURATION
-import com.android.systemui.keyguard.shared.model.KeyguardState
+import com.android.systemui.keyguard.shared.model.Edge
+import com.android.systemui.keyguard.shared.model.KeyguardState.DOZING
+import com.android.systemui.keyguard.shared.model.KeyguardState.GONE
import com.android.systemui.keyguard.ui.KeyguardTransitionAnimationFlow
import com.android.systemui.keyguard.ui.transitions.DeviceEntryIconTransition
+import com.android.systemui.scene.shared.model.Scenes
import javax.inject.Inject
import kotlin.time.Duration.Companion.milliseconds
import kotlinx.coroutines.ExperimentalCoroutinesApi
@@ -40,11 +43,14 @@
) : DeviceEntryIconTransition {
private val transitionAnimation =
- animationFlow.setup(
- duration = TO_DOZING_DURATION,
- from = KeyguardState.GONE,
- to = KeyguardState.DOZING,
- )
+ animationFlow
+ .setup(
+ duration = TO_DOZING_DURATION,
+ edge = Edge.create(from = Scenes.Gone, to = DOZING),
+ )
+ .setupWithoutSceneContainer(
+ edge = Edge.create(from = GONE, to = DOZING),
+ )
val lockscreenAlpha: Flow<Float> =
transitionAnimation.sharedFlow(
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/GoneToDreamingLockscreenHostedTransitionViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/GoneToDreamingLockscreenHostedTransitionViewModel.kt
index b527463..627f0de 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/GoneToDreamingLockscreenHostedTransitionViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/GoneToDreamingLockscreenHostedTransitionViewModel.kt
@@ -18,8 +18,11 @@
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.keyguard.domain.interactor.FromGoneTransitionInteractor.Companion.TO_DREAMING_DURATION
-import com.android.systemui.keyguard.shared.model.KeyguardState
+import com.android.systemui.keyguard.shared.model.Edge
+import com.android.systemui.keyguard.shared.model.KeyguardState.DREAMING_LOCKSCREEN_HOSTED
+import com.android.systemui.keyguard.shared.model.KeyguardState.GONE
import com.android.systemui.keyguard.ui.KeyguardTransitionAnimationFlow
+import com.android.systemui.scene.shared.model.Scenes
import javax.inject.Inject
import kotlin.time.Duration.Companion.milliseconds
import kotlinx.coroutines.flow.Flow
@@ -36,11 +39,14 @@
) {
private val transitionAnimation =
- animationFlow.setup(
- duration = TO_DREAMING_DURATION,
- from = KeyguardState.GONE,
- to = KeyguardState.DREAMING_LOCKSCREEN_HOSTED,
- )
+ animationFlow
+ .setup(
+ duration = TO_DREAMING_DURATION,
+ edge = Edge.create(from = Scenes.Gone, to = DREAMING_LOCKSCREEN_HOSTED),
+ )
+ .setupWithoutSceneContainer(
+ edge = Edge.create(from = GONE, to = DREAMING_LOCKSCREEN_HOSTED),
+ )
/** Lockscreen views alpha - hide immediately */
val lockscreenAlpha: Flow<Float> =
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/GoneToDreamingTransitionViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/GoneToDreamingTransitionViewModel.kt
index 102242a..f8b6e28 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/GoneToDreamingTransitionViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/GoneToDreamingTransitionViewModel.kt
@@ -19,8 +19,11 @@
import com.android.app.animation.Interpolators.EMPHASIZED_ACCELERATE
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.keyguard.domain.interactor.FromGoneTransitionInteractor.Companion.TO_DREAMING_DURATION
-import com.android.systemui.keyguard.shared.model.KeyguardState
+import com.android.systemui.keyguard.shared.model.Edge
+import com.android.systemui.keyguard.shared.model.KeyguardState.DREAMING
+import com.android.systemui.keyguard.shared.model.KeyguardState.GONE
import com.android.systemui.keyguard.ui.KeyguardTransitionAnimationFlow
+import com.android.systemui.scene.shared.model.Scenes
import javax.inject.Inject
import kotlin.time.Duration.Companion.milliseconds
import kotlinx.coroutines.flow.Flow
@@ -34,11 +37,14 @@
) {
private val transitionAnimation =
- animationFlow.setup(
- duration = TO_DREAMING_DURATION,
- from = KeyguardState.GONE,
- to = KeyguardState.DREAMING,
- )
+ animationFlow
+ .setup(
+ duration = TO_DREAMING_DURATION,
+ edge = Edge.create(from = Scenes.Gone, to = DREAMING),
+ )
+ .setupWithoutSceneContainer(
+ edge = Edge.create(from = GONE, to = DREAMING),
+ )
/** Lockscreen views y-translation */
fun lockscreenTranslationY(translatePx: Int): Flow<Float> {
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/GoneToLockscreenTransitionViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/GoneToLockscreenTransitionViewModel.kt
index a2ce408..08ec43f 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/GoneToLockscreenTransitionViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/GoneToLockscreenTransitionViewModel.kt
@@ -18,9 +18,12 @@
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.keyguard.domain.interactor.FromGoneTransitionInteractor.Companion.TO_LOCKSCREEN_DURATION
-import com.android.systemui.keyguard.shared.model.KeyguardState
+import com.android.systemui.keyguard.shared.model.Edge
+import com.android.systemui.keyguard.shared.model.KeyguardState.GONE
+import com.android.systemui.keyguard.shared.model.KeyguardState.LOCKSCREEN
import com.android.systemui.keyguard.ui.KeyguardTransitionAnimationFlow
import com.android.systemui.keyguard.ui.transitions.DeviceEntryIconTransition
+import com.android.systemui.scene.shared.model.Scenes
import javax.inject.Inject
import kotlin.time.Duration.Companion.milliseconds
import kotlinx.coroutines.flow.Flow
@@ -33,11 +36,14 @@
) : DeviceEntryIconTransition {
private val transitionAnimation =
- animationFlow.setup(
- duration = TO_LOCKSCREEN_DURATION,
- from = KeyguardState.GONE,
- to = KeyguardState.LOCKSCREEN
- )
+ animationFlow
+ .setup(
+ duration = TO_LOCKSCREEN_DURATION,
+ edge = Edge.create(from = Scenes.Gone, to = LOCKSCREEN),
+ )
+ .setupWithoutSceneContainer(
+ edge = Edge.create(from = GONE, to = LOCKSCREEN),
+ )
val shortcutsAlpha: Flow<Float> =
transitionAnimation.sharedFlow(
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardRootViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardRootViewModel.kt
index bbcea56..f8a9310 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardRootViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardRootViewModel.kt
@@ -30,6 +30,7 @@
import com.android.systemui.keyguard.domain.interactor.KeyguardInteractor
import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor
import com.android.systemui.keyguard.shared.model.BurnInModel
+import com.android.systemui.keyguard.shared.model.Edge
import com.android.systemui.keyguard.shared.model.KeyguardState
import com.android.systemui.keyguard.shared.model.KeyguardState.AOD
import com.android.systemui.keyguard.shared.model.KeyguardState.GONE
@@ -38,6 +39,7 @@
import com.android.systemui.keyguard.shared.model.TransitionState.RUNNING
import com.android.systemui.keyguard.shared.model.TransitionState.STARTED
import com.android.systemui.keyguard.ui.StateToValue
+import com.android.systemui.scene.shared.model.Scenes
import com.android.systemui.shade.domain.interactor.ShadeInteractor
import com.android.systemui.statusbar.notification.domain.interactor.NotificationsKeyguardInteractor
import com.android.systemui.statusbar.phone.DozeParameters
@@ -56,6 +58,7 @@
import kotlinx.coroutines.Job
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.MutableStateFlow
+import kotlinx.coroutines.flow.asStateFlow
import kotlinx.coroutines.flow.combine
import kotlinx.coroutines.flow.combineTransform
import kotlinx.coroutines.flow.distinctUntilChanged
@@ -115,14 +118,19 @@
private val shadeInteractor: ShadeInteractor,
) {
private var burnInJob: Job? = null
- private val burnInModel = MutableStateFlow(BurnInModel())
+ private val _burnInModel = MutableStateFlow(BurnInModel())
+ val burnInModel = _burnInModel.asStateFlow()
val burnInLayerVisibility: Flow<Int> =
keyguardTransitionInteractor.startedKeyguardState
.filter { it == AOD || it == LOCKSCREEN }
.map { VISIBLE }
- val goneToAodTransition = keyguardTransitionInteractor.transition(from = GONE, to = AOD)
+ val goneToAodTransition =
+ keyguardTransitionInteractor.transition(
+ edge = Edge.create(Scenes.Gone, AOD),
+ edgeWithoutSceneContainer = Edge.create(GONE, AOD)
+ )
private val goneToAodTransitionRunning: Flow<Boolean> =
goneToAodTransition
@@ -144,7 +152,10 @@
private val alphaOnShadeExpansion: Flow<Float> =
combineTransform(
- keyguardTransitionInteractor.isInTransition(from = LOCKSCREEN, to = GONE),
+ keyguardTransitionInteractor.isInTransition(
+ edge = Edge.create(from = LOCKSCREEN, to = Scenes.Gone),
+ edgeWithoutSceneContainer = Edge.create(from = LOCKSCREEN, to = GONE),
+ ),
isOnLockscreen,
shadeInteractor.qsExpansion,
shadeInteractor.shadeExpansion,
@@ -270,7 +281,7 @@
burnInJob =
scope.launch("$TAG#aodBurnInViewModel") {
- aodBurnInViewModel.movement(params).collect { burnInModel.value = it }
+ aodBurnInViewModel.movement(params).collect { _burnInModel.value = it }
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToAodTransitionViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToAodTransitionViewModel.kt
index 1f9f304..8b5b347 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToAodTransitionViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToAodTransitionViewModel.kt
@@ -20,7 +20,9 @@
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.deviceentry.domain.interactor.DeviceEntryUdfpsInteractor
import com.android.systemui.keyguard.domain.interactor.FromLockscreenTransitionInteractor
-import com.android.systemui.keyguard.shared.model.KeyguardState
+import com.android.systemui.keyguard.shared.model.Edge
+import com.android.systemui.keyguard.shared.model.KeyguardState.AOD
+import com.android.systemui.keyguard.shared.model.KeyguardState.LOCKSCREEN
import com.android.systemui.keyguard.ui.KeyguardTransitionAnimationFlow
import com.android.systemui.keyguard.ui.transitions.DeviceEntryIconTransition
import javax.inject.Inject
@@ -45,8 +47,7 @@
private val transitionAnimation =
animationFlow.setup(
duration = FromLockscreenTransitionInteractor.TO_AOD_DURATION,
- from = KeyguardState.LOCKSCREEN,
- to = KeyguardState.AOD,
+ edge = Edge.create(from = LOCKSCREEN, to = AOD),
)
val deviceEntryBackgroundViewAlpha: Flow<Float> =
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToDozingTransitionViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToDozingTransitionViewModel.kt
index c836f01..27a1f7a 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToDozingTransitionViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToDozingTransitionViewModel.kt
@@ -19,7 +19,9 @@
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.deviceentry.domain.interactor.DeviceEntryUdfpsInteractor
import com.android.systemui.keyguard.domain.interactor.FromLockscreenTransitionInteractor.Companion.TO_DOZING_DURATION
-import com.android.systemui.keyguard.shared.model.KeyguardState
+import com.android.systemui.keyguard.shared.model.Edge
+import com.android.systemui.keyguard.shared.model.KeyguardState.DOZING
+import com.android.systemui.keyguard.shared.model.KeyguardState.LOCKSCREEN
import com.android.systemui.keyguard.ui.KeyguardTransitionAnimationFlow
import com.android.systemui.keyguard.ui.transitions.DeviceEntryIconTransition
import javax.inject.Inject
@@ -40,8 +42,7 @@
private val transitionAnimation =
animationFlow.setup(
duration = TO_DOZING_DURATION,
- from = KeyguardState.LOCKSCREEN,
- to = KeyguardState.DOZING,
+ edge = Edge.create(from = LOCKSCREEN, to = DOZING),
)
val lockscreenAlpha: Flow<Float> =
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToDreamingHostedTransitionViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToDreamingHostedTransitionViewModel.kt
index 19b9cf47..778dbed 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToDreamingHostedTransitionViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToDreamingHostedTransitionViewModel.kt
@@ -18,7 +18,9 @@
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.keyguard.domain.interactor.FromLockscreenTransitionInteractor.Companion.TO_DREAMING_HOSTED_DURATION
-import com.android.systemui.keyguard.shared.model.KeyguardState
+import com.android.systemui.keyguard.shared.model.Edge
+import com.android.systemui.keyguard.shared.model.KeyguardState.DREAMING_LOCKSCREEN_HOSTED
+import com.android.systemui.keyguard.shared.model.KeyguardState.LOCKSCREEN
import com.android.systemui.keyguard.ui.KeyguardTransitionAnimationFlow
import javax.inject.Inject
import kotlin.time.Duration.Companion.milliseconds
@@ -34,8 +36,7 @@
private val transitionAnimation =
animationFlow.setup(
duration = TO_DREAMING_HOSTED_DURATION,
- from = KeyguardState.LOCKSCREEN,
- to = KeyguardState.DREAMING_LOCKSCREEN_HOSTED,
+ edge = Edge.create(from = LOCKSCREEN, to = DREAMING_LOCKSCREEN_HOSTED),
)
val shortcutsAlpha: Flow<Float> =
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToDreamingTransitionViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToDreamingTransitionViewModel.kt
index 13522a6..579abeb 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToDreamingTransitionViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToDreamingTransitionViewModel.kt
@@ -19,7 +19,9 @@
import com.android.app.animation.Interpolators.EMPHASIZED_ACCELERATE
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.keyguard.domain.interactor.FromLockscreenTransitionInteractor.Companion.TO_DREAMING_DURATION
-import com.android.systemui.keyguard.shared.model.KeyguardState
+import com.android.systemui.keyguard.shared.model.Edge
+import com.android.systemui.keyguard.shared.model.KeyguardState.DREAMING
+import com.android.systemui.keyguard.shared.model.KeyguardState.LOCKSCREEN
import com.android.systemui.keyguard.ui.KeyguardTransitionAnimationFlow
import com.android.systemui.keyguard.ui.transitions.DeviceEntryIconTransition
import javax.inject.Inject
@@ -40,8 +42,7 @@
private val transitionAnimation =
animationFlow.setup(
duration = TO_DREAMING_DURATION,
- from = KeyguardState.LOCKSCREEN,
- to = KeyguardState.DREAMING,
+ edge = Edge.create(from = LOCKSCREEN, to = DREAMING),
)
/** Lockscreen views y-translation */
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToGlanceableHubTransitionViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToGlanceableHubTransitionViewModel.kt
index dae7897..c7273b7 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToGlanceableHubTransitionViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToGlanceableHubTransitionViewModel.kt
@@ -20,10 +20,13 @@
import com.android.systemui.common.ui.domain.interactor.ConfigurationInteractor
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.keyguard.domain.interactor.FromLockscreenTransitionInteractor
-import com.android.systemui.keyguard.shared.model.KeyguardState
+import com.android.systemui.keyguard.shared.model.Edge
+import com.android.systemui.keyguard.shared.model.KeyguardState.GLANCEABLE_HUB
+import com.android.systemui.keyguard.shared.model.KeyguardState.LOCKSCREEN
import com.android.systemui.keyguard.ui.KeyguardTransitionAnimationFlow
import com.android.systemui.keyguard.ui.StateToValue
import com.android.systemui.res.R
+import com.android.systemui.scene.shared.model.Scenes
import javax.inject.Inject
import kotlin.time.Duration.Companion.milliseconds
import kotlinx.coroutines.ExperimentalCoroutinesApi
@@ -45,11 +48,14 @@
animationFlow: KeyguardTransitionAnimationFlow,
) {
private val transitionAnimation =
- animationFlow.setup(
- duration = FromLockscreenTransitionInteractor.TO_GLANCEABLE_HUB_DURATION,
- from = KeyguardState.LOCKSCREEN,
- to = KeyguardState.GLANCEABLE_HUB,
- )
+ animationFlow
+ .setup(
+ duration = FromLockscreenTransitionInteractor.TO_GLANCEABLE_HUB_DURATION,
+ edge = Edge.create(from = LOCKSCREEN, to = Scenes.Communal),
+ )
+ .setupWithoutSceneContainer(
+ edge = Edge.create(from = LOCKSCREEN, to = GLANCEABLE_HUB),
+ )
val keyguardAlpha: Flow<Float> =
transitionAnimation.sharedFlow(
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToGoneTransitionViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToGoneTransitionViewModel.kt
index f03625e..1314e88 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToGoneTransitionViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToGoneTransitionViewModel.kt
@@ -19,10 +19,13 @@
import android.util.MathUtils
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.keyguard.domain.interactor.FromLockscreenTransitionInteractor
-import com.android.systemui.keyguard.shared.model.KeyguardState
+import com.android.systemui.keyguard.shared.model.Edge
+import com.android.systemui.keyguard.shared.model.KeyguardState.GONE
+import com.android.systemui.keyguard.shared.model.KeyguardState.LOCKSCREEN
import com.android.systemui.keyguard.ui.KeyguardTransitionAnimationFlow
import com.android.systemui.keyguard.ui.KeyguardTransitionAnimationFlow.FlowBuilder
import com.android.systemui.keyguard.ui.transitions.DeviceEntryIconTransition
+import com.android.systemui.scene.shared.model.Scenes
import com.android.systemui.statusbar.SysuiStatusBarStateController
import javax.inject.Inject
import kotlin.time.Duration.Companion.milliseconds
@@ -42,11 +45,14 @@
) : DeviceEntryIconTransition {
private val transitionAnimation: FlowBuilder =
- animationFlow.setup(
- duration = FromLockscreenTransitionInteractor.TO_GONE_DURATION,
- from = KeyguardState.LOCKSCREEN,
- to = KeyguardState.GONE,
- )
+ animationFlow
+ .setup(
+ duration = FromLockscreenTransitionInteractor.TO_GONE_DURATION,
+ edge = Edge.create(from = LOCKSCREEN, to = Scenes.Gone),
+ )
+ .setupWithoutSceneContainer(
+ edge = Edge.create(from = LOCKSCREEN, to = GONE),
+ )
val shortcutsAlpha: Flow<Float> =
transitionAnimation.sharedFlow(
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToOccludedTransitionViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToOccludedTransitionViewModel.kt
index dd6652e..fcf8c14f 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToOccludedTransitionViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToOccludedTransitionViewModel.kt
@@ -20,7 +20,9 @@
import com.android.systemui.common.ui.domain.interactor.ConfigurationInteractor
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.keyguard.domain.interactor.FromLockscreenTransitionInteractor.Companion.TO_OCCLUDED_DURATION
+import com.android.systemui.keyguard.shared.model.Edge
import com.android.systemui.keyguard.shared.model.KeyguardState
+import com.android.systemui.keyguard.shared.model.KeyguardState.OCCLUDED
import com.android.systemui.keyguard.ui.KeyguardTransitionAnimationFlow
import com.android.systemui.keyguard.ui.transitions.DeviceEntryIconTransition
import com.android.systemui.res.R
@@ -45,8 +47,7 @@
private val transitionAnimation =
animationFlow.setup(
duration = TO_OCCLUDED_DURATION,
- from = KeyguardState.LOCKSCREEN,
- to = KeyguardState.OCCLUDED,
+ edge = Edge.create(from = KeyguardState.LOCKSCREEN, to = OCCLUDED),
)
/** Lockscreen views alpha */
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToPrimaryBouncerTransitionViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToPrimaryBouncerTransitionViewModel.kt
index 0cfc757..23c44b0a 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToPrimaryBouncerTransitionViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToPrimaryBouncerTransitionViewModel.kt
@@ -18,9 +18,12 @@
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.keyguard.domain.interactor.FromLockscreenTransitionInteractor
-import com.android.systemui.keyguard.shared.model.KeyguardState
+import com.android.systemui.keyguard.shared.model.Edge
+import com.android.systemui.keyguard.shared.model.KeyguardState.LOCKSCREEN
+import com.android.systemui.keyguard.shared.model.KeyguardState.PRIMARY_BOUNCER
import com.android.systemui.keyguard.ui.KeyguardTransitionAnimationFlow
import com.android.systemui.keyguard.ui.transitions.DeviceEntryIconTransition
+import com.android.systemui.scene.shared.model.Scenes
import javax.inject.Inject
import kotlin.time.Duration.Companion.milliseconds
import kotlinx.coroutines.ExperimentalCoroutinesApi
@@ -39,11 +42,14 @@
animationFlow: KeyguardTransitionAnimationFlow,
) : DeviceEntryIconTransition {
private val transitionAnimation =
- animationFlow.setup(
- duration = FromLockscreenTransitionInteractor.TO_PRIMARY_BOUNCER_DURATION,
- from = KeyguardState.LOCKSCREEN,
- to = KeyguardState.PRIMARY_BOUNCER,
- )
+ animationFlow
+ .setup(
+ duration = FromLockscreenTransitionInteractor.TO_PRIMARY_BOUNCER_DURATION,
+ edge = Edge.create(from = LOCKSCREEN, to = Scenes.Bouncer),
+ )
+ .setupWithoutSceneContainer(
+ edge = Edge.create(from = LOCKSCREEN, to = PRIMARY_BOUNCER),
+ )
val shortcutsAlpha: Flow<Float> =
transitionAnimation.sharedFlow(
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/OccludedToAodTransitionViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/OccludedToAodTransitionViewModel.kt
index d7ba46b..706a3c4 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/OccludedToAodTransitionViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/OccludedToAodTransitionViewModel.kt
@@ -19,7 +19,9 @@
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.deviceentry.domain.interactor.DeviceEntryUdfpsInteractor
import com.android.systemui.keyguard.domain.interactor.FromOccludedTransitionInteractor
-import com.android.systemui.keyguard.shared.model.KeyguardState
+import com.android.systemui.keyguard.shared.model.Edge
+import com.android.systemui.keyguard.shared.model.KeyguardState.AOD
+import com.android.systemui.keyguard.shared.model.KeyguardState.OCCLUDED
import com.android.systemui.keyguard.ui.KeyguardTransitionAnimationFlow
import com.android.systemui.keyguard.ui.transitions.DeviceEntryIconTransition
import javax.inject.Inject
@@ -41,8 +43,7 @@
private val transitionAnimation =
animationFlow.setup(
duration = FromOccludedTransitionInteractor.TO_AOD_DURATION,
- from = KeyguardState.OCCLUDED,
- to = KeyguardState.AOD,
+ edge = Edge.create(from = OCCLUDED, to = AOD),
)
val deviceEntryBackgroundViewAlpha: Flow<Float> =
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/OccludedToDozingTransitionViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/OccludedToDozingTransitionViewModel.kt
index 91554e3..af01930 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/OccludedToDozingTransitionViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/OccludedToDozingTransitionViewModel.kt
@@ -18,7 +18,9 @@
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.keyguard.domain.interactor.FromOccludedTransitionInteractor
-import com.android.systemui.keyguard.shared.model.KeyguardState
+import com.android.systemui.keyguard.shared.model.Edge
+import com.android.systemui.keyguard.shared.model.KeyguardState.DOZING
+import com.android.systemui.keyguard.shared.model.KeyguardState.OCCLUDED
import com.android.systemui.keyguard.ui.KeyguardTransitionAnimationFlow
import javax.inject.Inject
import kotlin.time.Duration.Companion.milliseconds
@@ -38,8 +40,7 @@
private val transitionAnimation =
animationFlow.setup(
duration = FromOccludedTransitionInteractor.TO_DOZING_DURATION,
- from = KeyguardState.OCCLUDED,
- to = KeyguardState.DOZING,
+ edge = Edge.create(from = OCCLUDED, to = DOZING),
)
/** Lockscreen views alpha */
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/OccludedToGlanceableHubTransitionViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/OccludedToGlanceableHubTransitionViewModel.kt
index 73a4a9d..47e202b 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/OccludedToGlanceableHubTransitionViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/OccludedToGlanceableHubTransitionViewModel.kt
@@ -18,9 +18,12 @@
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.keyguard.domain.interactor.FromOccludedTransitionInteractor.Companion.TO_GLANCEABLE_HUB_DURATION
-import com.android.systemui.keyguard.shared.model.KeyguardState
+import com.android.systemui.keyguard.shared.model.Edge
+import com.android.systemui.keyguard.shared.model.KeyguardState.GLANCEABLE_HUB
+import com.android.systemui.keyguard.shared.model.KeyguardState.OCCLUDED
import com.android.systemui.keyguard.ui.KeyguardTransitionAnimationFlow
import com.android.systemui.keyguard.ui.transitions.DeviceEntryIconTransition
+import com.android.systemui.scene.shared.model.Scenes
import javax.inject.Inject
import kotlinx.coroutines.flow.Flow
@@ -32,11 +35,12 @@
) : DeviceEntryIconTransition {
private val transitionAnimation =
- animationFlow.setup(
- duration = TO_GLANCEABLE_HUB_DURATION,
- from = KeyguardState.OCCLUDED,
- to = KeyguardState.GLANCEABLE_HUB,
- )
+ animationFlow
+ .setup(
+ duration = TO_GLANCEABLE_HUB_DURATION,
+ edge = Edge.create(OCCLUDED, Scenes.Communal)
+ )
+ .setupWithoutSceneContainer(edge = Edge.create(OCCLUDED, GLANCEABLE_HUB))
override val deviceEntryParentViewAlpha: Flow<Float> =
transitionAnimation.immediatelyTransitionTo(1f)
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/OccludedToGoneTransitionViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/OccludedToGoneTransitionViewModel.kt
index d2c9cfb..98dba39 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/OccludedToGoneTransitionViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/OccludedToGoneTransitionViewModel.kt
@@ -17,8 +17,11 @@
package com.android.systemui.keyguard.ui.viewmodel
import com.android.systemui.dagger.SysUISingleton
-import com.android.systemui.keyguard.shared.model.KeyguardState
+import com.android.systemui.keyguard.shared.model.Edge
+import com.android.systemui.keyguard.shared.model.KeyguardState.GONE
+import com.android.systemui.keyguard.shared.model.KeyguardState.OCCLUDED
import com.android.systemui.keyguard.ui.KeyguardTransitionAnimationFlow
+import com.android.systemui.scene.shared.model.Scenes
import javax.inject.Inject
import kotlin.time.Duration.Companion.milliseconds
import kotlinx.coroutines.ExperimentalCoroutinesApi
@@ -33,11 +36,14 @@
animationFlow: KeyguardTransitionAnimationFlow,
) {
private val transitionAnimation =
- animationFlow.setup(
- duration = DEFAULT_DURATION,
- from = KeyguardState.OCCLUDED,
- to = KeyguardState.GONE,
- )
+ animationFlow
+ .setup(
+ duration = DEFAULT_DURATION,
+ edge = Edge.create(from = OCCLUDED, to = Scenes.Gone),
+ )
+ .setupWithoutSceneContainer(
+ edge = Edge.create(from = OCCLUDED, to = GONE),
+ )
fun notificationAlpha(viewState: ViewStateAccessor): Flow<Float> {
var currentAlpha = 0f
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/OccludedToLockscreenTransitionViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/OccludedToLockscreenTransitionViewModel.kt
index a09d58a..36c7d5b 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/OccludedToLockscreenTransitionViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/OccludedToLockscreenTransitionViewModel.kt
@@ -23,7 +23,9 @@
import com.android.systemui.keyguard.domain.interactor.FromOccludedTransitionInteractor.Companion.TO_LOCKSCREEN_DURATION
import com.android.systemui.keyguard.domain.interactor.KeyguardInteractor
import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor
-import com.android.systemui.keyguard.shared.model.KeyguardState
+import com.android.systemui.keyguard.shared.model.Edge
+import com.android.systemui.keyguard.shared.model.KeyguardState.LOCKSCREEN
+import com.android.systemui.keyguard.shared.model.KeyguardState.OCCLUDED
import com.android.systemui.keyguard.ui.KeyguardTransitionAnimationFlow
import com.android.systemui.keyguard.ui.transitions.DeviceEntryIconTransition
import com.android.systemui.res.R
@@ -56,8 +58,7 @@
private val transitionAnimation =
animationFlow.setup(
duration = TO_LOCKSCREEN_DURATION,
- from = KeyguardState.OCCLUDED,
- to = KeyguardState.LOCKSCREEN,
+ edge = Edge.create(from = OCCLUDED, to = LOCKSCREEN),
)
/** Lockscreen views y-translation */
@@ -101,7 +102,7 @@
.filter { (wasOccluded, isOccluded) ->
wasOccluded &&
!isOccluded &&
- keyguardTransitionInteractor.getCurrentState() == KeyguardState.OCCLUDED
+ keyguardTransitionInteractor.getCurrentState() == OCCLUDED
}
.map { 0f }
)
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/OffToLockscreenTransitionViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/OffToLockscreenTransitionViewModel.kt
index cf6a533..1eecbd5 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/OffToLockscreenTransitionViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/OffToLockscreenTransitionViewModel.kt
@@ -17,7 +17,9 @@
package com.android.systemui.keyguard.ui.viewmodel
import com.android.systemui.dagger.SysUISingleton
-import com.android.systemui.keyguard.shared.model.KeyguardState
+import com.android.systemui.keyguard.shared.model.Edge
+import com.android.systemui.keyguard.shared.model.KeyguardState.LOCKSCREEN
+import com.android.systemui.keyguard.shared.model.KeyguardState.OFF
import com.android.systemui.keyguard.ui.KeyguardTransitionAnimationFlow
import com.android.systemui.keyguard.ui.transitions.DeviceEntryIconTransition
import javax.inject.Inject
@@ -34,8 +36,7 @@
private val transitionAnimation =
animationFlow.setup(
duration = 250.milliseconds,
- from = KeyguardState.OFF,
- to = KeyguardState.LOCKSCREEN,
+ edge = Edge.create(from = OFF, to = LOCKSCREEN),
)
val shortcutsAlpha: Flow<Float> =
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/PrimaryBouncerToAodTransitionViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/PrimaryBouncerToAodTransitionViewModel.kt
index 942903b..009f85d 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/PrimaryBouncerToAodTransitionViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/PrimaryBouncerToAodTransitionViewModel.kt
@@ -19,9 +19,12 @@
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.deviceentry.domain.interactor.DeviceEntryUdfpsInteractor
import com.android.systemui.keyguard.domain.interactor.FromPrimaryBouncerTransitionInteractor
-import com.android.systemui.keyguard.shared.model.KeyguardState
+import com.android.systemui.keyguard.shared.model.Edge
+import com.android.systemui.keyguard.shared.model.KeyguardState.AOD
+import com.android.systemui.keyguard.shared.model.KeyguardState.PRIMARY_BOUNCER
import com.android.systemui.keyguard.ui.KeyguardTransitionAnimationFlow
import com.android.systemui.keyguard.ui.transitions.DeviceEntryIconTransition
+import com.android.systemui.scene.shared.model.Scenes
import javax.inject.Inject
import kotlin.time.Duration.Companion.milliseconds
import kotlinx.coroutines.ExperimentalCoroutinesApi
@@ -42,11 +45,14 @@
animationFlow: KeyguardTransitionAnimationFlow,
) : DeviceEntryIconTransition {
private val transitionAnimation =
- animationFlow.setup(
- duration = FromPrimaryBouncerTransitionInteractor.TO_AOD_DURATION,
- from = KeyguardState.PRIMARY_BOUNCER,
- to = KeyguardState.AOD,
- )
+ animationFlow
+ .setup(
+ duration = FromPrimaryBouncerTransitionInteractor.TO_AOD_DURATION,
+ edge = Edge.create(from = Scenes.Bouncer, to = AOD),
+ )
+ .setupWithoutSceneContainer(
+ edge = Edge.create(from = PRIMARY_BOUNCER, to = AOD),
+ )
val deviceEntryBackgroundViewAlpha: Flow<Float> =
transitionAnimation.immediatelyTransitionTo(0f)
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/PrimaryBouncerToDozingTransitionViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/PrimaryBouncerToDozingTransitionViewModel.kt
index 13f651a..e5bb464 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/PrimaryBouncerToDozingTransitionViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/PrimaryBouncerToDozingTransitionViewModel.kt
@@ -19,9 +19,12 @@
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.deviceentry.domain.interactor.DeviceEntryUdfpsInteractor
import com.android.systemui.keyguard.domain.interactor.FromPrimaryBouncerTransitionInteractor.Companion.TO_DOZING_DURATION
-import com.android.systemui.keyguard.shared.model.KeyguardState
+import com.android.systemui.keyguard.shared.model.Edge
+import com.android.systemui.keyguard.shared.model.KeyguardState.DOZING
+import com.android.systemui.keyguard.shared.model.KeyguardState.PRIMARY_BOUNCER
import com.android.systemui.keyguard.ui.KeyguardTransitionAnimationFlow
import com.android.systemui.keyguard.ui.transitions.DeviceEntryIconTransition
+import com.android.systemui.scene.shared.model.Scenes
import javax.inject.Inject
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.flow.Flow
@@ -42,11 +45,14 @@
) : DeviceEntryIconTransition {
private val transitionAnimation =
- animationFlow.setup(
- duration = TO_DOZING_DURATION,
- from = KeyguardState.PRIMARY_BOUNCER,
- to = KeyguardState.DOZING,
- )
+ animationFlow
+ .setup(
+ duration = TO_DOZING_DURATION,
+ edge = Edge.create(from = Scenes.Bouncer, to = DOZING),
+ )
+ .setupWithoutSceneContainer(
+ edge = Edge.create(from = PRIMARY_BOUNCER, to = DOZING),
+ )
val deviceEntryBackgroundViewAlpha: Flow<Float> =
transitionAnimation.immediatelyTransitionTo(0f)
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/PrimaryBouncerToGoneTransitionViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/PrimaryBouncerToGoneTransitionViewModel.kt
index b1fa710..7ae4558 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/PrimaryBouncerToGoneTransitionViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/PrimaryBouncerToGoneTransitionViewModel.kt
@@ -20,11 +20,13 @@
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.keyguard.domain.interactor.FromPrimaryBouncerTransitionInteractor.Companion.TO_GONE_DURATION
import com.android.systemui.keyguard.domain.interactor.KeyguardDismissActionInteractor
+import com.android.systemui.keyguard.shared.model.Edge
import com.android.systemui.keyguard.shared.model.KeyguardState.GONE
import com.android.systemui.keyguard.shared.model.KeyguardState.PRIMARY_BOUNCER
import com.android.systemui.keyguard.shared.model.ScrimAlpha
import com.android.systemui.keyguard.ui.KeyguardTransitionAnimationFlow
import com.android.systemui.scene.shared.flag.SceneContainerFlag
+import com.android.systemui.scene.shared.model.Scenes
import com.android.systemui.statusbar.SysuiStatusBarStateController
import dagger.Lazy
import javax.inject.Inject
@@ -49,11 +51,14 @@
animationFlow: KeyguardTransitionAnimationFlow,
) {
private val transitionAnimation =
- animationFlow.setup(
- duration = TO_GONE_DURATION,
- from = PRIMARY_BOUNCER,
- to = GONE,
- )
+ animationFlow
+ .setup(
+ duration = TO_GONE_DURATION,
+ edge = Edge.create(from = PRIMARY_BOUNCER, to = Scenes.Gone),
+ )
+ .setupWithoutSceneContainer(
+ edge = Edge.create(from = PRIMARY_BOUNCER, to = GONE),
+ )
private var leaveShadeOpen: Boolean = false
private var willRunDismissFromKeyguard: Boolean = false
@@ -88,6 +93,7 @@
} else {
createBouncerAlphaFlow(primaryBouncerInteractor::willRunDismissFromKeyguard)
}
+
private fun createBouncerAlphaFlow(willRunAnimationOnKeyguard: () -> Boolean): Flow<Float> {
return transitionAnimation.sharedFlow(
duration = 200.milliseconds,
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/PrimaryBouncerToLockscreenTransitionViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/PrimaryBouncerToLockscreenTransitionViewModel.kt
index 2575041..7511101 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/PrimaryBouncerToLockscreenTransitionViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/PrimaryBouncerToLockscreenTransitionViewModel.kt
@@ -19,9 +19,12 @@
import com.android.app.animation.Interpolators.EMPHASIZED_ACCELERATE
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.keyguard.domain.interactor.FromPrimaryBouncerTransitionInteractor
-import com.android.systemui.keyguard.shared.model.KeyguardState
+import com.android.systemui.keyguard.shared.model.Edge
+import com.android.systemui.keyguard.shared.model.KeyguardState.LOCKSCREEN
+import com.android.systemui.keyguard.shared.model.KeyguardState.PRIMARY_BOUNCER
import com.android.systemui.keyguard.ui.KeyguardTransitionAnimationFlow
import com.android.systemui.keyguard.ui.transitions.DeviceEntryIconTransition
+import com.android.systemui.scene.shared.model.Scenes
import javax.inject.Inject
import kotlin.time.Duration.Companion.milliseconds
import kotlinx.coroutines.ExperimentalCoroutinesApi
@@ -39,11 +42,14 @@
animationFlow: KeyguardTransitionAnimationFlow,
) : DeviceEntryIconTransition {
private val transitionAnimation =
- animationFlow.setup(
- duration = FromPrimaryBouncerTransitionInteractor.TO_LOCKSCREEN_DURATION,
- from = KeyguardState.PRIMARY_BOUNCER,
- to = KeyguardState.LOCKSCREEN,
- )
+ animationFlow
+ .setup(
+ duration = FromPrimaryBouncerTransitionInteractor.TO_LOCKSCREEN_DURATION,
+ edge = Edge.create(from = Scenes.Bouncer, to = LOCKSCREEN),
+ )
+ .setupWithoutSceneContainer(
+ edge = Edge.create(from = PRIMARY_BOUNCER, to = LOCKSCREEN),
+ )
val shortcutsAlpha: Flow<Float> =
transitionAnimation.sharedFlow(
diff --git a/packages/SystemUI/src/com/android/systemui/media/controls/domain/pipeline/LegacyMediaDataFilterImpl.kt b/packages/SystemUI/src/com/android/systemui/media/controls/domain/pipeline/LegacyMediaDataFilterImpl.kt
index c02478b..96ef7d2 100644
--- a/packages/SystemUI/src/com/android/systemui/media/controls/domain/pipeline/LegacyMediaDataFilterImpl.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/controls/domain/pipeline/LegacyMediaDataFilterImpl.kt
@@ -206,11 +206,11 @@
listeners.forEach { it.onSmartspaceMediaDataLoaded(key, data, shouldPrioritizeMutable) }
}
- override fun onMediaDataRemoved(key: String) {
+ override fun onMediaDataRemoved(key: String, userInitiated: Boolean) {
allEntries.remove(key)
userEntries.remove(key)?.let {
// Only notify listeners if something actually changed
- listeners.forEach { it.onMediaDataRemoved(key) }
+ listeners.forEach { it.onMediaDataRemoved(key, userInitiated) }
}
}
@@ -246,7 +246,7 @@
// Only remove media when the profile is unavailable.
if (DEBUG) Log.d(TAG, "Removing $key after profile change")
userEntries.remove(key, data)
- listeners.forEach { listener -> listener.onMediaDataRemoved(key) }
+ listeners.forEach { listener -> listener.onMediaDataRemoved(key, false) }
}
}
}
@@ -261,7 +261,7 @@
userEntries.clear()
keyCopy.forEach {
if (DEBUG) Log.d(TAG, "Removing $it after user change")
- listenersCopy.forEach { listener -> listener.onMediaDataRemoved(it) }
+ listenersCopy.forEach { listener -> listener.onMediaDataRemoved(it, false) }
}
allEntries.forEach { (key, data) ->
diff --git a/packages/SystemUI/src/com/android/systemui/media/controls/domain/pipeline/LegacyMediaDataManagerImpl.kt b/packages/SystemUI/src/com/android/systemui/media/controls/domain/pipeline/LegacyMediaDataManagerImpl.kt
index 3a831156..143d66b 100644
--- a/packages/SystemUI/src/com/android/systemui/media/controls/domain/pipeline/LegacyMediaDataManagerImpl.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/controls/domain/pipeline/LegacyMediaDataManagerImpl.kt
@@ -545,8 +545,8 @@
* External listeners registered with [addListener] will be notified after the event propagates
* through the internal listener pipeline.
*/
- private fun notifyMediaDataRemoved(key: String) {
- internalListeners.forEach { it.onMediaDataRemoved(key) }
+ private fun notifyMediaDataRemoved(key: String, userInitiated: Boolean = false) {
+ internalListeners.forEach { it.onMediaDataRemoved(key, userInitiated) }
}
/**
@@ -578,7 +578,7 @@
if (it.active == !timedOut && !forceUpdate) {
if (it.resumption) {
if (DEBUG) Log.d(TAG, "timing out resume player $key")
- dismissMediaData(key, 0L /* delay */)
+ dismissMediaData(key, delay = 0L, userInitiated = false)
}
return
}
@@ -627,17 +627,17 @@
}
}
- private fun removeEntry(key: String, logEvent: Boolean = true) {
+ private fun removeEntry(key: String, logEvent: Boolean = true, userInitiated: Boolean = false) {
mediaEntries.remove(key)?.let {
if (logEvent) {
logger.logMediaRemoved(it.appUid, it.packageName, it.instanceId)
}
}
- notifyMediaDataRemoved(key)
+ notifyMediaDataRemoved(key, userInitiated)
}
/** Dismiss a media entry. Returns false if the key was not found. */
- override fun dismissMediaData(key: String, delay: Long): Boolean {
+ override fun dismissMediaData(key: String, delay: Long, userInitiated: Boolean): Boolean {
val existed = mediaEntries[key] != null
backgroundExecutor.execute {
mediaEntries[key]?.let { mediaData ->
@@ -649,7 +649,10 @@
}
}
}
- foregroundExecutor.executeDelayed({ removeEntry(key) }, delay)
+ foregroundExecutor.executeDelayed(
+ { removeEntry(key = key, userInitiated = userInitiated) },
+ delay
+ )
return existed
}
diff --git a/packages/SystemUI/src/com/android/systemui/media/controls/domain/pipeline/MediaDataCombineLatest.kt b/packages/SystemUI/src/com/android/systemui/media/controls/domain/pipeline/MediaDataCombineLatest.kt
index ad70db5..88910f9 100644
--- a/packages/SystemUI/src/com/android/systemui/media/controls/domain/pipeline/MediaDataCombineLatest.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/controls/domain/pipeline/MediaDataCombineLatest.kt
@@ -53,8 +53,8 @@
listeners.toSet().forEach { it.onSmartspaceMediaDataLoaded(key, data) }
}
- override fun onMediaDataRemoved(key: String) {
- remove(key)
+ override fun onMediaDataRemoved(key: String, userInitiated: Boolean) {
+ remove(key, userInitiated)
}
override fun onSmartspaceMediaDataRemoved(key: String, immediately: Boolean) {
@@ -71,8 +71,8 @@
}
}
- override fun onKeyRemoved(key: String) {
- remove(key)
+ override fun onKeyRemoved(key: String, userInitiated: Boolean) {
+ remove(key, userInitiated)
}
/**
@@ -92,10 +92,10 @@
}
}
- private fun remove(key: String) {
+ private fun remove(key: String, userInitiated: Boolean) {
entries.remove(key)?.let {
val listenersCopy = listeners.toSet()
- listenersCopy.forEach { it.onMediaDataRemoved(key) }
+ listenersCopy.forEach { it.onMediaDataRemoved(key, userInitiated) }
}
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/media/controls/domain/pipeline/MediaDataFilterImpl.kt b/packages/SystemUI/src/com/android/systemui/media/controls/domain/pipeline/MediaDataFilterImpl.kt
index 5432a18..8d19ce8 100644
--- a/packages/SystemUI/src/com/android/systemui/media/controls/domain/pipeline/MediaDataFilterImpl.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/controls/domain/pipeline/MediaDataFilterImpl.kt
@@ -213,7 +213,7 @@
listeners.forEach { it.onSmartspaceMediaDataLoaded(key, data, shouldPrioritizeMutable) }
}
- override fun onMediaDataRemoved(key: String) {
+ override fun onMediaDataRemoved(key: String, userInitiated: Boolean) {
mediaFilterRepository.removeMediaEntry(key)?.let { mediaData ->
val instanceId = mediaData.instanceId
mediaFilterRepository.removeSelectedUserMediaEntry(instanceId)?.let {
@@ -221,7 +221,7 @@
MediaDataLoadingModel.Removed(instanceId)
)
// Only notify listeners if something actually changed
- listeners.forEach { it.onMediaDataRemoved(key) }
+ listeners.forEach { it.onMediaDataRemoved(key, userInitiated) }
}
}
}
@@ -270,7 +270,7 @@
mediaFilterRepository.addMediaDataLoadingState(
MediaDataLoadingModel.Removed(data.instanceId)
)
- listeners.forEach { listener -> listener.onMediaDataRemoved(key) }
+ listeners.forEach { listener -> listener.onMediaDataRemoved(key, false) }
}
}
}
@@ -288,7 +288,7 @@
MediaDataLoadingModel.Removed(instanceId)
)
getKey(instanceId)?.let {
- listenersCopy.forEach { listener -> listener.onMediaDataRemoved(it) }
+ listenersCopy.forEach { listener -> listener.onMediaDataRemoved(it, false) }
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/media/controls/domain/pipeline/MediaDataManager.kt b/packages/SystemUI/src/com/android/systemui/media/controls/domain/pipeline/MediaDataManager.kt
index 2331aa21..8099e59 100644
--- a/packages/SystemUI/src/com/android/systemui/media/controls/domain/pipeline/MediaDataManager.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/controls/domain/pipeline/MediaDataManager.kt
@@ -60,7 +60,7 @@
)
/** Dismiss a media entry. Returns false if the key was not found. */
- fun dismissMediaData(key: String, delay: Long): Boolean
+ fun dismissMediaData(key: String, delay: Long, userInitiated: Boolean): Boolean
/**
* Called whenever the recommendation has been expired or removed by the user. This will remove
@@ -136,7 +136,7 @@
) {}
/** Called whenever a previously existing Media notification was removed. */
- override fun onMediaDataRemoved(key: String) {}
+ override fun onMediaDataRemoved(key: String, userInitiated: Boolean) {}
/**
* Called whenever a previously existing Smartspace media data was removed.
diff --git a/packages/SystemUI/src/com/android/systemui/media/controls/domain/pipeline/MediaDataProcessor.kt b/packages/SystemUI/src/com/android/systemui/media/controls/domain/pipeline/MediaDataProcessor.kt
index 1d7c025..eed7752 100644
--- a/packages/SystemUI/src/com/android/systemui/media/controls/domain/pipeline/MediaDataProcessor.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/controls/domain/pipeline/MediaDataProcessor.kt
@@ -498,8 +498,8 @@
* External listeners registered with [MediaCarouselInteractor.addListener] will be notified
* after the event propagates through the internal listener pipeline.
*/
- private fun notifyMediaDataRemoved(key: String) {
- internalListeners.forEach { it.onMediaDataRemoved(key) }
+ private fun notifyMediaDataRemoved(key: String, userInitiated: Boolean = false) {
+ internalListeners.forEach { it.onMediaDataRemoved(key, userInitiated) }
}
/**
@@ -531,7 +531,7 @@
if (it.active == !timedOut && !forceUpdate) {
if (it.resumption) {
if (DEBUG) Log.d(TAG, "timing out resume player $key")
- dismissMediaData(key, 0L /* delay */)
+ dismissMediaData(key, delayMs = 0L, userInitiated = false)
}
return
}
@@ -580,17 +580,17 @@
}
}
- private fun removeEntry(key: String, logEvent: Boolean = true) {
+ private fun removeEntry(key: String, logEvent: Boolean = true, userInitiated: Boolean = false) {
mediaDataRepository.removeMediaEntry(key)?.let {
if (logEvent) {
logger.logMediaRemoved(it.appUid, it.packageName, it.instanceId)
}
}
- notifyMediaDataRemoved(key)
+ notifyMediaDataRemoved(key, userInitiated)
}
/** Dismiss a media entry. Returns false if the key was not found. */
- fun dismissMediaData(key: String, delayMs: Long): Boolean {
+ fun dismissMediaData(key: String, delayMs: Long, userInitiated: Boolean): Boolean {
val existed = mediaDataRepository.mediaEntries.value[key] != null
backgroundExecutor.execute {
mediaDataRepository.mediaEntries.value[key]?.let { mediaData ->
@@ -602,16 +602,19 @@
}
}
}
- foregroundExecutor.executeDelayed({ removeEntry(key) }, delayMs)
+ foregroundExecutor.executeDelayed(
+ { removeEntry(key, userInitiated = userInitiated) },
+ delayMs
+ )
return existed
}
/** Dismiss a media entry. Returns false if the corresponding key was not found. */
- fun dismissMediaData(instanceId: InstanceId, delayMs: Long): Boolean {
+ fun dismissMediaData(instanceId: InstanceId, delayMs: Long, userInitiated: Boolean): Boolean {
val mediaEntries = mediaDataRepository.mediaEntries.value
val filteredEntries = mediaEntries.filter { (_, data) -> data.instanceId == instanceId }
return if (filteredEntries.isNotEmpty()) {
- dismissMediaData(filteredEntries.keys.first(), delayMs)
+ dismissMediaData(filteredEntries.keys.first(), delayMs, userInitiated)
} else {
false
}
@@ -1579,7 +1582,7 @@
) {}
/** Called whenever a previously existing Media notification was removed. */
- fun onMediaDataRemoved(key: String) {}
+ fun onMediaDataRemoved(key: String, userInitiated: Boolean) {}
/**
* Called whenever a previously existing Smartspace media data was removed.
diff --git a/packages/SystemUI/src/com/android/systemui/media/controls/domain/pipeline/MediaDeviceManager.kt b/packages/SystemUI/src/com/android/systemui/media/controls/domain/pipeline/MediaDeviceManager.kt
index 0e2814b..043fbfa 100644
--- a/packages/SystemUI/src/com/android/systemui/media/controls/domain/pipeline/MediaDeviceManager.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/controls/domain/pipeline/MediaDeviceManager.kt
@@ -111,10 +111,10 @@
}
}
- override fun onMediaDataRemoved(key: String) {
+ override fun onMediaDataRemoved(key: String, userInitiated: Boolean) {
val token = entries.remove(key)
token?.stop()
- token?.let { listeners.forEach { it.onKeyRemoved(key) } }
+ token?.let { listeners.forEach { it.onKeyRemoved(key, userInitiated) } }
}
fun dump(pw: PrintWriter) {
@@ -136,7 +136,7 @@
/** Called when the route has changed for a given notification. */
fun onMediaDeviceChanged(key: String, oldKey: String?, data: MediaDeviceData?)
/** Called when the notification was removed. */
- fun onKeyRemoved(key: String)
+ fun onKeyRemoved(key: String, userInitiated: Boolean)
}
private inner class Entry(
diff --git a/packages/SystemUI/src/com/android/systemui/media/controls/domain/pipeline/MediaSessionBasedFilter.kt b/packages/SystemUI/src/com/android/systemui/media/controls/domain/pipeline/MediaSessionBasedFilter.kt
index b2a8f2e..b178d84 100644
--- a/packages/SystemUI/src/com/android/systemui/media/controls/domain/pipeline/MediaSessionBasedFilter.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/controls/domain/pipeline/MediaSessionBasedFilter.kt
@@ -137,7 +137,7 @@
// farther and dismiss the media data so that media controls for the local session
// don't hang around while casting.
if (!keyedTokens.get(key)!!.contains(TokenId(remote.sessionToken))) {
- dispatchMediaDataRemoved(key)
+ dispatchMediaDataRemoved(key, userInitiated = false)
}
}
}
@@ -151,11 +151,11 @@
backgroundExecutor.execute { dispatchSmartspaceMediaDataLoaded(key, data) }
}
- override fun onMediaDataRemoved(key: String) {
+ override fun onMediaDataRemoved(key: String, userInitiated: Boolean) {
// Queue on background thread to ensure ordering of loaded and removed events is maintained.
backgroundExecutor.execute {
keyedTokens.remove(key)
- dispatchMediaDataRemoved(key)
+ dispatchMediaDataRemoved(key, userInitiated)
}
}
@@ -174,8 +174,10 @@
}
}
- private fun dispatchMediaDataRemoved(key: String) {
- foregroundExecutor.execute { listeners.toSet().forEach { it.onMediaDataRemoved(key) } }
+ private fun dispatchMediaDataRemoved(key: String, userInitiated: Boolean) {
+ foregroundExecutor.execute {
+ listeners.toSet().forEach { it.onMediaDataRemoved(key, userInitiated) }
+ }
}
private fun dispatchSmartspaceMediaDataLoaded(key: String, info: SmartspaceMediaData) {
diff --git a/packages/SystemUI/src/com/android/systemui/media/controls/domain/pipeline/MediaTimeoutListener.kt b/packages/SystemUI/src/com/android/systemui/media/controls/domain/pipeline/MediaTimeoutListener.kt
index 29f3967..fc31903 100644
--- a/packages/SystemUI/src/com/android/systemui/media/controls/domain/pipeline/MediaTimeoutListener.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/controls/domain/pipeline/MediaTimeoutListener.kt
@@ -169,7 +169,7 @@
mediaListeners[key] = PlaybackStateListener(key, data)
}
- override fun onMediaDataRemoved(key: String) {
+ override fun onMediaDataRemoved(key: String, userInitiated: Boolean) {
mediaListeners.remove(key)?.destroy()
}
diff --git a/packages/SystemUI/src/com/android/systemui/media/controls/domain/pipeline/interactor/MediaCarouselInteractor.kt b/packages/SystemUI/src/com/android/systemui/media/controls/domain/pipeline/interactor/MediaCarouselInteractor.kt
index c888935..9e62300 100644
--- a/packages/SystemUI/src/com/android/systemui/media/controls/domain/pipeline/interactor/MediaCarouselInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/controls/domain/pipeline/interactor/MediaCarouselInteractor.kt
@@ -205,12 +205,12 @@
)
}
- override fun dismissMediaData(key: String, delay: Long): Boolean {
- return mediaDataProcessor.dismissMediaData(key, delay)
+ override fun dismissMediaData(key: String, delay: Long, userInitiated: Boolean): Boolean {
+ return mediaDataProcessor.dismissMediaData(key, delay, userInitiated)
}
fun removeMediaControl(instanceId: InstanceId, delay: Long) {
- mediaDataProcessor.dismissMediaData(instanceId, delay)
+ mediaDataProcessor.dismissMediaData(instanceId, delay, userInitiated = false)
}
override fun dismissSmartspaceRecommendation(key: String, delay: Long) {
diff --git a/packages/SystemUI/src/com/android/systemui/media/controls/domain/pipeline/interactor/MediaControlInteractor.kt b/packages/SystemUI/src/com/android/systemui/media/controls/domain/pipeline/interactor/MediaControlInteractor.kt
index 9f2d132..1a0f582 100644
--- a/packages/SystemUI/src/com/android/systemui/media/controls/domain/pipeline/interactor/MediaControlInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/controls/domain/pipeline/interactor/MediaControlInteractor.kt
@@ -21,9 +21,7 @@
import android.app.PendingIntent
import android.content.Context
import android.content.Intent
-import android.media.session.MediaController
import android.media.session.MediaSession
-import android.media.session.PlaybackState
import android.provider.Settings
import android.util.Log
import com.android.internal.jank.Cuj
@@ -42,7 +40,6 @@
import com.android.systemui.plugins.ActivityStarter
import com.android.systemui.statusbar.NotificationLockscreenUserManager
import com.android.systemui.statusbar.policy.KeyguardStateController
-import com.android.systemui.util.kotlin.pairwiseBy
import dagger.assisted.Assisted
import dagger.assisted.AssistedInject
import kotlinx.coroutines.flow.Flow
@@ -70,19 +67,6 @@
.map { entries -> entries[instanceId]?.let { toMediaControlModel(it) } }
.distinctUntilChanged()
- val isStartedPlaying: Flow<Boolean> =
- mediaControl
- .map { mediaControl ->
- mediaControl?.token?.let { token ->
- MediaController(applicationContext, token).playbackState?.let {
- it.state == PlaybackState.STATE_PLAYING
- }
- }
- ?: false
- }
- .pairwiseBy(initialValue = false) { wasPlaying, isPlaying -> !wasPlaying && isPlaying }
- .distinctUntilChanged()
-
val onAnyMediaConfigurationChange: Flow<Unit> = repository.onAnyMediaConfigurationChange
fun removeMediaControl(
@@ -90,7 +74,8 @@
instanceId: InstanceId,
delayMs: Long
): Boolean {
- val dismissed = mediaDataProcessor.dismissMediaData(instanceId, delayMs)
+ val dismissed =
+ mediaDataProcessor.dismissMediaData(instanceId, delayMs, userInitiated = true)
if (!dismissed) {
Log.w(
TAG,
diff --git a/packages/SystemUI/src/com/android/systemui/media/controls/ui/binder/MediaControlViewBinder.kt b/packages/SystemUI/src/com/android/systemui/media/controls/ui/binder/MediaControlViewBinder.kt
index 73fb558..fed93f0 100644
--- a/packages/SystemUI/src/com/android/systemui/media/controls/ui/binder/MediaControlViewBinder.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/controls/ui/binder/MediaControlViewBinder.kt
@@ -260,44 +260,50 @@
}
SEMANTIC_ACTIONS_ALL.forEachIndexed { index, id ->
- val button = viewHolder.getAction(id)
- val actionViewModel = viewModel.actionButtons[index]
- if (button.id == R.id.actionPrev) {
- actionViewModel?.let {
- viewController.setUpPrevButtonInfo(true, it.notVisibleValue)
- }
- } else if (button.id == R.id.actionNext) {
- actionViewModel?.let {
- viewController.setUpNextButtonInfo(true, it.notVisibleValue)
- }
+ val buttonView = viewHolder.getAction(id)
+ val buttonModel = viewModel.actionButtons[index]
+ if (buttonView.id == R.id.actionPrev) {
+ viewController.setUpPrevButtonInfo(
+ buttonModel.isEnabled,
+ buttonModel.notVisibleValue
+ )
+ } else if (buttonView.id == R.id.actionNext) {
+ viewController.setUpNextButtonInfo(
+ buttonModel.isEnabled,
+ buttonModel.notVisibleValue
+ )
}
- actionViewModel?.let { action ->
- val animHandler = (button.tag ?: AnimationBindHandler()) as AnimationBindHandler
- animHandler.tryExecute {
- if (animHandler.updateRebindId(action.rebindId)) {
+ val animHandler = (buttonView.tag ?: AnimationBindHandler()) as AnimationBindHandler
+ animHandler.tryExecute {
+ if (buttonModel.isEnabled) {
+ if (animHandler.updateRebindId(buttonModel.rebindId)) {
animHandler.unregisterAll()
- animHandler.tryRegister(action.icon)
- animHandler.tryRegister(action.background)
+ animHandler.tryRegister(buttonModel.icon)
+ animHandler.tryRegister(buttonModel.background)
bindButtonCommon(
- button,
+ buttonView,
viewHolder.multiRippleView,
- action,
+ buttonModel,
viewController,
falsingManager,
)
}
- val visible = action.isVisibleWhenScrubbing || !viewController.isScrubbing
- setSemanticButtonVisibleAndAlpha(
- viewHolder.getAction(id),
- viewController.expandedLayout,
- viewController.collapsedLayout,
- visible,
- action.notVisibleValue,
- action.showInCollapsed
- )
+ } else {
+ animHandler.unregisterAll()
+ clearButton(buttonView)
}
+ val visible =
+ buttonModel.isEnabled &&
+ (buttonModel.isVisibleWhenScrubbing || !viewController.isScrubbing)
+ setSemanticButtonVisibleAndAlpha(
+ viewHolder.getAction(id),
+ viewController.expandedLayout,
+ viewController.collapsedLayout,
+ visible,
+ buttonModel.notVisibleValue,
+ buttonModel.showInCollapsed
+ )
}
- ?: clearButton(button)
}
} else {
// Hide buttons that only appear for semantic actions
@@ -309,22 +315,16 @@
// Set all generic buttons
genericButtons.forEachIndexed { index, button ->
if (index < viewModel.actionButtons.size) {
- viewModel.actionButtons[index]?.let { action ->
- bindButtonCommon(
- button,
- viewHolder.multiRippleView,
- action,
- viewController,
- falsingManager,
- )
- setVisibleAndAlpha(expandedSet, button.id, visible = true)
- setVisibleAndAlpha(
- collapsedSet,
- button.id,
- visible = action.showInCollapsed
- )
- }
- ?: clearButton(button)
+ val action = viewModel.actionButtons[index]
+ bindButtonCommon(
+ button,
+ viewHolder.multiRippleView,
+ action,
+ viewController,
+ falsingManager,
+ )
+ setVisibleAndAlpha(expandedSet, button.id, visible = true)
+ setVisibleAndAlpha(collapsedSet, button.id, visible = action.showInCollapsed)
} else {
// Hide any unused buttons
clearButton(button)
diff --git a/packages/SystemUI/src/com/android/systemui/media/controls/ui/controller/MediaCarouselController.kt b/packages/SystemUI/src/com/android/systemui/media/controls/ui/controller/MediaCarouselController.kt
index 45b68ca..19e3e07 100644
--- a/packages/SystemUI/src/com/android/systemui/media/controls/ui/controller/MediaCarouselController.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/controls/ui/controller/MediaCarouselController.kt
@@ -46,6 +46,7 @@
import com.android.systemui.dagger.qualifiers.Main
import com.android.systemui.dump.DumpManager
import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor
+import com.android.systemui.keyguard.shared.model.Edge
import com.android.systemui.keyguard.shared.model.KeyguardState
import com.android.systemui.keyguard.shared.model.KeyguardState.GONE
import com.android.systemui.keyguard.shared.model.KeyguardState.LOCKSCREEN
@@ -73,6 +74,9 @@
import com.android.systemui.plugins.FalsingManager
import com.android.systemui.qs.PageIndicator
import com.android.systemui.res.R
+import com.android.systemui.scene.domain.interactor.SceneInteractor
+import com.android.systemui.scene.shared.flag.SceneContainerFlag
+import com.android.systemui.scene.shared.model.Scenes
import com.android.systemui.shared.system.SysUiStatsLog
import com.android.systemui.shared.system.SysUiStatsLog.SMARTSPACE_CARD_REPORTED
import com.android.systemui.shared.system.SysUiStatsLog.SMART_SPACE_CARD_REPORTED__CARD_TYPE__UNKNOWN_CARD
@@ -142,6 +146,7 @@
private val secureSettings: SecureSettings,
private val mediaCarouselViewModel: MediaCarouselViewModel,
private val mediaViewControllerFactory: Provider<MediaViewController>,
+ private val sceneInteractor: SceneInteractor,
) : Dumpable {
/** The current width of the carousel */
var currentCarouselWidth: Int = 0
@@ -190,9 +195,11 @@
@VisibleForTesting
lateinit var settingsButton: View
private set
+
private val mediaContent: ViewGroup
@VisibleForTesting var pageIndicator: PageIndicator
private var needsReordering: Boolean = false
+ private var isUserInitiatedRemovalQueued: Boolean = false
private var keysNeedRemoval = mutableSetOf<String>()
var shouldScrollToKey: Boolean = false
private var isRtl: Boolean = false
@@ -301,7 +308,11 @@
* It will be called when the container is out of view.
*/
lateinit var updateUserVisibility: () -> Unit
- lateinit var updateHostVisibility: () -> Unit
+ var updateHostVisibility: () -> Unit = {}
+ set(value) {
+ field = value
+ mediaCarouselViewModel.updateHostVisibility = value
+ }
private val isReorderingAllowed: Boolean
get() = visualStabilityProvider.isReorderingAllowed
@@ -338,6 +349,20 @@
configurationController.addCallback(configListener)
if (!mediaFlags.isMediaControlsRefactorEnabled()) {
setUpListeners()
+ } else {
+ val visualStabilityCallback = OnReorderingAllowedListener {
+ mediaCarouselViewModel.onReorderingAllowed()
+
+ // Update user visibility so that no extra impression will be logged when
+ // activeMediaIndex resets to 0
+ if (this::updateUserVisibility.isInitialized) {
+ updateUserVisibility()
+ }
+
+ // Let's reset our scroll position
+ mediaCarouselScrollHandler.scrollToStart()
+ }
+ visualStabilityProvider.addPersistentReorderingAllowedListener(visualStabilityCallback)
}
mediaFrame.addOnLayoutChangeListener { _, _, _, _, _, _, _, _, _ ->
// The pageIndicator is not laid out yet when we get the current state update,
@@ -359,8 +384,6 @@
)
keyguardUpdateMonitor.registerCallback(keyguardUpdateMonitorCallback)
mediaCarousel.repeatWhenAttached {
- mediaCarouselViewModel.onAttached()
- mediaCarouselScrollHandler.scrollToStart()
repeatOnLifecycle(Lifecycle.State.STARTED) {
listenForAnyStateToGoneKeyguardTransition(this)
listenForAnyStateToLockscreenTransition(this)
@@ -385,12 +408,15 @@
reorderAllPlayers(previousVisiblePlayerKey = null)
}
- keysNeedRemoval.forEach { removePlayer(it) }
+ keysNeedRemoval.forEach {
+ removePlayer(it, userInitiated = isUserInitiatedRemovalQueued)
+ }
if (keysNeedRemoval.size > 0) {
// Carousel visibility may need to be updated after late removals
updateHostVisibility()
}
keysNeedRemoval.clear()
+ isUserInitiatedRemovalQueued = false
// Update user visibility so that no extra impression will be logged when
// activeMediaIndex resets to 0
@@ -474,18 +500,18 @@
val canRemove = data.isPlaying?.let { !it } ?: data.isClearable && !data.active
if (canRemove && !Utils.useMediaResumption(context)) {
- // This view isn't playing, let's remove this! This happens e.g. when
- // dismissing/timing out a view. We still have the data around because
- // resumption could be on, but we should save the resources and release
- // this.
+ // This media control is both paused and timed out, and the resumption
+ // setting is off - let's remove it
if (isReorderingAllowed) {
- onMediaDataRemoved(key)
+ onMediaDataRemoved(key, userInitiated = MediaPlayerData.isSwipedAway)
} else {
+ isUserInitiatedRemovalQueued = MediaPlayerData.isSwipedAway
keysNeedRemoval.add(key)
}
} else {
keysNeedRemoval.remove(key)
}
+ MediaPlayerData.isSwipedAway = false
}
override fun onSmartspaceMediaDataLoaded(
@@ -565,11 +591,12 @@
addSmartspaceMediaRecommendations(key, data, shouldPrioritize)
}
}
+ MediaPlayerData.isSwipedAway = false
}
- override fun onMediaDataRemoved(key: String) {
- debugLogger.logMediaRemoved(key)
- removePlayer(key)
+ override fun onMediaDataRemoved(key: String, userInitiated: Boolean) {
+ debugLogger.logMediaRemoved(key, userInitiated)
+ removePlayer(key, userInitiated = userInitiated)
}
override fun onSmartspaceMediaDataRemoved(key: String, immediately: Boolean) {
@@ -579,9 +606,7 @@
if (!immediately) {
// Although it wasn't requested, we were able to process the removal
// immediately since reordering is allowed. So, notify hosts to update
- if (this@MediaCarouselController::updateHostVisibility.isInitialized) {
- updateHostVisibility()
- }
+ updateHostVisibility()
}
} else {
keysNeedRemoval.add(key)
@@ -632,9 +657,13 @@
@VisibleForTesting
internal fun listenForAnyStateToGoneKeyguardTransition(scope: CoroutineScope): Job {
return scope.launch {
- keyguardTransitionInteractor
- .transition(to = GONE)
- .filter { it.transitionState == TransitionState.FINISHED }
+ if (SceneContainerFlag.isEnabled) {
+ sceneInteractor.transitionState.filter { it.isIdle(Scenes.Gone) }
+ } else {
+ keyguardTransitionInteractor.transition(Edge.create(to = GONE)).filter {
+ it.transitionState == TransitionState.FINISHED
+ }
+ }
.collect {
showMediaCarousel()
updateHostVisibility()
@@ -646,7 +675,7 @@
internal fun listenForAnyStateToLockscreenTransition(scope: CoroutineScope): Job {
return scope.launch {
keyguardTransitionInteractor
- .transition(to = LOCKSCREEN)
+ .transition(Edge.create(to = LOCKSCREEN))
.filter { it.transitionState == TransitionState.FINISHED }
.collect {
if (!allowMediaPlayerOnLockScreen) {
@@ -734,6 +763,7 @@
}
}
viewController.setListening(mediaCarouselScrollHandler.visibleToUser && currentlyExpanded)
+ controllerByViewModel[commonViewModel] = viewController
updateViewControllerToState(viewController, noAnimation = true)
updatePageIndicator()
if (
@@ -747,7 +777,6 @@
mediaCarouselScrollHandler.onPlayersChanged()
mediaFrame.requiresRemeasuring = true
commonViewModel.onAdded(commonViewModel)
- controllerByViewModel[commonViewModel] = viewController
}
private fun onUpdated(commonViewModel: MediaCommonViewModel) {
@@ -1033,7 +1062,8 @@
fun removePlayer(
key: String,
dismissMediaData: Boolean = true,
- dismissRecommendation: Boolean = true
+ dismissRecommendation: Boolean = true,
+ userInitiated: Boolean = false,
): MediaControlPanel? {
if (key == MediaPlayerData.smartspaceMediaKey()) {
MediaPlayerData.smartspaceMediaData?.let {
@@ -1052,7 +1082,7 @@
if (dismissMediaData) {
// Inform the media manager of a potentially late dismissal
- mediaManager.dismissMediaData(key, delay = 0L)
+ mediaManager.dismissMediaData(key, delay = 0L, userInitiated = userInitiated)
}
if (dismissRecommendation) {
// Inform the media manager of a potentially late dismissal
@@ -1512,7 +1542,8 @@
}
}
- private fun onSwipeToDismiss() {
+ @VisibleForTesting
+ fun onSwipeToDismiss() {
if (mediaFlags.isMediaControlsRefactorEnabled()) {
mediaCarouselViewModel.onSwipeToDismiss()
return
@@ -1531,6 +1562,7 @@
it.mIsImpressed = false
}
}
+ MediaPlayerData.isSwipedAway = true
logger.logSwipeDismiss()
mediaManager.onSwipeToDismiss()
}
@@ -1557,6 +1589,7 @@
"state: ${desiredHostState?.expansion}, " +
"only active ${desiredHostState?.showsOnlyActiveMedia}"
)
+ println("isSwipedAway: ${MediaPlayerData.isSwipedAway}")
}
}
}
@@ -1587,6 +1620,7 @@
// Whether should prioritize Smartspace card.
internal var shouldPrioritizeSs: Boolean = false
private set
+
internal var smartspaceMediaData: SmartspaceMediaData? = null
private set
@@ -1595,7 +1629,7 @@
val data: MediaData,
val key: String,
val updateTime: Long = 0,
- val isSsReactivated: Boolean = false
+ val isSsReactivated: Boolean = false,
)
private val comparator =
@@ -1620,6 +1654,9 @@
// A map that tracks order of visible media players before they get reordered.
private val visibleMediaPlayers = LinkedHashMap<String, MediaSortKey>()
+ // Whether the user swiped away the carousel since its last update
+ internal var isSwipedAway: Boolean = false
+
fun addMediaPlayer(
key: String,
data: MediaData,
diff --git a/packages/SystemUI/src/com/android/systemui/media/controls/ui/controller/MediaCarouselControllerLogger.kt b/packages/SystemUI/src/com/android/systemui/media/controls/ui/controller/MediaCarouselControllerLogger.kt
index ebf1c6a..1be25a7 100644
--- a/packages/SystemUI/src/com/android/systemui/media/controls/ui/controller/MediaCarouselControllerLogger.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/controls/ui/controller/MediaCarouselControllerLogger.kt
@@ -53,8 +53,16 @@
{ "add player $str1, active: $bool1" }
)
- fun logMediaRemoved(key: String) =
- buffer.log(TAG, LogLevel.DEBUG, { str1 = key }, { "removing player $str1" })
+ fun logMediaRemoved(key: String, userInitiated: Boolean) =
+ buffer.log(
+ TAG,
+ LogLevel.DEBUG,
+ {
+ str1 = key
+ bool1 = userInitiated
+ },
+ { "removing player $str1, by user $bool1" }
+ )
fun logRecommendationLoaded(key: String, isActive: Boolean) =
buffer.log(
diff --git a/packages/SystemUI/src/com/android/systemui/media/controls/ui/controller/MediaControlPanel.java b/packages/SystemUI/src/com/android/systemui/media/controls/ui/controller/MediaControlPanel.java
index e6c785e..0bc3c439 100644
--- a/packages/SystemUI/src/com/android/systemui/media/controls/ui/controller/MediaControlPanel.java
+++ b/packages/SystemUI/src/com/android/systemui/media/controls/ui/controller/MediaControlPanel.java
@@ -786,10 +786,11 @@
if (mKey != null) {
closeGuts();
if (!mMediaDataManagerLazy.get().dismissMediaData(mKey,
- MediaViewController.GUTS_ANIMATION_DURATION + 100)) {
+ /* delay */ MediaViewController.GUTS_ANIMATION_DURATION + 100,
+ /* userInitiated */ true)) {
Log.w(TAG, "Manager failed to dismiss media " + mKey);
// Remove directly from carousel so user isn't stuck with defunct controls
- mMediaCarouselController.removePlayer(mKey, false, false);
+ mMediaCarouselController.removePlayer(mKey, false, false, true);
}
} else {
Log.w(TAG, "Dismiss media with null notification. Token uid="
diff --git a/packages/SystemUI/src/com/android/systemui/media/controls/ui/controller/MediaViewController.kt b/packages/SystemUI/src/com/android/systemui/media/controls/ui/controller/MediaViewController.kt
index 2b59858..3837708 100644
--- a/packages/SystemUI/src/com/android/systemui/media/controls/ui/controller/MediaViewController.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/controls/ui/controller/MediaViewController.kt
@@ -709,12 +709,6 @@
// For Turbulence noise.
val loadingEffectView = mediaViewHolder.loadingEffectView
- turbulenceNoiseAnimationConfig =
- createTurbulenceNoiseConfig(
- loadingEffectView,
- turbulenceNoiseView,
- colorSchemeTransition
- )
noiseDrawCallback =
object : PaintDrawCallback {
override fun onDraw(paint: Paint) {
@@ -809,6 +803,14 @@
fun setUpTurbulenceNoise() {
if (!mediaFlags.isMediaControlsRefactorEnabled()) return
+ if (!this::turbulenceNoiseAnimationConfig.isInitialized) {
+ turbulenceNoiseAnimationConfig =
+ createTurbulenceNoiseConfig(
+ mediaViewHolder.loadingEffectView,
+ mediaViewHolder.turbulenceNoiseView,
+ colorSchemeTransition
+ )
+ }
if (Flags.shaderlibLoadingEffectRefactor()) {
if (!this::loadingEffect.isInitialized) {
loadingEffect =
diff --git a/packages/SystemUI/src/com/android/systemui/media/controls/ui/view/MediaHost.kt b/packages/SystemUI/src/com/android/systemui/media/controls/ui/view/MediaHost.kt
index eca76b6..91050c8 100644
--- a/packages/SystemUI/src/com/android/systemui/media/controls/ui/view/MediaHost.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/controls/ui/view/MediaHost.kt
@@ -105,7 +105,7 @@
updateViewVisibility()
}
- override fun onMediaDataRemoved(key: String) {
+ override fun onMediaDataRemoved(key: String, userInitiated: Boolean) {
updateViewVisibility()
}
diff --git a/packages/SystemUI/src/com/android/systemui/media/controls/ui/viewmodel/MediaCarouselViewModel.kt b/packages/SystemUI/src/com/android/systemui/media/controls/ui/viewmodel/MediaCarouselViewModel.kt
index fd5f445..4e90936 100644
--- a/packages/SystemUI/src/com/android/systemui/media/controls/ui/viewmodel/MediaCarouselViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/controls/ui/viewmodel/MediaCarouselViewModel.kt
@@ -57,12 +57,12 @@
val mediaItems: StateFlow<List<MediaCommonViewModel>> =
interactor.currentMedia
.map { sortedItems ->
- buildList {
+ val mediaList = buildList {
sortedItems.forEach { commonModel ->
// When view is started we should make sure to clean models that are pending
// removal.
// This action should only be triggered once.
- if (!isAttached || !modelsPendingRemoval.contains(commonModel)) {
+ if (!allowReorder || !modelsPendingRemoval.contains(commonModel)) {
when (commonModel) {
is MediaCommonModel.MediaControl -> add(toViewModel(commonModel))
is MediaCommonModel.MediaRecommendations ->
@@ -70,11 +70,16 @@
}
}
}
- if (isAttached) {
- modelsPendingRemoval.clear()
- }
- isAttached = false
}
+ if (allowReorder) {
+ if (modelsPendingRemoval.size > 0) {
+ updateHostVisibility()
+ }
+ modelsPendingRemoval.clear()
+ }
+ allowReorder = false
+
+ mediaList
}
.stateIn(
scope = applicationScope,
@@ -82,6 +87,8 @@
initialValue = emptyList(),
)
+ var updateHostVisibility: () -> Unit = {}
+
private val mediaControlByInstanceId =
mutableMapOf<InstanceId, MediaCommonViewModel.MediaControl>()
@@ -89,15 +96,15 @@
private var modelsPendingRemoval: MutableSet<MediaCommonModel> = mutableSetOf()
- private var isAttached = false
+ private var allowReorder = false
fun onSwipeToDismiss() {
logger.logSwipeDismiss()
interactor.onSwipeToDismiss()
}
- fun onAttached() {
- isAttached = true
+ fun onReorderingAllowed() {
+ allowReorder = true
interactor.reorderMedia()
}
@@ -194,7 +201,11 @@
) {
if (immediatelyRemove || isReorderingAllowed()) {
interactor.dismissSmartspaceRecommendation(commonModel.recsLoadingModel.key, 0L)
- // TODO if not immediate remove update host visibility
+ if (!immediatelyRemove) {
+ // Although it wasn't requested, we were able to process the removal
+ // immediately since reordering is allowed. So, notify hosts to update
+ updateHostVisibility()
+ }
} else {
modelsPendingRemoval.add(commonModel)
}
diff --git a/packages/SystemUI/src/com/android/systemui/media/controls/ui/viewmodel/MediaControlViewModel.kt b/packages/SystemUI/src/com/android/systemui/media/controls/ui/viewmodel/MediaControlViewModel.kt
index bc364c3..1944f07 100644
--- a/packages/SystemUI/src/com/android/systemui/media/controls/ui/viewmodel/MediaControlViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/controls/ui/viewmodel/MediaControlViewModel.kt
@@ -20,6 +20,7 @@
import android.content.pm.PackageManager
import android.media.session.MediaController
import android.media.session.MediaSession.Token
+import android.media.session.PlaybackState
import android.text.TextUtils
import android.util.Log
import androidx.constraintlayout.widget.ConstraintSet
@@ -40,16 +41,14 @@
import com.android.systemui.monet.ColorScheme
import com.android.systemui.monet.Style
import com.android.systemui.res.R
-import com.android.systemui.util.kotlin.sample
import java.util.concurrent.Executor
import kotlinx.coroutines.CoroutineDispatcher
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.flow.Flow
-import kotlinx.coroutines.flow.MutableStateFlow
-import kotlinx.coroutines.flow.combine
import kotlinx.coroutines.flow.distinctUntilChanged
import kotlinx.coroutines.flow.flatMapLatest
import kotlinx.coroutines.flow.flowOn
+import kotlinx.coroutines.flow.map
/** Models UI state and handles user input for a media control. */
class MediaControlViewModel(
@@ -60,31 +59,20 @@
private val logger: MediaUiEventLogger,
) {
- private val isAnyButtonClicked: MutableStateFlow<Boolean> = MutableStateFlow(false)
-
- private val playTurbulenceNoise: Flow<Boolean> =
- interactor.mediaControl.sample(
- combine(isAnyButtonClicked, interactor.isStartedPlaying) {
- isButtonClicked,
- isStartedPlaying ->
- isButtonClicked && isStartedPlaying
- }
- .distinctUntilChanged()
- )
-
@OptIn(ExperimentalCoroutinesApi::class)
val player: Flow<MediaPlayerViewModel?> =
interactor.onAnyMediaConfigurationChange
.flatMapLatest {
- combine(playTurbulenceNoise, interactor.mediaControl) {
- playTurbulenceNoise,
- mediaControl ->
- mediaControl?.let { toViewModel(it, playTurbulenceNoise) }
+ interactor.mediaControl.map { mediaControl ->
+ mediaControl?.let { toViewModel(it) }
}
}
.distinctUntilChanged()
.flowOn(backgroundDispatcher)
+ private var isPlaying = false
+ private var isAnyButtonClicked = false
+
private fun onDismissMediaData(
token: Token?,
uid: Int,
@@ -95,10 +83,8 @@
interactor.removeMediaControl(token, instanceId, MEDIA_PLAYER_ANIMATION_DELAY)
}
- private suspend fun toViewModel(
- model: MediaControlModel,
- playTurbulenceNoise: Boolean
- ): MediaPlayerViewModel? {
+ private suspend fun toViewModel(model: MediaControlModel): MediaPlayerViewModel? {
+ val mediaController = model.token?.let { MediaController(applicationContext, it) }
val wallpaperColors =
MediaArtworkHelper.getWallpaperColor(
applicationContext,
@@ -118,8 +104,14 @@
val gutsViewModel = toGutsViewModel(model, scheme)
+ // Set playing state
+ val wasPlaying = isPlaying
+ isPlaying =
+ mediaController?.playbackState?.let { it.state == PlaybackState.STATE_PLAYING } ?: false
+
// Resetting button clicks state.
- isAnyButtonClicked.value = false
+ val wasButtonClicked = isAnyButtonClicked
+ isAnyButtonClicked = false
return MediaPlayerViewModel(
contentDescription = { gutsVisible ->
@@ -144,7 +136,7 @@
shouldAddGradient = wallpaperColors != null,
colorScheme = scheme,
canShowTime = canShowScrubbingTimeViews(model.semanticActionButtons),
- playTurbulenceNoise = playTurbulenceNoise,
+ playTurbulenceNoise = isPlaying && !wasPlaying && wasButtonClicked,
useSemanticActions = model.semanticActionButtons != null,
actionButtons = toActionViewModels(model),
outputSwitcher = toOutputSwitcherViewModel(model),
@@ -168,9 +160,7 @@
seekBarViewModel.updateStaticProgress(model.resumeProgress)
} else {
backgroundExecutor.execute {
- seekBarViewModel.updateController(
- model.token?.let { MediaController(applicationContext, it) }
- )
+ seekBarViewModel.updateController(mediaController)
}
}
}
@@ -283,16 +273,17 @@
)
}
- private fun toActionViewModels(model: MediaControlModel): List<MediaActionViewModel?> {
+ private fun toActionViewModels(model: MediaControlModel): List<MediaActionViewModel> {
val semanticActionButtons =
model.semanticActionButtons?.let { mediaButton ->
- with(mediaButton) {
- val isScrubbingTimeEnabled = canShowScrubbingTimeViews(mediaButton)
- SEMANTIC_ACTIONS_ALL.map { buttonId ->
- getActionById(buttonId)?.let {
- toSemanticActionViewModel(model, it, buttonId, isScrubbingTimeEnabled)
- }
- }
+ val isScrubbingTimeEnabled = canShowScrubbingTimeViews(mediaButton)
+ SEMANTIC_ACTIONS_ALL.map { buttonId ->
+ toSemanticActionViewModel(
+ model,
+ mediaButton.getActionById(buttonId),
+ buttonId,
+ isScrubbingTimeEnabled
+ )
}
}
val notifActionButtons =
@@ -304,7 +295,7 @@
private fun toSemanticActionViewModel(
model: MediaControlModel,
- mediaAction: MediaAction,
+ mediaAction: MediaAction?,
buttonId: Int,
canShowScrubbingTimeViews: Boolean
): MediaActionViewModel {
@@ -312,9 +303,9 @@
val hideWhenScrubbing = SEMANTIC_ACTIONS_HIDE_WHEN_SCRUBBING.contains(buttonId)
val shouldHideWhenScrubbing = canShowScrubbingTimeViews && hideWhenScrubbing
return MediaActionViewModel(
- icon = mediaAction.icon,
- contentDescription = mediaAction.contentDescription,
- background = mediaAction.background,
+ icon = mediaAction?.icon,
+ contentDescription = mediaAction?.contentDescription,
+ background = mediaAction?.background,
isVisibleWhenScrubbing = !shouldHideWhenScrubbing,
notVisibleValue =
if (
@@ -326,11 +317,11 @@
ConstraintSet.GONE
},
showInCollapsed = showInCollapsed,
- rebindId = mediaAction.rebindId,
+ rebindId = mediaAction?.rebindId,
buttonId = buttonId,
- isEnabled = mediaAction.action != null,
+ isEnabled = mediaAction?.action != null,
onClicked = { id ->
- mediaAction.action?.let {
+ mediaAction?.action?.let {
onButtonClicked(id, model.uid, model.packageName, model.instanceId, it)
}
},
@@ -366,7 +357,7 @@
) {
logger.logTapAction(id, uid, packageName, instanceId)
// TODO (b/330897926) log smartspace card reported (SMARTSPACE_CARD_CLICK_EVENT)
- isAnyButtonClicked.value = true
+ isAnyButtonClicked = true
action.run()
}
diff --git a/packages/SystemUI/src/com/android/systemui/media/controls/ui/viewmodel/MediaPlayerViewModel.kt b/packages/SystemUI/src/com/android/systemui/media/controls/ui/viewmodel/MediaPlayerViewModel.kt
index d1014e8..4334341 100644
--- a/packages/SystemUI/src/com/android/systemui/media/controls/ui/viewmodel/MediaPlayerViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/controls/ui/viewmodel/MediaPlayerViewModel.kt
@@ -35,7 +35,7 @@
val canShowTime: Boolean,
val playTurbulenceNoise: Boolean,
val useSemanticActions: Boolean,
- val actionButtons: List<MediaActionViewModel?>,
+ val actionButtons: List<MediaActionViewModel>,
val outputSwitcher: MediaOutputSwitcherViewModel,
val gutsMenu: GutsViewModel,
val onClicked: (Expandable) -> Unit,
diff --git a/packages/SystemUI/src/com/android/systemui/media/controls/util/MediaFlags.kt b/packages/SystemUI/src/com/android/systemui/media/controls/util/MediaFlags.kt
index 6a6eba1..1e7bc0c 100644
--- a/packages/SystemUI/src/com/android/systemui/media/controls/util/MediaFlags.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/controls/util/MediaFlags.kt
@@ -54,5 +54,6 @@
fun isSceneContainerEnabled() = SceneContainerFlag.isEnabled
/** Check whether to use media refactor code */
- fun isMediaControlsRefactorEnabled() = MediaControlsRefactorFlag.isEnabled
+ fun isMediaControlsRefactorEnabled() =
+ MediaControlsRefactorFlag.isEnabled && SceneContainerFlag.isEnabled
}
diff --git a/packages/SystemUI/src/com/android/systemui/media/dream/MediaDreamSentinel.java b/packages/SystemUI/src/com/android/systemui/media/dream/MediaDreamSentinel.java
index 88a5f78..061e7ec 100644
--- a/packages/SystemUI/src/com/android/systemui/media/dream/MediaDreamSentinel.java
+++ b/packages/SystemUI/src/com/android/systemui/media/dream/MediaDreamSentinel.java
@@ -48,7 +48,7 @@
}
@Override
- public void onMediaDataRemoved(@NonNull String key) {
+ public void onMediaDataRemoved(@NonNull String key, boolean userInitiated) {
final boolean hasActiveMedia = mMediaDataManager.hasActiveMedia();
if (DEBUG) {
Log.d(TAG, "onMediaDataRemoved(" + key + "), mAdded=" + mAdded + ", hasActiveMedia="
diff --git a/packages/SystemUI/src/com/android/systemui/mediaprojection/appselector/view/MediaProjectionTaskView.kt b/packages/SystemUI/src/com/android/systemui/mediaprojection/appselector/view/MediaProjectionTaskView.kt
index 412c006..9265bfb 100644
--- a/packages/SystemUI/src/com/android/systemui/mediaprojection/appselector/view/MediaProjectionTaskView.kt
+++ b/packages/SystemUI/src/com/android/systemui/mediaprojection/appselector/view/MediaProjectionTaskView.kt
@@ -140,10 +140,11 @@
val bitmapShader = bitmapShader ?: return
val thumbnailData = thumbnailData ?: return
+ val thumbnail = thumbnailData.thumbnail ?: return
val display = context.display ?: return
val windowMetrics = windowManager.maximumWindowMetrics
- previewRect.set(0, 0, thumbnailData.thumbnail.width, thumbnailData.thumbnail.height)
+ previewRect.set(0, 0, thumbnail.width, thumbnail.height)
val currentRotation: Int = display.rotation
val isRtl = layoutDirection == LAYOUT_DIRECTION_RTL
diff --git a/packages/SystemUI/src/com/android/systemui/model/SceneContainerPlugin.kt b/packages/SystemUI/src/com/android/systemui/model/SceneContainerPlugin.kt
index 89e4760..a144dc2 100644
--- a/packages/SystemUI/src/com/android/systemui/model/SceneContainerPlugin.kt
+++ b/packages/SystemUI/src/com/android/systemui/model/SceneContainerPlugin.kt
@@ -29,6 +29,7 @@
import com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_QUICK_SETTINGS_EXPANDED
import com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_STATUS_BAR_KEYGUARD_SHOWING
import com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_STATUS_BAR_KEYGUARD_SHOWING_OCCLUDED
+import com.android.systemui.shared.system.QuickStepContract.SystemUiStateFlags
import dagger.Lazy
import javax.inject.Inject
@@ -48,7 +49,7 @@
* Returns an override value for the given [flag] or `null` if the scene framework isn't enabled
* or if the flag value doesn't need to be overridden.
*/
- fun flagValueOverride(flag: Int): Boolean? {
+ fun flagValueOverride(@SystemUiStateFlags flag: Long): Boolean? {
if (!SceneContainerFlag.isEnabled) {
return null
}
@@ -79,7 +80,7 @@
* to be overridden by the scene framework.
*/
val EvaluatorByFlag =
- mapOf<Int, (SceneContainerPluginState) -> Boolean>(
+ mapOf<Long, (SceneContainerPluginState) -> Boolean>(
SYSUI_STATE_NOTIFICATION_PANEL_VISIBLE to { it.scene != Scenes.Gone },
SYSUI_STATE_NOTIFICATION_PANEL_EXPANDED to
{
diff --git a/packages/SystemUI/src/com/android/systemui/model/SysUiState.java b/packages/SystemUI/src/com/android/systemui/model/SysUiState.java
index 2dd2327..67fe0e9 100644
--- a/packages/SystemUI/src/com/android/systemui/model/SysUiState.java
+++ b/packages/SystemUI/src/com/android/systemui/model/SysUiState.java
@@ -23,6 +23,7 @@
import com.android.systemui.dagger.SysUISingleton;
import com.android.systemui.settings.DisplayTracker;
import com.android.systemui.shared.system.QuickStepContract;
+import com.android.systemui.shared.system.QuickStepContract.SystemUiStateFlags;
import dalvik.annotation.optimization.NeverCompile;
@@ -42,10 +43,10 @@
private final DisplayTracker mDisplayTracker;
private final SceneContainerPlugin mSceneContainerPlugin;
- private @QuickStepContract.SystemUiStateFlags int mFlags;
+ private @SystemUiStateFlags long mFlags;
private final List<SysUiStateCallback> mCallbacks = new ArrayList<>();
- private int mFlagsToSet = 0;
- private int mFlagsToClear = 0;
+ private long mFlagsToSet = 0;
+ private long mFlagsToClear = 0;
public SysUiState(DisplayTracker displayTracker, SceneContainerPlugin sceneContainerPlugin) {
mDisplayTracker = displayTracker;
@@ -67,12 +68,17 @@
}
/** Returns the current sysui state flags. */
- public int getFlags() {
+ @SystemUiStateFlags
+ public long getFlags() {
return mFlags;
}
+ public boolean isFlagEnabled(@SystemUiStateFlags long flag) {
+ return (mFlags & flag) != 0;
+ }
+
/** Methods to this call can be chained together before calling {@link #commitUpdate(int)}. */
- public SysUiState setFlag(int flag, boolean enabled) {
+ public SysUiState setFlag(@SystemUiStateFlags long flag, boolean enabled) {
final Boolean overrideOrNull = mSceneContainerPlugin.flagValueOverride(flag);
if (overrideOrNull != null && enabled != overrideOrNull) {
if (DEBUG) {
@@ -91,7 +97,7 @@
return this;
}
- /** Call to save all the flags updated from {@link #setFlag(int, boolean)}. */
+ /** Call to save all the flags updated from {@link #setFlag(long, boolean)}. */
public void commitUpdate(int displayId) {
updateFlags(displayId);
mFlagsToSet = 0;
@@ -105,14 +111,14 @@
return;
}
- int newState = mFlags;
+ long newState = mFlags;
newState |= mFlagsToSet;
newState &= ~mFlagsToClear;
notifyAndSetSystemUiStateChanged(newState, mFlags);
}
/** Notify all those who are registered that the state has changed. */
- private void notifyAndSetSystemUiStateChanged(int newFlags, int oldFlags) {
+ private void notifyAndSetSystemUiStateChanged(long newFlags, long oldFlags) {
if (DEBUG) {
Log.d(TAG, "SysUiState changed: old=" + oldFlags + " new=" + newFlags);
}
@@ -137,7 +143,7 @@
/** Callback to be notified whenever system UI state flags are changed. */
public interface SysUiStateCallback{
/** To be called when any SysUiStateFlag gets updated */
- void onSystemUiStateChanged(@QuickStepContract.SystemUiStateFlags int sysUiFlags);
+ void onSystemUiStateChanged(@SystemUiStateFlags long sysUiFlags);
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/model/SysUiStateExt.kt b/packages/SystemUI/src/com/android/systemui/model/SysUiStateExt.kt
index 5c49156..1e18f24 100644
--- a/packages/SystemUI/src/com/android/systemui/model/SysUiStateExt.kt
+++ b/packages/SystemUI/src/com/android/systemui/model/SysUiStateExt.kt
@@ -40,7 +40,7 @@
*/
fun SysUiState.updateFlags(
@DisplayId displayId: Int,
- vararg flagValuePairs: Pair<Int, Boolean>,
+ vararg flagValuePairs: Pair<Long, Boolean>,
) {
flagValuePairs.forEach { (flag, enabled) -> setFlag(flag, enabled) }
commitUpdate(displayId)
diff --git a/packages/SystemUI/src/com/android/systemui/navigationbar/NavBarHelper.java b/packages/SystemUI/src/com/android/systemui/navigationbar/NavBarHelper.java
index a6b6d61..80c4379 100644
--- a/packages/SystemUI/src/com/android/systemui/navigationbar/NavBarHelper.java
+++ b/packages/SystemUI/src/com/android/systemui/navigationbar/NavBarHelper.java
@@ -128,7 +128,7 @@
private boolean mLongPressHomeEnabled;
private boolean mAssistantTouchGestureEnabled;
private int mNavBarMode;
- private int mA11yButtonState;
+ private long mA11yButtonState;
private int mRotationWatcherRotation;
private boolean mTogglingNavbarTaskbar;
private boolean mWallpaperVisible;
@@ -374,7 +374,7 @@
* {@link Secure#ACCESSIBILITY_BUTTON_MODE_GESTURE}, otherwise it is reset to 0.
*/
private void updateA11yState() {
- final int prevState = mA11yButtonState;
+ final long prevState = mA11yButtonState;
final boolean clickable;
final boolean longClickable;
if (mAccessibilityButtonModeObserver.getCurrentAccessibilityButtonMode()
@@ -431,7 +431,7 @@
* 48 = the combination of {@link QuickStepContract#SYSUI_STATE_A11Y_BUTTON_CLICKABLE} and
* {@link QuickStepContract#SYSUI_STATE_A11Y_BUTTON_LONG_CLICKABLE}
*/
- public int getA11yButtonState() {
+ public long getA11yButtonState() {
return mA11yButtonState;
}
diff --git a/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBar.java b/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBar.java
index 906ebad..0e819c2 100644
--- a/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBar.java
+++ b/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBar.java
@@ -1602,7 +1602,7 @@
void updateAccessibilityStateFlags() {
mLongPressHomeEnabled = mNavBarHelper.getLongPressHomeEnabled();
if (mView != null) {
- int a11yFlags = mNavBarHelper.getA11yButtonState();
+ long a11yFlags = mNavBarHelper.getA11yButtonState();
boolean clickable = (a11yFlags & SYSUI_STATE_A11Y_BUTTON_CLICKABLE) != 0;
boolean longClickable = (a11yFlags & SYSUI_STATE_A11Y_BUTTON_LONG_CLICKABLE) != 0;
mView.setAccessibilityButtonState(clickable, longClickable);
@@ -1611,7 +1611,7 @@
}
public void updateSystemUiStateFlags() {
- int a11yFlags = mNavBarHelper.getA11yButtonState();
+ long a11yFlags = mNavBarHelper.getA11yButtonState();
boolean clickable = (a11yFlags & SYSUI_STATE_A11Y_BUTTON_CLICKABLE) != 0;
boolean longClickable = (a11yFlags & SYSUI_STATE_A11Y_BUTTON_LONG_CLICKABLE) != 0;
diff --git a/packages/SystemUI/src/com/android/systemui/navigationbar/TaskbarDelegate.java b/packages/SystemUI/src/com/android/systemui/navigationbar/TaskbarDelegate.java
index f67973b..b360af0 100644
--- a/packages/SystemUI/src/com/android/systemui/navigationbar/TaskbarDelegate.java
+++ b/packages/SystemUI/src/com/android/systemui/navigationbar/TaskbarDelegate.java
@@ -298,7 +298,7 @@
}
private void updateSysuiFlags() {
- int a11yFlags = mNavBarHelper.getA11yButtonState();
+ long a11yFlags = mNavBarHelper.getA11yButtonState();
boolean clickable = (a11yFlags & SYSUI_STATE_A11Y_BUTTON_CLICKABLE) != 0;
boolean longClickable = (a11yFlags & SYSUI_STATE_A11Y_BUTTON_LONG_CLICKABLE) != 0;
diff --git a/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/EdgeBackGestureHandler.java b/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/EdgeBackGestureHandler.java
index 295b293..9487085 100644
--- a/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/EdgeBackGestureHandler.java
+++ b/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/EdgeBackGestureHandler.java
@@ -84,6 +84,7 @@
import com.android.systemui.shared.system.InputChannelCompat;
import com.android.systemui.shared.system.InputMonitorCompat;
import com.android.systemui.shared.system.QuickStepContract;
+import com.android.systemui.shared.system.QuickStepContract.SystemUiStateFlags;
import com.android.systemui.shared.system.SysUiStatsLog;
import com.android.systemui.shared.system.TaskStackChangeListener;
import com.android.systemui.shared.system.TaskStackChangeListeners;
@@ -270,7 +271,8 @@
private BackAnimation mBackAnimation;
private int mLeftInset;
private int mRightInset;
- private int mSysUiFlags;
+ @SystemUiStateFlags
+ private long mSysUiFlags;
// For Tf-Lite model.
private BackGestureTfClassifierProvider mBackGestureTfClassifierProvider;
@@ -334,7 +336,7 @@
private final SysUiState.SysUiStateCallback mSysUiStateCallback =
new SysUiState.SysUiStateCallback() {
@Override
- public void onSystemUiStateChanged(int sysUiFlags) {
+ public void onSystemUiStateChanged(@SystemUiStateFlags long sysUiFlags) {
mSysUiFlags = sysUiFlags;
}
};
diff --git a/packages/SystemUI/src/com/android/systemui/qrcodescanner/dagger/QRCodeScannerModule.kt b/packages/SystemUI/src/com/android/systemui/qrcodescanner/dagger/QRCodeScannerModule.kt
index 3907a72..5e6ee4d 100644
--- a/packages/SystemUI/src/com/android/systemui/qrcodescanner/dagger/QRCodeScannerModule.kt
+++ b/packages/SystemUI/src/com/android/systemui/qrcodescanner/dagger/QRCodeScannerModule.kt
@@ -16,12 +16,20 @@
package com.android.systemui.qrcodescanner.dagger
+import com.android.systemui.Flags
import com.android.systemui.qs.QsEventLogger
import com.android.systemui.qs.pipeline.shared.TileSpec
import com.android.systemui.qs.tileimpl.QSTileImpl
import com.android.systemui.qs.tiles.QRCodeScannerTile
+import com.android.systemui.qs.tiles.base.viewmodel.QSTileViewModelFactory
+import com.android.systemui.qs.tiles.impl.qr.domain.interactor.QRCodeScannerTileDataInteractor
+import com.android.systemui.qs.tiles.impl.qr.domain.interactor.QRCodeScannerTileUserActionInteractor
+import com.android.systemui.qs.tiles.impl.qr.domain.model.QRCodeScannerTileModel
+import com.android.systemui.qs.tiles.impl.qr.ui.QRCodeScannerTileMapper
import com.android.systemui.qs.tiles.viewmodel.QSTileConfig
import com.android.systemui.qs.tiles.viewmodel.QSTileUIConfig
+import com.android.systemui.qs.tiles.viewmodel.QSTileViewModel
+import com.android.systemui.qs.tiles.viewmodel.StubQSTileViewModel
import com.android.systemui.res.R
import dagger.Binds
import dagger.Module
@@ -54,5 +62,24 @@
),
instanceId = uiEventLogger.getNewInstanceId(),
)
+
+ /** Inject QR Code Scanner Tile into tileViewModelMap in QSModule. */
+ @Provides
+ @IntoMap
+ @StringKey(QR_CODE_SCANNER_TILE_SPEC)
+ fun provideQRCodeScannerTileViewModel(
+ factory: QSTileViewModelFactory.Static<QRCodeScannerTileModel>,
+ mapper: QRCodeScannerTileMapper,
+ stateInteractor: QRCodeScannerTileDataInteractor,
+ userActionInteractor: QRCodeScannerTileUserActionInteractor
+ ): QSTileViewModel =
+ if (Flags.qsNewTilesFuture())
+ factory.create(
+ TileSpec.create(QR_CODE_SCANNER_TILE_SPEC),
+ userActionInteractor,
+ stateInteractor,
+ mapper,
+ )
+ else StubQSTileViewModel
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/TileStateToProto.kt b/packages/SystemUI/src/com/android/systemui/qs/TileStateToProto.kt
index 2c8a5a4..1336d64 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/TileStateToProto.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/TileStateToProto.kt
@@ -18,6 +18,7 @@
import android.service.quicksettings.Tile
import android.text.TextUtils
+import android.widget.Switch
import com.android.systemui.plugins.qs.QSTile
import com.android.systemui.qs.external.CustomTile
import com.android.systemui.qs.nano.QsTileState
@@ -44,8 +45,8 @@
}
label?.let { state.label = it.toString() }
secondaryLabel?.let { state.secondaryLabel = it.toString() }
- if (this is QSTile.BooleanState) {
- state.booleanState = value
+ if (expandedAccessibilityClassName == Switch::class.java.name) {
+ state.booleanState = state.state == QsTileState.ACTIVE
}
return state
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/external/CustomTile.java b/packages/SystemUI/src/com/android/systemui/qs/external/CustomTile.java
index d26ae0a..5d35a69 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/external/CustomTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/external/CustomTile.java
@@ -42,6 +42,7 @@
import android.util.Log;
import android.view.IWindowManager;
import android.view.WindowManagerGlobal;
+import android.widget.Button;
import android.widget.Switch;
import androidx.annotation.Nullable;
@@ -502,6 +503,8 @@
if (state instanceof BooleanState) {
state.expandedAccessibilityClassName = Switch.class.getName();
((BooleanState) state).value = (state.state == Tile.STATE_ACTIVE);
+ } else {
+ state.expandedAccessibilityClassName = Button.class.getName();
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/panels/dagger/PanelsModule.kt b/packages/SystemUI/src/com/android/systemui/qs/panels/dagger/PanelsModule.kt
index 0696fbe..2cc3985 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/panels/dagger/PanelsModule.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/panels/dagger/PanelsModule.kt
@@ -29,8 +29,10 @@
import com.android.systemui.qs.panels.shared.model.GridConsistencyLog
import com.android.systemui.qs.panels.shared.model.GridLayoutType
import com.android.systemui.qs.panels.shared.model.InfiniteGridLayoutType
+import com.android.systemui.qs.panels.shared.model.StretchedGridLayoutType
import com.android.systemui.qs.panels.ui.compose.GridLayout
import com.android.systemui.qs.panels.ui.compose.InfiniteGridLayout
+import com.android.systemui.qs.panels.ui.compose.StretchedGridLayout
import dagger.Binds
import dagger.Module
import dagger.Provides
@@ -63,6 +65,14 @@
}
@Provides
+ @IntoSet
+ fun provideStretchedGridLayout(
+ gridLayout: StretchedGridLayout
+ ): Pair<GridLayoutType, GridLayout> {
+ return Pair(StretchedGridLayoutType, gridLayout)
+ }
+
+ @Provides
fun provideGridLayoutMap(
entries: Set<@JvmSuppressWildcards Pair<GridLayoutType, GridLayout>>
): Map<GridLayoutType, GridLayout> {
@@ -70,6 +80,13 @@
}
@Provides
+ fun provideGridLayoutTypes(
+ entries: Set<@JvmSuppressWildcards Pair<GridLayoutType, GridLayout>>
+ ): Set<GridLayoutType> {
+ return entries.map { it.first }.toSet()
+ }
+
+ @Provides
@IntoSet
fun provideGridConsistencyInteractor(
consistencyInteractor: InfiniteGridConsistencyInteractor
@@ -78,6 +95,14 @@
}
@Provides
+ @IntoSet
+ fun provideStretchedGridConsistencyInteractor(
+ consistencyInteractor: NoopGridConsistencyInteractor
+ ): Pair<GridLayoutType, GridTypeConsistencyInteractor> {
+ return Pair(StretchedGridLayoutType, consistencyInteractor)
+ }
+
+ @Provides
fun provideGridConsistencyInteractorMap(
entries: Set<@JvmSuppressWildcards Pair<GridLayoutType, GridTypeConsistencyInteractor>>
): Map<GridLayoutType, GridTypeConsistencyInteractor> {
diff --git a/packages/SystemUI/src/com/android/systemui/qs/panels/data/repository/GridLayoutTypeRepository.kt b/packages/SystemUI/src/com/android/systemui/qs/panels/data/repository/GridLayoutTypeRepository.kt
index 542d0cb..31795d5 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/panels/data/repository/GridLayoutTypeRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/panels/data/repository/GridLayoutTypeRepository.kt
@@ -26,10 +26,17 @@
interface GridLayoutTypeRepository {
val layout: StateFlow<GridLayoutType>
+ fun setLayout(type: GridLayoutType)
}
@SysUISingleton
class GridLayoutTypeRepositoryImpl @Inject constructor() : GridLayoutTypeRepository {
private val _layout: MutableStateFlow<GridLayoutType> = MutableStateFlow(InfiniteGridLayoutType)
override val layout = _layout.asStateFlow()
+
+ override fun setLayout(type: GridLayoutType) {
+ if (_layout.value != type) {
+ _layout.value = type
+ }
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/panels/domain/interactor/GridLayoutTypeInteractor.kt b/packages/SystemUI/src/com/android/systemui/qs/panels/domain/interactor/GridLayoutTypeInteractor.kt
index b6be578..4af1b22 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/panels/domain/interactor/GridLayoutTypeInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/panels/domain/interactor/GridLayoutTypeInteractor.kt
@@ -20,9 +20,13 @@
import com.android.systemui.qs.panels.data.repository.GridLayoutTypeRepository
import com.android.systemui.qs.panels.shared.model.GridLayoutType
import javax.inject.Inject
-import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.StateFlow
@SysUISingleton
-class GridLayoutTypeInteractor @Inject constructor(repo: GridLayoutTypeRepository) {
- val layout: Flow<GridLayoutType> = repo.layout
+class GridLayoutTypeInteractor @Inject constructor(private val repo: GridLayoutTypeRepository) {
+ val layout: StateFlow<GridLayoutType> = repo.layout
+
+ fun setLayoutType(type: GridLayoutType) {
+ repo.setLayout(type)
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/panels/domain/interactor/InfiniteGridConsistencyInteractor.kt b/packages/SystemUI/src/com/android/systemui/qs/panels/domain/interactor/InfiniteGridConsistencyInteractor.kt
index 74e906c..b437f64 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/panels/domain/interactor/InfiniteGridConsistencyInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/panels/domain/interactor/InfiniteGridConsistencyInteractor.kt
@@ -18,6 +18,8 @@
import android.util.Log
import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.qs.panels.shared.model.SizedTile
+import com.android.systemui.qs.panels.shared.model.TileRow
import com.android.systemui.qs.pipeline.shared.TileSpec
import javax.inject.Inject
@@ -35,7 +37,7 @@
*/
override fun reconcileTiles(tiles: List<TileSpec>): List<TileSpec> {
val newTiles: MutableList<TileSpec> = mutableListOf()
- val row = TileRow(columns = gridSizeInteractor.columns.value)
+ val row = TileRow<TileSpec>(columns = gridSizeInteractor.columns.value)
val iconTilesSet = iconTilesInteractor.iconTilesSpecs.value
val tilesQueue =
ArrayDeque(
@@ -54,7 +56,7 @@
while (tilesQueue.isNotEmpty()) {
if (row.isFull()) {
- newTiles.addAll(row.tileSpecs())
+ newTiles.addAll(row.tiles.map { it.tile })
row.clear()
}
@@ -66,13 +68,13 @@
// We'll try to either add an icon tile from the queue to complete the row, or
// remove an icon tile from the current row to free up space.
- val iconTile: SizedTile? = tilesQueue.firstOrNull { it.width == 1 }
+ val iconTile: SizedTile<TileSpec>? = tilesQueue.firstOrNull { it.width == 1 }
if (iconTile != null) {
tilesQueue.remove(iconTile)
tilesQueue.addFirst(tile)
row.maybeAddTile(iconTile)
} else {
- val tileToRemove: SizedTile? = row.findLastIconTile()
+ val tileToRemove: SizedTile<TileSpec>? = row.findLastIconTile()
if (tileToRemove != null) {
row.removeTile(tileToRemove)
row.maybeAddTile(tile)
@@ -84,7 +86,7 @@
// If the row does not have an icon tile, add the incomplete row.
// Note: this shouldn't happen because an icon tile is guaranteed to be in a
// row that doesn't have enough space for a large tile.
- val tileSpecs = row.tileSpecs()
+ val tileSpecs = row.tiles.map { it.tile }
Log.wtf(TAG, "Uneven row does not have an icon tile to remove: $tileSpecs")
newTiles.addAll(tileSpecs)
row.clear()
@@ -95,48 +97,11 @@
}
// Add last row that might be incomplete
- newTiles.addAll(row.tileSpecs())
+ newTiles.addAll(row.tiles.map { it.tile })
return newTiles.toList()
}
- /** Tile with a width representing the number of columns it should take. */
- private data class SizedTile(val spec: TileSpec, val width: Int)
-
- private class TileRow(private val columns: Int) {
- private var availableColumns = columns
- private val tiles: MutableList<SizedTile> = mutableListOf()
-
- fun tileSpecs(): List<TileSpec> {
- return tiles.map { it.spec }
- }
-
- fun maybeAddTile(tile: SizedTile): Boolean {
- if (availableColumns - tile.width >= 0) {
- tiles.add(tile)
- availableColumns -= tile.width
- return true
- }
- return false
- }
-
- fun findLastIconTile(): SizedTile? {
- return tiles.findLast { it.width == 1 }
- }
-
- fun removeTile(tile: SizedTile) {
- tiles.remove(tile)
- availableColumns += tile.width
- }
-
- fun clear() {
- tiles.clear()
- availableColumns = columns
- }
-
- fun isFull(): Boolean = availableColumns == 0
- }
-
private companion object {
const val TAG = "InfiniteGridConsistencyInteractor"
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/panels/domain/interactor/NoopConsistencyInteractor.kt b/packages/SystemUI/src/com/android/systemui/qs/panels/domain/interactor/NoopConsistencyInteractor.kt
new file mode 100644
index 0000000..97ceacc
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/qs/panels/domain/interactor/NoopConsistencyInteractor.kt
@@ -0,0 +1,26 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.qs.panels.domain.interactor
+
+import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.qs.pipeline.shared.TileSpec
+import javax.inject.Inject
+
+@SysUISingleton
+class NoopConsistencyInteractor @Inject constructor() : GridTypeConsistencyInteractor {
+ override fun reconcileTiles(tiles: List<TileSpec>): List<TileSpec> = tiles
+}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/panels/shared/model/GridLayoutType.kt b/packages/SystemUI/src/com/android/systemui/qs/panels/shared/model/GridLayoutType.kt
index 23110dc..501730a 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/panels/shared/model/GridLayoutType.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/panels/shared/model/GridLayoutType.kt
@@ -25,3 +25,9 @@
/** Grid type representing a scrollable vertical grid. */
data object InfiniteGridLayoutType : GridLayoutType
+
+/**
+ * Grid type representing a scrollable vertical grid where tiles will stretch to fill in empty
+ * spaces.
+ */
+data object StretchedGridLayoutType : GridLayoutType
diff --git a/packages/SystemUI/src/com/android/systemui/qs/panels/shared/model/TileRow.kt b/packages/SystemUI/src/com/android/systemui/qs/panels/shared/model/TileRow.kt
new file mode 100644
index 0000000..7e4381b
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/qs/panels/shared/model/TileRow.kt
@@ -0,0 +1,53 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.qs.panels.shared.model
+
+/** Represents a tile of type [T] associated with a width */
+data class SizedTile<T>(val tile: T, val width: Int)
+
+/** Represents a row of [SizedTile] with a maximum width of [columns] */
+class TileRow<T>(private val columns: Int) {
+ private var availableColumns = columns
+ private val _tiles: MutableList<SizedTile<T>> = mutableListOf()
+ val tiles: List<SizedTile<T>>
+ get() = _tiles.toList()
+
+ fun maybeAddTile(tile: SizedTile<T>): Boolean {
+ if (availableColumns - tile.width >= 0) {
+ _tiles.add(tile)
+ availableColumns -= tile.width
+ return true
+ }
+ return false
+ }
+
+ fun findLastIconTile(): SizedTile<T>? {
+ return _tiles.findLast { it.width == 1 }
+ }
+
+ fun removeTile(tile: SizedTile<T>) {
+ _tiles.remove(tile)
+ availableColumns += tile.width
+ }
+
+ fun clear() {
+ _tiles.clear()
+ availableColumns = columns
+ }
+
+ fun isFull(): Boolean = availableColumns == 0
+}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/EditMode.kt b/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/EditMode.kt
index 5c17fd1..3bda775 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/EditMode.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/EditMode.kt
@@ -20,9 +20,9 @@
import androidx.compose.foundation.layout.Column
import androidx.compose.runtime.Composable
import androidx.compose.runtime.DisposableEffect
-import androidx.compose.runtime.collectAsState
import androidx.compose.runtime.getValue
import androidx.compose.ui.Modifier
+import androidx.lifecycle.compose.collectAsStateWithLifecycle
import com.android.systemui.qs.panels.ui.viewmodel.EditModeViewModel
@Composable
@@ -30,8 +30,8 @@
viewModel: EditModeViewModel,
modifier: Modifier = Modifier,
) {
- val gridLayout by viewModel.gridLayout.collectAsState()
- val tiles by viewModel.tiles.collectAsState(emptyList())
+ val gridLayout by viewModel.gridLayout.collectAsStateWithLifecycle()
+ val tiles by viewModel.tiles.collectAsStateWithLifecycle(emptyList())
BackHandler { viewModel.stopEditing() }
diff --git a/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/InfiniteGridLayout.kt b/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/InfiniteGridLayout.kt
index dc43091..f5ee720 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/InfiniteGridLayout.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/InfiniteGridLayout.kt
@@ -16,85 +16,23 @@
package com.android.systemui.qs.panels.ui.compose
-import android.graphics.drawable.Animatable
-import android.text.TextUtils
-import androidx.appcompat.content.res.AppCompatResources
-import androidx.compose.animation.graphics.ExperimentalAnimationGraphicsApi
-import androidx.compose.animation.graphics.res.animatedVectorResource
-import androidx.compose.animation.graphics.res.rememberAnimatedVectorPainter
-import androidx.compose.animation.graphics.vector.AnimatedImageVector
-import androidx.compose.foundation.ExperimentalFoundationApi
-import androidx.compose.foundation.Image
-import androidx.compose.foundation.background
-import androidx.compose.foundation.basicMarquee
-import androidx.compose.foundation.clickable
-import androidx.compose.foundation.combinedClickable
-import androidx.compose.foundation.layout.Arrangement
-import androidx.compose.foundation.layout.Arrangement.spacedBy
-import androidx.compose.foundation.layout.Box
-import androidx.compose.foundation.layout.Column
-import androidx.compose.foundation.layout.Row
-import androidx.compose.foundation.layout.fillMaxHeight
-import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.height
-import androidx.compose.foundation.layout.padding
-import androidx.compose.foundation.layout.size
import androidx.compose.foundation.lazy.grid.GridCells
import androidx.compose.foundation.lazy.grid.GridItemSpan
-import androidx.compose.foundation.lazy.grid.LazyGridScope
-import androidx.compose.foundation.lazy.grid.LazyVerticalGrid
-import androidx.compose.foundation.shape.CircleShape
-import androidx.compose.foundation.shape.RoundedCornerShape
-import androidx.compose.material.icons.Icons
-import androidx.compose.material.icons.filled.Add
-import androidx.compose.material.icons.filled.Remove
-import androidx.compose.material3.Icon
-import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.DisposableEffect
-import androidx.compose.runtime.LaunchedEffect
-import androidx.compose.runtime.collectAsState
import androidx.compose.runtime.getValue
-import androidx.compose.runtime.mutableStateOf
-import androidx.compose.runtime.remember
-import androidx.compose.runtime.rememberUpdatedState
-import androidx.compose.runtime.setValue
-import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
-import androidx.compose.ui.draw.clip
-import androidx.compose.ui.graphics.Color
-import androidx.compose.ui.graphics.ColorFilter
-import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.res.dimensionResource
-import androidx.compose.ui.res.stringResource
-import androidx.compose.ui.semantics.contentDescription
-import androidx.compose.ui.semantics.onClick
-import androidx.compose.ui.semantics.semantics
-import androidx.compose.ui.semantics.stateDescription
-import androidx.compose.ui.unit.dp
-import com.android.compose.animation.Expandable
-import com.android.compose.theme.colorAttr
-import com.android.systemui.common.shared.model.Icon
-import com.android.systemui.common.ui.compose.Icon
-import com.android.systemui.common.ui.compose.load
+import androidx.lifecycle.compose.collectAsStateWithLifecycle
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.qs.panels.domain.interactor.IconTilesInteractor
import com.android.systemui.qs.panels.domain.interactor.InfiniteGridSizeInteractor
-import com.android.systemui.qs.panels.ui.viewmodel.ActiveTileColorAttributes
-import com.android.systemui.qs.panels.ui.viewmodel.AvailableEditActions
import com.android.systemui.qs.panels.ui.viewmodel.EditTileViewModel
-import com.android.systemui.qs.panels.ui.viewmodel.TileColorAttributes
-import com.android.systemui.qs.panels.ui.viewmodel.TileUiState
import com.android.systemui.qs.panels.ui.viewmodel.TileViewModel
-import com.android.systemui.qs.panels.ui.viewmodel.toUiState
-import com.android.systemui.qs.pipeline.domain.interactor.CurrentTilesInteractor.Companion.POSITION_AT_END
import com.android.systemui.qs.pipeline.shared.TileSpec
-import com.android.systemui.qs.tileimpl.QSTileImpl
import com.android.systemui.res.R
import javax.inject.Inject
-import kotlinx.coroutines.ExperimentalCoroutinesApi
-import kotlinx.coroutines.delay
-import kotlinx.coroutines.flow.mapLatest
@SysUISingleton
class InfiniteGridLayout
@@ -104,8 +42,6 @@
private val gridSizeInteractor: InfiniteGridSizeInteractor
) : GridLayout {
- private object TileType
-
@Composable
override fun TileGrid(
tiles: List<TileViewModel>,
@@ -116,8 +52,8 @@
tiles.forEach { it.startListening(token) }
onDispose { tiles.forEach { it.stopListening(token) } }
}
- val iconTilesSpecs by iconTilesInteractor.iconTilesSpecs.collectAsState()
- val columns by gridSizeInteractor.columns.collectAsState()
+ val iconTilesSpecs by iconTilesInteractor.iconTilesSpecs.collectAsStateWithLifecycle()
+ val columns by gridSizeInteractor.columns.collectAsStateWithLifecycle()
TileLazyGrid(modifier = modifier, columns = GridCells.Fixed(columns)) {
items(
@@ -140,55 +76,6 @@
}
}
- @OptIn(ExperimentalCoroutinesApi::class, ExperimentalFoundationApi::class)
- @Composable
- private fun Tile(
- tile: TileViewModel,
- iconOnly: Boolean,
- modifier: Modifier,
- ) {
- val state: TileUiState by
- tile.state
- .mapLatest { it.toUiState() }
- .collectAsState(initial = tile.currentState.toUiState())
- val context = LocalContext.current
-
- Expandable(
- color = colorAttr(state.colors.background),
- shape = RoundedCornerShape(dimensionResource(R.dimen.qs_corner_radius)),
- ) {
- Row(
- modifier =
- modifier
- .combinedClickable(
- onClick = { tile.onClick(it) },
- onLongClick = { tile.onLongClick(it) }
- )
- .tileModifier(state.colors),
- verticalAlignment = Alignment.CenterVertically,
- horizontalArrangement = tileHorizontalArrangement(iconOnly),
- ) {
- val icon =
- remember(state.icon) {
- state.icon.get().let {
- if (it is QSTileImpl.ResourceIcon) {
- Icon.Resource(it.resId, null)
- } else {
- Icon.Loaded(it.getDrawable(context), null)
- }
- }
- }
- TileContent(
- label = state.label.toString(),
- secondaryLabel = state.secondaryLabel?.toString(),
- icon = icon,
- colors = state.colors,
- iconOnly = iconOnly
- )
- }
- }
- }
-
@Composable
override fun EditTileGrid(
tiles: List<EditTileViewModel>,
@@ -196,259 +83,16 @@
onAddTile: (TileSpec, Int) -> Unit,
onRemoveTile: (TileSpec) -> Unit,
) {
- val (currentTiles, otherTiles) = tiles.partition { it.isCurrent }
- val (otherTilesStock, otherTilesCustom) = otherTiles.partition { it.appName == null }
- val addTileToEnd: (TileSpec) -> Unit by rememberUpdatedState {
- onAddTile(it, POSITION_AT_END)
- }
- val iconOnlySpecs by iconTilesInteractor.iconTilesSpecs.collectAsState(initial = emptySet())
- val isIconOnly: (TileSpec) -> Boolean =
- remember(iconOnlySpecs) { { tileSpec: TileSpec -> tileSpec in iconOnlySpecs } }
- val columns by gridSizeInteractor.columns.collectAsState()
+ val iconOnlySpecs by iconTilesInteractor.iconTilesSpecs.collectAsStateWithLifecycle()
+ val columns by gridSizeInteractor.columns.collectAsStateWithLifecycle()
- TileLazyGrid(modifier = modifier, columns = GridCells.Fixed(columns)) {
- // These Text are just placeholders to see the different sections. Not final UI.
- item(span = { GridItemSpan(maxLineSpan) }) {
- Text("Current tiles", color = Color.White)
- }
-
- editTiles(
- currentTiles,
- ClickAction.REMOVE,
- onRemoveTile,
- isIconOnly,
- indicatePosition = true,
- )
-
- item(span = { GridItemSpan(maxLineSpan) }) { Text("Tiles to add", color = Color.White) }
-
- editTiles(
- otherTilesStock,
- ClickAction.ADD,
- addTileToEnd,
- isIconOnly,
- )
-
- item(span = { GridItemSpan(maxLineSpan) }) {
- Text("Custom tiles to add", color = Color.White)
- }
-
- editTiles(
- otherTilesCustom,
- ClickAction.ADD,
- addTileToEnd,
- isIconOnly,
- )
- }
- }
-
- private fun LazyGridScope.editTiles(
- tiles: List<EditTileViewModel>,
- clickAction: ClickAction,
- onClick: (TileSpec) -> Unit,
- isIconOnly: (TileSpec) -> Boolean,
- indicatePosition: Boolean = false,
- ) {
- items(
- count = tiles.size,
- key = { tiles[it].tileSpec.spec },
- span = { GridItemSpan(if (isIconOnly(tiles[it].tileSpec)) 1 else 2) },
- contentType = { TileType }
- ) {
- val viewModel = tiles[it]
- val canClick =
- when (clickAction) {
- ClickAction.ADD -> AvailableEditActions.ADD in viewModel.availableEditActions
- ClickAction.REMOVE ->
- AvailableEditActions.REMOVE in viewModel.availableEditActions
- }
- val onClickActionName =
- when (clickAction) {
- ClickAction.ADD ->
- stringResource(id = R.string.accessibility_qs_edit_tile_add_action)
- ClickAction.REMOVE ->
- stringResource(id = R.string.accessibility_qs_edit_remove_tile_action)
- }
- val stateDescription =
- if (indicatePosition) {
- stringResource(id = R.string.accessibility_qs_edit_position, it + 1)
- } else {
- ""
- }
-
- Box(
- modifier =
- Modifier.clickable(enabled = canClick) { onClick.invoke(viewModel.tileSpec) }
- .animateItem()
- .semantics {
- onClick(onClickActionName) { false }
- this.stateDescription = stateDescription
- }
- ) {
- EditTile(
- tileViewModel = viewModel,
- isIconOnly(viewModel.tileSpec),
- modifier = Modifier.height(dimensionResource(id = R.dimen.qs_tile_height))
- )
- if (canClick) {
- Badge(clickAction, Modifier.align(Alignment.TopEnd))
- }
- }
- }
- }
-
- @Composable
- private fun Badge(action: ClickAction, modifier: Modifier = Modifier) {
- Box(modifier = modifier.size(16.dp).background(Color.Cyan, shape = CircleShape)) {
- Icon(
- imageVector =
- when (action) {
- ClickAction.ADD -> Icons.Filled.Add
- ClickAction.REMOVE -> Icons.Filled.Remove
- },
- "",
- tint = Color.Black,
- )
- }
- }
-
- @Composable
- private fun EditTile(
- tileViewModel: EditTileViewModel,
- iconOnly: Boolean,
- modifier: Modifier = Modifier,
- ) {
- val label = tileViewModel.label.load() ?: tileViewModel.tileSpec.spec
- val colors = ActiveTileColorAttributes
-
- Row(
- modifier = modifier.tileModifier(colors).semantics { this.contentDescription = label },
- verticalAlignment = Alignment.CenterVertically,
- horizontalArrangement = tileHorizontalArrangement(iconOnly)
- ) {
- TileContent(
- label = label,
- secondaryLabel = tileViewModel.appName?.load(),
- colors = colors,
- icon = tileViewModel.icon,
- iconOnly = iconOnly,
- animateIconToEnd = true,
- )
- }
- }
-
- private enum class ClickAction {
- ADD,
- REMOVE,
- }
-}
-
-@OptIn(ExperimentalAnimationGraphicsApi::class)
-@Composable
-private fun TileIcon(
- icon: Icon,
- color: Color,
- animateToEnd: Boolean = false,
-) {
- val modifier = Modifier.size(dimensionResource(id = R.dimen.qs_icon_size))
- val context = LocalContext.current
- val loadedDrawable =
- remember(icon, context) {
- when (icon) {
- is Icon.Loaded -> icon.drawable
- is Icon.Resource -> AppCompatResources.getDrawable(context, icon.res)
- }
- }
- if (loadedDrawable !is Animatable) {
- Icon(
- icon = icon,
- tint = color,
+ DefaultEditTileGrid(
+ tiles = tiles,
+ iconOnlySpecs = iconOnlySpecs,
+ columns = GridCells.Fixed(columns),
modifier = modifier,
+ onAddTile = onAddTile,
+ onRemoveTile = onRemoveTile,
)
- } else if (icon is Icon.Resource) {
- val image = AnimatedImageVector.animatedVectorResource(id = icon.res)
- val painter =
- if (animateToEnd) {
- rememberAnimatedVectorPainter(animatedImageVector = image, atEnd = true)
- } else {
- var atEnd by remember(icon.res) { mutableStateOf(false) }
- LaunchedEffect(key1 = icon.res) {
- delay(350)
- atEnd = true
- }
- rememberAnimatedVectorPainter(animatedImageVector = image, atEnd = atEnd)
- }
- Image(
- painter = painter,
- contentDescription = null,
- colorFilter = ColorFilter.tint(color = color),
- modifier = modifier
- )
- }
-}
-
-@Composable
-private fun TileLazyGrid(
- modifier: Modifier = Modifier,
- columns: GridCells,
- content: LazyGridScope.() -> Unit,
-) {
- LazyVerticalGrid(
- columns = columns,
- verticalArrangement = spacedBy(dimensionResource(R.dimen.qs_tile_margin_vertical)),
- horizontalArrangement = spacedBy(dimensionResource(R.dimen.qs_tile_margin_horizontal)),
- modifier = modifier,
- content = content,
- )
-}
-
-@Composable
-private fun Modifier.tileModifier(colors: TileColorAttributes): Modifier {
- return fillMaxWidth()
- .clip(RoundedCornerShape(dimensionResource(R.dimen.qs_corner_radius)))
- .background(colorAttr(colors.background))
- .padding(horizontal = dimensionResource(id = R.dimen.qs_label_container_margin))
-}
-
-@Composable
-private fun tileHorizontalArrangement(iconOnly: Boolean): Arrangement.Horizontal {
- val horizontalAlignment =
- if (iconOnly) {
- Alignment.CenterHorizontally
- } else {
- Alignment.Start
- }
- return spacedBy(
- space = dimensionResource(id = R.dimen.qs_label_container_margin),
- alignment = horizontalAlignment
- )
-}
-
-@Composable
-private fun TileContent(
- label: String,
- secondaryLabel: String?,
- icon: Icon,
- colors: TileColorAttributes,
- iconOnly: Boolean,
- animateIconToEnd: Boolean = false,
-) {
- TileIcon(icon, colorAttr(colors.icon), animateIconToEnd)
-
- if (!iconOnly) {
- Column(verticalArrangement = Arrangement.Center, modifier = Modifier.fillMaxHeight()) {
- Text(
- label,
- color = colorAttr(colors.label),
- modifier = Modifier.basicMarquee(),
- )
- if (!TextUtils.isEmpty(secondaryLabel)) {
- Text(
- secondaryLabel ?: "",
- color = colorAttr(colors.secondaryLabel),
- modifier = Modifier.basicMarquee(),
- )
- }
- }
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/StretchedGridLayout.kt b/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/StretchedGridLayout.kt
new file mode 100644
index 0000000..ddd97c2
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/StretchedGridLayout.kt
@@ -0,0 +1,139 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.qs.panels.ui.compose
+
+import androidx.compose.foundation.layout.height
+import androidx.compose.foundation.lazy.grid.GridCells
+import androidx.compose.foundation.lazy.grid.GridItemSpan
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.DisposableEffect
+import androidx.compose.runtime.getValue
+import androidx.compose.runtime.remember
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.res.dimensionResource
+import androidx.lifecycle.compose.collectAsStateWithLifecycle
+import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.qs.panels.domain.interactor.IconTilesInteractor
+import com.android.systemui.qs.panels.domain.interactor.InfiniteGridSizeInteractor
+import com.android.systemui.qs.panels.shared.model.SizedTile
+import com.android.systemui.qs.panels.shared.model.TileRow
+import com.android.systemui.qs.panels.ui.viewmodel.EditTileViewModel
+import com.android.systemui.qs.panels.ui.viewmodel.TileViewModel
+import com.android.systemui.qs.pipeline.shared.TileSpec
+import com.android.systemui.res.R
+import javax.inject.Inject
+
+@SysUISingleton
+class StretchedGridLayout
+@Inject
+constructor(
+ private val iconTilesInteractor: IconTilesInteractor,
+ private val gridSizeInteractor: InfiniteGridSizeInteractor,
+) : GridLayout {
+
+ @Composable
+ override fun TileGrid(
+ tiles: List<TileViewModel>,
+ modifier: Modifier,
+ ) {
+ DisposableEffect(tiles) {
+ val token = Any()
+ tiles.forEach { it.startListening(token) }
+ onDispose { tiles.forEach { it.stopListening(token) } }
+ }
+
+ // Tile widths [normal|stretched]
+ // Icon [3 | 4]
+ // Large [6 | 8]
+ val columns = 12
+ val iconTilesSpecs by iconTilesInteractor.iconTilesSpecs.collectAsStateWithLifecycle()
+ val stretchedTiles =
+ remember(tiles) {
+ val sizedTiles =
+ tiles.map {
+ SizedTile(
+ it,
+ if (iconTilesSpecs.contains(it.spec)) {
+ 3
+ } else {
+ 6
+ }
+ )
+ }
+ splitInRows(sizedTiles, columns)
+ }
+
+ TileLazyGrid(columns = GridCells.Fixed(columns), modifier = modifier) {
+ items(stretchedTiles.size, span = { GridItemSpan(stretchedTiles[it].width) }) { index ->
+ Tile(
+ stretchedTiles[index].tile,
+ iconTilesSpecs.contains(stretchedTiles[index].tile.spec),
+ Modifier.height(dimensionResource(id = R.dimen.qs_tile_height))
+ )
+ }
+ }
+ }
+
+ @Composable
+ override fun EditTileGrid(
+ tiles: List<EditTileViewModel>,
+ modifier: Modifier,
+ onAddTile: (TileSpec, Int) -> Unit,
+ onRemoveTile: (TileSpec) -> Unit
+ ) {
+ val iconOnlySpecs by iconTilesInteractor.iconTilesSpecs.collectAsStateWithLifecycle()
+ val columns by gridSizeInteractor.columns.collectAsStateWithLifecycle()
+
+ DefaultEditTileGrid(
+ tiles = tiles,
+ iconOnlySpecs = iconOnlySpecs,
+ columns = GridCells.Fixed(columns),
+ modifier = modifier,
+ onAddTile = onAddTile,
+ onRemoveTile = onRemoveTile,
+ )
+ }
+
+ private fun splitInRows(
+ tiles: List<SizedTile<TileViewModel>>,
+ columns: Int
+ ): List<SizedTile<TileViewModel>> {
+ val row = TileRow<TileViewModel>(columns)
+
+ return buildList {
+ for (tile in tiles) {
+ if (row.maybeAddTile(tile)) {
+ if (row.isFull()) {
+ // Row is full, no need to stretch tiles
+ addAll(row.tiles)
+ row.clear()
+ }
+ } else {
+ if (row.isFull()) {
+ addAll(row.tiles)
+ } else {
+ // Stretching tiles when row isn't full
+ addAll(row.tiles.map { it.copy(width = it.width + (it.width / 3)) })
+ }
+ row.clear()
+ row.maybeAddTile(tile)
+ }
+ }
+ addAll(row.tiles)
+ }
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/Tile.kt b/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/Tile.kt
new file mode 100644
index 0000000..eb45110
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/Tile.kt
@@ -0,0 +1,403 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.qs.panels.ui.compose
+
+import android.graphics.drawable.Animatable
+import android.text.TextUtils
+import androidx.appcompat.content.res.AppCompatResources
+import androidx.compose.animation.graphics.ExperimentalAnimationGraphicsApi
+import androidx.compose.animation.graphics.res.animatedVectorResource
+import androidx.compose.animation.graphics.res.rememberAnimatedVectorPainter
+import androidx.compose.animation.graphics.vector.AnimatedImageVector
+import androidx.compose.foundation.ExperimentalFoundationApi
+import androidx.compose.foundation.Image
+import androidx.compose.foundation.background
+import androidx.compose.foundation.basicMarquee
+import androidx.compose.foundation.clickable
+import androidx.compose.foundation.combinedClickable
+import androidx.compose.foundation.layout.Arrangement
+import androidx.compose.foundation.layout.Arrangement.spacedBy
+import androidx.compose.foundation.layout.Box
+import androidx.compose.foundation.layout.Column
+import androidx.compose.foundation.layout.Row
+import androidx.compose.foundation.layout.fillMaxHeight
+import androidx.compose.foundation.layout.fillMaxWidth
+import androidx.compose.foundation.layout.height
+import androidx.compose.foundation.layout.padding
+import androidx.compose.foundation.layout.size
+import androidx.compose.foundation.lazy.grid.GridCells
+import androidx.compose.foundation.lazy.grid.GridItemSpan
+import androidx.compose.foundation.lazy.grid.LazyGridScope
+import androidx.compose.foundation.lazy.grid.LazyVerticalGrid
+import androidx.compose.foundation.shape.CircleShape
+import androidx.compose.foundation.shape.RoundedCornerShape
+import androidx.compose.material.icons.Icons
+import androidx.compose.material.icons.filled.Add
+import androidx.compose.material.icons.filled.Remove
+import androidx.compose.material3.Icon
+import androidx.compose.material3.Text
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.LaunchedEffect
+import androidx.compose.runtime.getValue
+import androidx.compose.runtime.mutableStateOf
+import androidx.compose.runtime.remember
+import androidx.compose.runtime.rememberUpdatedState
+import androidx.compose.runtime.setValue
+import androidx.compose.ui.Alignment
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.draw.clip
+import androidx.compose.ui.graphics.Color
+import androidx.compose.ui.graphics.ColorFilter
+import androidx.compose.ui.platform.LocalContext
+import androidx.compose.ui.res.dimensionResource
+import androidx.compose.ui.res.stringResource
+import androidx.compose.ui.semantics.contentDescription
+import androidx.compose.ui.semantics.onClick
+import androidx.compose.ui.semantics.semantics
+import androidx.compose.ui.semantics.stateDescription
+import androidx.compose.ui.unit.dp
+import androidx.lifecycle.compose.collectAsStateWithLifecycle
+import com.android.compose.animation.Expandable
+import com.android.compose.theme.colorAttr
+import com.android.systemui.common.shared.model.Icon
+import com.android.systemui.common.ui.compose.Icon
+import com.android.systemui.common.ui.compose.load
+import com.android.systemui.qs.panels.ui.viewmodel.ActiveTileColorAttributes
+import com.android.systemui.qs.panels.ui.viewmodel.AvailableEditActions
+import com.android.systemui.qs.panels.ui.viewmodel.EditTileViewModel
+import com.android.systemui.qs.panels.ui.viewmodel.TileColorAttributes
+import com.android.systemui.qs.panels.ui.viewmodel.TileUiState
+import com.android.systemui.qs.panels.ui.viewmodel.TileViewModel
+import com.android.systemui.qs.panels.ui.viewmodel.toUiState
+import com.android.systemui.qs.pipeline.domain.interactor.CurrentTilesInteractor
+import com.android.systemui.qs.pipeline.shared.TileSpec
+import com.android.systemui.qs.tileimpl.QSTileImpl
+import com.android.systemui.res.R
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.delay
+import kotlinx.coroutines.flow.mapLatest
+
+object TileType
+
+@OptIn(ExperimentalCoroutinesApi::class, ExperimentalFoundationApi::class)
+@Composable
+fun Tile(
+ tile: TileViewModel,
+ iconOnly: Boolean,
+ modifier: Modifier,
+) {
+ val state: TileUiState by
+ tile.state
+ .mapLatest { it.toUiState() }
+ .collectAsStateWithLifecycle(tile.currentState.toUiState())
+ val context = LocalContext.current
+
+ Expandable(
+ color = colorAttr(state.colors.background),
+ shape = RoundedCornerShape(dimensionResource(R.dimen.qs_corner_radius)),
+ ) {
+ Row(
+ modifier =
+ modifier
+ .combinedClickable(
+ onClick = { tile.onClick(it) },
+ onLongClick = { tile.onLongClick(it) }
+ )
+ .tileModifier(state.colors),
+ verticalAlignment = Alignment.CenterVertically,
+ horizontalArrangement = tileHorizontalArrangement(iconOnly),
+ ) {
+ val icon =
+ remember(state.icon) {
+ state.icon.get().let {
+ if (it is QSTileImpl.ResourceIcon) {
+ Icon.Resource(it.resId, null)
+ } else {
+ Icon.Loaded(it.getDrawable(context), null)
+ }
+ }
+ }
+ TileContent(
+ label = state.label.toString(),
+ secondaryLabel = state.secondaryLabel.toString(),
+ icon = icon,
+ colors = state.colors,
+ iconOnly = iconOnly
+ )
+ }
+ }
+}
+
+@Composable
+fun TileLazyGrid(
+ modifier: Modifier = Modifier,
+ columns: GridCells,
+ content: LazyGridScope.() -> Unit,
+) {
+ LazyVerticalGrid(
+ columns = columns,
+ verticalArrangement = spacedBy(dimensionResource(R.dimen.qs_tile_margin_vertical)),
+ horizontalArrangement = spacedBy(dimensionResource(R.dimen.qs_tile_margin_horizontal)),
+ modifier = modifier,
+ content = content,
+ )
+}
+
+@Composable
+fun DefaultEditTileGrid(
+ tiles: List<EditTileViewModel>,
+ iconOnlySpecs: Set<TileSpec>,
+ columns: GridCells,
+ modifier: Modifier,
+ onAddTile: (TileSpec, Int) -> Unit,
+ onRemoveTile: (TileSpec) -> Unit,
+) {
+ val (currentTiles, otherTiles) = tiles.partition { it.isCurrent }
+ val (otherTilesStock, otherTilesCustom) = otherTiles.partition { it.appName == null }
+ val addTileToEnd: (TileSpec) -> Unit by rememberUpdatedState {
+ onAddTile(it, CurrentTilesInteractor.POSITION_AT_END)
+ }
+ val isIconOnly: (TileSpec) -> Boolean =
+ remember(iconOnlySpecs) { { tileSpec: TileSpec -> tileSpec in iconOnlySpecs } }
+
+ TileLazyGrid(modifier = modifier, columns = columns) {
+ // These Text are just placeholders to see the different sections. Not final UI.
+ item(span = { GridItemSpan(maxLineSpan) }) { Text("Current tiles", color = Color.White) }
+
+ editTiles(
+ currentTiles,
+ ClickAction.REMOVE,
+ onRemoveTile,
+ isIconOnly,
+ indicatePosition = true,
+ )
+
+ item(span = { GridItemSpan(maxLineSpan) }) { Text("Tiles to add", color = Color.White) }
+
+ editTiles(
+ otherTilesStock,
+ ClickAction.ADD,
+ addTileToEnd,
+ isIconOnly,
+ )
+
+ item(span = { GridItemSpan(maxLineSpan) }) {
+ Text("Custom tiles to add", color = Color.White)
+ }
+
+ editTiles(
+ otherTilesCustom,
+ ClickAction.ADD,
+ addTileToEnd,
+ isIconOnly,
+ )
+ }
+}
+
+private fun LazyGridScope.editTiles(
+ tiles: List<EditTileViewModel>,
+ clickAction: ClickAction,
+ onClick: (TileSpec) -> Unit,
+ isIconOnly: (TileSpec) -> Boolean,
+ indicatePosition: Boolean = false,
+) {
+ items(
+ count = tiles.size,
+ key = { tiles[it].tileSpec.spec },
+ span = { GridItemSpan(if (isIconOnly(tiles[it].tileSpec)) 1 else 2) },
+ contentType = { TileType }
+ ) {
+ val viewModel = tiles[it]
+ val canClick =
+ when (clickAction) {
+ ClickAction.ADD -> AvailableEditActions.ADD in viewModel.availableEditActions
+ ClickAction.REMOVE -> AvailableEditActions.REMOVE in viewModel.availableEditActions
+ }
+ val onClickActionName =
+ when (clickAction) {
+ ClickAction.ADD ->
+ stringResource(id = R.string.accessibility_qs_edit_tile_add_action)
+ ClickAction.REMOVE ->
+ stringResource(id = R.string.accessibility_qs_edit_remove_tile_action)
+ }
+ val stateDescription =
+ if (indicatePosition) {
+ stringResource(id = R.string.accessibility_qs_edit_position, it + 1)
+ } else {
+ ""
+ }
+
+ Box(
+ modifier =
+ Modifier.clickable(enabled = canClick) { onClick.invoke(viewModel.tileSpec) }
+ .animateItem()
+ .semantics {
+ onClick(onClickActionName) { false }
+ this.stateDescription = stateDescription
+ }
+ ) {
+ EditTile(
+ tileViewModel = viewModel,
+ isIconOnly(viewModel.tileSpec),
+ modifier = Modifier.height(dimensionResource(id = R.dimen.qs_tile_height))
+ )
+ if (canClick) {
+ Badge(clickAction, Modifier.align(Alignment.TopEnd))
+ }
+ }
+ }
+}
+
+@Composable
+fun Badge(action: ClickAction, modifier: Modifier = Modifier) {
+ Box(modifier = modifier.size(16.dp).background(Color.Cyan, shape = CircleShape)) {
+ Icon(
+ imageVector =
+ when (action) {
+ ClickAction.ADD -> Icons.Filled.Add
+ ClickAction.REMOVE -> Icons.Filled.Remove
+ },
+ "",
+ tint = Color.Black,
+ )
+ }
+}
+
+@Composable
+fun EditTile(
+ tileViewModel: EditTileViewModel,
+ iconOnly: Boolean,
+ modifier: Modifier = Modifier,
+) {
+ val label = tileViewModel.label.load() ?: tileViewModel.tileSpec.spec
+ val colors = ActiveTileColorAttributes
+
+ Row(
+ modifier = modifier.tileModifier(colors).semantics { this.contentDescription = label },
+ verticalAlignment = Alignment.CenterVertically,
+ horizontalArrangement = tileHorizontalArrangement(iconOnly)
+ ) {
+ TileContent(
+ label = label,
+ secondaryLabel = tileViewModel.appName?.load(),
+ colors = colors,
+ icon = tileViewModel.icon,
+ iconOnly = iconOnly,
+ animateIconToEnd = true,
+ )
+ }
+}
+
+enum class ClickAction {
+ ADD,
+ REMOVE,
+}
+
+@OptIn(ExperimentalAnimationGraphicsApi::class)
+@Composable
+private fun TileIcon(
+ icon: Icon,
+ color: Color,
+ animateToEnd: Boolean = false,
+) {
+ val modifier = Modifier.size(dimensionResource(id = R.dimen.qs_icon_size))
+ val context = LocalContext.current
+ val loadedDrawable =
+ remember(icon, context) {
+ when (icon) {
+ is Icon.Loaded -> icon.drawable
+ is Icon.Resource -> AppCompatResources.getDrawable(context, icon.res)
+ }
+ }
+ if (loadedDrawable !is Animatable) {
+ Icon(
+ icon = icon,
+ tint = color,
+ modifier = modifier,
+ )
+ } else if (icon is Icon.Resource) {
+ val image = AnimatedImageVector.animatedVectorResource(id = icon.res)
+ val painter =
+ if (animateToEnd) {
+ rememberAnimatedVectorPainter(animatedImageVector = image, atEnd = true)
+ } else {
+ var atEnd by remember(icon.res) { mutableStateOf(false) }
+ LaunchedEffect(key1 = icon.res) {
+ delay(350)
+ atEnd = true
+ }
+ rememberAnimatedVectorPainter(animatedImageVector = image, atEnd = atEnd)
+ }
+ Image(
+ painter = painter,
+ contentDescription = null,
+ colorFilter = ColorFilter.tint(color = color),
+ modifier = modifier
+ )
+ }
+}
+
+@Composable
+private fun Modifier.tileModifier(colors: TileColorAttributes): Modifier {
+ return fillMaxWidth()
+ .clip(RoundedCornerShape(dimensionResource(R.dimen.qs_corner_radius)))
+ .background(colorAttr(colors.background))
+ .padding(horizontal = dimensionResource(id = R.dimen.qs_label_container_margin))
+}
+
+@Composable
+private fun tileHorizontalArrangement(iconOnly: Boolean): Arrangement.Horizontal {
+ val horizontalAlignment =
+ if (iconOnly) {
+ Alignment.CenterHorizontally
+ } else {
+ Alignment.Start
+ }
+ return spacedBy(
+ space = dimensionResource(id = R.dimen.qs_label_container_margin),
+ alignment = horizontalAlignment
+ )
+}
+
+@Composable
+private fun TileContent(
+ label: String,
+ secondaryLabel: String?,
+ icon: Icon,
+ colors: TileColorAttributes,
+ iconOnly: Boolean,
+ animateIconToEnd: Boolean = false,
+) {
+ TileIcon(icon, colorAttr(colors.icon), animateIconToEnd)
+
+ if (!iconOnly) {
+ Column(verticalArrangement = Arrangement.Center, modifier = Modifier.fillMaxHeight()) {
+ Text(
+ label,
+ color = colorAttr(colors.label),
+ modifier = Modifier.basicMarquee(),
+ )
+ if (!TextUtils.isEmpty(secondaryLabel)) {
+ Text(
+ secondaryLabel ?: "",
+ color = colorAttr(colors.secondaryLabel),
+ modifier = Modifier.basicMarquee(),
+ )
+ }
+ }
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/TileGrid.kt b/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/TileGrid.kt
index 2f32d72..2dab7c3 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/TileGrid.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/TileGrid.kt
@@ -17,15 +17,15 @@
package com.android.systemui.qs.panels.ui.compose
import androidx.compose.runtime.Composable
-import androidx.compose.runtime.collectAsState
import androidx.compose.runtime.getValue
import androidx.compose.ui.Modifier
+import androidx.lifecycle.compose.collectAsStateWithLifecycle
import com.android.systemui.qs.panels.ui.viewmodel.TileGridViewModel
@Composable
fun TileGrid(viewModel: TileGridViewModel, modifier: Modifier = Modifier) {
- val gridLayout by viewModel.gridLayout.collectAsState()
- val tiles by viewModel.tileViewModels.collectAsState(emptyList())
+ val gridLayout by viewModel.gridLayout.collectAsStateWithLifecycle()
+ val tiles by viewModel.tileViewModels.collectAsStateWithLifecycle(emptyList())
gridLayout.TileGrid(tiles, modifier)
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileViewImpl.kt b/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileViewImpl.kt
index 4fd0df4..c6dfdd5 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileViewImpl.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileViewImpl.kt
@@ -148,7 +148,8 @@
*/
protected var showRippleEffect = true
- private lateinit var qsTileBackground: LayerDrawable
+ private lateinit var qsTileBackground: RippleDrawable
+ private lateinit var qsTileFocusBackground: Drawable
private lateinit var backgroundDrawable: LayerDrawable
private lateinit var backgroundBaseDrawable: Drawable
private lateinit var backgroundOverlayDrawable: Drawable
@@ -313,10 +314,11 @@
private fun createTileBackground(): Drawable {
qsTileBackground = if (Flags.qsTileFocusState()) {
- mContext.getDrawable(R.drawable.qs_tile_background_flagged) as LayerDrawable
+ mContext.getDrawable(R.drawable.qs_tile_background_flagged) as RippleDrawable
} else {
mContext.getDrawable(R.drawable.qs_tile_background) as RippleDrawable
}
+ qsTileFocusBackground = mContext.getDrawable(R.drawable.qs_tile_focused_background)!!
backgroundDrawable =
qsTileBackground.findDrawableByLayerId(R.id.background) as LayerDrawable
backgroundBaseDrawable =
@@ -332,6 +334,17 @@
updateHeight()
}
+ override fun onFocusChanged(gainFocus: Boolean, direction: Int, previouslyFocusedRect: Rect?) {
+ super.onFocusChanged(gainFocus, direction, previouslyFocusedRect)
+ if (Flags.qsTileFocusState()) {
+ if (gainFocus) {
+ qsTileFocusBackground.setBounds(0, 0, width, height)
+ overlay.add(qsTileFocusBackground)
+ } else {
+ overlay.clear()
+ }
+ }
+ }
private fun updateHeight() {
// TODO(b/332900989): Find a more robust way of resetting the tile if not reset by the
// launch animation.
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/AirplaneModeTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/AirplaneModeTile.java
index 2068799..71b69c9 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/AirplaneModeTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/AirplaneModeTile.java
@@ -16,6 +16,8 @@
package com.android.systemui.qs.tiles;
+import static com.android.settingslib.satellite.SatelliteDialogUtils.TYPE_IS_AIRPLANE_MODE;
+
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
@@ -32,9 +34,12 @@
import android.widget.Switch;
import androidx.annotation.Nullable;
+import androidx.annotation.VisibleForTesting;
import com.android.internal.logging.MetricsLogger;
import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
+import com.android.internal.telephony.flags.Flags;
+import com.android.settingslib.satellite.SatelliteDialogUtils;
import com.android.systemui.animation.Expandable;
import com.android.systemui.broadcast.BroadcastDispatcher;
import com.android.systemui.dagger.qualifiers.Background;
@@ -54,6 +59,8 @@
import dagger.Lazy;
+import kotlinx.coroutines.Job;
+
import javax.inject.Inject;
/** Quick settings tile: Airplane mode **/
@@ -66,6 +73,9 @@
private final Lazy<ConnectivityManager> mLazyConnectivityManager;
private boolean mListening;
+ @Nullable
+ @VisibleForTesting
+ Job mClickJob;
@Inject
public AirplaneModeTile(
@@ -111,6 +121,21 @@
new Intent(TelephonyManager.ACTION_SHOW_NOTICE_ECM_BLOCK_OTHERS), 0);
return;
}
+
+ if (Flags.oemEnabledSatelliteFlag()) {
+ if (mClickJob != null && !mClickJob.isCompleted()) {
+ return;
+ }
+ mClickJob = SatelliteDialogUtils.mayStartSatelliteWarningDialog(
+ mContext, this, TYPE_IS_AIRPLANE_MODE, isAllowClick -> {
+ if (isAllowClick) {
+ setEnabled(!airplaneModeEnabled);
+ }
+ return null;
+ });
+ return;
+ }
+
setEnabled(!airplaneModeEnabled);
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/BluetoothTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/BluetoothTile.java
index 9af34f6..9f41d98 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/BluetoothTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/BluetoothTile.java
@@ -16,6 +16,7 @@
package com.android.systemui.qs.tiles;
+import static com.android.settingslib.satellite.SatelliteDialogUtils.TYPE_IS_BLUETOOTH;
import static com.android.systemui.util.PluralMessageFormaterKt.icuMessageFormat;
import android.annotation.Nullable;
@@ -33,11 +34,14 @@
import android.util.Log;
import android.widget.Switch;
+import androidx.annotation.VisibleForTesting;
+
import com.android.internal.logging.MetricsLogger;
import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
import com.android.settingslib.Utils;
import com.android.settingslib.bluetooth.BluetoothUtils;
import com.android.settingslib.bluetooth.CachedBluetoothDevice;
+import com.android.settingslib.satellite.SatelliteDialogUtils;
import com.android.systemui.animation.Expandable;
import com.android.systemui.bluetooth.qsdialog.BluetoothTileDialogViewModel;
import com.android.systemui.dagger.qualifiers.Background;
@@ -55,6 +59,8 @@
import com.android.systemui.res.R;
import com.android.systemui.statusbar.policy.BluetoothController;
+import kotlinx.coroutines.Job;
+
import java.util.List;
import java.util.concurrent.Executor;
@@ -78,6 +84,9 @@
private final BluetoothTileDialogViewModel mDialogViewModel;
private final FeatureFlags mFeatureFlags;
+ @Nullable
+ @VisibleForTesting
+ Job mClickJob;
@Inject
public BluetoothTile(
@@ -110,6 +119,24 @@
@Override
protected void handleClick(@Nullable Expandable expandable) {
+ if (com.android.internal.telephony.flags.Flags.oemEnabledSatelliteFlag()) {
+ if (mClickJob != null && !mClickJob.isCompleted()) {
+ return;
+ }
+ mClickJob = SatelliteDialogUtils.mayStartSatelliteWarningDialog(
+ mContext, this, TYPE_IS_BLUETOOTH, isAllowClick -> {
+ if (!isAllowClick) {
+ return null;
+ }
+ handleClickEvent(expandable);
+ return null;
+ });
+ return;
+ }
+ handleClickEvent(expandable);
+ }
+
+ private void handleClickEvent(@Nullable Expandable expandable) {
if (mFeatureFlags.isEnabled(Flags.BLUETOOTH_QS_TILE_DIALOG)) {
mDialogViewModel.showDialog(expandable);
} else {
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/base/actions/QSTileIntentUserInputHandler.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/base/actions/QSTileIntentUserInputHandler.kt
index 2d3120a..972b20e 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/base/actions/QSTileIntentUserInputHandler.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/base/actions/QSTileIntentUserInputHandler.kt
@@ -29,11 +29,15 @@
/**
* Provides a shortcut to start an activity from [QSTileUserActionInteractor]. It supports keyguard
- * dismissing and tile from-view animations.
+ * dismissing and tile from-view animations, as well as the option to show over lockscreen.
*/
interface QSTileIntentUserInputHandler {
- fun handle(expandable: Expandable?, intent: Intent)
+ fun handle(
+ expandable: Expandable?,
+ intent: Intent,
+ dismissShadeShowOverLockScreenWhenLocked: Boolean = false
+ )
/** @param requestLaunchingDefaultActivity used in case !pendingIndent.isActivity */
fun handle(
@@ -52,12 +56,25 @@
private val userHandle: UserHandle,
) : QSTileIntentUserInputHandler {
- override fun handle(expandable: Expandable?, intent: Intent) {
+ override fun handle(
+ expandable: Expandable?,
+ intent: Intent,
+ dismissShadeShowOverLockScreenWhenLocked: Boolean
+ ) {
val animationController: ActivityTransitionAnimator.Controller? =
expandable?.activityTransitionController(
InteractionJankMonitor.CUJ_SHADE_APP_LAUNCH_FROM_QS_TILE
)
- activityStarter.postStartActivityDismissingKeyguard(intent, 0, animationController)
+ if (dismissShadeShowOverLockScreenWhenLocked) {
+ activityStarter.startActivity(
+ intent,
+ true /* dismissShade */,
+ animationController,
+ true /* showOverLockscreenWhenLocked */
+ )
+ } else {
+ activityStarter.postStartActivityDismissingKeyguard(intent, 0, animationController)
+ }
}
// TODO(b/249804373): make sure to allow showing activities over the lockscreen. See b/292112939
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetDialogDelegate.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetDialogDelegate.java
index c9c4443..c971f54 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetDialogDelegate.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetDialogDelegate.java
@@ -15,6 +15,7 @@
*/
package com.android.systemui.qs.tiles.dialog;
+import static com.android.settingslib.satellite.SatelliteDialogUtils.TYPE_IS_WIFI;
import static com.android.systemui.Prefs.Key.QS_HAS_TURNED_OFF_MOBILE_DATA;
import static com.android.systemui.qs.tiles.dialog.InternetDialogController.MAX_WIFI_ENTRY_COUNT;
@@ -57,6 +58,8 @@
import com.android.internal.logging.UiEvent;
import com.android.internal.logging.UiEventLogger;
+import com.android.internal.telephony.flags.Flags;
+import com.android.settingslib.satellite.SatelliteDialogUtils;
import com.android.settingslib.wifi.WifiEnterpriseRestrictionUtils;
import com.android.systemui.Prefs;
import com.android.systemui.accessibility.floatingmenu.AnnotationLinkSpan;
@@ -73,6 +76,7 @@
import dagger.assisted.AssistedInject;
import kotlinx.coroutines.CoroutineScope;
+import kotlinx.coroutines.Job;
import java.util.List;
import java.util.concurrent.Executor;
@@ -161,6 +165,9 @@
// Wi-Fi scanning progress bar
protected boolean mIsProgressBarVisible;
private SystemUIDialog mDialog;
+ private final CoroutineScope mCoroutineScope;
+ @Nullable
+ private Job mClickJob;
@AssistedFactory
public interface Factory {
@@ -203,7 +210,7 @@
mCanConfigWifi = canConfigWifi;
mCanChangeWifiState = WifiEnterpriseRestrictionUtils.isChangeWifiStateAllowed(context);
mKeyguard = keyguardStateController;
-
+ mCoroutineScope = coroutineScope;
mUiEventLogger = uiEventLogger;
mDialogTransitionAnimator = dialogTransitionAnimator;
mAdapter = new InternetAdapter(mInternetDialogController, coroutineScope);
@@ -388,11 +395,9 @@
});
mConnectedWifListLayout.setOnClickListener(this::onClickConnectedWifi);
mSeeAllLayout.setOnClickListener(this::onClickSeeMoreButton);
- mWiFiToggle.setOnCheckedChangeListener(
- (buttonView, isChecked) -> {
- if (mInternetDialogController.isWifiEnabled() == isChecked) return;
- mInternetDialogController.setWifiEnabled(isChecked);
- });
+ mWiFiToggle.setOnClickListener(v -> {
+ handleWifiToggleClicked(mWiFiToggle.isChecked());
+ });
mDoneButton.setOnClickListener(v -> dialog.dismiss());
mShareWifiButton.setOnClickListener(v -> {
if (mInternetDialogController.mayLaunchShareWifiSettings(mConnectedWifiEntry, v)) {
@@ -404,6 +409,32 @@
});
}
+ private void handleWifiToggleClicked(boolean isChecked) {
+ if (Flags.oemEnabledSatelliteFlag()) {
+ if (mClickJob != null && !mClickJob.isCompleted()) {
+ return;
+ }
+ mClickJob = SatelliteDialogUtils.mayStartSatelliteWarningDialog(
+ mDialog.getContext(), mCoroutineScope, TYPE_IS_WIFI, isAllowClick -> {
+ if (isAllowClick) {
+ setWifiEnable(isChecked);
+ } else {
+ mWiFiToggle.setChecked(!isChecked);
+ }
+ return null;
+ });
+ return;
+ }
+ setWifiEnable(isChecked);
+ }
+
+ private void setWifiEnable(boolean isChecked) {
+ if (mInternetDialogController.isWifiEnabled() == isChecked) {
+ return;
+ }
+ mInternetDialogController.setWifiEnabled(isChecked);
+ }
+
@MainThread
private void updateEthernet() {
mEthernetLayout.setVisibility(
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/qr/domain/interactor/QRCodeScannerTileDataInteractor.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/qr/domain/interactor/QRCodeScannerTileDataInteractor.kt
new file mode 100644
index 0000000..1e8ce58
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/qr/domain/interactor/QRCodeScannerTileDataInteractor.kt
@@ -0,0 +1,85 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.qs.tiles.impl.qr.domain.interactor
+
+import android.os.UserHandle
+import com.android.systemui.dagger.qualifiers.Application
+import com.android.systemui.dagger.qualifiers.Background
+import com.android.systemui.qrcodescanner.controller.QRCodeScannerController
+import com.android.systemui.qrcodescanner.controller.QRCodeScannerController.DEFAULT_QR_CODE_SCANNER_CHANGE
+import com.android.systemui.qs.tiles.base.interactor.DataUpdateTrigger
+import com.android.systemui.qs.tiles.base.interactor.QSTileDataInteractor
+import com.android.systemui.qs.tiles.impl.qr.domain.model.QRCodeScannerTileModel
+import com.android.systemui.utils.coroutines.flow.conflatedCallbackFlow
+import javax.inject.Inject
+import kotlin.coroutines.CoroutineContext
+import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.channels.awaitClose
+import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.SharingStarted
+import kotlinx.coroutines.flow.flowOf
+import kotlinx.coroutines.flow.flowOn
+import kotlinx.coroutines.flow.onStart
+import kotlinx.coroutines.flow.stateIn
+
+/** Observes one qr scanner state changes providing the [QRCodeScannerTileModel]. */
+class QRCodeScannerTileDataInteractor
+@Inject
+constructor(
+ @Background private val bgCoroutineContext: CoroutineContext,
+ @Application private val scope: CoroutineScope,
+ private val qrController: QRCodeScannerController,
+) : QSTileDataInteractor<QRCodeScannerTileModel> {
+ override fun tileData(
+ user: UserHandle,
+ triggers: Flow<DataUpdateTrigger>
+ ): Flow<QRCodeScannerTileModel> =
+ conflatedCallbackFlow {
+ qrController.registerQRCodeScannerChangeObservers(DEFAULT_QR_CODE_SCANNER_CHANGE)
+ val callback =
+ object : QRCodeScannerController.Callback {
+ override fun onQRCodeScannerActivityChanged() {
+ trySend(generateModel())
+ }
+ }
+ qrController.addCallback(callback)
+ awaitClose {
+ qrController.removeCallback(callback)
+ qrController.unregisterQRCodeScannerChangeObservers(
+ DEFAULT_QR_CODE_SCANNER_CHANGE
+ )
+ }
+ }
+ .onStart { emit(generateModel()) }
+ .flowOn(bgCoroutineContext)
+ .stateIn(
+ scope,
+ SharingStarted.WhileSubscribed(),
+ QRCodeScannerTileModel.TemporarilyUnavailable
+ )
+
+ override fun availability(user: UserHandle): Flow<Boolean> =
+ flowOf(qrController.isCameraAvailable)
+
+ private fun generateModel(): QRCodeScannerTileModel {
+ val intent = qrController.intent
+
+ return if (qrController.isAbleToLaunchScannerActivity && intent != null)
+ QRCodeScannerTileModel.Available(intent)
+ else QRCodeScannerTileModel.TemporarilyUnavailable
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/qr/domain/interactor/QRCodeScannerTileUserActionInteractor.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/qr/domain/interactor/QRCodeScannerTileUserActionInteractor.kt
new file mode 100644
index 0000000..7c0c41e
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/qr/domain/interactor/QRCodeScannerTileUserActionInteractor.kt
@@ -0,0 +1,50 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.qs.tiles.impl.qr.domain.interactor
+
+import com.android.systemui.qs.tiles.base.actions.QSTileIntentUserInputHandler
+import com.android.systemui.qs.tiles.base.interactor.QSTileInput
+import com.android.systemui.qs.tiles.base.interactor.QSTileUserActionInteractor
+import com.android.systemui.qs.tiles.impl.qr.domain.model.QRCodeScannerTileModel
+import com.android.systemui.qs.tiles.viewmodel.QSTileUserAction
+import javax.inject.Inject
+
+/** Handles qr tile clicks. */
+class QRCodeScannerTileUserActionInteractor
+@Inject
+constructor(
+ private val qsTileIntentUserActionHandler: QSTileIntentUserInputHandler,
+) : QSTileUserActionInteractor<QRCodeScannerTileModel> {
+
+ override suspend fun handleInput(input: QSTileInput<QRCodeScannerTileModel>): Unit =
+ with(input) {
+ when (action) {
+ is QSTileUserAction.Click -> {
+ when (data) {
+ is QRCodeScannerTileModel.Available ->
+ qsTileIntentUserActionHandler.handle(
+ action.expandable,
+ data.intent,
+ true
+ )
+ is QRCodeScannerTileModel.TemporarilyUnavailable -> {} // no-op
+ }
+ }
+ is QSTileUserAction.LongClick -> {} // no-op
+ }
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/qr/domain/model/QRCodeScannerTileModel.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/qr/domain/model/QRCodeScannerTileModel.kt
new file mode 100644
index 0000000..22c9b66
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/qr/domain/model/QRCodeScannerTileModel.kt
@@ -0,0 +1,25 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.qs.tiles.impl.qr.domain.model
+
+import android.content.Intent
+
+/** qr scanner tile model. */
+sealed interface QRCodeScannerTileModel {
+ data class Available(val intent: Intent) : QRCodeScannerTileModel
+ data object TemporarilyUnavailable : QRCodeScannerTileModel
+}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/qr/ui/QRCodeScannerTileMapper.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/qr/ui/QRCodeScannerTileMapper.kt
new file mode 100644
index 0000000..45a7717
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/qr/ui/QRCodeScannerTileMapper.kt
@@ -0,0 +1,59 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.qs.tiles.impl.qr.ui
+
+import android.content.res.Resources
+import com.android.systemui.common.shared.model.Icon
+import com.android.systemui.dagger.qualifiers.Main
+import com.android.systemui.qs.tiles.base.interactor.QSTileDataToStateMapper
+import com.android.systemui.qs.tiles.impl.qr.domain.model.QRCodeScannerTileModel
+import com.android.systemui.qs.tiles.viewmodel.QSTileConfig
+import com.android.systemui.qs.tiles.viewmodel.QSTileState
+import com.android.systemui.res.R
+import javax.inject.Inject
+
+/** Maps [QRCodeScannerTileModel] to [QSTileState]. */
+class QRCodeScannerTileMapper
+@Inject
+constructor(
+ @Main private val resources: Resources,
+ private val theme: Resources.Theme,
+) : QSTileDataToStateMapper<QRCodeScannerTileModel> {
+
+ override fun map(config: QSTileConfig, data: QRCodeScannerTileModel): QSTileState =
+ QSTileState.build(resources, theme, config.uiConfig) {
+ label = resources.getString(R.string.qr_code_scanner_title)
+ contentDescription = label
+ icon = {
+ Icon.Loaded(resources.getDrawable(R.drawable.ic_qr_code_scanner, theme), null)
+ }
+ sideViewIcon = QSTileState.SideViewIcon.Chevron
+ supportedActions = setOf(QSTileState.UserAction.CLICK)
+
+ when (data) {
+ is QRCodeScannerTileModel.Available -> {
+ activationState = QSTileState.ActivationState.INACTIVE
+ secondaryLabel = null
+ }
+ is QRCodeScannerTileModel.TemporarilyUnavailable -> {
+ activationState = QSTileState.ActivationState.UNAVAILABLE
+ secondaryLabel =
+ resources.getString(R.string.qr_code_scanner_updating_secondary_label)
+ }
+ }
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/ui/adapter/QSSceneAdapter.kt b/packages/SystemUI/src/com/android/systemui/qs/ui/adapter/QSSceneAdapter.kt
index 63acbb0..fb872d5 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/ui/adapter/QSSceneAdapter.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/ui/adapter/QSSceneAdapter.kt
@@ -127,28 +127,28 @@
val isVisible: Boolean
val expansion: Float
- val squishiness: Float
+ val squishiness: () -> Float
data object CLOSED : State {
override val isVisible = false
override val expansion = 0f
- override val squishiness = 1f
+ override val squishiness = { 1f }
}
/** State for expanding between QQS and QS */
data class Expanding(override val expansion: Float) : State {
override val isVisible = true
- override val squishiness = 1f
+ override val squishiness = { 1f }
}
/** State for appearing QQS from Lockscreen or Gone */
- data class UnsquishingQQS(override val squishiness: Float) : State {
+ data class UnsquishingQQS(override val squishiness: () -> Float) : State {
override val isVisible = true
override val expansion = 0f
}
/** State for appearing QS from Lockscreen or Gone, used in Split shade */
- data class UnsquishingQS(override val squishiness: Float) : State {
+ data class UnsquishingQS(override val squishiness: () -> Float) : State {
override val isVisible = true
override val expansion = 1f
}
@@ -370,7 +370,7 @@
setQsVisible(state.isVisible)
setExpanded(state.isVisible && state.expansion > 0f)
setListening(state.isVisible)
- setQsExpansion(state.expansion, 1f, 0f, state.squishiness)
+ setQsExpansion(state.expansion, 1f, 0f, state.squishiness())
}
override fun dump(pw: PrintWriter, args: Array<out String>) {
diff --git a/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java b/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java
index 931f00b..0327ec7 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java
@@ -108,13 +108,14 @@
import com.android.systemui.shared.recents.IOverviewProxy;
import com.android.systemui.shared.recents.ISystemUiProxy;
import com.android.systemui.shared.system.QuickStepContract;
+import com.android.systemui.shared.system.QuickStepContract.SystemUiStateFlags;
import com.android.systemui.shared.system.smartspace.ISysuiUnlockAnimationController;
import com.android.systemui.statusbar.CommandQueue;
import com.android.systemui.statusbar.NotificationShadeWindowController;
import com.android.systemui.statusbar.phone.StatusBarWindowCallback;
import com.android.systemui.statusbar.policy.CallbackController;
import com.android.systemui.unfold.progress.UnfoldTransitionProgressForwarder;
-import com.android.wm.shell.desktopmode.DesktopModeStatus;
+import com.android.wm.shell.shared.DesktopModeStatus;
import com.android.wm.shell.sysui.ShellInterface;
import dagger.Lazy;
@@ -771,7 +772,7 @@
}
}
- private void notifySystemUiStateFlags(int flags) {
+ private void notifySystemUiStateFlags(@SystemUiStateFlags long flags) {
if (SysUiState.DEBUG) {
Log.d(TAG_OPS, "Notifying sysui state change to overview service: proxy="
+ mOverviewProxy + " flags=" + flags);
diff --git a/packages/SystemUI/src/com/android/systemui/recordissue/IssueRecordingService.kt b/packages/SystemUI/src/com/android/systemui/recordissue/IssueRecordingService.kt
index 4e290e6..6694878 100644
--- a/packages/SystemUI/src/com/android/systemui/recordissue/IssueRecordingService.kt
+++ b/packages/SystemUI/src/com/android/systemui/recordissue/IssueRecordingService.kt
@@ -20,7 +20,6 @@
import android.app.NotificationManager
import android.content.Context
import android.content.Intent
-import android.content.pm.LauncherApps
import android.content.res.Resources
import android.net.Uri
import android.os.Handler
@@ -63,7 +62,6 @@
private val panelInteractor: PanelInteractor,
private val issueRecordingState: IssueRecordingState,
private val iActivityManager: IActivityManager,
- private val launcherApps: LauncherApps,
) :
RecordingService(
controller,
@@ -85,7 +83,7 @@
when (intent?.action) {
ACTION_START -> {
TraceUtils.traceStart(
- contentResolver,
+ this,
DEFAULT_TRACE_TAGS,
DEFAULT_BUFFER_SIZE,
DEFAULT_IS_INCLUDING_WINSCOPE,
@@ -104,11 +102,7 @@
}
ACTION_STOP,
ACTION_STOP_NOTIF -> {
- // ViewCapture needs to save it's data before it is disabled, or else the data will
- // be lost. This is expected to change in the near future, and when that happens
- // this line should be removed.
- launcherApps.saveViewCaptureData()
- TraceUtils.traceStop(contentResolver)
+ TraceUtils.traceStop(this)
issueRecordingState.isRecording = false
}
ACTION_SHARE -> {
@@ -142,7 +136,7 @@
private fun shareRecording(screenRecording: Uri?) {
val traces =
- TraceUtils.traceDump(contentResolver, TRACE_FILE_NAME).getOrElse {
+ TraceUtils.traceDump(this, TRACE_FILE_NAME).getOrElse {
Log.v(
TAG,
"Traces were not present. This can happen if users double" +
diff --git a/packages/SystemUI/src/com/android/systemui/scene/data/repository/SceneContainerRepository.kt b/packages/SystemUI/src/com/android/systemui/scene/data/repository/SceneContainerRepository.kt
index eabc42b..3e2c630 100644
--- a/packages/SystemUI/src/com/android/systemui/scene/data/repository/SceneContainerRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/scene/data/repository/SceneContainerRepository.kt
@@ -21,6 +21,7 @@
import com.android.compose.animation.scene.ObservableTransitionState
import com.android.compose.animation.scene.SceneKey
import com.android.compose.animation.scene.TransitionKey
+import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Application
import com.android.systemui.scene.shared.model.SceneContainerConfig
import com.android.systemui.scene.shared.model.SceneDataSource
@@ -36,6 +37,7 @@
import kotlinx.coroutines.flow.flowOf
import kotlinx.coroutines.flow.stateIn
+@SysUISingleton
/** Source of truth for scene framework application state. */
class SceneContainerRepository
@Inject
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 08efe39..0d0f6e0 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
@@ -55,6 +55,18 @@
private val deviceUnlockedInteractor: DeviceUnlockedInteractor,
) {
+ interface OnSceneAboutToChangeListener {
+
+ /**
+ * Notifies that the scene is about to change to [toScene].
+ *
+ * The implementation can choose to consume the [sceneState] to prepare the incoming scene.
+ */
+ fun onSceneAboutToChange(toScene: SceneKey, sceneState: Any?)
+ }
+
+ private val onSceneAboutToChangeListener = mutableSetOf<OnSceneAboutToChangeListener>()
+
/**
* The current scene.
*
@@ -149,6 +161,10 @@
return repository.allSceneKeys()
}
+ fun registerSceneStateProcessor(processor: OnSceneAboutToChangeListener) {
+ onSceneAboutToChangeListener.add(processor)
+ }
+
/**
* Requests a scene change to the given scene.
*
@@ -161,6 +177,7 @@
toScene: SceneKey,
loggingReason: String,
transitionKey: TransitionKey? = null,
+ sceneState: Any? = null,
) {
val currentSceneKey = currentScene.value
if (
@@ -180,6 +197,7 @@
isInstant = false,
)
+ onSceneAboutToChangeListener.forEach { it.onSceneAboutToChange(toScene, sceneState) }
repository.changeScene(toScene, transitionKey)
}
diff --git a/packages/SystemUI/src/com/android/systemui/scene/domain/startable/SceneContainerStartable.kt b/packages/SystemUI/src/com/android/systemui/scene/domain/startable/SceneContainerStartable.kt
index 4a64277..3ce12dd 100644
--- a/packages/SystemUI/src/com/android/systemui/scene/domain/startable/SceneContainerStartable.kt
+++ b/packages/SystemUI/src/com/android/systemui/scene/domain/startable/SceneContainerStartable.kt
@@ -234,7 +234,7 @@
bouncerInteractor.onImeHiddenByUser.collectLatest {
if (sceneInteractor.currentScene.value == Scenes.Bouncer) {
sceneInteractor.changeScene(
- toScene = Scenes.Lockscreen,
+ toScene = Scenes.Lockscreen, // TODO(b/336581871): add sceneState?
loggingReason = "IME hidden",
)
}
@@ -252,6 +252,7 @@
when {
isAnySimLocked -> {
switchToScene(
+ // TODO(b/336581871): add sceneState?
targetSceneKey = Scenes.Bouncer,
loggingReason = "Need to authenticate locked SIM card."
)
@@ -259,6 +260,7 @@
unlockStatus.isUnlocked &&
deviceEntryInteractor.canSwipeToEnter.value == false -> {
switchToScene(
+ // TODO(b/336581871): add sceneState?
targetSceneKey = Scenes.Gone,
loggingReason =
"All SIM cards unlocked and device already unlocked and " +
@@ -267,6 +269,7 @@
}
else -> {
switchToScene(
+ // TODO(b/336581871): add sceneState?
targetSceneKey = Scenes.Lockscreen,
loggingReason =
"All SIM cards unlocked and device still locked" +
@@ -325,7 +328,8 @@
Scenes.Gone to "device was unlocked in Bouncer scene"
} else {
val prevScene = previousScene.value
- (prevScene ?: Scenes.Gone) to
+ (prevScene
+ ?: Scenes.Gone) to
"device was unlocked in Bouncer scene, from sceneKey=$prevScene"
}
isOnLockscreen ->
@@ -364,6 +368,7 @@
powerInteractor.isAsleep.collect { isAsleep ->
if (isAsleep) {
switchToScene(
+ // TODO(b/336581871): add sceneState?
targetSceneKey = Scenes.Lockscreen,
loggingReason = "device is starting to sleep",
)
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/ActionExecutor.kt b/packages/SystemUI/src/com/android/systemui/screenshot/ActionExecutor.kt
index caa67df..1868b4a 100644
--- a/packages/SystemUI/src/com/android/systemui/screenshot/ActionExecutor.kt
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/ActionExecutor.kt
@@ -25,7 +25,6 @@
import android.os.UserHandle
import android.util.Log
import android.util.Pair
-import android.view.View
import android.view.Window
import com.android.app.tracing.coroutines.launch
import com.android.internal.app.ChooserActivity
@@ -41,8 +40,8 @@
private val intentExecutor: ActionIntentExecutor,
@Application private val applicationScope: CoroutineScope,
@Assisted val window: Window,
- @Assisted val transitionView: View,
- @Assisted val onDismiss: (() -> Unit)
+ @Assisted val viewProxy: ScreenshotViewProxy,
+ @Assisted val finishDismiss: () -> Unit,
) {
var isPendingSharedTransition = false
@@ -50,6 +49,7 @@
fun startSharedTransition(intent: Intent, user: UserHandle, overrideTransition: Boolean) {
isPendingSharedTransition = true
+ viewProxy.fadeForSharedTransition()
val windowTransition = createWindowTransition()
applicationScope.launch("$TAG#launchIntentAsync") {
intentExecutor.launchIntent(
@@ -70,7 +70,7 @@
ActivityOptions.MODE_BACKGROUND_ACTIVITY_START_ALLOWED
)
pendingIntent.send(options.toBundle())
- onDismiss.invoke()
+ viewProxy.requestDismissal(null)
} catch (e: PendingIntent.CanceledException) {
Log.e(TAG, "Intent cancelled", e)
}
@@ -89,7 +89,7 @@
override fun hideSharedElements() {
isPendingSharedTransition = false
- onDismiss.invoke()
+ finishDismiss.invoke()
}
override fun onFinish() {}
@@ -98,13 +98,20 @@
window,
callbacks,
null,
- Pair.create(transitionView, ChooserActivity.FIRST_IMAGE_PREVIEW_TRANSITION_NAME)
+ Pair.create(
+ viewProxy.screenshotPreview,
+ ChooserActivity.FIRST_IMAGE_PREVIEW_TRANSITION_NAME
+ )
)
}
@AssistedFactory
interface Factory {
- fun create(window: Window, transitionView: View, onDismiss: (() -> Unit)): ActionExecutor
+ fun create(
+ window: Window,
+ viewProxy: ScreenshotViewProxy,
+ finishDismiss: (() -> Unit)
+ ): ActionExecutor
}
companion object {
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/ActionIntentCreator.kt b/packages/SystemUI/src/com/android/systemui/screenshot/ActionIntentCreator.kt
index a0cef52..15638d3 100644
--- a/packages/SystemUI/src/com/android/systemui/screenshot/ActionIntentCreator.kt
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/ActionIntentCreator.kt
@@ -97,6 +97,7 @@
.putExtra(LongScreenshotActivity.EXTRA_SCREENSHOT_USER_HANDLE, owner)
.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP)
+ .addFlags(Intent.FLAG_ACTIVITY_NO_ANIMATION)
}
private const val EXTRA_EDIT_SOURCE = "edit_source"
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/ActionIntentExecutor.kt b/packages/SystemUI/src/com/android/systemui/screenshot/ActionIntentExecutor.kt
index 4eca51d..4ab0918 100644
--- a/packages/SystemUI/src/com/android/systemui/screenshot/ActionIntentExecutor.kt
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/ActionIntentExecutor.kt
@@ -33,10 +33,11 @@
import android.view.WindowManagerGlobal
import com.android.app.tracing.coroutines.launch
import com.android.internal.infra.ServiceConnector
-import com.android.systemui.Flags.screenshotActionDismissSystemWindows
+import com.android.systemui.Flags
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Application
import com.android.systemui.dagger.qualifiers.Main
+import com.android.systemui.screenshot.proxy.SystemUiProxy
import com.android.systemui.settings.DisplayTracker
import com.android.systemui.shared.system.ActivityManagerWrapper
import com.android.systemui.statusbar.phone.CentralSurfaces
@@ -54,8 +55,8 @@
private val activityManagerWrapper: ActivityManagerWrapper,
@Application private val applicationScope: CoroutineScope,
@Main private val mainDispatcher: CoroutineDispatcher,
+ private val systemUiProxy: SystemUiProxy,
private val displayTracker: DisplayTracker,
- private val keyguardController: ScreenshotKeyguardController,
) {
/**
* Execute the given intent with startActivity while performing operations for screenshot action
@@ -83,14 +84,12 @@
options: ActivityOptions?,
transitionCoordinator: ExitTransitionCoordinator?,
) {
- if (screenshotActionDismissSystemWindows()) {
- keyguardController.dismiss()
+ if (Flags.fixScreenshotActionDismissSystemWindows()) {
activityManagerWrapper.closeSystemWindows(
CentralSurfaces.SYSTEM_DIALOG_REASON_SCREENSHOT
)
- } else {
- dismissKeyguard()
}
+ systemUiProxy.dismissKeyguard()
transitionCoordinator?.startExit()
if (user == myUserHandle()) {
@@ -110,27 +109,6 @@
}
}
- private val proxyConnector: ServiceConnector<IScreenshotProxy> =
- ServiceConnector.Impl(
- context,
- Intent(context, ScreenshotProxyService::class.java),
- Context.BIND_AUTO_CREATE or Context.BIND_WAIVE_PRIORITY or Context.BIND_NOT_VISIBLE,
- context.userId,
- IScreenshotProxy.Stub::asInterface,
- )
-
- private suspend fun dismissKeyguard() {
- val completion = CompletableDeferred<Unit>()
- val onDoneBinder =
- object : IOnDoneCallback.Stub() {
- override fun onDone(success: Boolean) {
- completion.complete(Unit)
- }
- }
- proxyConnector.post { it.dismissKeyguard(onDoneBinder) }
- completion.await()
- }
-
private fun getCrossProfileConnector(user: UserHandle): ServiceConnector<ICrossProfileService> =
ServiceConnector.Impl<ICrossProfileService>(
context,
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/LegacyScreenshotViewProxy.kt b/packages/SystemUI/src/com/android/systemui/screenshot/LegacyScreenshotViewProxy.kt
index 4cf18fb..3d024a6 100644
--- a/packages/SystemUI/src/com/android/systemui/screenshot/LegacyScreenshotViewProxy.kt
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/LegacyScreenshotViewProxy.kt
@@ -157,6 +157,8 @@
override fun restoreNonScrollingUi() = view.restoreNonScrollingUi()
+ override fun fadeForSharedTransition() {} // unused
+
override fun stopInputListening() = view.stopInputListening()
override fun requestFocus() {
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotController.java b/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotController.java
index 2f026ae..9ad6d0f 100644
--- a/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotController.java
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotController.java
@@ -317,9 +317,9 @@
mConfigChanges.applyNewConfig(context.getResources());
reloadAssets();
- mActionExecutor = actionExecutorFactory.create(mWindow, mViewProxy.getScreenshotPreview(),
+ mActionExecutor = actionExecutorFactory.create(mWindow, mViewProxy,
() -> {
- requestDismissal(null);
+ finishDismiss();
return Unit.INSTANCE;
});
@@ -623,9 +623,11 @@
(response) -> {
mUiEventLogger.log(ScreenshotEvent.SCREENSHOT_LONG_SCREENSHOT_IMPRESSION,
0, response.getPackageName());
- if (screenshotShelfUi2() && mActionsProvider != null) {
- mActionsProvider.onScrollChipReady(
- () -> onScrollButtonClicked(owner, response));
+ if (screenshotShelfUi2()) {
+ if (mActionsProvider != null) {
+ mActionsProvider.onScrollChipReady(
+ () -> onScrollButtonClicked(owner, response));
+ }
} else {
mViewProxy.showScrollChip(response.getPackageName(),
() -> onScrollButtonClicked(owner, response));
@@ -657,9 +659,7 @@
() -> {
final Intent intent = ActionIntentCreator.INSTANCE.createLongScreenshotIntent(
owner, mContext);
- mActionIntentExecutor.launchIntentAsync(intent, owner, true,
- ActivityOptions.makeCustomAnimation(mContext, 0, 0), null);
-
+ mActionIntentExecutor.launchIntentAsync(intent, owner, true, null, null);
},
mViewProxy::restoreNonScrollingUi,
mViewProxy::startLongScreenshotTransition);
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotKeyguardController.kt b/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotKeyguardController.kt
deleted file mode 100644
index 7696bbe..0000000
--- a/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotKeyguardController.kt
+++ /dev/null
@@ -1,46 +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.screenshot
-
-import android.content.Context
-import android.content.Intent
-import com.android.internal.infra.ServiceConnector
-import javax.inject.Inject
-import kotlinx.coroutines.CompletableDeferred
-
-open class ScreenshotKeyguardController @Inject constructor(context: Context) {
- private val proxyConnector: ServiceConnector<IScreenshotProxy> =
- ServiceConnector.Impl(
- context,
- Intent(context, ScreenshotProxyService::class.java),
- Context.BIND_AUTO_CREATE or Context.BIND_WAIVE_PRIORITY or Context.BIND_NOT_VISIBLE,
- context.userId,
- IScreenshotProxy.Stub::asInterface
- )
-
- suspend fun dismiss() {
- val completion = CompletableDeferred<Unit>()
- val onDoneBinder =
- object : IOnDoneCallback.Stub() {
- override fun onDone(success: Boolean) {
- completion.complete(Unit)
- }
- }
- proxyConnector.post { it.dismissKeyguard(onDoneBinder) }
- completion.await()
- }
-}
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotShelfViewProxy.kt b/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotShelfViewProxy.kt
index 1e66cd1..3ac070a 100644
--- a/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotShelfViewProxy.kt
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotShelfViewProxy.kt
@@ -31,6 +31,7 @@
import android.view.WindowManager
import android.window.OnBackInvokedCallback
import android.window.OnBackInvokedDispatcher
+import androidx.appcompat.content.res.AppCompatResources
import androidx.core.animation.doOnEnd
import androidx.core.animation.doOnStart
import com.android.internal.logging.UiEventLogger
@@ -58,6 +59,7 @@
private val logger: UiEventLogger,
private val viewModel: ScreenshotViewModel,
private val windowManager: WindowManager,
+ shelfViewBinder: ScreenshotShelfViewBinder,
private val thumbnailObserver: ThumbnailObserver,
@Assisted private val context: Context,
@Assisted private val displayId: Int
@@ -69,7 +71,17 @@
override var callbacks: ScreenshotView.ScreenshotViewCallback? = null
override var screenshot: ScreenshotData? = null
set(value) {
- viewModel.setScreenshotBitmap(value?.bitmap)
+ value?.let {
+ viewModel.setScreenshotBitmap(it.bitmap)
+ val badgeBg =
+ AppCompatResources.getDrawable(context, R.drawable.overlay_badge_background)
+ val user = it.userHandle
+ if (badgeBg != null && user != null) {
+ viewModel.setScreenshotBadge(
+ context.packageManager.getUserBadgedIcon(badgeBg, user)
+ )
+ }
+ }
field = value
}
@@ -78,15 +90,15 @@
override var isDismissing = false
override var isPendingSharedTransition = false
- private val animationController = ScreenshotAnimationController(view)
+ private val animationController = ScreenshotAnimationController(view, viewModel)
init {
- ScreenshotShelfViewBinder.bind(
+ shelfViewBinder.bind(
view,
viewModel,
+ animationController,
LayoutInflater.from(context),
onDismissalRequested = { event, velocity -> requestDismissal(event, velocity) },
- onDismissalCancelled = { animationController.getSwipeReturnAnimation().start() },
onUserInteraction = { callbacks?.onUserInteraction() }
)
view.updateInsets(windowManager.currentWindowMetrics.windowInsets)
@@ -176,24 +188,53 @@
override fun prepareScrollingTransition(
response: ScrollCaptureResponse,
- screenBitmap: Bitmap,
+ screenBitmap: Bitmap, // unused
newScreenshot: Bitmap,
screenshotTakenInPortrait: Boolean,
onTransitionPrepared: Runnable,
) {
- onTransitionPrepared.run()
+ viewModel.setScrollingScrimBitmap(newScreenshot)
+ viewModel.setScrollableRect(scrollableAreaOnScreen(response))
+ animationController.fadeForLongScreenshotTransition()
+ view.post { onTransitionPrepared.run() }
+ }
+
+ private fun scrollableAreaOnScreen(response: ScrollCaptureResponse): Rect {
+ val r = Rect(response.boundsInWindow)
+ val windowInScreen = response.windowBounds
+ r.offset(windowInScreen?.left ?: 0, windowInScreen?.top ?: 0)
+ r.intersect(
+ Rect(
+ 0,
+ 0,
+ context.resources.displayMetrics.widthPixels,
+ context.resources.displayMetrics.heightPixels
+ )
+ )
+ return r
}
override fun startLongScreenshotTransition(
transitionDestination: Rect,
onTransitionEnd: Runnable,
- longScreenshot: ScrollCaptureController.LongScreenshot
+ longScreenshot: ScrollCaptureController.LongScreenshot,
) {
- onTransitionEnd.run()
- callbacks?.onDismiss()
+ val transitionAnimation =
+ animationController.runLongScreenshotTransition(
+ transitionDestination,
+ longScreenshot,
+ onTransitionEnd
+ )
+ transitionAnimation.doOnEnd { callbacks?.onDismiss() }
+ transitionAnimation.start()
}
- override fun restoreNonScrollingUi() {}
+ override fun restoreNonScrollingUi() {
+ viewModel.setScrollableRect(null)
+ viewModel.setScrollingScrimBitmap(null)
+ animationController.restoreUI()
+ callbacks?.onUserInteraction() // reset the timeout
+ }
override fun stopInputListening() {}
@@ -216,6 +257,10 @@
)
}
+ override fun fadeForSharedTransition() {
+ animationController.fadeForSharedTransition()
+ }
+
private fun addPredictiveBackListener(onDismissRequested: (ScreenshotEvent) -> Unit) {
val onBackInvokedCallback = OnBackInvokedCallback {
debugLog(DEBUG_INPUT) { "Predictive Back callback dispatched" }
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotViewProxy.kt b/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotViewProxy.kt
index a4069d1..df93a5e 100644
--- a/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotViewProxy.kt
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotViewProxy.kt
@@ -63,6 +63,7 @@
longScreenshot: ScrollCaptureController.LongScreenshot
)
fun restoreNonScrollingUi()
+ fun fadeForSharedTransition()
fun stopInputListening()
fun requestFocus()
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/scroll/CropView.java b/packages/SystemUI/src/com/android/systemui/screenshot/scroll/CropView.java
index 5e561cf..ee1944e 100644
--- a/packages/SystemUI/src/com/android/systemui/screenshot/scroll/CropView.java
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/scroll/CropView.java
@@ -45,6 +45,7 @@
import androidx.interpolator.view.animation.FastOutSlowInInterpolator;
import com.android.internal.graphics.ColorUtils;
+import com.android.systemui.Flags;
import com.android.systemui.res.R;
import java.util.List;
@@ -378,8 +379,14 @@
upper = 1;
break;
}
- Log.i(TAG, "getAllowedValues: " + boundary + ", "
- + "result=[lower=" + lower + ", upper=" + upper + "]");
+ if (lower >= upper) {
+ Log.wtf(TAG, "getAllowedValues computed an invalid range "
+ + "[" + lower + ", " + upper + "]");
+ if (Flags.screenshotScrollCropViewCrashFix()) {
+ lower = Math.min(lower, upper);
+ upper = lower;
+ }
+ }
return new Range<>(lower, upper);
}
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/ui/ScreenshotAnimationController.kt b/packages/SystemUI/src/com/android/systemui/screenshot/ui/ScreenshotAnimationController.kt
index 06e88f4..a4906c1 100644
--- a/packages/SystemUI/src/com/android/systemui/screenshot/ui/ScreenshotAnimationController.kt
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/ui/ScreenshotAnimationController.kt
@@ -20,6 +20,10 @@
import android.animation.AnimatorSet
import android.animation.ObjectAnimator
import android.animation.ValueAnimator
+import android.content.res.ColorStateList
+import android.graphics.BlendMode
+import android.graphics.Color
+import android.graphics.Matrix
import android.graphics.PointF
import android.graphics.Rect
import android.util.MathUtils
@@ -29,13 +33,21 @@
import androidx.core.animation.doOnEnd
import androidx.core.animation.doOnStart
import com.android.systemui.res.R
+import com.android.systemui.screenshot.scroll.ScrollCaptureController
+import com.android.systemui.screenshot.ui.viewmodel.ScreenshotViewModel
import kotlin.math.abs
import kotlin.math.max
import kotlin.math.sign
-class ScreenshotAnimationController(private val view: ScreenshotShelfView) {
+class ScreenshotAnimationController(
+ private val view: ScreenshotShelfView,
+ private val viewModel: ScreenshotViewModel
+) {
private var animator: Animator? = null
private val screenshotPreview = view.requireViewById<ImageView>(R.id.screenshot_preview)
+ private val scrollingScrim = view.requireViewById<ImageView>((R.id.screenshot_scrolling_scrim))
+ private val scrollTransitionPreview =
+ view.requireViewById<ImageView>(R.id.screenshot_scrollable_preview)
private val flashView = view.requireViewById<View>(R.id.screenshot_flash)
private val actionContainer = view.requireViewById<View>(R.id.actions_container_background)
private val fastOutSlowIn =
@@ -46,6 +58,14 @@
view.requireViewById(R.id.screenshot_badge),
view.requireViewById(R.id.screenshot_dismiss_button)
)
+ private val fadeUI =
+ listOf<View>(
+ view.requireViewById(R.id.screenshot_preview_border),
+ view.requireViewById(R.id.actions_container_background),
+ view.requireViewById(R.id.screenshot_badge),
+ view.requireViewById(R.id.screenshot_dismiss_button),
+ view.requireViewById(R.id.screenshot_message_container),
+ )
fun getEntranceAnimation(
bounds: Rect,
@@ -96,15 +116,108 @@
}
entranceAnimation.play(fadeInAnimator).after(previewAnimator)
entranceAnimation.doOnStart {
+ viewModel.setIsAnimating(true)
for (child in staticUI) {
child.alpha = 0f
}
}
+ entranceAnimation.doOnEnd { viewModel.setIsAnimating(false) }
this.animator = entranceAnimation
return entranceAnimation
}
+ fun fadeForSharedTransition() {
+ animator?.cancel()
+ val fadeAnimator = ValueAnimator.ofFloat(1f, 0f)
+ fadeAnimator.addUpdateListener {
+ for (view in fadeUI) {
+ view.alpha = it.animatedValue as Float
+ }
+ }
+ animator = fadeAnimator
+ fadeAnimator.start()
+ }
+
+ fun runLongScreenshotTransition(
+ destRect: Rect,
+ longScreenshot: ScrollCaptureController.LongScreenshot,
+ onTransitionEnd: Runnable
+ ): Animator {
+ val animSet = AnimatorSet()
+
+ val scrimAnim = ValueAnimator.ofFloat(0f, 1f)
+ scrimAnim.addUpdateListener { animation: ValueAnimator ->
+ scrollingScrim.setAlpha(1 - animation.animatedFraction)
+ }
+ scrollTransitionPreview.visibility = View.VISIBLE
+ if (true) {
+ scrollTransitionPreview.setImageBitmap(longScreenshot.toBitmap())
+ val startX: Float = scrollTransitionPreview.x
+ val startY: Float = scrollTransitionPreview.y
+ val locInScreen: IntArray = scrollTransitionPreview.getLocationOnScreen()
+ destRect.offset(startX.toInt() - locInScreen[0], startY.toInt() - locInScreen[1])
+ scrollTransitionPreview.pivotX = 0f
+ scrollTransitionPreview.pivotY = 0f
+ scrollTransitionPreview.setAlpha(1f)
+ val currentScale: Float = scrollTransitionPreview.width / longScreenshot.width.toFloat()
+ val matrix = Matrix()
+ matrix.setScale(currentScale, currentScale)
+ matrix.postTranslate(
+ longScreenshot.left * currentScale,
+ longScreenshot.top * currentScale
+ )
+ scrollTransitionPreview.setImageMatrix(matrix)
+ val destinationScale: Float = destRect.width() / scrollTransitionPreview.width.toFloat()
+ val previewAnim = ValueAnimator.ofFloat(0f, 1f)
+ previewAnim.addUpdateListener { animation: ValueAnimator ->
+ val t = animation.animatedFraction
+ val currScale = MathUtils.lerp(1f, destinationScale, t)
+ scrollTransitionPreview.scaleX = currScale
+ scrollTransitionPreview.scaleY = currScale
+ scrollTransitionPreview.x = MathUtils.lerp(startX, destRect.left.toFloat(), t)
+ scrollTransitionPreview.y = MathUtils.lerp(startY, destRect.top.toFloat(), t)
+ }
+ val previewFadeAnim = ValueAnimator.ofFloat(1f, 0f)
+ previewFadeAnim.addUpdateListener { animation: ValueAnimator ->
+ scrollTransitionPreview.setAlpha(1 - animation.animatedFraction)
+ }
+ previewAnim.doOnEnd { onTransitionEnd.run() }
+ animSet.play(previewAnim).with(scrimAnim).before(previewFadeAnim)
+ } else {
+ // if we switched orientations between the original screenshot and the long screenshot
+ // capture, just fade out the scrim instead of running the preview animation
+ scrimAnim.doOnEnd { onTransitionEnd.run() }
+ animSet.play(scrimAnim)
+ }
+ animator = animSet
+ return animSet
+ }
+
+ fun fadeForLongScreenshotTransition() {
+ scrollingScrim.imageTintBlendMode = BlendMode.SRC_ATOP
+ val anim = ValueAnimator.ofFloat(0f, .3f)
+ anim.addUpdateListener {
+ scrollingScrim.setImageTintList(
+ ColorStateList.valueOf(Color.argb(it.animatedValue as Float, 0f, 0f, 0f))
+ )
+ }
+ for (view in fadeUI) {
+ view.alpha = 0f
+ }
+ screenshotPreview.alpha = 0f
+ anim.setDuration(200)
+ anim.start()
+ }
+
+ fun restoreUI() {
+ animator?.cancel()
+ for (view in fadeUI) {
+ view.alpha = 1f
+ }
+ screenshotPreview.alpha = 1f
+ }
+
fun getSwipeReturnAnimation(): Animator {
animator?.cancel()
val animator = ValueAnimator.ofFloat(view.translationX, 0f)
@@ -114,6 +227,7 @@
}
fun getSwipeDismissAnimation(requestedVelocity: Float?): Animator {
+ animator?.cancel()
val velocity = getAdjustedVelocity(requestedVelocity)
val screenWidth = view.resources.displayMetrics.widthPixels
// translation at which point the visible UI is fully off the screen (in the direction
@@ -131,6 +245,8 @@
view.alpha = 1f - it.animatedFraction
}
animator.duration = ((abs(distance / velocity))).toLong()
+ animator.doOnStart { viewModel.setIsAnimating(true) }
+ animator.doOnEnd { viewModel.setIsAnimating(false) }
this.animator = animator
return animator
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/ui/ScreenshotShelfView.kt b/packages/SystemUI/src/com/android/systemui/screenshot/ui/ScreenshotShelfView.kt
index bd93226..969cf48 100644
--- a/packages/SystemUI/src/com/android/systemui/screenshot/ui/ScreenshotShelfView.kt
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/ui/ScreenshotShelfView.kt
@@ -22,6 +22,8 @@
import android.graphics.Rect
import android.graphics.Region
import android.util.AttributeSet
+import android.view.GestureDetector
+import android.view.GestureDetector.SimpleOnGestureListener
import android.view.MotionEvent
import android.view.View
import android.view.ViewGroup
@@ -43,13 +45,43 @@
private val displayMetrics = context.resources.displayMetrics
private val tmpRect = Rect()
private lateinit var actionsContainerBackground: View
+ private lateinit var actionsContainer: View
private lateinit var dismissButton: View
+ // Prepare an internal `GestureDetector` to determine when we can initiate a touch-interception
+ // session (with the client's provided `onTouchInterceptListener`). We delegate out to their
+ // listener only for gestures that can't be handled by scrolling our `actionsContainer`.
+ private val gestureDetector =
+ GestureDetector(
+ context,
+ object : SimpleOnGestureListener() {
+ override fun onScroll(
+ ev1: MotionEvent?,
+ ev2: MotionEvent,
+ distanceX: Float,
+ distanceY: Float
+ ): Boolean {
+ actionsContainer.getBoundsOnScreen(tmpRect)
+ val touchedInActionsContainer =
+ tmpRect.contains(ev2.rawX.toInt(), ev2.rawY.toInt())
+ val canHandleInternallyByScrolling =
+ touchedInActionsContainer
+ && actionsContainer.canScrollHorizontally(distanceX.toInt())
+ return !canHandleInternallyByScrolling
+ }
+ }
+ )
+
init {
- setOnTouchListener({ _: View, _: MotionEvent ->
+
+ // Delegate to the client-provided `onTouchInterceptListener` if we've already initiated
+ // touch-interception.
+ setOnTouchListener({ _: View, ev: MotionEvent ->
userInteractionCallback?.invoke()
- true
+ onTouchInterceptListener?.invoke(ev) ?: false
})
+
+ gestureDetector.setIsLongpressEnabled(false)
}
override fun onFinishInflate() {
@@ -60,7 +92,15 @@
blurredScreenshotPreview = requireViewById(R.id.screenshot_preview_blur)
screenshotStatic = requireViewById(R.id.screenshot_static)
actionsContainerBackground = requireViewById(R.id.actions_container_background)
+ actionsContainer = requireViewById(R.id.actions_container)
dismissButton = requireViewById(R.id.screenshot_dismiss_button)
+
+ // Configure to extend the timeout during ongoing gestures (i.e. scrolls) that are already
+ // being handled by our child views.
+ actionsContainer.setOnTouchListener({ _: View, ev: MotionEvent ->
+ userInteractionCallback?.invoke()
+ false
+ })
}
fun getTouchRegion(gestureInsets: Insets): Region {
@@ -171,9 +211,16 @@
override fun onInterceptTouchEvent(ev: MotionEvent): Boolean {
userInteractionCallback?.invoke()
- if (onTouchInterceptListener?.invoke(ev) == true) {
- return true
+ // Let the client-provided listener see all `DOWN` events so that they'll be able to
+ // interpret the remainder of the gesture, even if interception starts partway-through.
+ // TODO: is this really necessary? And if we don't go on to start interception, should we
+ // follow up with `ACTION_CANCEL`?
+ if (ev.getActionMasked() == MotionEvent.ACTION_DOWN) {
+ onTouchInterceptListener?.invoke(ev)
}
- return super.onInterceptTouchEvent(ev)
+
+ // Only allow the client-provided touch interceptor to take over the gesture if our
+ // top-level `GestureDetector` decides not to scroll the action container.
+ return gestureDetector.onTouchEvent(ev)
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/ui/binder/ActionButtonViewBinder.kt b/packages/SystemUI/src/com/android/systemui/screenshot/ui/binder/ActionButtonViewBinder.kt
index 2243ade..36aa39f 100644
--- a/packages/SystemUI/src/com/android/systemui/screenshot/ui/binder/ActionButtonViewBinder.kt
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/ui/binder/ActionButtonViewBinder.kt
@@ -23,8 +23,9 @@
import com.android.systemui.res.R
import com.android.systemui.screenshot.ui.TransitioningIconDrawable
import com.android.systemui.screenshot.ui.viewmodel.ActionButtonViewModel
+import javax.inject.Inject
-object ActionButtonViewBinder {
+class ActionButtonViewBinder @Inject constructor() {
/** Binds the given view to the given view-model */
fun bind(view: View, viewModel: ActionButtonViewModel) {
val iconView = view.requireViewById<ImageView>(R.id.overlay_action_chip_icon)
@@ -36,6 +37,10 @@
// Note we never re-bind a view to a different ActionButtonViewModel, different view
// models would remove/create separate views.
drawable?.setIcon(viewModel.appearance.icon)
+ iconView.setImageDrawable(viewModel.appearance.icon)
+ if (!viewModel.appearance.tint) {
+ iconView.setImageTintList(null)
+ }
textView.text = viewModel.appearance.label
viewModel.appearance.customBackground?.also {
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/ui/binder/ScreenshotShelfViewBinder.kt b/packages/SystemUI/src/com/android/systemui/screenshot/ui/binder/ScreenshotShelfViewBinder.kt
index 89f904a..442b387 100644
--- a/packages/SystemUI/src/com/android/systemui/screenshot/ui/binder/ScreenshotShelfViewBinder.kt
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/ui/binder/ScreenshotShelfViewBinder.kt
@@ -16,7 +16,11 @@
package com.android.systemui.screenshot.ui.binder
+import android.content.res.Configuration
import android.graphics.Bitmap
+import android.graphics.Matrix
+import android.graphics.Rect
+import android.util.LayoutDirection
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
@@ -29,22 +33,26 @@
import com.android.systemui.lifecycle.repeatWhenAttached
import com.android.systemui.res.R
import com.android.systemui.screenshot.ScreenshotEvent
+import com.android.systemui.screenshot.ui.ScreenshotAnimationController
import com.android.systemui.screenshot.ui.ScreenshotShelfView
import com.android.systemui.screenshot.ui.SwipeGestureListener
import com.android.systemui.screenshot.ui.viewmodel.ActionButtonViewModel
import com.android.systemui.screenshot.ui.viewmodel.AnimationState
import com.android.systemui.screenshot.ui.viewmodel.ScreenshotViewModel
import com.android.systemui.util.children
+import javax.inject.Inject
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
-object ScreenshotShelfViewBinder {
+class ScreenshotShelfViewBinder
+@Inject
+constructor(private val buttonViewBinder: ActionButtonViewBinder) {
fun bind(
view: ScreenshotShelfView,
viewModel: ScreenshotViewModel,
+ animationController: ScreenshotAnimationController,
layoutInflater: LayoutInflater,
onDismissalRequested: (event: ScreenshotEvent, velocity: Float?) -> Unit,
- onDismissalCancelled: () -> Unit,
onUserInteraction: () -> Unit
) {
val swipeGestureListener =
@@ -53,7 +61,7 @@
onDismiss = {
onDismissalRequested(ScreenshotEvent.SCREENSHOT_SWIPE_DISMISSED, it)
},
- onCancel = onDismissalCancelled
+ onCancel = { animationController.getSwipeReturnAnimation().start() }
)
view.onTouchInterceptListener = { swipeGestureListener.onMotionEvent(it) }
view.userInteractionCallback = onUserInteraction
@@ -63,11 +71,15 @@
val previewBorder = view.requireViewById<View>(R.id.screenshot_preview_border)
previewView.clipToOutline = true
previewViewBlur.clipToOutline = true
+ val actionsContainer: LinearLayout = view.requireViewById(R.id.screenshot_actions)
val dismissButton = view.requireViewById<View>(R.id.screenshot_dismiss_button)
dismissButton.visibility = if (viewModel.showDismissButton) View.VISIBLE else View.GONE
dismissButton.setOnClickListener {
onDismissalRequested(ScreenshotEvent.SCREENSHOT_EXPLICIT_DISMISSAL, null)
}
+ val scrollingScrim: ImageView = view.requireViewById(R.id.screenshot_scrolling_scrim)
+ val scrollablePreview: ImageView = view.requireViewById(R.id.screenshot_scrollable_preview)
+ val badgeView = view.requireViewById<ImageView>(R.id.screenshot_badge)
// use immediate dispatcher to ensure screenshot bitmap is set before animation
view.repeatWhenAttached(Dispatchers.Main.immediate) {
@@ -87,11 +99,48 @@
}
}
launch {
+ viewModel.scrollingScrim.collect { bitmap ->
+ if (bitmap != null) {
+ scrollingScrim.setImageBitmap(bitmap)
+ scrollingScrim.visibility = View.VISIBLE
+ } else {
+ scrollingScrim.visibility = View.GONE
+ }
+ }
+ }
+ launch {
+ viewModel.scrollableRect.collect { rect ->
+ if (rect != null) {
+ setScrollablePreview(
+ scrollablePreview,
+ viewModel.preview.value,
+ rect
+ )
+ } else {
+ scrollablePreview.visibility = View.GONE
+ }
+ }
+ }
+ launch {
+ viewModel.badge.collect { badge ->
+ badgeView.setImageDrawable(badge)
+ badgeView.visibility = if (badge != null) View.VISIBLE else View.GONE
+ }
+ }
+ launch {
viewModel.previewAction.collect { onClick ->
previewView.setOnClickListener { onClick?.invoke() }
}
}
launch {
+ viewModel.isAnimating.collect { isAnimating ->
+ previewView.isClickable = !isAnimating
+ for (child in actionsContainer.children) {
+ child.isClickable = !isAnimating
+ }
+ }
+ }
+ launch {
viewModel.actions.collect { actions ->
updateActions(
actions,
@@ -151,14 +200,14 @@
val currentView: View? = actionsContainer.getChildAt(index)
if (action.id == currentView?.tag) {
// Same ID, update the display
- ActionButtonViewBinder.bind(currentView, action)
+ buttonViewBinder.bind(currentView, action)
} else {
// Different ID. Removals have already happened so this must
// mean that the new action must be inserted here.
val actionButton =
layoutInflater.inflate(R.layout.shelf_action_chip, actionsContainer, false)
actionsContainer.addView(actionButton, index)
- ActionButtonViewBinder.bind(actionButton, action)
+ buttonViewBinder.bind(actionButton, action)
}
}
}
@@ -181,4 +230,35 @@
screenshotPreview.layoutParams = params
screenshotPreview.requestLayout()
}
+
+ private fun setScrollablePreview(
+ scrollablePreview: ImageView,
+ bitmap: Bitmap?,
+ scrollableRect: Rect
+ ) {
+ if (bitmap == null) {
+ return
+ }
+ val fixedSize = scrollablePreview.resources.getDimensionPixelSize(R.dimen.overlay_x_scale)
+ val inPortrait =
+ scrollablePreview.resources.configuration.orientation ==
+ Configuration.ORIENTATION_PORTRAIT
+ val scale: Float = fixedSize / ((if (inPortrait) bitmap.width else bitmap.height).toFloat())
+ val params = scrollablePreview.layoutParams
+
+ params.width = (scale * scrollableRect.width()).toInt()
+ params.height = (scale * scrollableRect.height()).toInt()
+ val matrix = Matrix()
+ matrix.setScale(scale, scale)
+ matrix.postTranslate(-scrollableRect.left * scale, -scrollableRect.top * scale)
+
+ scrollablePreview.translationX =
+ (scale *
+ if (scrollablePreview.layoutDirection == LayoutDirection.LTR) scrollableRect.left
+ else scrollableRect.right - (scrollablePreview.parent as View).width)
+ scrollablePreview.translationY = scale * scrollableRect.top
+ scrollablePreview.setImageMatrix(matrix)
+ scrollablePreview.setImageBitmap(bitmap)
+ scrollablePreview.setVisibility(View.VISIBLE)
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/ui/viewmodel/ActionButtonAppearance.kt b/packages/SystemUI/src/com/android/systemui/screenshot/ui/viewmodel/ActionButtonAppearance.kt
index 2982ea0..42ad326 100644
--- a/packages/SystemUI/src/com/android/systemui/screenshot/ui/viewmodel/ActionButtonAppearance.kt
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/ui/viewmodel/ActionButtonAppearance.kt
@@ -25,5 +25,6 @@
val icon: Drawable?,
val label: CharSequence?,
val description: CharSequence,
+ val tint: Boolean = true,
val customBackground: Drawable? = null,
)
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/ui/viewmodel/ScreenshotViewModel.kt b/packages/SystemUI/src/com/android/systemui/screenshot/ui/viewmodel/ScreenshotViewModel.kt
index 5f36f73..3f99bc4 100644
--- a/packages/SystemUI/src/com/android/systemui/screenshot/ui/viewmodel/ScreenshotViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/ui/viewmodel/ScreenshotViewModel.kt
@@ -17,6 +17,8 @@
package com.android.systemui.screenshot.ui.viewmodel
import android.graphics.Bitmap
+import android.graphics.Rect
+import android.graphics.drawable.Drawable
import android.util.Log
import android.view.accessibility.AccessibilityManager
import kotlinx.coroutines.flow.MutableStateFlow
@@ -25,6 +27,10 @@
class ScreenshotViewModel(private val accessibilityManager: AccessibilityManager) {
private val _preview = MutableStateFlow<Bitmap?>(null)
val preview: StateFlow<Bitmap?> = _preview
+ private val _scrollingScrim = MutableStateFlow<Bitmap?>(null)
+ val scrollingScrim: StateFlow<Bitmap?> = _scrollingScrim
+ private val _badge = MutableStateFlow<Drawable?>(null)
+ val badge: StateFlow<Drawable?> = _badge
private val _previewAction = MutableStateFlow<(() -> Unit)?>(null)
val previewAction: StateFlow<(() -> Unit)?> = _previewAction
private val _actions = MutableStateFlow(emptyList<ActionButtonViewModel>())
@@ -32,6 +38,10 @@
private val _animationState = MutableStateFlow(AnimationState.NOT_STARTED)
val animationState: StateFlow<AnimationState> = _animationState
+ private val _isAnimating = MutableStateFlow(false)
+ val isAnimating: StateFlow<Boolean> = _isAnimating
+ private val _scrollableRect = MutableStateFlow<Rect?>(null)
+ val scrollableRect: StateFlow<Rect?> = _scrollableRect
val showDismissButton: Boolean
get() = accessibilityManager.isEnabled
@@ -39,6 +49,14 @@
_preview.value = bitmap
}
+ fun setScrollingScrimBitmap(bitmap: Bitmap?) {
+ _scrollingScrim.value = bitmap
+ }
+
+ fun setScreenshotBadge(badge: Drawable?) {
+ _badge.value = badge
+ }
+
fun setPreviewAction(onClick: () -> Unit) {
_previewAction.value = onClick
}
@@ -107,11 +125,23 @@
_animationState.value = state
}
+ fun setIsAnimating(isAnimating: Boolean) {
+ _isAnimating.value = isAnimating
+ }
+
+ fun setScrollableRect(rect: Rect?) {
+ _scrollableRect.value = rect
+ }
+
fun reset() {
_preview.value = null
+ _scrollingScrim.value = null
+ _badge.value = null
_previewAction.value = null
_actions.value = listOf()
_animationState.value = AnimationState.NOT_STARTED
+ _isAnimating.value = false
+ _scrollableRect.value = null
}
companion object {
diff --git a/packages/SystemUI/src/com/android/systemui/settings/brightness/ToggleSeekBar.java b/packages/SystemUI/src/com/android/systemui/settings/brightness/ToggleSeekBar.java
index 288ff09..84156eeb 100644
--- a/packages/SystemUI/src/com/android/systemui/settings/brightness/ToggleSeekBar.java
+++ b/packages/SystemUI/src/com/android/systemui/settings/brightness/ToggleSeekBar.java
@@ -51,6 +51,16 @@
return super.onTouchEvent(event);
}
+ @Override
+ public boolean onHoverEvent(MotionEvent event) {
+ if (event.getAction() == MotionEvent.ACTION_HOVER_ENTER) {
+ setHovered(true);
+ } else if (event.getAction() == MotionEvent.ACTION_HOVER_EXIT) {
+ setHovered(false);
+ }
+ return true;
+ }
+
public void setAccessibilityLabel(String label) {
mAccessibilityLabel = label;
}
diff --git a/packages/SystemUI/src/com/android/systemui/shade/GlanceableHubContainerController.kt b/packages/SystemUI/src/com/android/systemui/shade/GlanceableHubContainerController.kt
index ee7b4be..22aa492 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/GlanceableHubContainerController.kt
+++ b/packages/SystemUI/src/com/android/systemui/shade/GlanceableHubContainerController.kt
@@ -123,15 +123,9 @@
private var anyBouncerShowing = false
/**
- * True if the shade is fully expanded and the user is not interacting with it anymore, meaning
- * the hub should not receive any touch input.
+ * True if the shade is fully expanded, meaning the hub should not receive any touch input.
*
- * We need to not pause the touch handling lifecycle as soon as the shade opens because if the
- * user swipes down, then back up without lifting their finger, the lifecycle will be paused
- * then resumed, and resuming force-stops all active touch sessions. This means the shade will
- * not receive the end of the gesture and will be stuck open.
- *
- * Based on [ShadeInteractor.isAnyFullyExpanded] and [ShadeInteractor.isUserInteracting].
+ * Tracks [ShadeInteractor.isAnyFullyExpanded].
*/
private var shadeShowing = false
diff --git a/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java b/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java
index 7051d5f..3826b50 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java
+++ b/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java
@@ -141,6 +141,7 @@
import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor;
import com.android.systemui.keyguard.domain.interactor.NaturalScrollingSettingObserver;
import com.android.systemui.keyguard.shared.ComposeLockscreen;
+import com.android.systemui.keyguard.shared.model.Edge;
import com.android.systemui.keyguard.shared.model.TransitionState;
import com.android.systemui.keyguard.shared.model.TransitionStep;
import com.android.systemui.keyguard.ui.binder.KeyguardLongPressViewBinder;
@@ -170,6 +171,7 @@
import com.android.systemui.power.shared.model.WakefulnessModel;
import com.android.systemui.res.R;
import com.android.systemui.scene.shared.flag.SceneContainerFlag;
+import com.android.systemui.scene.shared.model.Scenes;
import com.android.systemui.shade.data.repository.FlingInfo;
import com.android.systemui.shade.data.repository.ShadeRepository;
import com.android.systemui.shade.domain.interactor.ShadeAnimationInteractor;
@@ -251,6 +253,7 @@
import java.util.ArrayList;
import java.util.Collections;
import java.util.Optional;
+import java.util.Set;
import java.util.function.Consumer;
import javax.inject.Inject;
@@ -447,6 +450,9 @@
private final ShadeHeadsUpTrackerImpl mShadeHeadsUpTracker = new ShadeHeadsUpTrackerImpl();
private final ShadeFoldAnimatorImpl mShadeFoldAnimator = new ShadeFoldAnimatorImpl();
+ @VisibleForTesting
+ Set<Animator> mTestSetOfAnimatorsUsed;
+
private boolean mShowIconsWhenExpanded;
private int mIndicationBottomPadding;
private int mAmbientIndicationBottomPadding;
@@ -1130,8 +1136,12 @@
controller.setup(mNotificationContainerParent));
// Dreaming->Lockscreen
- collectFlow(mView, mKeyguardTransitionInteractor.transition(DREAMING, LOCKSCREEN),
- mDreamingToLockscreenTransition, mMainDispatcher);
+ collectFlow(
+ mView,
+ mKeyguardTransitionInteractor.transition(
+ Edge.Companion.create(DREAMING, LOCKSCREEN)),
+ mDreamingToLockscreenTransition,
+ mMainDispatcher);
collectFlow(mView, mDreamingToLockscreenTransitionViewModel.getLockscreenAlpha(),
setDreamLockscreenTransitionAlpha(mNotificationStackScrollLayoutController),
mMainDispatcher);
@@ -1141,7 +1151,8 @@
// Gone -> Dreaming hosted in lockscreen
collectFlow(mView, mKeyguardTransitionInteractor
- .transition(GONE, DREAMING_LOCKSCREEN_HOSTED),
+ .transition(Edge.Companion.create(Scenes.Gone, DREAMING_LOCKSCREEN_HOSTED),
+ Edge.Companion.create(GONE, DREAMING_LOCKSCREEN_HOSTED)),
mGoneToDreamingLockscreenHostedTransition, mMainDispatcher);
collectFlow(mView, mGoneToDreamingLockscreenHostedTransitionViewModel.getLockscreenAlpha(),
setTransitionAlpha(mNotificationStackScrollLayoutController),
@@ -1149,16 +1160,17 @@
// Lockscreen -> Dreaming hosted in lockscreen
collectFlow(mView, mKeyguardTransitionInteractor
- .transition(LOCKSCREEN, DREAMING_LOCKSCREEN_HOSTED),
+ .transition(Edge.Companion.create(LOCKSCREEN, DREAMING_LOCKSCREEN_HOSTED)),
mLockscreenToDreamingLockscreenHostedTransition, mMainDispatcher);
// Dreaming hosted in lockscreen -> Lockscreen
collectFlow(mView, mKeyguardTransitionInteractor
- .transition(DREAMING_LOCKSCREEN_HOSTED, LOCKSCREEN),
+ .transition(Edge.Companion.create(DREAMING_LOCKSCREEN_HOSTED, LOCKSCREEN)),
mDreamingLockscreenHostedToLockscreenTransition, mMainDispatcher);
// Occluded->Lockscreen
- collectFlow(mView, mKeyguardTransitionInteractor.transition(OCCLUDED, LOCKSCREEN),
+ collectFlow(mView, mKeyguardTransitionInteractor.transition(
+ Edge.Companion.create(OCCLUDED, LOCKSCREEN)),
mOccludedToLockscreenTransition, mMainDispatcher);
if (!MigrateClocksToBlueprint.isEnabled()) {
collectFlow(mView, mOccludedToLockscreenTransitionViewModel.getLockscreenAlpha(),
@@ -1169,7 +1181,8 @@
}
// Lockscreen->Dreaming
- collectFlow(mView, mKeyguardTransitionInteractor.transition(LOCKSCREEN, DREAMING),
+ collectFlow(mView, mKeyguardTransitionInteractor.transition(
+ Edge.Companion.create(LOCKSCREEN, DREAMING)),
mLockscreenToDreamingTransition, mMainDispatcher);
if (!MigrateClocksToBlueprint.isEnabled()) {
collectFlow(mView, mLockscreenToDreamingTransitionViewModel.getLockscreenAlpha(),
@@ -1181,7 +1194,9 @@
setTransitionY(mNotificationStackScrollLayoutController), mMainDispatcher);
// Gone->Dreaming
- collectFlow(mView, mKeyguardTransitionInteractor.transition(GONE, DREAMING),
+ collectFlow(mView, mKeyguardTransitionInteractor.transition(
+ Edge.Companion.create(Scenes.Gone, DREAMING),
+ Edge.Companion.create(GONE, DREAMING)),
mGoneToDreamingTransition, mMainDispatcher);
if (!MigrateClocksToBlueprint.isEnabled()) {
collectFlow(mView, mGoneToDreamingTransitionViewModel.getLockscreenAlpha(),
@@ -1192,7 +1207,8 @@
setTransitionY(mNotificationStackScrollLayoutController), mMainDispatcher);
// Lockscreen->Occluded
- collectFlow(mView, mKeyguardTransitionInteractor.transition(LOCKSCREEN, OCCLUDED),
+ collectFlow(mView, mKeyguardTransitionInteractor.transition(
+ Edge.Companion.create(LOCKSCREEN, OCCLUDED)),
mLockscreenToOccludedTransition, mMainDispatcher);
if (!MigrateClocksToBlueprint.isEnabled()) {
collectFlow(mView, mLockscreenToOccludedTransitionViewModel.getLockscreenAlpha(),
@@ -4137,6 +4153,8 @@
}
private void setAnimator(ValueAnimator animator) {
+ // TODO(b/341163515): Should we clean up the old animator?
+ registerAnimatorForTest(animator);
mHeightAnimator = animator;
if (animator == null && mPanelUpdateWhenAnimatorEnds) {
mPanelUpdateWhenAnimatorEnds = false;
@@ -4181,6 +4199,7 @@
private ValueAnimator createHeightAnimator(float targetHeight, float overshootAmount) {
float startExpansion = mOverExpansion;
ValueAnimator animator = ValueAnimator.ofFloat(mExpandedHeight, targetHeight);
+ registerAnimatorForTest(animator);
animator.addUpdateListener(
animation -> {
if (overshootAmount > 0.0f
@@ -4198,6 +4217,12 @@
return animator;
}
+ private void registerAnimatorForTest(Animator animator) {
+ if (mTestSetOfAnimatorsUsed != null && animator != null) {
+ mTestSetOfAnimatorsUsed.add(animator);
+ }
+ }
+
/** Update the visibility of {@link NotificationPanelView} if necessary. */
private void updateVisibility() {
mView.setVisibility(shouldPanelBeVisible() ? VISIBLE : INVISIBLE);
diff --git a/packages/SystemUI/src/com/android/systemui/shade/NotificationShadeWindowViewController.java b/packages/SystemUI/src/com/android/systemui/shade/NotificationShadeWindowViewController.java
index b50a3cd..6efa633 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/NotificationShadeWindowViewController.java
+++ b/packages/SystemUI/src/com/android/systemui/shade/NotificationShadeWindowViewController.java
@@ -49,6 +49,7 @@
import com.android.systemui.keyguard.KeyguardUnlockAnimationController;
import com.android.systemui.keyguard.MigrateClocksToBlueprint;
import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor;
+import com.android.systemui.keyguard.shared.model.Edge;
import com.android.systemui.keyguard.shared.model.TransitionState;
import com.android.systemui.keyguard.shared.model.TransitionStep;
import com.android.systemui.res.R;
@@ -137,11 +138,6 @@
private final PanelExpansionInteractor mPanelExpansionInteractor;
private final ShadeExpansionStateManager mShadeExpansionStateManager;
- /**
- * If {@code true}, an external touch sent in {@link #handleExternalTouch(MotionEvent)} has been
- * intercepted and all future touch events for the gesture should be processed by this view.
- */
- private boolean mExternalTouchIntercepted = false;
private boolean mIsTrackingBarGesture = false;
private boolean mIsOcclusionTransitionRunning = false;
private DisableSubpixelTextTransitionListener mDisableSubpixelTextTransitionListener;
@@ -225,7 +221,8 @@
mDisableSubpixelTextTransitionListener = new DisableSubpixelTextTransitionListener(mView);
bouncerViewBinder.bind(mView.findViewById(R.id.keyguard_bouncer_container));
- collectFlow(mView, keyguardTransitionInteractor.transition(LOCKSCREEN, DREAMING),
+ collectFlow(mView, keyguardTransitionInteractor.transition(
+ Edge.Companion.create(LOCKSCREEN, DREAMING)),
mLockscreenToDreamingTransition);
collectFlow(
mView,
@@ -258,28 +255,11 @@
}
/**
- * Handle a touch event while dreaming or on the hub by forwarding the event to the content
- * view.
- * <p>
- * Since important logic for handling touches lives in the dispatch/intercept phases, we
- * simulate going through all of these stages before sending onTouchEvent if intercepted.
- *
+ * Handle a touch event while dreaming by forwarding the event to the content view.
* @param event The event to forward.
*/
- public void handleExternalTouch(MotionEvent event) {
- if (event.getActionMasked() == MotionEvent.ACTION_DOWN) {
- mExternalTouchIntercepted = false;
- }
-
- if (!mView.dispatchTouchEvent(event)) {
- return;
- }
- if (!mExternalTouchIntercepted) {
- mExternalTouchIntercepted = mView.onInterceptTouchEvent(event);
- }
- if (mExternalTouchIntercepted) {
- mView.onTouchEvent(event);
- }
+ public void handleDreamTouch(MotionEvent event) {
+ mView.dispatchTouchEvent(event);
}
/** Inflates the {@link R.layout#status_bar_expanded} layout and sets it up. */
diff --git a/packages/SystemUI/src/com/android/systemui/shade/ShadeControllerSceneImpl.kt b/packages/SystemUI/src/com/android/systemui/shade/ShadeControllerSceneImpl.kt
index d2c93da..884ccef 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/ShadeControllerSceneImpl.kt
+++ b/packages/SystemUI/src/com/android/systemui/shade/ShadeControllerSceneImpl.kt
@@ -140,7 +140,7 @@
private fun animateCollapseShadeInternal() {
sceneInteractor.changeScene(
- getCollapseDestinationScene(),
+ getCollapseDestinationScene(), // TODO(b/336581871): add sceneState?
"ShadeController.animateCollapseShade",
SlightlyFasterShadeCollapse,
)
diff --git a/packages/SystemUI/src/com/android/systemui/shade/domain/interactor/ShadeBackActionInteractorImpl.kt b/packages/SystemUI/src/com/android/systemui/shade/domain/interactor/ShadeBackActionInteractorImpl.kt
index c9949cd..55bd8c6 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/domain/interactor/ShadeBackActionInteractorImpl.kt
+++ b/packages/SystemUI/src/com/android/systemui/shade/domain/interactor/ShadeBackActionInteractorImpl.kt
@@ -44,6 +44,7 @@
} else {
Scenes.Shade
}
+ // TODO(b/336581871): add sceneState?
sceneInteractor.changeScene(key, "animateCollapseQs")
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardIndicationController.java b/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardIndicationController.java
index 2446473..3d4b421 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardIndicationController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardIndicationController.java
@@ -633,6 +633,7 @@
INDICATION_TYPE_BIOMETRIC_MESSAGE,
new KeyguardIndication.Builder()
.setMessage(mBiometricMessage)
+ .setForceAccessibilityLiveRegionAssertive()
.setMinVisibilityMillis(IMPORTANT_MSG_MIN_DURATION)
.setTextColor(mInitialTextColorState)
.build(),
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationMediaManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationMediaManager.java
index d7d3732..5bf2f41 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationMediaManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationMediaManager.java
@@ -15,6 +15,8 @@
*/
package com.android.systemui.statusbar;
+import static com.android.systemui.Flags.mediaControlsUserInitiatedDismiss;
+
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.app.Notification;
@@ -175,14 +177,18 @@
}
@Override
- public void onMediaDataRemoved(@NonNull String key) {
+ public void onMediaDataRemoved(@NonNull String key, boolean userInitiated) {
+ if (mediaControlsUserInitiatedDismiss() && !userInitiated) {
+ // Dismissing the notification will send the app's deleteIntent, so ignore if
+ // this was an automatic removal
+ Log.d(TAG, "Not dismissing " + key + " because it was removed by the system");
+ return;
+ }
mNotifPipeline.getAllNotifs()
.stream()
.filter(entry -> Objects.equals(entry.getKey(), key))
.findAny()
.ifPresent(entry -> {
- // TODO(b/160713608): "removing" this notification won't happen and
- // won't send the 'deleteIntent' if the notification is ongoing.
mNotifCollection.dismissNotification(entry,
getDismissedByUserStats(entry));
});
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ongoingcall/OngoingCallBackgroundContainer.kt b/packages/SystemUI/src/com/android/systemui/statusbar/chips/ui/view/ChipBackgroundContainer.kt
similarity index 81%
rename from packages/SystemUI/src/com/android/systemui/statusbar/phone/ongoingcall/OngoingCallBackgroundContainer.kt
rename to packages/SystemUI/src/com/android/systemui/statusbar/chips/ui/view/ChipBackgroundContainer.kt
index ce88a5f..cae86a6 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ongoingcall/OngoingCallBackgroundContainer.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/chips/ui/view/ChipBackgroundContainer.kt
@@ -14,18 +14,18 @@
* limitations under the License.
*/
-package com.android.systemui.statusbar.phone.ongoingcall
+package com.android.systemui.statusbar.chips.ui.view
import android.content.Context
import android.util.AttributeSet
import com.android.systemui.animation.view.LaunchableLinearLayout
/**
- * A container view for the ongoing call chip background. Needed so that we can limit the height of
- * the background when the font size is very large (200%), in which case the background would go
+ * A container view for the ongoing activity chip background. Needed so that we can limit the height
+ * of the background when the font size is very large (200%), in which case the background would go
* past the bounds of the status bar.
*/
-class OngoingCallBackgroundContainer(context: Context, attrs: AttributeSet) :
+class ChipBackgroundContainer(context: Context, attrs: AttributeSet) :
LaunchableLinearLayout(context, attrs) {
/** Sets where this view should fetch its max height from. */
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ongoingcall/OngoingCallChronometer.kt b/packages/SystemUI/src/com/android/systemui/statusbar/chips/ui/view/ChipChronometer.kt
similarity index 72%
rename from packages/SystemUI/src/com/android/systemui/statusbar/phone/ongoingcall/OngoingCallChronometer.kt
rename to packages/SystemUI/src/com/android/systemui/statusbar/chips/ui/view/ChipChronometer.kt
index bb7ba4c..ff3061e 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ongoingcall/OngoingCallChronometer.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/chips/ui/view/ChipChronometer.kt
@@ -14,36 +14,34 @@
* limitations under the License.
*/
-package com.android.systemui.statusbar.phone.ongoingcall
+package com.android.systemui.statusbar.chips.ui.view
import android.content.Context
import android.util.AttributeSet
-
import android.widget.Chronometer
import androidx.annotation.UiThread
/**
- * A [Chronometer] specifically for the ongoing call chip in the status bar.
+ * A [Chronometer] specifically for chips in the status bar that show ongoing duration of an
+ * activity.
*
* This class handles:
- * 1) Setting the text width. If we used a basic WRAP_CONTENT for width, the chip width would
- * change slightly each second because the width of each number is slightly different.
+ * 1) Setting the text width. If we used a basic WRAP_CONTENT for width, the chip width would change
+ * slightly each second because the width of each number is slightly different.
*
- * Instead, we save the largest number width seen so far and ensure that the chip is at least
- * that wide. This means the chip may get larger over time (e.g. in the transition from 59:59
- * to 1:00:00), but never smaller.
- *
- * 2) Hiding the text if the time gets too long for the space available. Once the text has been
- * hidden, it remains hidden for the duration of the call.
+ * Instead, we save the largest number width seen so far and ensure that the chip is at least
+ * that wide. This means the chip may get larger over time (e.g. in the transition from 59:59 to
+ * 1:00:00), but never smaller.
+ * 2) Hiding the text if the time gets too long for the space available. Once the text has been
+ * hidden, it remains hidden for the duration of the activity.
*
* Note that if the text was too big in portrait mode, resulting in the text being hidden, then the
* text will also be hidden in landscape (even if there is enough space for it in landscape).
*/
-class OngoingCallChronometer @JvmOverloads constructor(
- context: Context,
- attrs: AttributeSet? = null,
- defStyle: Int = 0
-) : Chronometer(context, attrs, defStyle) {
+class ChipChronometer
+@JvmOverloads
+constructor(context: Context, attrs: AttributeSet? = null, defStyle: Int = 0) :
+ Chronometer(context, attrs, defStyle) {
// Minimum width that the text view can be. Corresponds with the largest number width seen so
// far.
@@ -53,8 +51,8 @@
private var shouldHideText: Boolean = false
override fun setBase(base: Long) {
- // These variables may have changed during the previous call, so re-set them before the new
- // call starts.
+ // These variables may have changed during the previous activity, so re-set them before the
+ // new activity starts.
minimumTextWidth = 0
shouldHideText = false
visibility = VISIBLE
@@ -75,9 +73,7 @@
}
// Evaluate how wide the text *wants* to be if it had unlimited space.
- super.onMeasure(
- MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED),
- heightMeasureSpec)
+ super.onMeasure(MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED), heightMeasureSpec)
val desiredTextWidth = measuredWidth
// Evaluate how wide the text *can* be based on the enforced constraints
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/lockscreen/LockscreenSmartspaceController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/lockscreen/LockscreenSmartspaceController.kt
index 446a0d7..455c964 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/lockscreen/LockscreenSmartspaceController.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/lockscreen/LockscreenSmartspaceController.kt
@@ -310,11 +310,9 @@
fun isWeatherEnabled(): Boolean {
execution.assertIsMainThread()
- val defaultValue = context.getResources().getBoolean(
- com.android.internal.R.bool.config_lockscreenWeatherEnabledByDefault)
val showWeather = secureSettings.getIntForUser(
LOCK_SCREEN_WEATHER_ENABLED,
- if (defaultValue) 1 else 0,
+ 1,
userTracker.userId) == 1
return showWeather
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationSectionsFeatureManager.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationSectionsFeatureManager.kt
index bd9383d..2f293e0 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationSectionsFeatureManager.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationSectionsFeatureManager.kt
@@ -22,11 +22,13 @@
import com.android.internal.annotations.VisibleForTesting
import com.android.internal.config.sysui.SystemUiDeviceConfigFlags.NOTIFICATIONS_USE_PEOPLE_FILTERING
import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.statusbar.notification.shared.PriorityPeopleSection
import com.android.systemui.statusbar.notification.stack.BUCKET_ALERTING
import com.android.systemui.statusbar.notification.stack.BUCKET_FOREGROUND_SERVICE
import com.android.systemui.statusbar.notification.stack.BUCKET_HEADS_UP
import com.android.systemui.statusbar.notification.stack.BUCKET_MEDIA_CONTROLS
import com.android.systemui.statusbar.notification.stack.BUCKET_PEOPLE
+import com.android.systemui.statusbar.notification.stack.BUCKET_PRIORITY_PEOPLE
import com.android.systemui.statusbar.notification.stack.BUCKET_SILENT
import com.android.systemui.util.DeviceConfigProxy
import com.android.systemui.util.Utils
@@ -53,6 +55,18 @@
}
fun getNotificationBuckets(): IntArray {
+ if (PriorityPeopleSection.isEnabled) {
+ // We don't need this list to be adaptive, it can be the superset of all features.
+ return intArrayOf(
+ BUCKET_MEDIA_CONTROLS,
+ BUCKET_HEADS_UP,
+ BUCKET_FOREGROUND_SERVICE,
+ BUCKET_PRIORITY_PEOPLE,
+ BUCKET_PEOPLE,
+ BUCKET_ALERTING,
+ BUCKET_SILENT,
+ )
+ }
return when {
isFilteringEnabled() && isMediaControlsEnabled() ->
intArrayOf(BUCKET_HEADS_UP, BUCKET_FOREGROUND_SERVICE, BUCKET_MEDIA_CONTROLS,
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/ConversationCoordinator.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/ConversationCoordinator.kt
index 3d0fd89..af2c197 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/ConversationCoordinator.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/ConversationCoordinator.kt
@@ -31,8 +31,10 @@
import com.android.systemui.statusbar.notification.icon.ConversationIconManager
import com.android.systemui.statusbar.notification.people.PeopleNotificationIdentifier
import com.android.systemui.statusbar.notification.people.PeopleNotificationIdentifier.Companion.PeopleNotificationType
+import com.android.systemui.statusbar.notification.people.PeopleNotificationIdentifier.Companion.TYPE_IMPORTANT_PERSON
import com.android.systemui.statusbar.notification.people.PeopleNotificationIdentifier.Companion.TYPE_NON_PERSON
import com.android.systemui.statusbar.notification.stack.BUCKET_PEOPLE
+import com.android.systemui.statusbar.notification.stack.BUCKET_PRIORITY_PEOPLE
import javax.inject.Inject
/**
@@ -81,6 +83,13 @@
}
}
+ val priorityPeopleSectioner =
+ object : NotifSectioner("Priority People", BUCKET_PRIORITY_PEOPLE) {
+ override fun isInSection(entry: ListEntry): Boolean {
+ return getPeopleType(entry) == TYPE_IMPORTANT_PERSON
+ }
+ }
+
// TODO(b/330193582): Rename to just "People"
val peopleAlertingSectioner = object : NotifSectioner("People(alerting)", BUCKET_PEOPLE) {
override fun isInSection(entry: ListEntry): Boolean {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/KeyguardCoordinator.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/KeyguardCoordinator.kt
index 1a223c1..42bf4e7 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/KeyguardCoordinator.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/KeyguardCoordinator.kt
@@ -18,12 +18,10 @@
package com.android.systemui.statusbar.notification.collection.coordinator
-import android.os.SystemProperties
import android.os.UserHandle
import android.provider.Settings
import androidx.annotation.VisibleForTesting
import com.android.systemui.Dumpable
-import com.android.systemui.Flags.notificationMinimalismPrototype
import com.android.systemui.dagger.qualifiers.Application
import com.android.systemui.dagger.qualifiers.Background
import com.android.systemui.dump.DumpManager
@@ -33,14 +31,20 @@
import com.android.systemui.plugins.statusbar.StatusBarStateController
import com.android.systemui.statusbar.StatusBarState
import com.android.systemui.statusbar.expansionChanges
+import com.android.systemui.statusbar.notification.collection.GroupEntry
+import com.android.systemui.statusbar.notification.collection.ListEntry
import com.android.systemui.statusbar.notification.collection.NotifPipeline
import com.android.systemui.statusbar.notification.collection.NotificationEntry
import com.android.systemui.statusbar.notification.collection.coordinator.dagger.CoordinatorScope
import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.NotifFilter
+import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.NotifPromoter
+import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.NotifSectioner
import com.android.systemui.statusbar.notification.collection.notifcollection.NotifCollectionListener
import com.android.systemui.statusbar.notification.collection.provider.SectionHeaderVisibilityProvider
import com.android.systemui.statusbar.notification.domain.interactor.SeenNotificationsInteractor
import com.android.systemui.statusbar.notification.interruption.KeyguardNotificationVisibilityProvider
+import com.android.systemui.statusbar.notification.shared.NotificationMinimalismPrototype
+import com.android.systemui.statusbar.notification.stack.BUCKET_FOREGROUND_SERVICE
import com.android.systemui.statusbar.policy.HeadsUpManager
import com.android.systemui.statusbar.policy.headsUpEvents
import com.android.systemui.util.asIndenting
@@ -107,6 +111,10 @@
}
private fun attachUnseenFilter(pipeline: NotifPipeline) {
+ if (NotificationMinimalismPrototype.V2.isEnabled) {
+ pipeline.addPromoter(unseenNotifPromoter)
+ pipeline.addOnBeforeTransformGroupsListener(::pickOutTopUnseenNotif)
+ }
pipeline.addFinalizeFilter(unseenNotifFilter)
pipeline.addCollectionListener(collectionListener)
scope.launch { trackUnseenFilterSettingChanges() }
@@ -264,7 +272,10 @@
}
private fun unseenFeatureEnabled(): Flow<Boolean> {
- if (notificationMinimalismPrototype()) {
+ if (
+ NotificationMinimalismPrototype.V1.isEnabled ||
+ NotificationMinimalismPrototype.V2.isEnabled
+ ) {
return flowOf(true)
}
return secureSettings
@@ -335,6 +346,57 @@
}
}
+ private fun pickOutTopUnseenNotif(list: List<ListEntry>) {
+ if (NotificationMinimalismPrototype.V2.isUnexpectedlyInLegacyMode()) return
+ // Only ever elevate a top unseen notification on keyguard, not even locked shade
+ if (statusBarStateController.state != StatusBarState.KEYGUARD) {
+ seenNotificationsInteractor.setTopUnseenNotification(null)
+ return
+ }
+ // On keyguard pick the top-ranked unseen or ongoing notification to elevate
+ seenNotificationsInteractor.setTopUnseenNotification(
+ list
+ .asSequence()
+ .flatMap {
+ when (it) {
+ is NotificationEntry -> listOfNotNull(it)
+ is GroupEntry -> it.children
+ else -> error("unhandled type of $it")
+ }
+ }
+ .filter { shouldIgnoreUnseenCheck(it) || it in unseenNotifications }
+ .minByOrNull { it.ranking.rank }
+ )
+ }
+
+ @VisibleForTesting
+ internal val unseenNotifPromoter =
+ object : NotifPromoter("$TAG-unseen") {
+ override fun shouldPromoteToTopLevel(child: NotificationEntry): Boolean =
+ if (NotificationMinimalismPrototype.V2.isUnexpectedlyInLegacyMode()) false
+ else
+ seenNotificationsInteractor.isTopUnseenNotification(child) &&
+ NotificationMinimalismPrototype.V2.ungroupTopUnseen
+ }
+
+ val unseenNotifSectioner =
+ object : NotifSectioner("Unseen", BUCKET_FOREGROUND_SERVICE) {
+ override fun isInSection(entry: ListEntry): Boolean {
+ if (NotificationMinimalismPrototype.V2.isUnexpectedlyInLegacyMode()) return false
+ if (
+ seenNotificationsInteractor.isTopUnseenNotification(entry.representativeEntry)
+ ) {
+ return true
+ }
+ if (entry !is GroupEntry) {
+ return false
+ }
+ return entry.children.any {
+ seenNotificationsInteractor.isTopUnseenNotification(it)
+ }
+ }
+ }
+
@VisibleForTesting
internal val unseenNotifFilter =
object : NotifFilter("$TAG-unseen") {
@@ -342,18 +404,6 @@
var hasFilteredAnyNotifs = false
/**
- * the [notificationMinimalismPrototype] will now show seen notifications on the locked
- * shade by default, but this property read allows that to be quickly disabled for
- * testing
- */
- private val minimalismShowOnLockedShade
- get() =
- SystemProperties.getBoolean(
- "persist.notification_minimalism_prototype.show_on_locked_shade",
- true
- )
-
- /**
* Encapsulates a definition of "being on the keyguard". Note that these two definitions
* are wildly different: [StatusBarState.KEYGUARD] is when on the lock screen and does
* not include shade or occluded states, whereas [KeyguardRepository.isKeyguardShowing]
@@ -364,7 +414,12 @@
* allow seen notifications to appear in the locked shade.
*/
private fun isOnKeyguard(): Boolean =
- if (notificationMinimalismPrototype() && minimalismShowOnLockedShade) {
+ if (NotificationMinimalismPrototype.V2.isEnabled) {
+ false // disable this feature under this prototype
+ } else if (
+ NotificationMinimalismPrototype.V1.isEnabled &&
+ NotificationMinimalismPrototype.V1.showOnLockedShade
+ ) {
statusBarStateController.state == StatusBarState.KEYGUARD
} else {
keyguardRepository.isKeyguardShowing()
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/NotifCoordinators.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/NotifCoordinators.kt
index 36c12a7..4506385 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/NotifCoordinators.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/NotifCoordinators.kt
@@ -24,47 +24,51 @@
import com.android.systemui.statusbar.notification.collection.coordinator.dagger.CoordinatorScope
import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.NotifSectioner
import com.android.systemui.statusbar.notification.collection.provider.SectionStyleProvider
+import com.android.systemui.statusbar.notification.shared.NotificationMinimalismPrototype
import com.android.systemui.statusbar.notification.shared.NotificationsLiveDataStoreRefactor
+import com.android.systemui.statusbar.notification.shared.PriorityPeopleSection
import javax.inject.Inject
/**
- * Handles the attachment of [Coordinator]s to the [NotifPipeline] so that the
- * Coordinators can register their respective callbacks.
+ * Handles the attachment of [Coordinator]s to the [NotifPipeline] so that the Coordinators can
+ * register their respective callbacks.
*/
interface NotifCoordinators : Coordinator, PipelineDumpable
@CoordinatorScope
-class NotifCoordinatorsImpl @Inject constructor(
- sectionStyleProvider: SectionStyleProvider,
- featureFlags: FeatureFlags,
- dataStoreCoordinator: DataStoreCoordinator,
- hideLocallyDismissedNotifsCoordinator: HideLocallyDismissedNotifsCoordinator,
- hideNotifsForOtherUsersCoordinator: HideNotifsForOtherUsersCoordinator,
- keyguardCoordinator: KeyguardCoordinator,
- rankingCoordinator: RankingCoordinator,
- colorizedFgsCoordinator: ColorizedFgsCoordinator,
- deviceProvisionedCoordinator: DeviceProvisionedCoordinator,
- bubbleCoordinator: BubbleCoordinator,
- headsUpCoordinator: HeadsUpCoordinator,
- gutsCoordinator: GutsCoordinator,
- conversationCoordinator: ConversationCoordinator,
- debugModeCoordinator: DebugModeCoordinator,
- groupCountCoordinator: GroupCountCoordinator,
- groupWhenCoordinator: GroupWhenCoordinator,
- mediaCoordinator: MediaCoordinator,
- preparationCoordinator: PreparationCoordinator,
- remoteInputCoordinator: RemoteInputCoordinator,
- rowAlertTimeCoordinator: RowAlertTimeCoordinator,
- rowAppearanceCoordinator: RowAppearanceCoordinator,
- stackCoordinator: StackCoordinator,
- shadeEventCoordinator: ShadeEventCoordinator,
- smartspaceDedupingCoordinator: SmartspaceDedupingCoordinator,
- viewConfigCoordinator: ViewConfigCoordinator,
- visualStabilityCoordinator: VisualStabilityCoordinator,
- sensitiveContentCoordinator: SensitiveContentCoordinator,
- dismissibilityCoordinator: DismissibilityCoordinator,
- dreamCoordinator: DreamCoordinator,
- statsLoggerCoordinator: NotificationStatsLoggerCoordinator,
+class NotifCoordinatorsImpl
+@Inject
+constructor(
+ sectionStyleProvider: SectionStyleProvider,
+ featureFlags: FeatureFlags,
+ dataStoreCoordinator: DataStoreCoordinator,
+ hideLocallyDismissedNotifsCoordinator: HideLocallyDismissedNotifsCoordinator,
+ hideNotifsForOtherUsersCoordinator: HideNotifsForOtherUsersCoordinator,
+ keyguardCoordinator: KeyguardCoordinator,
+ rankingCoordinator: RankingCoordinator,
+ colorizedFgsCoordinator: ColorizedFgsCoordinator,
+ deviceProvisionedCoordinator: DeviceProvisionedCoordinator,
+ bubbleCoordinator: BubbleCoordinator,
+ headsUpCoordinator: HeadsUpCoordinator,
+ gutsCoordinator: GutsCoordinator,
+ conversationCoordinator: ConversationCoordinator,
+ debugModeCoordinator: DebugModeCoordinator,
+ groupCountCoordinator: GroupCountCoordinator,
+ groupWhenCoordinator: GroupWhenCoordinator,
+ mediaCoordinator: MediaCoordinator,
+ preparationCoordinator: PreparationCoordinator,
+ remoteInputCoordinator: RemoteInputCoordinator,
+ rowAlertTimeCoordinator: RowAlertTimeCoordinator,
+ rowAppearanceCoordinator: RowAppearanceCoordinator,
+ stackCoordinator: StackCoordinator,
+ shadeEventCoordinator: ShadeEventCoordinator,
+ smartspaceDedupingCoordinator: SmartspaceDedupingCoordinator,
+ viewConfigCoordinator: ViewConfigCoordinator,
+ visualStabilityCoordinator: VisualStabilityCoordinator,
+ sensitiveContentCoordinator: SensitiveContentCoordinator,
+ dismissibilityCoordinator: DismissibilityCoordinator,
+ dreamCoordinator: DreamCoordinator,
+ statsLoggerCoordinator: NotificationStatsLoggerCoordinator,
) : NotifCoordinators {
private val mCoreCoordinators: MutableList<CoreCoordinator> = ArrayList()
@@ -114,6 +118,12 @@
// Manually add Ordered Sections
mOrderedSections.add(headsUpCoordinator.sectioner) // HeadsUp
mOrderedSections.add(colorizedFgsCoordinator.sectioner) // ForegroundService
+ if (NotificationMinimalismPrototype.V2.isEnabled) {
+ mOrderedSections.add(keyguardCoordinator.unseenNotifSectioner) // Unseen (FGS)
+ }
+ if (PriorityPeopleSection.isEnabled) {
+ mOrderedSections.add(conversationCoordinator.priorityPeopleSectioner) // Priority People
+ }
mOrderedSections.add(conversationCoordinator.peopleAlertingSectioner) // People Alerting
if (!SortBySectionTimeFlag.isEnabled) {
mOrderedSections.add(conversationCoordinator.peopleSilentSectioner) // People Silent
@@ -124,22 +134,26 @@
sectionStyleProvider.setMinimizedSections(setOf(rankingCoordinator.minimizedSectioner))
if (SortBySectionTimeFlag.isEnabled) {
- sectionStyleProvider.setSilentSections(listOf(
+ sectionStyleProvider.setSilentSections(
+ listOf(
rankingCoordinator.silentSectioner,
rankingCoordinator.minimizedSectioner,
- ))
+ )
+ )
} else {
- sectionStyleProvider.setSilentSections(listOf(
+ sectionStyleProvider.setSilentSections(
+ listOf(
conversationCoordinator.peopleSilentSectioner,
rankingCoordinator.silentSectioner,
rankingCoordinator.minimizedSectioner,
- ))
+ )
+ )
}
}
/**
- * Sends the pipeline to each coordinator when the pipeline is ready to accept
- * [Pluggable]s, [NotifCollectionListener]s and [NotifLifetimeExtender]s.
+ * Sends the pipeline to each coordinator when the pipeline is ready to accept [Pluggable]s,
+ * [NotifCollectionListener]s and [NotifLifetimeExtender]s.
*/
override fun attach(pipeline: NotifPipeline) {
for (c in mCoreCoordinators) {
@@ -155,10 +169,11 @@
* As part of the NotifPipeline dumpable, dumps the list of coordinators; sections are omitted
* as they are dumped in the RenderStageManager instead.
*/
- override fun dumpPipeline(d: PipelineDumper) = with(d) {
- dump("core coordinators", mCoreCoordinators)
- dump("normal coordinators", mCoordinators)
- }
+ override fun dumpPipeline(d: PipelineDumper) =
+ with(d) {
+ dump("core coordinators", mCoreCoordinators)
+ dump("normal coordinators", mCoordinators)
+ }
companion object {
private const val TAG = "NotifCoordinators"
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/VisualStabilityCoordinator.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/VisualStabilityCoordinator.java
index 350e88e..caa6c17 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/VisualStabilityCoordinator.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/VisualStabilityCoordinator.java
@@ -38,6 +38,8 @@
import com.android.systemui.statusbar.notification.collection.NotificationEntry;
import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.NotifStabilityManager;
import com.android.systemui.statusbar.notification.collection.provider.VisualStabilityProvider;
+import com.android.systemui.statusbar.notification.domain.interactor.SeenNotificationsInteractor;
+import com.android.systemui.statusbar.notification.shared.NotificationMinimalismPrototype;
import com.android.systemui.statusbar.policy.HeadsUpManager;
import com.android.systemui.util.Compile;
import com.android.systemui.util.concurrency.DelayableExecutor;
@@ -63,6 +65,7 @@
public static final boolean DEBUG = Compile.IS_DEBUG && Log.isLoggable(TAG, Log.VERBOSE);
private final DelayableExecutor mDelayableExecutor;
private final HeadsUpManager mHeadsUpManager;
+ private final SeenNotificationsInteractor mSeenNotificationsInteractor;
private final ShadeAnimationInteractor mShadeAnimationInteractor;
private final StatusBarStateController mStatusBarStateController;
private final JavaAdapter mJavaAdapter;
@@ -101,6 +104,7 @@
HeadsUpManager headsUpManager,
ShadeAnimationInteractor shadeAnimationInteractor,
JavaAdapter javaAdapter,
+ SeenNotificationsInteractor seenNotificationsInteractor,
StatusBarStateController statusBarStateController,
VisibilityLocationProvider visibilityLocationProvider,
VisualStabilityProvider visualStabilityProvider,
@@ -109,6 +113,7 @@
mHeadsUpManager = headsUpManager;
mShadeAnimationInteractor = shadeAnimationInteractor;
mJavaAdapter = javaAdapter;
+ mSeenNotificationsInteractor = seenNotificationsInteractor;
mVisibilityLocationProvider = visibilityLocationProvider;
mVisualStabilityProvider = visualStabilityProvider;
mWakefulnessLifecycle = wakefulnessLifecycle;
@@ -142,8 +147,15 @@
private final NotifStabilityManager mNotifStabilityManager =
new NotifStabilityManager("VisualStabilityCoordinator") {
private boolean canMoveForHeadsUp(NotificationEntry entry) {
- return entry != null && mHeadsUpManager.isHeadsUpEntry(entry.getKey())
- && !mVisibilityLocationProvider.isInVisibleLocation(entry);
+ if (entry == null) {
+ return false;
+ }
+ boolean isTopUnseen = NotificationMinimalismPrototype.V2.isEnabled()
+ && mSeenNotificationsInteractor.isTopUnseenNotification(entry);
+ if (isTopUnseen || mHeadsUpManager.isHeadsUpEntry(entry.getKey())) {
+ return !mVisibilityLocationProvider.isInVisibleLocation(entry);
+ }
+ return false;
}
@Override
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/data/repository/ActiveNotificationListRepository.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/data/repository/ActiveNotificationListRepository.kt
index 5c844bc..e2c9e02 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/data/repository/ActiveNotificationListRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/data/repository/ActiveNotificationListRepository.kt
@@ -41,6 +41,9 @@
/** Stats about the list of notifications attached to the shade */
val notifStats = MutableStateFlow(NotifStats.empty)
+
+ /** The key of the top unseen notification */
+ val topUnseenNotificationKey = MutableStateFlow<String?>(null)
}
/** Represents the notification list, comprised of groups and individual notifications. */
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/domain/interactor/SeenNotificationsInteractor.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/domain/interactor/SeenNotificationsInteractor.kt
index 1f7ab96..42828d9 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/domain/interactor/SeenNotificationsInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/domain/interactor/SeenNotificationsInteractor.kt
@@ -17,7 +17,9 @@
package com.android.systemui.statusbar.notification.domain.interactor
import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.statusbar.notification.collection.NotificationEntry
import com.android.systemui.statusbar.notification.data.repository.ActiveNotificationListRepository
+import com.android.systemui.statusbar.notification.shared.NotificationMinimalismPrototype
import javax.inject.Inject
import kotlinx.coroutines.flow.StateFlow
@@ -36,4 +38,15 @@
fun setHasFilteredOutSeenNotifications(value: Boolean) {
notificationListRepository.hasFilteredOutSeenNotifications.value = value
}
+
+ /** Set the entry that is identified as the top unseen notification. */
+ fun setTopUnseenNotification(entry: NotificationEntry?) {
+ if (NotificationMinimalismPrototype.V2.isUnexpectedlyInLegacyMode()) return
+ notificationListRepository.topUnseenNotificationKey.value = entry?.key
+ }
+
+ /** Determine if the given notification is the top unseen notification. */
+ fun isTopUnseenNotification(entry: NotificationEntry?): Boolean =
+ if (NotificationMinimalismPrototype.V2.isUnexpectedlyInLegacyMode()) false
+ else entry != null && notificationListRepository.topUnseenNotificationKey.value == entry.key
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/footer/ui/view/FooterView.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/footer/ui/view/FooterView.java
index 968b591..5a616df 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/footer/ui/view/FooterView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/footer/ui/view/FooterView.java
@@ -18,7 +18,7 @@
import static android.graphics.PorterDuff.Mode.SRC_ATOP;
-import static com.android.systemui.Flags.notificationBackgroundTintOptimization;
+import static com.android.systemui.Flags.notificationFooterBackgroundTintOptimization;
import static com.android.systemui.util.ColorUtilKt.hexColorString;
import android.annotation.ColorInt;
@@ -407,7 +407,7 @@
final Drawable clearAllBg = theme.getDrawable(R.drawable.notif_footer_btn_background);
final Drawable manageBg = theme.getDrawable(R.drawable.notif_footer_btn_background);
final @ColorInt int scHigh;
- if (!notificationBackgroundTintOptimization()) {
+ if (!notificationFooterBackgroundTintOptimization()) {
scHigh = Utils.getColorAttrDefaultColor(mContext,
com.android.internal.R.attr.materialColorSurfaceContainerHigh);
if (scHigh != 0) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/CommonVisualInterruptionSuppressors.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/CommonVisualInterruptionSuppressors.kt
index bfc5932..87f11f13 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/CommonVisualInterruptionSuppressors.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/CommonVisualInterruptionSuppressors.kt
@@ -16,6 +16,7 @@
package com.android.systemui.statusbar.notification.interruption
+import android.Manifest.permission.RECEIVE_EMERGENCY_BROADCAST
import android.app.Notification
import android.app.Notification.BubbleMetadata
import android.app.Notification.CATEGORY_EVENT
@@ -23,6 +24,8 @@
import android.app.Notification.VISIBILITY_PRIVATE
import android.app.NotificationManager.IMPORTANCE_DEFAULT
import android.app.NotificationManager.IMPORTANCE_HIGH
+import android.content.pm.PackageManager
+import android.content.pm.PackageManager.PERMISSION_GRANTED
import android.database.ContentObserver
import android.hardware.display.AmbientDisplayConfiguration
import android.os.Handler
@@ -234,6 +237,7 @@
private val avalancheProvider: AvalancheProvider,
private val systemClock: SystemClock,
private val systemSettings: SystemSettings,
+ private val packageManager: PackageManager,
) :
VisualInterruptionFilter(
types = setOf(PEEK, PULSE),
@@ -241,9 +245,6 @@
) {
val TAG = "AvalancheSuppressor"
- override var reason: String = "avalanche"
- protected set
-
enum class State {
ALLOW_CONVERSATION_AFTER_AVALANCHE,
ALLOW_HIGH_PRIORITY_CONVERSATION_ANY_TIME,
@@ -252,24 +253,21 @@
ALLOW_CATEGORY_EVENT,
ALLOW_FSI_WITH_PERMISSION_ON,
ALLOW_COLORIZED,
+ ALLOW_EMERGENCY,
SUPPRESS
}
override fun shouldSuppress(entry: NotificationEntry): Boolean {
if (!isCooldownEnabled()) {
- reason = "FALSE avalanche cooldown setting DISABLED"
return false
}
val timeSinceAvalancheMs = systemClock.currentTimeMillis() - avalancheProvider.startTime
val timedOut = timeSinceAvalancheMs >= avalancheProvider.timeoutMs
if (timedOut) {
- reason = "FALSE avalanche event TIMED OUT. " +
- "${timeSinceAvalancheMs/1000} seconds since last avalanche"
return false
}
val state = calculateState(entry)
if (state != State.SUPPRESS) {
- reason = "FALSE avalanche IN ALLOWLIST: $state"
return false
}
return true
@@ -306,13 +304,20 @@
if (entry.sbn.notification.isColorized) {
return State.ALLOW_COLORIZED
}
+ if (entry.sbn.notification.isColorized) {
+ return State.ALLOW_COLORIZED
+ }
+ if (
+ packageManager.checkPermission(RECEIVE_EMERGENCY_BROADCAST, entry.sbn.packageName) ==
+ PERMISSION_GRANTED
+ ) {
+ return State.ALLOW_EMERGENCY
+ }
return State.SUPPRESS
}
private fun isCooldownEnabled(): Boolean {
- return systemSettings.getInt(
- Settings.System.NOTIFICATION_COOLDOWN_ENABLED,
- /* def */ 1
- ) == 1
+ return systemSettings.getInt(Settings.System.NOTIFICATION_COOLDOWN_ENABLED, /* def */ 1) ==
+ 1
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/VisualInterruptionDecisionProviderImpl.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/VisualInterruptionDecisionProviderImpl.kt
index e6d97c2..f68e194 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/VisualInterruptionDecisionProviderImpl.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/VisualInterruptionDecisionProviderImpl.kt
@@ -15,6 +15,7 @@
*/
package com.android.systemui.statusbar.notification.interruption
+import android.content.pm.PackageManager
import android.hardware.display.AmbientDisplayConfiguration
import android.os.Handler
import android.os.PowerManager
@@ -63,7 +64,8 @@
private val uiEventLogger: UiEventLogger,
private val userTracker: UserTracker,
private val avalancheProvider: AvalancheProvider,
- private val systemSettings: SystemSettings
+ private val systemSettings: SystemSettings,
+ private val packageManager: PackageManager
) : VisualInterruptionDecisionProvider {
init {
@@ -172,7 +174,9 @@
addFilter(AlertKeyguardVisibilitySuppressor(keyguardNotificationVisibilityProvider))
if (NotificationAvalancheSuppression.isEnabled) {
- addFilter(AvalancheSuppressor(avalancheProvider, systemClock, systemSettings))
+ addFilter(
+ AvalancheSuppressor(avalancheProvider, systemClock, systemSettings, packageManager)
+ )
avalancheProvider.register()
}
started = true
@@ -232,14 +236,17 @@
private fun makeLoggablePeekDecision(entry: NotificationEntry): LoggableDecision =
checkConditions(PEEK)
- ?: checkFilters(PEEK, entry) ?: checkSuppressInterruptions(entry)
- ?: checkSuppressAwakeInterruptions(entry) ?: checkSuppressAwakeHeadsUp(entry)
- ?: LoggableDecision.unsuppressed
+ ?: checkFilters(PEEK, entry)
+ ?: checkSuppressInterruptions(entry)
+ ?: checkSuppressAwakeInterruptions(entry)
+ ?: checkSuppressAwakeHeadsUp(entry)
+ ?: LoggableDecision.unsuppressed
private fun makeLoggablePulseDecision(entry: NotificationEntry): LoggableDecision =
checkConditions(PULSE)
- ?: checkFilters(PULSE, entry) ?: checkSuppressInterruptions(entry)
- ?: LoggableDecision.unsuppressed
+ ?: checkFilters(PULSE, entry)
+ ?: checkSuppressInterruptions(entry)
+ ?: LoggableDecision.unsuppressed
override fun makeAndLogBubbleDecision(entry: NotificationEntry): Decision =
traceSection("VisualInterruptionDecisionProviderImpl#makeAndLogBubbleDecision") {
@@ -252,8 +259,10 @@
private fun makeLoggableBubbleDecision(entry: NotificationEntry): LoggableDecision =
checkConditions(BUBBLE)
- ?: checkFilters(BUBBLE, entry) ?: checkSuppressInterruptions(entry)
- ?: checkSuppressAwakeInterruptions(entry) ?: LoggableDecision.unsuppressed
+ ?: checkFilters(BUBBLE, entry)
+ ?: checkSuppressInterruptions(entry)
+ ?: checkSuppressAwakeInterruptions(entry)
+ ?: LoggableDecision.unsuppressed
private fun logDecision(
type: VisualInterruptionType,
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/logging/NotificationPanelLogger.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/logging/NotificationPanelLogger.java
index 89aa3ab..9e0dd8fc 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/logging/NotificationPanelLogger.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/logging/NotificationPanelLogger.java
@@ -21,6 +21,7 @@
import static com.android.systemui.statusbar.notification.stack.NotificationPriorityBucketKt.BUCKET_HEADS_UP;
import static com.android.systemui.statusbar.notification.stack.NotificationPriorityBucketKt.BUCKET_MEDIA_CONTROLS;
import static com.android.systemui.statusbar.notification.stack.NotificationPriorityBucketKt.BUCKET_PEOPLE;
+import static com.android.systemui.statusbar.notification.stack.NotificationPriorityBucketKt.BUCKET_PRIORITY_PEOPLE;
import static com.android.systemui.statusbar.notification.stack.NotificationPriorityBucketKt.BUCKET_SILENT;
import android.annotation.Nullable;
@@ -130,7 +131,8 @@
case BUCKET_HEADS_UP: return Notifications.Notification.SECTION_HEADS_UP;
case BUCKET_FOREGROUND_SERVICE:
return Notifications.Notification.SECTION_FOREGROUND_SERVICE;
- case BUCKET_PEOPLE: return Notifications.Notification.SECTION_PEOPLE;
+ case BUCKET_PEOPLE, BUCKET_PRIORITY_PEOPLE:
+ return Notifications.Notification.SECTION_PEOPLE;
case BUCKET_ALERTING: return Notifications.Notification.SECTION_ALERTING;
case BUCKET_SILENT: return Notifications.Notification.SECTION_SILENT;
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java
index 747cb3c..edd2961 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java
@@ -101,6 +101,7 @@
import com.android.systemui.statusbar.notification.people.PeopleNotificationIdentifier;
import com.android.systemui.statusbar.notification.row.NotificationRowContentBinder.InflationFlag;
import com.android.systemui.statusbar.notification.row.shared.AsyncGroupHeaderViewInflation;
+import com.android.systemui.statusbar.notification.row.wrapper.NotificationCompactMessagingTemplateViewWrapper;
import com.android.systemui.statusbar.notification.row.wrapper.NotificationViewWrapper;
import com.android.systemui.statusbar.notification.shared.NotificationContentAlphaOptimization;
import com.android.systemui.statusbar.notification.stack.AmbientState;
@@ -590,7 +591,9 @@
mMenuRow.setAppName(mAppName);
}
if (mIsSummaryWithChildren) {
- if (!AsyncGroupHeaderViewInflation.isEnabled()) {
+ if (AsyncGroupHeaderViewInflation.isEnabled()) {
+ mChildrenContainer.updateGroupHeaderExpandState();
+ } else {
// We create the header from the background thread instead
mChildrenContainer.recreateNotificationHeader(mExpandClickListener,
isConversation());
@@ -843,6 +846,16 @@
}
/**
+ *
+ * @return true when compact version of Heads Up is on the screen.
+ */
+ public boolean isCompactConversationHeadsUpOnScreen() {
+ final NotificationViewWrapper viewWrapper =
+ getVisibleNotificationViewWrapper();
+
+ return viewWrapper instanceof NotificationCompactMessagingTemplateViewWrapper;
+ }
+ /**
* @see NotificationChildrenContainer#setUntruncatedChildCount(int)
*/
public void setUntruncatedChildCount(int childCount) {
@@ -2788,7 +2801,10 @@
}
}
- protected void expandNotification() {
+ /**
+ * Triggers expand click listener to expand the notification.
+ */
+ public void expandNotification() {
mExpandClickListener.onClick(this);
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/HeadsUpStyleProvider.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/HeadsUpStyleProvider.kt
index db3cf5a..9d0fcd3 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/HeadsUpStyleProvider.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/HeadsUpStyleProvider.kt
@@ -17,6 +17,7 @@
package com.android.systemui.statusbar.notification.row
import android.app.Flags
+import android.os.SystemProperties
import com.android.systemui.statusbar.data.repository.StatusBarModeRepositoryStore
import javax.inject.Inject
@@ -34,8 +35,13 @@
HeadsUpStyleProvider {
override fun shouldApplyCompactStyle(): Boolean {
- // Use compact HUN for immersive mode.
- return Flags.compactHeadsUpNotification() &&
- statusBarModeRepositoryStore.defaultDisplay.isInFullscreenMode.value
+ return Flags.compactHeadsUpNotification() && (isInImmersiveMode() || alwaysShow())
}
+
+ private fun isInImmersiveMode() =
+ statusBarModeRepositoryStore.defaultDisplay.isInFullscreenMode.value
+
+ /** developer setting to always show Minimal HUN, even if the device is not in full screen */
+ private fun alwaysShow() =
+ SystemProperties.getBoolean("persist.compact_heads_up_notification.always_show", false)
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationCompactHeadsUpTemplateViewWrapper.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationCompactHeadsUpTemplateViewWrapper.kt
index ce87d2f..3a5f3b2 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationCompactHeadsUpTemplateViewWrapper.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationCompactHeadsUpTemplateViewWrapper.kt
@@ -24,7 +24,7 @@
/**
* Compact Heads up Notifications template that doesn't set feedback icon and audibly alert icons
*/
-class NotificationCompactHeadsUpTemplateViewWrapper(
+open class NotificationCompactHeadsUpTemplateViewWrapper(
ctx: Context,
view: View,
row: ExpandableNotificationRow
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationCompactMessagingTemplateViewWrapper.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationCompactMessagingTemplateViewWrapper.kt
new file mode 100644
index 0000000..20f04f9
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationCompactMessagingTemplateViewWrapper.kt
@@ -0,0 +1,73 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package com.android.systemui.statusbar.notification.row.wrapper
+
+import android.content.Context
+import android.view.View
+import android.view.ViewGroup
+import com.android.internal.R
+import com.android.internal.widget.CachingIconView
+import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow
+
+/** Wraps a notification containing a messaging or conversation template */
+class NotificationCompactMessagingTemplateViewWrapper
+constructor(ctx: Context, view: View, row: ExpandableNotificationRow) :
+ NotificationCompactHeadsUpTemplateViewWrapper(ctx, view, row) {
+
+ private val compactMessagingView: ViewGroup = requireNotNull(view as? ViewGroup)
+
+ private var conversationIconView: CachingIconView? = null
+ private var expandBtn: View? = null
+ private var titleView: View? = null
+ private var headerTextSecondary: View? = null
+ private var subText: View? = null
+ private var facePileTop: View? = null
+ private var facePileBottom: View? = null
+ private var facePileBottomBg: View? = null
+ override fun onContentUpdated(row: ExpandableNotificationRow?) {
+ resolveViews()
+ super.onContentUpdated(row)
+ }
+
+ private fun resolveViews() {
+ conversationIconView = compactMessagingView.requireViewById(R.id.conversation_icon)
+ titleView = compactMessagingView.findViewById(R.id.title)
+ headerTextSecondary = compactMessagingView.findViewById(R.id.header_text_secondary)
+ subText = compactMessagingView.findViewById(R.id.header_text)
+ facePileTop = compactMessagingView.findViewById(R.id.conversation_face_pile_top)
+ facePileBottom = compactMessagingView.findViewById(R.id.conversation_face_pile_bottom)
+ facePileBottomBg =
+ compactMessagingView.findViewById(R.id.conversation_face_pile_bottom_background)
+
+ expandBtn = compactMessagingView.requireViewById(R.id.expand_button)
+ }
+
+ override fun updateTransformedTypes() {
+ super.updateTransformedTypes()
+
+ addViewsTransformingToSimilar(
+ conversationIconView,
+ titleView,
+ headerTextSecondary,
+ subText,
+ facePileTop,
+ facePileBottom,
+ facePileBottomBg,
+ expandBtn,
+ )
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationViewWrapper.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationViewWrapper.java
index 4244542..22b95ef 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationViewWrapper.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationViewWrapper.java
@@ -74,6 +74,8 @@
return new NotificationCallTemplateViewWrapper(ctx, v, row);
} else if ("compactHUN".equals((v.getTag()))) {
return new NotificationCompactHeadsUpTemplateViewWrapper(ctx, v, row);
+ } else if ("compactMessagingHUN".equals((v.getTag()))) {
+ return new NotificationCompactMessagingTemplateViewWrapper(ctx, v, row);
}
if (row.getEntry().getSbn().getNotification().isStyle(
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/shared/NotificationHeadsUpCycling.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/shared/NotificationHeadsUpCycling.kt
index d4f8ea3..d6c73a9 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/shared/NotificationHeadsUpCycling.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/shared/NotificationHeadsUpCycling.kt
@@ -23,8 +23,8 @@
/** Helper for reading or using the heads-up cycling flag state. */
@Suppress("NOTHING_TO_INLINE")
object NotificationHeadsUpCycling {
- /** The aconfig flag name */
- const val FLAG_NAME = Flags.FLAG_NOTIFICATION_HEADS_UP_CYCLING
+ /** The aconfig flag name - enable this feature when FLAG_NOTIFICATION_THROTTLE_HUN is on. */
+ const val FLAG_NAME = Flags.FLAG_NOTIFICATION_THROTTLE_HUN
/** A token used for dependency declaration */
val token: FlagToken
@@ -33,7 +33,7 @@
/** Is the heads-up cycling animation enabled */
@JvmStatic
inline val isEnabled
- get() = Flags.notificationHeadsUpCycling()
+ get() = Flags.notificationThrottleHun()
/** Whether to animate the bottom line when transiting from a tall HUN to a short HUN */
@JvmStatic
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/shared/NotificationMinimalismPrototype.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/shared/NotificationMinimalismPrototype.kt
new file mode 100644
index 0000000..bf37036
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/shared/NotificationMinimalismPrototype.kt
@@ -0,0 +1,125 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.statusbar.notification.shared
+
+import android.os.SystemProperties
+import com.android.systemui.Flags
+import com.android.systemui.flags.FlagToken
+import com.android.systemui.flags.RefactorFlagUtils
+
+/** Helper for reading or using the minimalism prototype flag state. */
+@Suppress("NOTHING_TO_INLINE")
+object NotificationMinimalismPrototype {
+
+ val version: Int by lazy {
+ SystemProperties.getInt("persist.notification_minimalism_prototype.version", 2)
+ }
+
+ object V1 {
+ /** The aconfig flag name */
+ const val FLAG_NAME = Flags.FLAG_NOTIFICATION_MINIMALISM_PROTOTYPE
+
+ /** A token used for dependency declaration */
+ val token: FlagToken
+ get() = FlagToken(FLAG_NAME, isEnabled)
+
+ /** Is the heads-up cycling animation enabled */
+ @JvmStatic
+ inline val isEnabled
+ get() = Flags.notificationMinimalismPrototype() && version == 1
+
+ /**
+ * the prototype will now show seen notifications on the locked shade by default, but this
+ * property read allows that to be quickly disabled for testing
+ */
+ val showOnLockedShade: Boolean
+ get() =
+ if (isUnexpectedlyInLegacyMode()) false
+ else
+ SystemProperties.getBoolean(
+ "persist.notification_minimalism_prototype.show_on_locked_shade",
+ true
+ )
+
+ /** gets the configurable max number of notifications */
+ val maxNotifs: Int
+ get() =
+ if (isUnexpectedlyInLegacyMode()) -1
+ else
+ SystemProperties.getInt(
+ "persist.notification_minimalism_prototype.lock_screen_max_notifs",
+ 1
+ )
+
+ /**
+ * Called to ensure code is only run when the flag is enabled. This protects users from the
+ * unintended behaviors caused by accidentally running new logic, while also crashing on an
+ * eng build to ensure that the refactor author catches issues in testing.
+ */
+ @JvmStatic
+ inline fun isUnexpectedlyInLegacyMode() =
+ RefactorFlagUtils.isUnexpectedlyInLegacyMode(isEnabled, FLAG_NAME)
+
+ /**
+ * Called to ensure code is only run when the flag is disabled. This will throw an exception
+ * if the flag is enabled to ensure that the refactor author catches issues in testing.
+ */
+ @JvmStatic
+ inline fun assertInLegacyMode() = RefactorFlagUtils.assertInLegacyMode(isEnabled, FLAG_NAME)
+ }
+ object V2 {
+ const val FLAG_NAME = Flags.FLAG_NOTIFICATION_MINIMALISM_PROTOTYPE
+
+ /** A token used for dependency declaration */
+ val token: FlagToken
+ get() = FlagToken(FLAG_NAME, isEnabled)
+
+ /** Is the heads-up cycling animation enabled */
+ @JvmStatic
+ inline val isEnabled
+ get() = Flags.notificationMinimalismPrototype() && version == 2
+
+ /**
+ * The prototype will (by default) use a promoter to ensure that the top unseen notification
+ * is not grouped, but this property read allows that behavior to be disabled.
+ */
+ val ungroupTopUnseen: Boolean
+ get() =
+ if (isUnexpectedlyInLegacyMode()) false
+ else
+ SystemProperties.getBoolean(
+ "persist.notification_minimalism_prototype.ungroup_top_unseen",
+ true
+ )
+
+ /**
+ * Called to ensure code is only run when the flag is enabled. This protects users from the
+ * unintended behaviors caused by accidentally running new logic, while also crashing on an
+ * eng build to ensure that the refactor author catches issues in testing.
+ */
+ @JvmStatic
+ inline fun isUnexpectedlyInLegacyMode() =
+ RefactorFlagUtils.isUnexpectedlyInLegacyMode(isEnabled, FLAG_NAME)
+
+ /**
+ * Called to ensure code is only run when the flag is disabled. This will throw an exception
+ * if the flag is enabled to ensure that the refactor author catches issues in testing.
+ */
+ @JvmStatic
+ inline fun assertInLegacyMode() = RefactorFlagUtils.assertInLegacyMode(isEnabled, FLAG_NAME)
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/shared/PriorityPeopleSection.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/shared/PriorityPeopleSection.kt
new file mode 100644
index 0000000..472fd95
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/shared/PriorityPeopleSection.kt
@@ -0,0 +1,51 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.systemui.statusbar.notification.shared
+
+import com.android.systemui.Flags
+import com.android.systemui.flags.FlagToken
+import com.android.systemui.flags.RefactorFlagUtils
+
+/** Helper for com.android.systemui.Flags.FLAG_PRIORITY_PEOPLE_SECTION */
+@Suppress("NOTHING_TO_INLINE")
+object PriorityPeopleSection {
+ const val FLAG_NAME = Flags.FLAG_PRIORITY_PEOPLE_SECTION
+
+ /** A token used for dependency declaration */
+ val token: FlagToken
+ get() = FlagToken(FLAG_NAME, isEnabled)
+
+ /** Are sections sorted by time? */
+ @JvmStatic
+ inline val isEnabled
+ get() = Flags.priorityPeopleSection()
+
+ /**
+ * Called to ensure code is only run when the flag is enabled. This protects users from the
+ * unintended behaviors caused by accidentally running new logic, while also crashing on an eng
+ * build to ensure that the refactor author catches issues in testing.
+ */
+ @JvmStatic
+ inline fun isUnexpectedlyInLegacyMode() =
+ RefactorFlagUtils.isUnexpectedlyInLegacyMode(isEnabled, FLAG_NAME)
+
+ /**
+ * Called to ensure code is only run when the flag is disabled. This will throw an exception if
+ * the flag is enabled to ensure that the refactor author catches issues in testing.
+ */
+ @JvmStatic
+ inline fun assertInLegacyMode() = RefactorFlagUtils.assertInLegacyMode(isEnabled, FLAG_NAME)
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationChildrenContainer.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationChildrenContainer.java
index 92c597c..48796d8 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationChildrenContainer.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationChildrenContainer.java
@@ -439,6 +439,15 @@
Trace.endSection();
}
+ /**
+ * Update the expand state of the group header.
+ */
+ public void updateGroupHeaderExpandState() {
+ if (mGroupHeaderWrapper != null) {
+ mGroupHeaderWrapper.setExpanded(mChildrenExpanded);
+ }
+ }
+
private void removeGroupHeader() {
if (mGroupHeader == null) {
return;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationPriorityBucket.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationPriorityBucket.kt
index 31f4857..fc28a99 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationPriorityBucket.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationPriorityBucket.kt
@@ -3,15 +3,22 @@
import android.annotation.IntDef
/**
- * For now, declare the available notification buckets (sections) here so that other
- * presentation code can decide what to do based on an entry's buckets
+ * For now, declare the available notification buckets (sections) here so that other presentation
+ * code can decide what to do based on an entry's buckets
*/
@Retention(AnnotationRetention.SOURCE)
@IntDef(
- prefix = ["BUCKET_"],
- value = [
- BUCKET_UNKNOWN, BUCKET_MEDIA_CONTROLS, BUCKET_HEADS_UP, BUCKET_FOREGROUND_SERVICE,
- BUCKET_PEOPLE, BUCKET_ALERTING, BUCKET_SILENT
+ prefix = ["BUCKET_"],
+ value =
+ [
+ BUCKET_UNKNOWN,
+ BUCKET_MEDIA_CONTROLS,
+ BUCKET_HEADS_UP,
+ BUCKET_FOREGROUND_SERVICE,
+ BUCKET_PRIORITY_PEOPLE,
+ BUCKET_PEOPLE,
+ BUCKET_ALERTING,
+ BUCKET_SILENT
]
)
annotation class PriorityBucket
@@ -20,6 +27,7 @@
const val BUCKET_MEDIA_CONTROLS = 1
const val BUCKET_HEADS_UP = 2
const val BUCKET_FOREGROUND_SERVICE = 3
+const val BUCKET_PRIORITY_PEOPLE = 7
const val BUCKET_PEOPLE = 4
const val BUCKET_ALERTING = 5
const val BUCKET_SILENT = 6
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationSection.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationSection.java
index 2d0395a..5a433a1 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationSection.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationSection.java
@@ -16,17 +16,6 @@
package com.android.systemui.statusbar.notification.stack;
-import static com.android.systemui.statusbar.notification.stack.NotificationPriorityBucketKt.BUCKET_MEDIA_CONTROLS;
-
-import android.animation.Animator;
-import android.animation.AnimatorListenerAdapter;
-import android.animation.ObjectAnimator;
-import android.animation.PropertyValuesHolder;
-import android.graphics.Rect;
-import android.view.View;
-import android.view.animation.Interpolator;
-
-import com.android.app.animation.Interpolators;
import com.android.systemui.statusbar.notification.row.ExpandableView;
/**
@@ -35,165 +24,18 @@
*/
public class NotificationSection {
private @PriorityBucket final int mBucket;
- private final View mOwningView;
- private final Rect mBounds = new Rect();
- private final Rect mCurrentBounds = new Rect(-1, -1, -1, -1);
- private final Rect mStartAnimationRect = new Rect();
- private final Rect mEndAnimationRect = new Rect();
- private ObjectAnimator mTopAnimator = null;
- private ObjectAnimator mBottomAnimator = null;
private ExpandableView mFirstVisibleChild;
private ExpandableView mLastVisibleChild;
- NotificationSection(View owningView, @PriorityBucket int bucket) {
- mOwningView = owningView;
+ NotificationSection(@PriorityBucket int bucket) {
mBucket = bucket;
}
- public void cancelAnimators() {
- if (mBottomAnimator != null) {
- mBottomAnimator.cancel();
- }
- if (mTopAnimator != null) {
- mTopAnimator.cancel();
- }
- }
-
- public Rect getCurrentBounds() {
- return mCurrentBounds;
- }
-
- public Rect getBounds() {
- return mBounds;
- }
-
- public boolean didBoundsChange() {
- return !mCurrentBounds.equals(mBounds);
- }
-
- public boolean areBoundsAnimating() {
- return mBottomAnimator != null || mTopAnimator != null;
- }
-
@PriorityBucket
public int getBucket() {
return mBucket;
}
- public void startBackgroundAnimation(boolean animateTop, boolean animateBottom) {
- // Left and right bounds are always applied immediately.
- mCurrentBounds.left = mBounds.left;
- mCurrentBounds.right = mBounds.right;
- startBottomAnimation(animateBottom);
- startTopAnimation(animateTop);
- }
-
-
- private void startTopAnimation(boolean animate) {
- int previousEndValue = mEndAnimationRect.top;
- int newEndValue = mBounds.top;
- ObjectAnimator previousAnimator = mTopAnimator;
- if (previousAnimator != null && previousEndValue == newEndValue) {
- return;
- }
- if (!animate) {
- // just a local update was performed
- if (previousAnimator != null) {
- // we need to increase all animation keyframes of the previous animator by the
- // relative change to the end value
- int previousStartValue = mStartAnimationRect.top;
- PropertyValuesHolder[] values = previousAnimator.getValues();
- values[0].setIntValues(previousStartValue, newEndValue);
- mStartAnimationRect.top = previousStartValue;
- mEndAnimationRect.top = newEndValue;
- previousAnimator.setCurrentPlayTime(previousAnimator.getCurrentPlayTime());
- return;
- } else {
- // no new animation needed, let's just apply the value
- setBackgroundTop(newEndValue);
- return;
- }
- }
- if (previousAnimator != null) {
- previousAnimator.cancel();
- }
- ObjectAnimator animator = ObjectAnimator.ofInt(this, "backgroundTop",
- mCurrentBounds.top, newEndValue);
- Interpolator interpolator = Interpolators.FAST_OUT_SLOW_IN;
- animator.setInterpolator(interpolator);
- animator.setDuration(StackStateAnimator.ANIMATION_DURATION_STANDARD);
- // remove the tag when the animation is finished
- animator.addListener(new AnimatorListenerAdapter() {
- @Override
- public void onAnimationEnd(Animator animation) {
- mStartAnimationRect.top = -1;
- mEndAnimationRect.top = -1;
- mTopAnimator = null;
- }
- });
- animator.start();
- mStartAnimationRect.top = mCurrentBounds.top;
- mEndAnimationRect.top = newEndValue;
- mTopAnimator = animator;
- }
-
- private void startBottomAnimation(boolean animate) {
- int previousStartValue = mStartAnimationRect.bottom;
- int previousEndValue = mEndAnimationRect.bottom;
- int newEndValue = mBounds.bottom;
- ObjectAnimator previousAnimator = mBottomAnimator;
- if (previousAnimator != null && previousEndValue == newEndValue) {
- return;
- }
- if (!animate) {
- // just a local update was performed
- if (previousAnimator != null) {
- // we need to increase all animation keyframes of the previous animator by the
- // relative change to the end value
- PropertyValuesHolder[] values = previousAnimator.getValues();
- values[0].setIntValues(previousStartValue, newEndValue);
- mStartAnimationRect.bottom = previousStartValue;
- mEndAnimationRect.bottom = newEndValue;
- previousAnimator.setCurrentPlayTime(previousAnimator.getCurrentPlayTime());
- return;
- } else {
- // no new animation needed, let's just apply the value
- setBackgroundBottom(newEndValue);
- return;
- }
- }
- if (previousAnimator != null) {
- previousAnimator.cancel();
- }
- ObjectAnimator animator = ObjectAnimator.ofInt(this, "backgroundBottom",
- mCurrentBounds.bottom, newEndValue);
- Interpolator interpolator = Interpolators.FAST_OUT_SLOW_IN;
- animator.setInterpolator(interpolator);
- animator.setDuration(StackStateAnimator.ANIMATION_DURATION_STANDARD);
- // remove the tag when the animation is finished
- animator.addListener(new AnimatorListenerAdapter() {
- @Override
- public void onAnimationEnd(Animator animation) {
- mStartAnimationRect.bottom = -1;
- mEndAnimationRect.bottom = -1;
- mBottomAnimator = null;
- }
- });
- animator.start();
- mStartAnimationRect.bottom = mCurrentBounds.bottom;
- mEndAnimationRect.bottom = newEndValue;
- mBottomAnimator = animator;
- }
-
- private void setBackgroundTop(int top) {
- mCurrentBounds.top = top;
- mOwningView.invalidate();
- }
-
- private void setBackgroundBottom(int bottom) {
- mCurrentBounds.bottom = bottom;
- mOwningView.invalidate();
- }
public ExpandableView getFirstVisibleChild() {
return mFirstVisibleChild;
@@ -215,91 +57,4 @@
return changed;
}
- public void resetCurrentBounds() {
- mCurrentBounds.set(mBounds);
- }
-
- /**
- * Returns true if {@code top} is equal to the top of this section (if not currently animating)
- * or where the top of this section will be when animation completes.
- */
- public boolean isTargetTop(int top) {
- return (mTopAnimator == null && mCurrentBounds.top == top)
- || (mTopAnimator != null && mEndAnimationRect.top == top);
- }
-
- /**
- * Returns true if {@code bottom} is equal to the bottom of this section (if not currently
- * animating) or where the bottom of this section will be when animation completes.
- */
- public boolean isTargetBottom(int bottom) {
- return (mBottomAnimator == null && mCurrentBounds.bottom == bottom)
- || (mBottomAnimator != null && mEndAnimationRect.bottom == bottom);
- }
-
- /**
- * Update the bounds of this section based on it's views
- *
- * @param minTopPosition the minimum position that the top needs to have
- * @param minBottomPosition the minimum position that the bottom needs to have
- * @return the position of the new bottom
- */
- public int updateBounds(int minTopPosition, int minBottomPosition,
- boolean shiftBackgroundWithFirst) {
- int top = minTopPosition;
- int bottom = minTopPosition;
- ExpandableView firstView = getFirstVisibleChild();
- if (firstView != null) {
- // Round Y up to avoid seeing the background during animation
- int finalTranslationY = (int) Math.ceil(ViewState.getFinalTranslationY(firstView));
- // TODO: look into the already animating part
- int newTop;
- if (isTargetTop(finalTranslationY)) {
- // we're ending up at the same location as we are now, let's just skip the
- // animation
- newTop = finalTranslationY;
- } else {
- newTop = (int) Math.ceil(firstView.getTranslationY());
- }
- top = Math.max(newTop, top);
- if (firstView.showingPulsing()) {
- // If we're pulsing, the notification can actually go below!
- bottom = Math.max(bottom, finalTranslationY
- + ExpandableViewState.getFinalActualHeight(firstView));
- if (shiftBackgroundWithFirst) {
- mBounds.left += Math.max(firstView.getTranslation(), 0);
- mBounds.right += Math.min(firstView.getTranslation(), 0);
- }
- }
- }
- ExpandableView lastView = getLastVisibleChild();
- if (lastView != null) {
- float finalTranslationY = ViewState.getFinalTranslationY(lastView);
- int finalHeight = ExpandableViewState.getFinalActualHeight(lastView);
- // Round Y down to avoid seeing the background during animation
- int finalBottom = (int) Math.floor(
- finalTranslationY + finalHeight - lastView.getClipBottomAmount());
- int newBottom;
- if (isTargetBottom(finalBottom)) {
- // we're ending up at the same location as we are now, lets just skip the animation
- newBottom = finalBottom;
- } else {
- newBottom = (int) (lastView.getTranslationY() + lastView.getActualHeight()
- - lastView.getClipBottomAmount());
- // The background can never be lower than the end of the last view
- minBottomPosition = (int) Math.min(
- lastView.getTranslationY() + lastView.getActualHeight(),
- minBottomPosition);
- }
- bottom = Math.max(bottom, Math.max(newBottom, minBottomPosition));
- }
- bottom = Math.max(top, bottom);
- mBounds.top = top;
- mBounds.bottom = bottom;
- return bottom;
- }
-
- public boolean needsBackground() {
- return mFirstVisibleChild != null && mBucket != BUCKET_MEDIA_CONTROLS;
- }
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationSectionsManager.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationSectionsManager.kt
index d269eda..3400ad1 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationSectionsManager.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationSectionsManager.kt
@@ -40,7 +40,9 @@
*
* TODO: Move remaining sections logic from NSSL into this class.
*/
-class NotificationSectionsManager @Inject internal constructor(
+class NotificationSectionsManager
+@Inject
+internal constructor(
private val configurationController: ConfigurationController,
private val keyguardMediaController: KeyguardMediaController,
private val sectionsFeatureManager: NotificationSectionsFeatureManager,
@@ -52,11 +54,12 @@
@SilentHeader private val silentHeaderController: SectionHeaderController
) : SectionProvider {
- private val configurationListener = object : ConfigurationController.ConfigurationListener {
- override fun onLocaleListChanged() {
- reinflateViews()
+ private val configurationListener =
+ object : ConfigurationController.ConfigurationListener {
+ override fun onLocaleListChanged() {
+ reinflateViews()
+ }
}
- }
private lateinit var parent: NotificationStackScrollLayout
private var initialized = false
@@ -81,7 +84,7 @@
val mediaControlsView: MediaContainerView?
get() = mediaContainerController.mediaContainerView
- /** Must be called before use. */
+ /** Must be called before use. */
fun initialize(parent: NotificationStackScrollLayout) {
check(!initialized) { "NotificationSectionsManager already initialized" }
initialized = true
@@ -91,13 +94,12 @@
}
fun createSectionsForBuckets(): Array<NotificationSection> =
- sectionsFeatureManager.getNotificationBuckets()
- .map { NotificationSection(parent, it) }
- .toTypedArray()
+ sectionsFeatureManager
+ .getNotificationBuckets()
+ .map { NotificationSection(it) }
+ .toTypedArray()
- /**
- * Reinflates the entire notification header, including all decoration views.
- */
+ /** Reinflates the entire notification header, including all decoration views. */
fun reinflateViews() {
silentHeaderController.reinflateView(parent)
alertingHeaderController.reinflateView(parent)
@@ -108,44 +110,44 @@
}
override fun beginsSection(view: View, previous: View?): Boolean =
- view === silentHeaderView ||
+ view === silentHeaderView ||
view === mediaControlsView ||
view === peopleHeaderView ||
view === alertingHeaderView ||
view === incomingHeaderView ||
getBucket(view) != getBucket(previous)
- private fun getBucket(view: View?): Int? = when {
- view === silentHeaderView -> BUCKET_SILENT
- view === incomingHeaderView -> BUCKET_HEADS_UP
- view === mediaControlsView -> BUCKET_MEDIA_CONTROLS
- view === peopleHeaderView -> BUCKET_PEOPLE
- view === alertingHeaderView -> BUCKET_ALERTING
- view is ExpandableNotificationRow -> view.entry.bucket
- else -> null
- }
+ private fun getBucket(view: View?): Int? =
+ when {
+ view === silentHeaderView -> BUCKET_SILENT
+ view === incomingHeaderView -> BUCKET_HEADS_UP
+ view === mediaControlsView -> BUCKET_MEDIA_CONTROLS
+ view === peopleHeaderView -> BUCKET_PEOPLE
+ view === alertingHeaderView -> BUCKET_ALERTING
+ view is ExpandableNotificationRow -> view.entry.bucket
+ else -> null
+ }
private sealed class SectionBounds {
- data class Many(
- val first: ExpandableView,
- val last: ExpandableView
- ) : SectionBounds()
+ data class Many(val first: ExpandableView, val last: ExpandableView) : SectionBounds()
data class One(val lone: ExpandableView) : SectionBounds()
object None : SectionBounds()
- fun addNotif(notif: ExpandableView): SectionBounds = when (this) {
- is None -> One(notif)
- is One -> Many(lone, notif)
- is Many -> copy(last = notif)
- }
+ fun addNotif(notif: ExpandableView): SectionBounds =
+ when (this) {
+ is None -> One(notif)
+ is One -> Many(lone, notif)
+ is Many -> copy(last = notif)
+ }
- fun updateSection(section: NotificationSection): Boolean = when (this) {
- is None -> section.setFirstAndLastVisibleChildren(null, null)
- is One -> section.setFirstAndLastVisibleChildren(lone, lone)
- is Many -> section.setFirstAndLastVisibleChildren(first, last)
- }
+ fun updateSection(section: NotificationSection): Boolean =
+ when (this) {
+ is None -> section.setFirstAndLastVisibleChildren(null, null)
+ is One -> section.setFirstAndLastVisibleChildren(lone, lone)
+ is Many -> section.setFirstAndLastVisibleChildren(first, last)
+ }
private fun NotificationSection.setFirstAndLastVisibleChildren(
first: ExpandableView?,
@@ -167,17 +169,19 @@
children: List<ExpandableView>
): Boolean {
// Create mapping of bucket to section
- val sectionBounds = children.asSequence()
+ val sectionBounds =
+ children
+ .asSequence()
// Group children by bucket
.groupingBy {
getBucket(it)
- ?: throw IllegalArgumentException("Cannot find section bucket for view")
+ ?: throw IllegalArgumentException("Cannot find section bucket for view")
}
// Combine each bucket into a SectionBoundary
.foldToSparseArray(
- SectionBounds.None,
- size = sections.size,
- operation = SectionBounds::addNotif
+ SectionBounds.None,
+ size = sections.size,
+ operation = SectionBounds::addNotif
)
// Build a set of the old first/last Views of the sections
@@ -185,11 +189,12 @@
val oldLastChildren = sections.mapNotNull { it.lastVisibleChild }.toSet().toMutableSet()
// Update each section with the associated boundary, tracking if there was a change
- val changed = sections.fold(false) { changed, section ->
- val bounds = sectionBounds[section.bucket] ?: SectionBounds.None
- val isSectionChanged = bounds.updateSection(section)
- isSectionChanged || changed
- }
+ val changed =
+ sections.fold(false) { changed, section ->
+ val bounds = sectionBounds[section.bucket] ?: SectionBounds.None
+ val isSectionChanged = bounds.updateSection(section)
+ isSectionChanged || changed
+ }
val newFirstChildren = sections.mapNotNull { it.firstVisibleChild }
val newLastChildren = sections.mapNotNull { it.lastVisibleChild }
@@ -229,16 +234,18 @@
private fun logSections(sections: Array<NotificationSection>) {
for (i in sections.indices) {
val s = sections[i]
- val fs = when (val first = s.firstVisibleChild) {
- null -> "(null)"
- is ExpandableNotificationRow -> first.entry.key
- else -> Integer.toHexString(System.identityHashCode(first))
- }
- val ls = when (val last = s.lastVisibleChild) {
- null -> "(null)"
- is ExpandableNotificationRow -> last.entry.key
- else -> Integer.toHexString(System.identityHashCode(last))
- }
+ val fs =
+ when (val first = s.firstVisibleChild) {
+ null -> "(null)"
+ is ExpandableNotificationRow -> first.entry.key
+ else -> Integer.toHexString(System.identityHashCode(first))
+ }
+ val ls =
+ when (val last = s.lastVisibleChild) {
+ null -> "(null)"
+ is ExpandableNotificationRow -> last.entry.key
+ else -> Integer.toHexString(System.identityHashCode(last))
+ }
Log.d(TAG, "updateSections: f=$fs s=$i")
Log.d(TAG, "updateSections: l=$ls s=$i")
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java
index abbe7d7..a9d7cc0 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java
@@ -674,7 +674,7 @@
void setOverExpansion(float margin) {
mAmbientState.setOverExpansion(margin);
if (notificationOverExpansionClippingFix() && !SceneContainerFlag.isEnabled()) {
- setRoundingClippingYTranslation((int) margin);
+ setRoundingClippingYTranslation(mShouldUseSplitNotificationShade ? (int) margin : 0);
}
updateStackPosition();
requestChildrenUpdate();
@@ -2974,7 +2974,7 @@
private void updateFirstAndLastBackgroundViews() {
ExpandableView lastChild = getLastChildWithBackground();
- boolean sectionViewsChanged = mSectionsManager.updateFirstAndLastViewsForAllSections(
+ mSectionsManager.updateFirstAndLastViewsForAllSections(
mSections, getChildrenWithBackground());
mAmbientState.setLastVisibleBackgroundChild(lastChild);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutController.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutController.java
index c1c63cd..6a3055f 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutController.java
@@ -23,6 +23,7 @@
import static com.android.internal.jank.InteractionJankMonitor.CUJ_NOTIFICATION_SHADE_SCROLL_FLING;
import static com.android.server.notification.Flags.screenshareNotificationHiding;
import static com.android.systemui.Dependency.ALLOW_NOTIFICATION_LONG_PRESS_NAME;
+import static com.android.systemui.Flags.confineNotificationTouchToViewWidth;
import static com.android.systemui.statusbar.StatusBarState.KEYGUARD;
import static com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayout.OnEmptySpaceClickListener;
import static com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayout.OnOverscrollTopChangedListener;
@@ -597,7 +598,7 @@
ev.getY(),
true /* requireMinHeight */,
false /* ignoreDecors */,
- true /* ignoreWidth */);
+ !confineNotificationTouchToViewWidth() /* ignoreWidth */);
if (child instanceof ExpandableNotificationRow row) {
ExpandableNotificationRow parent = row.getNotificationParent();
if (parent != null && parent.areChildrenExpanded()
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackSizeCalculator.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackSizeCalculator.kt
index 5bd4c75..4b0b1e0 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackSizeCalculator.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackSizeCalculator.kt
@@ -17,11 +17,9 @@
package com.android.systemui.statusbar.notification.stack
import android.content.res.Resources
-import android.os.SystemProperties
import android.util.Log
import android.view.View.GONE
import androidx.annotation.VisibleForTesting
-import com.android.systemui.Flags.notificationMinimalismPrototype
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Main
import com.android.systemui.media.controls.domain.pipeline.MediaDataManager
@@ -31,6 +29,7 @@
import com.android.systemui.statusbar.SysuiStatusBarStateController
import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow
import com.android.systemui.statusbar.notification.row.ExpandableView
+import com.android.systemui.statusbar.notification.shared.NotificationMinimalismPrototype
import com.android.systemui.statusbar.policy.SplitShadeStateController
import com.android.systemui.util.Compile
import com.android.systemui.util.children
@@ -73,6 +72,9 @@
*/
private var maxNotificationsExcludesMedia = false
+ /** Whether we allow keyguard to show less important notifications above the shelf. */
+ private var limitLockScreenToImportant = false
+
/** Minimum space between two notifications, see [calculateGapAndDividerHeight]. */
private var dividerHeight by notNull<Float>()
@@ -86,6 +88,14 @@
updateResources()
}
+ private fun allowedByPolicy(stackHeight: StackHeight): Boolean =
+ if (limitLockScreenToImportant && stackHeight.includesLessImportantNotification) {
+ log { "\tallowedByPolicy = false" }
+ false
+ } else {
+ true
+ }
+
/**
* Returns whether notifications and (shelf if visible) can fit in total space available.
* [shelfSpace] is extra vertical space allowed for the shelf to overlap the lock icon.
@@ -184,11 +194,12 @@
log { "\tGet maxNotifWithoutSavingSpace ---" }
val maxNotifWithoutSavingSpace =
stackHeightSequence.lastIndexWhile { heightResult ->
- canStackFitInSpace(
- heightResult,
- notifSpace = notifSpace,
- shelfSpace = shelfSpace
- ) == FitResult.FIT
+ allowedByPolicy(heightResult) &&
+ canStackFitInSpace(
+ heightResult,
+ notifSpace = notifSpace,
+ shelfSpace = shelfSpace
+ ) == FitResult.FIT
}
// How many notifications we can show at heightWithoutLockscreenConstraints
@@ -213,11 +224,12 @@
saveSpaceOnLockscreen = true
maxNotifications =
stackHeightSequence.lastIndexWhile { heightResult ->
- canStackFitInSpace(
- heightResult,
- notifSpace = notifSpace,
- shelfSpace = shelfSpace
- ) != FitResult.NO_FIT
+ allowedByPolicy(heightResult) &&
+ canStackFitInSpace(
+ heightResult,
+ notifSpace = notifSpace,
+ shelfSpace = shelfSpace
+ ) != FitResult.NO_FIT
}
log { "\t--- maxNotifications=$maxNotifications" }
}
@@ -319,7 +331,10 @@
// Float height of shelf (0 if shelf is not showing), and space before the shelf that
// changes during the lockscreen <=> full shade transition.
- val shelfHeightWithSpaceBefore: Float
+ val shelfHeightWithSpaceBefore: Float,
+
+ /** Whether this stack height includes less at least one important notification. */
+ val includesLessImportantNotification: Boolean
)
private fun computeHeightPerNotificationLimit(
@@ -332,12 +347,15 @@
var previous: ExpandableView? = null
val onLockscreen = onLockscreen()
+ var includesLessImportantNotification = false
+
// Only shelf. This should never happen, since we allow 1 view minimum (EmptyViewState).
yield(
StackHeight(
notifsHeight = 0f,
notifsHeightSavingSpace = 0f,
- shelfHeightWithSpaceBefore = shelfHeight
+ shelfHeightWithSpaceBefore = shelfHeight,
+ includesLessImportantNotification = includesLessImportantNotification,
)
)
@@ -363,6 +381,19 @@
spaceBeforeShelf + shelfHeight
}
+ if (limitLockScreenToImportant && !includesLessImportantNotification) {
+ val bucket = (currentNotification as? ExpandableNotificationRow)?.entry?.bucket
+ includesLessImportantNotification =
+ when (bucket) {
+ null,
+ BUCKET_MEDIA_CONTROLS,
+ BUCKET_HEADS_UP,
+ BUCKET_FOREGROUND_SERVICE,
+ BUCKET_PRIORITY_PEOPLE -> false
+ else -> true
+ }
+ }
+
log {
"\tcomputeHeightPerNotificationLimit i=$i notifs=$notifications " +
"notifsHeightSavingSpace=$notifsWithCollapsedHun" +
@@ -372,7 +403,8 @@
StackHeight(
notifsHeight = notifications,
notifsHeightSavingSpace = notifsWithCollapsedHun,
- shelfHeightWithSpaceBefore = shelfWithSpaceBefore
+ shelfHeightWithSpaceBefore = shelfWithSpaceBefore,
+ includesLessImportantNotification = includesLessImportantNotification,
)
)
}
@@ -381,16 +413,18 @@
fun updateResources() {
maxKeyguardNotifications =
infiniteIfNegative(
- if (notificationMinimalismPrototype()) {
- SystemProperties.getInt(
- "persist.notification_minimalism_prototype.lock_screen_max_notifs",
- 1
- )
+ if (NotificationMinimalismPrototype.V1.isEnabled) {
+ NotificationMinimalismPrototype.V1.maxNotifs
+ } else if (NotificationMinimalismPrototype.V2.isEnabled) {
+ 1
} else {
resources.getInteger(R.integer.keyguard_max_notification_count)
}
)
- maxNotificationsExcludesMedia = notificationMinimalismPrototype()
+ maxNotificationsExcludesMedia =
+ NotificationMinimalismPrototype.V1.isEnabled ||
+ NotificationMinimalismPrototype.V2.isEnabled
+ limitLockScreenToImportant = NotificationMinimalismPrototype.V2.isEnabled
dividerHeight =
max(1f, resources.getDimensionPixelSize(R.dimen.notification_divider_height).toFloat())
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/data/repository/NotificationPlaceholderRepository.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/data/repository/NotificationPlaceholderRepository.kt
index dacafc4..db544ce 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/data/repository/NotificationPlaceholderRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/data/repository/NotificationPlaceholderRepository.kt
@@ -38,16 +38,6 @@
*/
val shadeScrimBounds = MutableStateFlow<ShadeScrimBounds?>(null)
- /**
- * The y-coordinate in px of top of the contents of the notification stack. This value can be
- * negative, if the stack is scrolled such that its top extends beyond the top edge of the
- * screen.
- */
- val stackTop = MutableStateFlow(0f)
-
- /** the bottom-most acceptable y-position for the bottom of the stack / shelf */
- val stackBottom = MutableStateFlow(0f)
-
/** the y position of the top of the HUN area */
val headsUpTop = MutableStateFlow(0f)
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/domain/interactor/NotificationStackAppearanceInteractor.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/domain/interactor/NotificationStackAppearanceInteractor.kt
index 365ead6..e7acbe3 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/domain/interactor/NotificationStackAppearanceInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/domain/interactor/NotificationStackAppearanceInteractor.kt
@@ -72,12 +72,6 @@
val alphaForBrightnessMirror: StateFlow<Float> =
placeholderRepository.alphaForBrightnessMirror.asStateFlow()
- /** The y-coordinate in px of top of the contents of the notification stack. */
- val stackTop: StateFlow<Float> = placeholderRepository.stackTop.asStateFlow()
-
- /** The y-coordinate in px of bottom of the contents of the notification stack. */
- val stackBottom: StateFlow<Float> = placeholderRepository.stackBottom.asStateFlow()
-
/** The height of the keyguard's available space bounds */
val constrainedAvailableSpace: StateFlow<Int> =
placeholderRepository.constrainedAvailableSpace.asStateFlow()
@@ -121,16 +115,6 @@
viewHeightRepository.headsUpHeight.value = height
}
- /** Sets the y-coord in px of the top of the contents of the notification stack. */
- fun setStackTop(stackTop: Float) {
- placeholderRepository.stackTop.value = stackTop
- }
-
- /** Sets the y-coord in px of the bottom of the contents of the notification stack. */
- fun setStackBottom(stackBottom: Float) {
- placeholderRepository.stackBottom.value = stackBottom
- }
-
/** Sets whether the notification stack is scrolled to the top. */
fun setScrolledToTop(scrolledToTop: Boolean) {
placeholderRepository.scrolledToTop.value = scrolledToTop
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewbinder/NotificationScrollViewBinder.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewbinder/NotificationScrollViewBinder.kt
index 3c44713..622d8e7 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewbinder/NotificationScrollViewBinder.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewbinder/NotificationScrollViewBinder.kt
@@ -79,8 +79,6 @@
}
launch { viewModel.maxAlpha.collect { view.setMaxAlpha(it) } }
- launch { viewModel.stackTop.collect { view.setStackTop(it) } }
- launch { viewModel.stackBottom.collect { view.setStackBottom(it) } }
launch { viewModel.scrolledToTop.collect { view.setScrolledToTop(it) } }
launch { viewModel.headsUpTop.collect { view.setHeadsUpTop(it) } }
launch { viewModel.expandFraction.collect { view.setExpandFraction(it.coerceIn(0f, 1f)) } }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/NotificationScrollViewModel.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/NotificationScrollViewModel.kt
index 082f6b6..6137381 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/NotificationScrollViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/NotificationScrollViewModel.kt
@@ -130,10 +130,6 @@
val maxAlpha: Flow<Float> =
stackAppearanceInteractor.alphaForBrightnessMirror.dumpValue("maxAlpha")
- /** The y-coordinate in px of top of the contents of the notification stack. */
- val stackTop: Flow<Float> = stackAppearanceInteractor.stackTop.dumpValue("stackTop")
- /** The y-coordinate in px of bottom of the contents of the notification stack. */
- val stackBottom: Flow<Float> = stackAppearanceInteractor.stackBottom.dumpValue("stackBottom")
/**
* Whether the notification stack is scrolled to the top; i.e., it cannot be scrolled down any
* further.
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/NotificationsPlaceholderViewModel.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/NotificationsPlaceholderViewModel.kt
index 736058a..97b86e3 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/NotificationsPlaceholderViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/NotificationsPlaceholderViewModel.kt
@@ -16,7 +16,6 @@
package com.android.systemui.statusbar.notification.stack.ui.viewmodel
-import com.android.systemui.common.shared.model.NotificationContainerBounds
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dump.DumpManager
import com.android.systemui.flags.FeatureFlagsClassic
@@ -57,18 +56,6 @@
interactor.setShadeScrimBounds(bounds)
}
- /** Notifies that the bounds of the notification placeholder have changed. */
- fun onStackBoundsChanged(
- top: Float,
- bottom: Float,
- ) {
- keyguardInteractor.setNotificationContainerBounds(
- NotificationContainerBounds(top = top, bottom = bottom)
- )
- interactor.setStackTop(top)
- interactor.setStackBottom(bottom)
- }
-
/** Sets the available space */
fun onConstrainedAvailableSpaceChanged(height: Int) {
interactor.setConstrainedAvailableSpace(height)
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/SharedNotificationContainerViewModel.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/SharedNotificationContainerViewModel.kt
index 0ba7b3c..3393321 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/SharedNotificationContainerViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/SharedNotificationContainerViewModel.kt
@@ -26,6 +26,7 @@
import com.android.systemui.dump.DumpManager
import com.android.systemui.keyguard.domain.interactor.KeyguardInteractor
import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor
+import com.android.systemui.keyguard.shared.model.Edge
import com.android.systemui.keyguard.shared.model.KeyguardState
import com.android.systemui.keyguard.shared.model.KeyguardState.ALTERNATE_BOUNCER
import com.android.systemui.keyguard.shared.model.KeyguardState.AOD
@@ -64,6 +65,7 @@
import com.android.systemui.keyguard.ui.viewmodel.PrimaryBouncerToLockscreenTransitionViewModel
import com.android.systemui.keyguard.ui.viewmodel.ViewStateAccessor
import com.android.systemui.scene.shared.flag.SceneContainerFlag
+import com.android.systemui.scene.shared.model.Scenes
import com.android.systemui.shade.domain.interactor.ShadeInteractor
import com.android.systemui.statusbar.notification.stack.domain.interactor.NotificationStackAppearanceInteractor
import com.android.systemui.statusbar.notification.stack.domain.interactor.SharedNotificationContainerInteractor
@@ -295,8 +297,7 @@
return combine(
isOnLockscreenWithoutShade,
keyguardTransitionInteractor.isInTransition(
- from = LOCKSCREEN,
- to = AOD,
+ edge = Edge.create(from = LOCKSCREEN, to = AOD)
),
::Pair
)
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfaces.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfaces.java
index 7d97428..8fb552f 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfaces.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfaces.java
@@ -283,12 +283,11 @@
void awakenDreams();
/**
- * Handle a touch event while dreaming or on the glanceable hub when the touch was initiated
- * within a prescribed swipeable area. This method is provided for cases where swiping in
- * certain areas should be handled by CentralSurfaces instead (e.g. swiping hub open, opening
- * the notification shade over dream or hub).
+ * Handle a touch event while dreaming when the touch was initiated within a prescribed
+ * swipeable area. This method is provided for cases where swiping in certain areas of a dream
+ * should be handled by CentralSurfaces instead (e.g. swiping communal hub open).
*/
- void handleExternalShadeWindowTouch(MotionEvent event);
+ void handleDreamTouch(MotionEvent event);
boolean isBouncerShowing();
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesEmptyImpl.kt b/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesEmptyImpl.kt
index d5e66ff..8af7ee8 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesEmptyImpl.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesEmptyImpl.kt
@@ -79,7 +79,7 @@
override fun updateScrimController() {}
override fun shouldIgnoreTouch() = false
override fun isDeviceInteractive() = false
- override fun handleExternalShadeWindowTouch(event: MotionEvent?) {}
+ override fun handleDreamTouch(event: MotionEvent?) {}
override fun awakenDreams() {}
override fun isBouncerShowing() = false
override fun isBouncerShowingScrimmed() = false
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesImpl.java
index aa55f37..d3d2b1e 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesImpl.java
@@ -2805,7 +2805,16 @@
mScrimController.setExpansionAffectsAlpha(!unlocking);
if (mAlternateBouncerInteractor.isVisibleState()) {
- if (!DeviceEntryUdfpsRefactor.isEnabled()) {
+ if (DeviceEntryUdfpsRefactor.isEnabled()) {
+ if ((!mKeyguardStateController.isOccluded() || mShadeSurface.isPanelExpanded())
+ && (mState == StatusBarState.SHADE || mState == StatusBarState.SHADE_LOCKED
+ || mTransitionToFullShadeProgress > 0f)) {
+ // Assume scrim state for shade is already correct and do nothing
+ } else {
+ // Safeguard which prevents the scrim from being stuck in the wrong state
+ mScrimController.transitionTo(ScrimState.KEYGUARD);
+ }
+ } else {
if ((!mKeyguardStateController.isOccluded() || mShadeSurface.isPanelExpanded())
&& (mState == StatusBarState.SHADE || mState == StatusBarState.SHADE_LOCKED
|| mTransitionToFullShadeProgress > 0f)) {
@@ -2814,7 +2823,6 @@
mScrimController.transitionTo(ScrimState.AUTH_SCRIMMED);
}
}
-
// This will cancel the keyguardFadingAway animation if it is running. We need to do
// this as otherwise it can remain pending and leave keyguard in a weird state.
mUnlockScrimCallback.onCancelled();
@@ -2932,8 +2940,8 @@
};
@Override
- public void handleExternalShadeWindowTouch(MotionEvent event) {
- getNotificationShadeWindowViewController().handleExternalTouch(event);
+ public void handleDreamTouch(MotionEvent event) {
+ getNotificationShadeWindowViewController().handleDreamTouch(event);
}
@Override
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardIndicationTextView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardIndicationTextView.java
index c4e0f31..16e9c71 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardIndicationTextView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardIndicationTextView.java
@@ -219,6 +219,7 @@
}
private void setNextIndication() {
+ boolean forceAssertiveAccessibilityLiveRegion = false;
if (mKeyguardIndicationInfo != null) {
// First, update the style.
// If a background is set on the text, we don't want shadow on the text
@@ -239,8 +240,16 @@
}
}
setCompoundDrawablesRelativeWithIntrinsicBounds(icon, null, null, null);
+ forceAssertiveAccessibilityLiveRegion =
+ mKeyguardIndicationInfo.getForceAssertiveAccessibilityLiveRegion();
+ }
+ if (!forceAssertiveAccessibilityLiveRegion) {
+ setAccessibilityLiveRegion(ACCESSIBILITY_LIVE_REGION_NONE);
}
setText(mMessage);
+ if (forceAssertiveAccessibilityLiveRegion) {
+ setAccessibilityLiveRegion(ACCESSIBILITY_LIVE_REGION_ASSERTIVE);
+ }
if (mAlwaysAnnounceText) {
announceForAccessibility(mMessage);
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardStatusBarViewController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardStatusBarViewController.java
index f219b9d..2b26e3f 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardStatusBarViewController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardStatusBarViewController.java
@@ -54,7 +54,6 @@
import com.android.systemui.scene.shared.flag.SceneContainerFlag;
import com.android.systemui.shade.ShadeViewStateProvider;
import com.android.systemui.statusbar.CommandQueue;
-import com.android.systemui.statusbar.NotificationMediaManager;
import com.android.systemui.statusbar.StatusBarState;
import com.android.systemui.statusbar.SysuiStatusBarStateController;
import com.android.systemui.statusbar.disableflags.DisableStateTracker;
@@ -133,9 +132,6 @@
private View mSystemIconsContainer;
private final StatusOverlayHoverListenerFactory mStatusOverlayHoverListenerFactory;
- // TODO(b/273443374): remove
- private NotificationMediaManager mNotificationMediaManager;
-
private final ConfigurationController.ConfigurationListener mConfigurationListener =
new ConfigurationController.ConfigurationListener() {
@Override
@@ -302,7 +298,6 @@
@Main Executor mainExecutor,
@Background Executor backgroundExecutor,
KeyguardLogger logger,
- NotificationMediaManager notificationMediaManager,
StatusOverlayHoverListenerFactory statusOverlayHoverListenerFactory
) {
super(view);
@@ -357,7 +352,6 @@
/* mask2= */ DISABLE2_SYSTEM_ICONS,
this::updateViewState
);
- mNotificationMediaManager = notificationMediaManager;
mStatusOverlayHoverListenerFactory = statusOverlayHoverListenerFactory;
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java
index 74182fc..fe001b3 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java
@@ -64,6 +64,7 @@
import com.android.systemui.keyguard.KeyguardUnlockAnimationController;
import com.android.systemui.keyguard.domain.interactor.KeyguardInteractor;
import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor;
+import com.android.systemui.keyguard.shared.model.Edge;
import com.android.systemui.keyguard.shared.model.KeyguardState;
import com.android.systemui.keyguard.shared.model.ScrimAlpha;
import com.android.systemui.keyguard.shared.model.TransitionState;
@@ -71,6 +72,7 @@
import com.android.systemui.keyguard.ui.viewmodel.AlternateBouncerToGoneTransitionViewModel;
import com.android.systemui.keyguard.ui.viewmodel.PrimaryBouncerToGoneTransitionViewModel;
import com.android.systemui.res.R;
+import com.android.systemui.scene.shared.model.Scenes;
import com.android.systemui.scrim.ScrimView;
import com.android.systemui.shade.ShadeViewController;
import com.android.systemui.shade.transition.LargeScreenShadeInterpolator;
@@ -454,23 +456,32 @@
};
// PRIMARY_BOUNCER->GONE
- collectFlow(behindScrim, mKeyguardTransitionInteractor.transition(PRIMARY_BOUNCER, GONE),
+ collectFlow(behindScrim, mKeyguardTransitionInteractor.transition(
+ Edge.Companion.create(PRIMARY_BOUNCER, GONE)),
mBouncerToGoneTransition, mMainDispatcher);
collectFlow(behindScrim, mPrimaryBouncerToGoneTransitionViewModel.getScrimAlpha(),
mScrimAlphaConsumer, mMainDispatcher);
// ALTERNATE_BOUNCER->GONE
- collectFlow(behindScrim, mKeyguardTransitionInteractor.transition(ALTERNATE_BOUNCER, GONE),
+ collectFlow(behindScrim, mKeyguardTransitionInteractor.transition(
+ Edge.Companion.create(ALTERNATE_BOUNCER, Scenes.Gone),
+ Edge.Companion.create(ALTERNATE_BOUNCER, GONE)),
mBouncerToGoneTransition, mMainDispatcher);
collectFlow(behindScrim, mAlternateBouncerToGoneTransitionViewModel.getScrimAlpha(),
mScrimAlphaConsumer, mMainDispatcher);
// LOCKSCREEN<->GLANCEABLE_HUB
+ collectFlow(
+ behindScrim,
+ mKeyguardTransitionInteractor.transition(
+ Edge.Companion.create(LOCKSCREEN, Scenes.Communal),
+ Edge.Companion.create(LOCKSCREEN, GLANCEABLE_HUB)),
+ mGlanceableHubConsumer,
+ mMainDispatcher);
collectFlow(behindScrim,
- mKeyguardTransitionInteractor.transition(LOCKSCREEN, GLANCEABLE_HUB),
- mGlanceableHubConsumer, mMainDispatcher);
- collectFlow(behindScrim,
- mKeyguardTransitionInteractor.transition(GLANCEABLE_HUB, LOCKSCREEN),
+ mKeyguardTransitionInteractor.transition(
+ Edge.Companion.create(Scenes.Communal, LOCKSCREEN),
+ Edge.Companion.create(GLANCEABLE_HUB, LOCKSCREEN)),
mGlanceableHubConsumer, mMainDispatcher);
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java
index f0dab3b..d9f88c4 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java
@@ -695,10 +695,6 @@
public void show(Bundle options) {
Trace.beginSection("StatusBarKeyguardViewManager#show");
mNotificationShadeWindowController.setKeyguardShowing(true);
- if (SceneContainerFlag.isEnabled()) {
- mSceneInteractorLazy.get().changeScene(
- Scenes.Lockscreen, "StatusBarKeyguardViewManager.show");
- }
mKeyguardStateController.notifyKeyguardState(true, mKeyguardStateController.isOccluded());
reset(true /* hideBouncerWhenShowing */);
SysUiStatsLog.write(SysUiStatsLog.KEYGUARD_STATE_CHANGED,
@@ -1548,7 +1544,8 @@
}
if (KeyguardWmStateRefactor.isEnabled()) {
- mKeyguardTransitionInteractor.startDismissKeyguardTransition();
+ mKeyguardTransitionInteractor.startDismissKeyguardTransition(
+ "SBKVM#keyguardAuthenticated");
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarRemoteInputCallback.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarRemoteInputCallback.java
index a6284e3..4505a1d 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarRemoteInputCallback.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarRemoteInputCallback.java
@@ -196,7 +196,22 @@
// The group isn't expanded, let's make sure it's visible!
mGroupExpansionManager.toggleGroupExpansion(row.getEntry());
}
- row.setUserExpanded(true);
+
+ if (android.app.Flags.compactHeadsUpNotificationReply()
+ && row.isCompactConversationHeadsUpOnScreen()) {
+ // Notification can be system expanded true and it is set user expanded in
+ // activateRemoteInput. notifyHeightChanged also doesn't work as visibleType doesn't
+ // change. To expand huning notification properly, we need set userExpanded false.
+ if (!row.isPinned() && row.isExpanded()) {
+ row.setUserExpanded(false);
+ }
+ // expand notification emits expanded information to HUN listener.
+ row.expandNotification();
+ } else {
+ // Note: Since Normal HUN has remote input view in it, we don't expect to hit
+ // onMakeExpandedVisibleForRemoteInput from activateRemoteInput for Normal HUN.
+ row.setUserExpanded(true);
+ }
row.getPrivateLayout().setOnExpandedVisibleListener(runnable);
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/fragment/CollapsedStatusBarFragment.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/fragment/CollapsedStatusBarFragment.java
index 4fc11df..a858fb0 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/fragment/CollapsedStatusBarFragment.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/fragment/CollapsedStatusBarFragment.java
@@ -119,7 +119,7 @@
private MultiSourceMinAlphaController mEndSideAlphaController;
private LinearLayout mEndSideContent;
private View mClockView;
- private View mOngoingCallChip;
+ private View mOngoingActivityChip;
private View mNotificationIconAreaInner;
// Visibilities come in from external system callers via disable flags, but we also sometimes
// modify the visibilities internally. We need to store both so that we don't accidentally
@@ -345,7 +345,7 @@
mEndSideContent = mStatusBar.findViewById(R.id.status_bar_end_side_content);
mEndSideAlphaController = new MultiSourceMinAlphaController(mEndSideContent);
mClockView = mStatusBar.findViewById(R.id.clock);
- mOngoingCallChip = mStatusBar.findViewById(R.id.ongoing_call_chip);
+ mOngoingActivityChip = mStatusBar.findViewById(R.id.ongoing_activity_chip);
showEndSideContent(false);
showClock(false);
initOperatorName();
@@ -594,9 +594,9 @@
// so if the icons are disabled then the call chip should be, too.)
boolean showOngoingCallChip = hasOngoingCall && !disableNotifications;
if (showOngoingCallChip) {
- showOngoingCallChip(animate);
+ showOngoingActivityChip(animate);
} else {
- hideOngoingCallChip(animate);
+ hideOngoingActivityChip(animate);
}
mOngoingCallController.notifyChipVisibilityChanged(showOngoingCallChip);
}
@@ -688,14 +688,19 @@
animateShow(mClockView, animate);
}
- /** Hides the ongoing call chip. */
- public void hideOngoingCallChip(boolean animate) {
- animateHiddenState(mOngoingCallChip, View.GONE, animate);
+ /** Hides the ongoing activity chip. */
+ private void hideOngoingActivityChip(boolean animate) {
+ animateHiddenState(mOngoingActivityChip, View.GONE, animate);
}
- /** Displays the ongoing call chip. */
- public void showOngoingCallChip(boolean animate) {
- animateShow(mOngoingCallChip, animate);
+ /**
+ * Displays the ongoing activity chip.
+ *
+ * For now, this chip will only ever contain the ongoing call information, but after b/332662551
+ * feature is implemented, it will support different kinds of ongoing activities.
+ */
+ private void showOngoingActivityChip(boolean animate) {
+ animateShow(mOngoingActivityChip, animate);
}
/**
@@ -803,7 +808,7 @@
private void initOngoingCallChip() {
mOngoingCallController.addCallback(mOngoingCallListener);
- mOngoingCallController.setChipView(mOngoingCallChip);
+ mOngoingCallController.setChipView(mOngoingActivityChip);
}
@Override
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ongoingcall/OngoingCallController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ongoingcall/OngoingCallController.kt
index ec88b6c..a7d4ce3 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ongoingcall/OngoingCallController.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ongoingcall/OngoingCallController.kt
@@ -36,6 +36,8 @@
import com.android.systemui.dagger.qualifiers.Main
import com.android.systemui.dump.DumpManager
import com.android.systemui.plugins.ActivityStarter
+import com.android.systemui.statusbar.chips.ui.view.ChipChronometer
+import com.android.systemui.statusbar.chips.ui.view.ChipBackgroundContainer
import com.android.systemui.statusbar.data.repository.StatusBarModeRepositoryStore
import com.android.systemui.statusbar.gesture.SwipeStatusBarAwayGestureHandler
import com.android.systemui.statusbar.notification.collection.NotificationEntry
@@ -145,8 +147,8 @@
fun setChipView(chipView: View) {
tearDownChipView()
this.chipView = chipView
- val backgroundView: OngoingCallBackgroundContainer? =
- chipView.findViewById(R.id.ongoing_call_chip_background)
+ val backgroundView: ChipBackgroundContainer? =
+ chipView.findViewById(R.id.ongoing_activity_chip_background)
backgroundView?.maxHeightFetcher = { statusBarWindowController.statusBarHeight }
if (hasOngoingCall()) {
updateChip()
@@ -226,7 +228,7 @@
if (callNotificationInfo == null) { return }
val currentChipView = chipView
val backgroundView =
- currentChipView?.findViewById<View>(R.id.ongoing_call_chip_background)
+ currentChipView?.findViewById<View>(R.id.ongoing_activity_chip_background)
val intent = callNotificationInfo?.intent
if (currentChipView != null && backgroundView != null && intent != null) {
currentChipView.setOnClickListener {
@@ -271,8 +273,8 @@
@VisibleForTesting
fun tearDownChipView() = chipView?.getTimeView()?.stop()
- private fun View.getTimeView(): OngoingCallChronometer? {
- return this.findViewById(R.id.ongoing_call_chip_time)
+ private fun View.getTimeView(): ChipChronometer? {
+ return this.findViewById(R.id.ongoing_activity_chip_time)
}
/**
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ongoingcall/data/repository/OngoingCallRepository.kt b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ongoingcall/data/repository/OngoingCallRepository.kt
index 9c78ab4..886481e 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ongoingcall/data/repository/OngoingCallRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ongoingcall/data/repository/OngoingCallRepository.kt
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2023 The Android Open Source Project
+ * Copyright (C) 2024 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/MobileInputLogger.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/MobileInputLogger.kt
index d4b2dbf..2e54972 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/MobileInputLogger.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/MobileInputLogger.kt
@@ -53,6 +53,27 @@
)
}
+ fun logTopLevelServiceStateBroadcastEmergencyOnly(subId: Int, serviceState: ServiceState) {
+ buffer.log(
+ TAG,
+ LogLevel.INFO,
+ {
+ int1 = subId
+ bool1 = serviceState.isEmergencyOnly
+ },
+ { "ACTION_SERVICE_STATE for subId=$int1. ServiceState.isEmergencyOnly=$bool1" }
+ )
+ }
+
+ fun logTopLevelServiceStateBroadcastMissingExtras(subId: Int) {
+ buffer.log(
+ TAG,
+ LogLevel.INFO,
+ { int1 = subId },
+ { "ACTION_SERVICE_STATE for subId=$int1. Intent is missing extras. Ignoring" }
+ )
+ }
+
fun logOnSignalStrengthsChanged(signalStrength: SignalStrength, subId: Int) {
buffer.log(
TAG,
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/model/ServiceStateModel.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/model/ServiceStateModel.kt
new file mode 100644
index 0000000..cce3eb0
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/model/ServiceStateModel.kt
@@ -0,0 +1,31 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.statusbar.pipeline.mobile.data.model
+
+import android.telephony.ServiceState
+
+/**
+ * Simplified representation of a [ServiceState] for use in SystemUI. Add any fields that we need to
+ * extract from service state here for consumption downstream
+ */
+data class ServiceStateModel(val isEmergencyOnly: Boolean) {
+ companion object {
+ fun fromServiceState(serviceState: ServiceState): ServiceStateModel {
+ return ServiceStateModel(isEmergencyOnly = serviceState.isEmergencyOnly)
+ }
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/MobileConnectionsRepository.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/MobileConnectionsRepository.kt
index 9471574..5ad8bf1 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/MobileConnectionsRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/MobileConnectionsRepository.kt
@@ -21,6 +21,7 @@
import com.android.settingslib.SignalIcon.MobileIconGroup
import com.android.settingslib.mobile.MobileMappings
import com.android.settingslib.mobile.MobileMappings.Config
+import com.android.systemui.statusbar.pipeline.mobile.data.model.ServiceStateModel
import com.android.systemui.statusbar.pipeline.mobile.data.model.SubscriptionModel
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.StateFlow
@@ -92,6 +93,19 @@
val defaultMobileIconGroup: Flow<MobileIconGroup>
/**
+ * [deviceServiceState] is equivalent to the last [Intent.ACTION_SERVICE_STATE] broadcast with a
+ * subscriptionId of -1 (aka [SubscriptionManager.INVALID_SUBSCRIPTION_ID]).
+ *
+ * While each [MobileConnectionsRepository] listens for the service state of each subscription,
+ * there is potentially a service state associated with the device itself. This value can be
+ * used to calculate e.g., the emergency calling capability of the device (as opposed to the
+ * emergency calling capability of an individual mobile connection)
+ *
+ * Note: this is a [StateFlow] using an eager sharing strategy.
+ */
+ val deviceServiceState: StateFlow<ServiceStateModel?>
+
+ /**
* If any active SIM on the device is in
* [android.telephony.TelephonyManager.SIM_STATE_PIN_REQUIRED] or
* [android.telephony.TelephonyManager.SIM_STATE_PUK_REQUIRED] or
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/MobileRepositorySwitcher.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/MobileRepositorySwitcher.kt
index 8a8e33e..b068152 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/MobileRepositorySwitcher.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/MobileRepositorySwitcher.kt
@@ -25,6 +25,7 @@
import com.android.systemui.dagger.qualifiers.Application
import com.android.systemui.demomode.DemoMode
import com.android.systemui.demomode.DemoModeController
+import com.android.systemui.statusbar.pipeline.mobile.data.model.ServiceStateModel
import com.android.systemui.statusbar.pipeline.mobile.data.model.SubscriptionModel
import com.android.systemui.statusbar.pipeline.mobile.data.repository.demo.DemoMobileConnectionsRepository
import com.android.systemui.statusbar.pipeline.mobile.data.repository.prod.MobileConnectionsRepositoryImpl
@@ -151,6 +152,15 @@
override val defaultMobileIconGroup: Flow<SignalIcon.MobileIconGroup> =
activeRepo.flatMapLatest { it.defaultMobileIconGroup }
+ override val deviceServiceState: StateFlow<ServiceStateModel?> =
+ activeRepo
+ .flatMapLatest { it.deviceServiceState }
+ .stateIn(
+ scope,
+ SharingStarted.WhileSubscribed(),
+ realRepository.deviceServiceState.value
+ )
+
override val isAnySimSecure: Flow<Boolean> = activeRepo.flatMapLatest { it.isAnySimSecure }
override fun getIsAnySimSecure(): Boolean = activeRepo.value.getIsAnySimSecure()
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/demo/DemoMobileConnectionsRepository.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/demo/DemoMobileConnectionsRepository.kt
index 2b3c632..a944e91 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/demo/DemoMobileConnectionsRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/demo/DemoMobileConnectionsRepository.kt
@@ -27,6 +27,7 @@
import com.android.systemui.log.table.TableLogBufferFactory
import com.android.systemui.statusbar.pipeline.mobile.data.model.ResolvedNetworkType
import com.android.systemui.statusbar.pipeline.mobile.data.model.ResolvedNetworkType.DefaultNetworkType
+import com.android.systemui.statusbar.pipeline.mobile.data.model.ServiceStateModel
import com.android.systemui.statusbar.pipeline.mobile.data.model.SubscriptionModel
import com.android.systemui.statusbar.pipeline.mobile.data.repository.MobileConnectionRepository
import com.android.systemui.statusbar.pipeline.mobile.data.repository.MobileConnectionsRepository
@@ -136,6 +137,9 @@
override val defaultMobileIconGroup = flowOf(TelephonyIcons.THREE_G)
+ // TODO(b/339023069): demo command for device-based connectivity state
+ override val deviceServiceState: StateFlow<ServiceStateModel?> = MutableStateFlow(null)
+
override val isAnySimSecure: Flow<Boolean> = flowOf(getIsAnySimSecure())
override fun getIsAnySimSecure(): Boolean = false
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/MobileConnectionsRepositoryImpl.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/MobileConnectionsRepositoryImpl.kt
index 0073e9c..c32f0e8 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/MobileConnectionsRepositoryImpl.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/MobileConnectionsRepositoryImpl.kt
@@ -18,8 +18,10 @@
import android.annotation.SuppressLint
import android.content.Context
+import android.content.Intent
import android.content.IntentFilter
import android.telephony.CarrierConfigManager
+import android.telephony.ServiceState
import android.telephony.SubscriptionInfo
import android.telephony.SubscriptionManager
import android.telephony.SubscriptionManager.INVALID_SUBSCRIPTION_ID
@@ -35,7 +37,6 @@
import com.android.settingslib.mobile.MobileMappings.Config
import com.android.systemui.Dumpable
import com.android.systemui.broadcast.BroadcastDispatcher
-import com.android.systemui.common.coroutine.ConflatedCallbackFlow.conflatedCallbackFlow
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Application
import com.android.systemui.dagger.qualifiers.Background
@@ -47,6 +48,7 @@
import com.android.systemui.statusbar.pipeline.dagger.MobileSummaryLog
import com.android.systemui.statusbar.pipeline.mobile.data.MobileInputLogger
import com.android.systemui.statusbar.pipeline.mobile.data.model.NetworkNameModel
+import com.android.systemui.statusbar.pipeline.mobile.data.model.ServiceStateModel
import com.android.systemui.statusbar.pipeline.mobile.data.model.SubscriptionModel
import com.android.systemui.statusbar.pipeline.mobile.data.repository.MobileConnectionsRepository
import com.android.systemui.statusbar.pipeline.mobile.util.MobileMappingsProxy
@@ -55,6 +57,7 @@
import com.android.systemui.statusbar.pipeline.wifi.data.repository.WifiRepository
import com.android.systemui.statusbar.pipeline.wifi.shared.model.WifiNetworkModel
import com.android.systemui.util.kotlin.pairwise
+import com.android.systemui.utils.coroutines.flow.conflatedCallbackFlow
import java.io.PrintWriter
import java.lang.ref.WeakReference
import javax.inject.Inject
@@ -68,6 +71,7 @@
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.combine
import kotlinx.coroutines.flow.distinctUntilChanged
+import kotlinx.coroutines.flow.filterNotNull
import kotlinx.coroutines.flow.flowOn
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.flow.mapLatest
@@ -169,6 +173,35 @@
}
.flowOn(bgDispatcher)
+ /** Note that this flow is eager, so we don't miss any state */
+ override val deviceServiceState: StateFlow<ServiceStateModel?> =
+ broadcastDispatcher
+ .broadcastFlow(IntentFilter(Intent.ACTION_SERVICE_STATE)) { intent, _ ->
+ val subId =
+ intent.getIntExtra(
+ SubscriptionManager.EXTRA_SUBSCRIPTION_INDEX,
+ INVALID_SUBSCRIPTION_ID
+ )
+
+ val extras = intent.extras
+ if (extras == null) {
+ logger.logTopLevelServiceStateBroadcastMissingExtras(subId)
+ return@broadcastFlow null
+ }
+
+ val serviceState = ServiceState.newFromBundle(extras)
+ logger.logTopLevelServiceStateBroadcastEmergencyOnly(subId, serviceState)
+ if (subId == INVALID_SUBSCRIPTION_ID) {
+ // Assume that -1 here is the device's service state. We don't care about
+ // other ones.
+ ServiceStateModel.fromServiceState(serviceState)
+ } else {
+ null
+ }
+ }
+ .filterNotNull()
+ .stateIn(scope, SharingStarted.Eagerly, null)
+
/**
* State flow that emits the set of mobile data subscriptions, each represented by its own
* [SubscriptionModel].
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/domain/interactor/MobileIconsInteractor.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/domain/interactor/MobileIconsInteractor.kt
index 91d7ca6..cc4d568 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/domain/interactor/MobileIconsInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/domain/interactor/MobileIconsInteractor.kt
@@ -111,6 +111,13 @@
val isForceHidden: Flow<Boolean>
/**
+ * True if the device-level service state (with -1 subscription id) reports emergency calls
+ * only. This value is only useful when there are no other subscriptions OR all existing
+ * subscriptions report that they are not in service.
+ */
+ val isDeviceInEmergencyCallsOnlyMode: Flow<Boolean>
+
+ /**
* Vends out a [MobileIconInteractor] tracking the [MobileConnectionRepository] for the given
* subId.
*/
@@ -377,6 +384,9 @@
.map { it.contains(ConnectivitySlot.MOBILE) }
.stateIn(scope, SharingStarted.WhileSubscribed(), false)
+ override val isDeviceInEmergencyCallsOnlyMode: Flow<Boolean> =
+ mobileConnectionsRepo.deviceServiceState.map { it?.isEmergencyOnly ?: false }
+
/** Vends out new [MobileIconInteractor] for a particular subId */
override fun getMobileConnectionInteractorForSubId(subId: Int): MobileIconInteractor =
reuseCache[subId]?.get() ?: createMobileConnectionInteractorForSubId(subId)
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/satellite/domain/interactor/DeviceBasedSatelliteInteractor.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/satellite/domain/interactor/DeviceBasedSatelliteInteractor.kt
index 51c053e..5b954b2 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/satellite/domain/interactor/DeviceBasedSatelliteInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/satellite/domain/interactor/DeviceBasedSatelliteInteractor.kt
@@ -19,6 +19,9 @@
import com.android.internal.telephony.flags.Flags
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Application
+import com.android.systemui.log.LogBuffer
+import com.android.systemui.log.core.LogLevel
+import com.android.systemui.statusbar.pipeline.dagger.OemSatelliteInputLog
import com.android.systemui.statusbar.pipeline.mobile.domain.interactor.MobileIconsInteractor
import com.android.systemui.statusbar.pipeline.satellite.data.DeviceBasedSatelliteRepository
import com.android.systemui.statusbar.pipeline.satellite.shared.model.SatelliteConnectionState
@@ -45,6 +48,7 @@
deviceProvisioningInteractor: DeviceProvisioningInteractor,
wifiInteractor: WifiInteractor,
@Application scope: CoroutineScope,
+ @OemSatelliteInputLog private val logBuffer: LogBuffer,
) {
/** Must be observed by any UI showing Satellite iconography */
val isSatelliteAllowed =
@@ -79,25 +83,52 @@
val isWifiActive: Flow<Boolean> =
wifiInteractor.wifiNetwork.map { it is WifiNetworkModel.Active }
+ private val allConnectionsOos =
+ iconsInteractor.icons.aggregateOver(
+ selector = { intr ->
+ combine(intr.isInService, intr.isEmergencyOnly, intr.isNonTerrestrial) {
+ isInService,
+ isEmergencyOnly,
+ isNtn ->
+ !isInService && !isEmergencyOnly && !isNtn
+ }
+ },
+ defaultValue = true, // no connections == everything is OOS
+ ) { isOosAndNotEmergencyAndNotSatellite ->
+ isOosAndNotEmergencyAndNotSatellite.all { it }
+ }
+
/** When all connections are considered OOS, satellite connectivity is potentially valid */
val areAllConnectionsOutOfService =
if (Flags.oemEnabledSatelliteFlag()) {
- iconsInteractor.icons.aggregateOver(
- selector = { intr ->
- combine(intr.isInService, intr.isEmergencyOnly, intr.isNonTerrestrial) {
- isInService,
- isEmergencyOnly,
- isNtn ->
- !isInService && !(isEmergencyOnly || isNtn)
- }
- }
- ) { isOosAndNotEmergencyOnlyOrSatellite ->
- isOosAndNotEmergencyOnlyOrSatellite.all { it }
+ combine(
+ allConnectionsOos,
+ iconsInteractor.isDeviceInEmergencyCallsOnlyMode,
+ ) { connectionsOos, deviceEmergencyOnly ->
+ logBuffer.log(
+ TAG,
+ LogLevel.INFO,
+ {
+ bool1 = connectionsOos
+ bool2 = deviceEmergencyOnly
+ },
+ {
+ "Updating OOS status. allConnectionsOOs=$bool1 " +
+ "deviceEmergencyOnly=$bool2"
+ },
+ )
+ // If no connections exist, or all are OOS, then we look to the device-based
+ // service state to detect if any calls are possible
+ connectionsOos && !deviceEmergencyOnly
}
} else {
flowOf(false)
}
.stateIn(scope, SharingStarted.WhileSubscribed(), true)
+
+ companion object {
+ const val TAG = "DeviceBasedSatelliteInteractor"
+ }
}
/**
@@ -106,12 +137,22 @@
*
* Provides a way to connect the reactivity of the top-level flow with the reactivity of an
* arbitrarily-defined relationship ([selector]) from R to the flow that R exposes.
+ *
+ * [defaultValue] allows for a default value to be used if there are no leaf nodes after applying
+ * [selector]. E.g., if there are no mobile connections, assume that there is no service.
*/
@OptIn(ExperimentalCoroutinesApi::class)
private inline fun <R, reified S, T> Flow<List<R>>.aggregateOver(
crossinline selector: (R) -> Flow<S>,
- crossinline transform: (Array<S>) -> T
+ defaultValue: T,
+ crossinline transform: (Array<S>) -> T,
): Flow<T> {
return map { list -> list.map { selector(it) } }
- .flatMapLatest { newFlows -> combine(newFlows) { newVals -> transform(newVals) } }
+ .flatMapLatest { newFlows ->
+ if (newFlows.isEmpty()) {
+ flowOf(defaultValue)
+ } else {
+ combine(newFlows) { newVals -> transform(newVals) }
+ }
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/shared/ui/viewmodel/CollapsedStatusBarViewModel.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/shared/ui/viewmodel/CollapsedStatusBarViewModel.kt
index cc87e8a..0a6e95e 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/shared/ui/viewmodel/CollapsedStatusBarViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/shared/ui/viewmodel/CollapsedStatusBarViewModel.kt
@@ -19,6 +19,7 @@
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Application
import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor
+import com.android.systemui.keyguard.shared.model.Edge
import com.android.systemui.keyguard.shared.model.KeyguardState.DREAMING
import com.android.systemui.keyguard.shared.model.KeyguardState.LOCKSCREEN
import com.android.systemui.keyguard.shared.model.KeyguardState.OCCLUDED
@@ -81,12 +82,12 @@
) : CollapsedStatusBarViewModel {
override val isTransitioningFromLockscreenToOccluded: StateFlow<Boolean> =
keyguardTransitionInteractor
- .isInTransition(LOCKSCREEN, OCCLUDED)
+ .isInTransition(Edge.create(from = LOCKSCREEN, to = OCCLUDED))
.stateIn(coroutineScope, SharingStarted.WhileSubscribed(), initialValue = false)
override val transitionFromLockscreenToDreamStartedEvent: Flow<Unit> =
keyguardTransitionInteractor
- .transition(LOCKSCREEN, DREAMING)
+ .transition(Edge.create(from = LOCKSCREEN, to = DREAMING))
.filter { it.transitionState == TransitionState.STARTED }
.map {}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/AvalancheController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/policy/AvalancheController.kt
index fa8a7d8..8b48bd3 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/AvalancheController.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/AvalancheController.kt
@@ -22,6 +22,7 @@
import com.android.systemui.dump.DumpManager
import com.android.systemui.statusbar.notification.shared.NotificationThrottleHun
import com.android.systemui.statusbar.policy.BaseHeadsUpManager.HeadsUpEntry
+import com.android.systemui.util.Compile
import java.io.PrintWriter
import javax.inject.Inject
@@ -30,12 +31,14 @@
* succession, by delaying visual listener side effects and removal handling from BaseHeadsUpManager
*/
@SysUISingleton
-class AvalancheController @Inject constructor(
+class AvalancheController
+@Inject
+constructor(
dumpManager: DumpManager,
) : Dumpable {
private val tag = "AvalancheController"
- private val debug = false
+ private val debug = Compile.IS_DEBUG && Log.isLoggable(tag, Log.DEBUG)
// HUN showing right now, in the floating state where full shade is hidden, on launcher or AOD
@VisibleForTesting var headsUpEntryShowing: HeadsUpEntry? = null
@@ -79,7 +82,7 @@
val fn = "[$label] => AvalancheController.update [${getKey(entry)}]"
if (entry == null) {
log { "Entry is NULL, stop update." }
- return;
+ return
}
if (debug) {
debugRunnableLabelMap[runnable] = label
@@ -106,7 +109,10 @@
if (isOnlyNextEntry) {
// HeadsUpEntry.updateEntry recursively calls AvalancheController#update
// and goes to the isShowing case above
- headsUpEntryShowing!!.updateEntry(false, "avalanche duration update")
+ headsUpEntryShowing!!.updateEntry(
+ /* updatePostTime= */ false,
+ /* updateEarliestRemovalTime= */ false,
+ /* reason= */ "avalanche duration update")
}
}
logState("after $fn")
@@ -142,9 +148,12 @@
} else if (isShowing(entry)) {
log { "$fn => [remove showing ${getKey(entry)}]" }
previousHunKey = getKey(headsUpEntryShowing)
-
+ // Show the next HUN before removing this one, so that we don't tell listeners
+ // onHeadsUpPinnedModeChanged, which causes
+ // NotificationPanelViewController.updateTouchableRegion to hide the window while the
+ // HUN is animating out, resulting in a flicker.
+ showNext()
runnable.run()
- showNextAfterRemove()
} else {
log { "$fn => [removing untracked ${getKey(entry)}]" }
}
@@ -247,7 +256,7 @@
}
}
- private fun showNextAfterRemove() {
+ private fun showNext() {
log { "SHOW NEXT" }
headsUpEntryShowing = null
@@ -294,17 +303,21 @@
private fun getStateStr(): String {
return "SHOWING: [${getKey(headsUpEntryShowing)}]" +
- "\nPREVIOUS: [$previousHunKey]" +
- "\nNEXT LIST: $nextListStr" +
- "\nNEXT MAP: $nextMapStr" +
- "\nDROPPED: $dropSetStr"
+ "\nPREVIOUS: [$previousHunKey]" +
+ "\nNEXT LIST: $nextListStr" +
+ "\nNEXT MAP: $nextMapStr" +
+ "\nDROPPED: $dropSetStr"
}
private fun logState(reason: String) {
- log { "\n================================================================================="}
+ log {
+ "\n================================================================================="
+ }
log { "STATE $reason" }
log { getStateStr() }
- log { "=================================================================================\n"}
+ log {
+ "=================================================================================\n"
+ }
}
private val dropSetStr: String
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/BaseHeadsUpManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/BaseHeadsUpManager.java
index b8318a7..a7fe49b 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/BaseHeadsUpManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/BaseHeadsUpManager.java
@@ -39,6 +39,7 @@
import com.android.systemui.res.R;
import com.android.systemui.statusbar.notification.collection.NotificationEntry;
import com.android.systemui.statusbar.notification.row.NotificationRowContentBinder.InflationFlag;
+import com.android.systemui.statusbar.notification.shared.NotificationThrottleHun;
import com.android.systemui.statusbar.notification.shared.NotificationsHeadsUpRefactor;
import com.android.systemui.util.ListenerSet;
import com.android.systemui.util.concurrency.DelayableExecutor;
@@ -114,7 +115,8 @@
mUiEventLogger = uiEventLogger;
mAvalancheController = avalancheController;
Resources resources = context.getResources();
- mMinimumDisplayTime = resources.getInteger(R.integer.heads_up_notification_minimum_time);
+ mMinimumDisplayTime = NotificationThrottleHun.isEnabled()
+ ? 500 : resources.getInteger(R.integer.heads_up_notification_minimum_time);
mStickyForSomeTimeAutoDismissTime = resources.getInteger(
R.integer.sticky_heads_up_notification_time);
mAutoDismissTime = resources.getInteger(R.integer.heads_up_notification_decay);
@@ -765,11 +767,23 @@
* @param updatePostTime whether or not to refresh the post time
*/
public void updateEntry(boolean updatePostTime, @Nullable String reason) {
+ updateEntry(updatePostTime, /* updateEarliestRemovalTime= */ true, reason);
+ }
+
+ /**
+ * Updates an entry's removal time.
+ * @param updatePostTime whether or not to refresh the post time
+ * @param updateEarliestRemovalTime whether this update should further delay removal
+ */
+ public void updateEntry(boolean updatePostTime, boolean updateEarliestRemovalTime,
+ @Nullable String reason) {
Runnable runnable = () -> {
mLogger.logUpdateEntry(mEntry, updatePostTime, reason);
final long now = mSystemClock.elapsedRealtime();
- mEarliestRemovalTime = now + mMinimumDisplayTime;
+ if (updateEarliestRemovalTime) {
+ mEarliestRemovalTime = now + mMinimumDisplayTime;
+ }
if (updatePostTime) {
mPostTime = Math.max(mPostTime, now);
@@ -785,7 +799,9 @@
FinishTimeUpdater finishTimeCalculator = () -> {
final long finishTime = calculateFinishTime();
final long now = mSystemClock.elapsedRealtime();
- final long timeLeft = Math.max(finishTime - now, mMinimumDisplayTime);
+ final long timeLeft = NotificationThrottleHun.isEnabled()
+ ? Math.max(finishTime, mEarliestRemovalTime) - now
+ : Math.max(finishTime - now, mMinimumDisplayTime);
return timeLeft;
};
scheduleAutoRemovalCallback(finishTimeCalculator, "updateEntry (not sticky)");
@@ -818,13 +834,6 @@
}
public int compareNonTimeFields(HeadsUpEntry headsUpEntry) {
- boolean isPinned = mEntry.isRowPinned();
- boolean otherPinned = headsUpEntry.mEntry.isRowPinned();
- if (isPinned && !otherPinned) {
- return -1;
- } else if (!isPinned && otherPinned) {
- return 1;
- }
boolean selfFullscreen = hasFullScreenIntent(mEntry);
boolean otherFullscreen = hasFullScreenIntent(headsUpEntry.mEntry);
if (selfFullscreen && !otherFullscreen) {
@@ -851,6 +860,13 @@
}
public int compareTo(@NonNull HeadsUpEntry headsUpEntry) {
+ boolean isPinned = mEntry.isRowPinned();
+ boolean otherPinned = headsUpEntry.mEntry.isRowPinned();
+ if (isPinned && !otherPinned) {
+ return -1;
+ } else if (!isPinned && otherPinned) {
+ return 1;
+ }
int nonTimeCompareResult = compareNonTimeFields(headsUpEntry);
if (nonTimeCompareResult != 0) {
return nonTimeCompareResult;
diff --git a/packages/SystemUI/src/com/android/systemui/theme/CustomDynamicColors.java b/packages/SystemUI/src/com/android/systemui/theme/CustomDynamicColors.java
new file mode 100644
index 0000000..efeb2f9
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/theme/CustomDynamicColors.java
@@ -0,0 +1,322 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.theme;
+
+import com.google.ux.material.libmonet.dynamiccolor.ContrastCurve;
+import com.google.ux.material.libmonet.dynamiccolor.DynamicColor;
+import com.google.ux.material.libmonet.dynamiccolor.MaterialDynamicColors;
+import com.google.ux.material.libmonet.dynamiccolor.ToneDeltaPair;
+import com.google.ux.material.libmonet.dynamiccolor.TonePolarity;
+
+class CustomDynamicColors {
+ private final MaterialDynamicColors mMdc;
+
+ CustomDynamicColors(boolean isExtendedFidelity) {
+ this.mMdc = new MaterialDynamicColors(isExtendedFidelity);
+ }
+
+ // CLOCK COLORS
+
+ public DynamicColor widgetBackground() {
+ return new DynamicColor(
+ /* name= */ "widget_background",
+ /* palette= */ (s) -> s.primaryPalette,
+ /* tone= */ (s) -> s.isDark ? 20.0 : 95.0,
+ /* isBackground= */ true,
+ /* background= */ null,
+ /* secondBackground= */ null,
+ /* contrastCurve= */ null,
+ /* toneDeltaPair= */ null);
+ }
+
+ public DynamicColor clockHour() {
+ return new DynamicColor(
+ /* name= */ "clock_hour",
+ /* palette= */ (s) -> s.secondaryPalette,
+ /* tone= */ (s) -> s.isDark ? 30.0 : 60.0,
+ /* isBackground= */ false,
+ /* background= */ (s) -> widgetBackground(),
+ /* secondBackground= */ null,
+ /* contrastCurve= */ new ContrastCurve(1.0, 4.0, 5.0, 15.0),
+ /* toneDeltaPair= */
+ (s) -> new ToneDeltaPair(clockHour(), clockMinute(), 10.0, TonePolarity.DARKER,
+ false));
+ }
+
+ public DynamicColor clockMinute() {
+ return new DynamicColor(
+ /* name= */ "clock_minute",
+ /* palette= */ (s) -> s.primaryPalette,
+ /* tone= */ (s) -> s.isDark ? 40.0 : 90.0,
+ /* isBackground= */ false,
+ /* background= */ (s) -> widgetBackground(),
+ /* secondBackground= */ null,
+ /* contrastCurve= */ new ContrastCurve(1.0, 6.5, 10.0, 15.0),
+ /* toneDeltaPair= */ null);
+ }
+
+ public DynamicColor clockSecond() {
+ return new DynamicColor(
+ /* name= */ "clock_second",
+ /* palette= */ (s) -> s.tertiaryPalette,
+ /* tone= */ (s) -> s.isDark ? 40.0 : 90.0,
+ /* isBackground= */ false,
+ /* background= */ (s) -> widgetBackground(),
+ /* secondBackground= */ null,
+ /* contrastCurve= */ new ContrastCurve(1.0, 5.0, 70.0, 11.0),
+ /* toneDeltaPair= */ null);
+ }
+
+ public DynamicColor weatherTemp() {
+ return new DynamicColor(
+ /* name= */ "clock_second",
+ /* palette= */ (s) -> s.primaryPalette,
+ /* tone= */ (s) -> s.isDark ? 55.0 : 80.0,
+ /* isBackground= */ false,
+ /* background= */ (s) -> widgetBackground(),
+ /* secondBackground= */ null,
+ /* contrastCurve= */ new ContrastCurve(1.0, 5.0, 70.0, 11.0),
+ /* toneDeltaPair= */ null);
+ }
+
+ // THEME APP ICONS
+
+ public DynamicColor themeApp() {
+ return new DynamicColor(
+ /* name= */ "theme_app",
+ /* palette= */ (s) -> s.primaryPalette,
+ /* tone= */ (s) -> s.isDark ? 90.0 : 30.0, // Adjusted values
+ /* isBackground= */ true,
+ /* background= */ null,
+ /* secondBackground= */ null,
+ /* contrastCurve= */ null,
+ /* toneDeltaPair= */ null);
+ }
+
+ public DynamicColor onThemeApp() {
+ return new DynamicColor(
+ /* name= */ "on_theme_app",
+ /* palette= */ (s) -> s.primaryPalette,
+ /* tone= */ (s) -> s.isDark ? 40.0 : 80.0, // Adjusted values
+ /* isBackground= */ false,
+ /* background= */ (s) -> themeApp(),
+ /* secondBackground= */ null,
+ /* contrastCurve= */ new ContrastCurve(1.0, 3.0, 7.0, 10.0),
+ /* toneDeltaPair= */ null);
+ }
+
+ public DynamicColor themeAppRing() {
+ return new DynamicColor(
+ /* name= */ "theme_app_ring",
+ /* palette= */ (s) -> s.primaryPalette,
+ /* tone= */ (s) -> 70.0,
+ /* isBackground= */ true,
+ /* background= */ null,
+ /* secondBackground= */ null,
+ /* contrastCurve= */ new ContrastCurve(1.0, 1.0, 1.0, 1.0),
+ /* toneDeltaPair= */ null);
+ }
+
+ public DynamicColor themeNotif() {
+ return new DynamicColor(
+ /* name= */ "theme_notif",
+ /* palette= */ (s) -> s.tertiaryPalette,
+ /* tone= */ (s) -> s.isDark ? 80.0 : 90.0,
+ /* isBackground= */ false,
+ /* background= */ (s) -> themeAppRing(),
+ /* secondBackground= */ null,
+ /* contrastCurve= */ new ContrastCurve(1.0, 1.0, 1.0, 1.0),
+ /* toneDeltaPair= */
+ (s) -> new ToneDeltaPair(themeNotif(), themeAppRing(), 10.0, TonePolarity.NEARER,
+ false));
+ }
+
+ // SUPER G COLORS
+
+ public DynamicColor brandA() {
+ return new DynamicColor(
+ /* name= */ "brand_a",
+ /* palette= */ (s) -> s.primaryPalette,
+ /* tone= */ (s) -> s.isDark ? 40.0 : 80.0,
+ /* isBackground= */ true,
+ /* background= */ (s) -> mMdc.surfaceContainerLow(),
+ /* secondBackground= */ null,
+ /* contrastCurve= */ new ContrastCurve(1.0, 3.0, 7.0, 17.0),
+ /* toneDeltaPair= */
+ (s) -> new ToneDeltaPair(brandA(), brandB(), 10.0, TonePolarity.NEARER, false));
+ }
+
+ public DynamicColor brandB() {
+ return new DynamicColor(
+ /* name= */ "brand_b",
+ /* palette= */ (s) -> s.secondaryPalette,
+ /* tone= */ (s) -> s.isDark ? 70.0 : 98.0,
+ /* isBackground= */ true,
+ /* background= */ (s) -> mMdc.surfaceContainerLow(),
+ /* secondBackground= */ null,
+ /* contrastCurve= */ new ContrastCurve(1.0, 3.0, 3.0, 6.0),
+ /* toneDeltaPair= */
+ (s) -> new ToneDeltaPair(brandB(), brandC(), 10.0, TonePolarity.NEARER, false));
+ }
+
+ public DynamicColor brandC() {
+ return new DynamicColor(
+ /* name= */ "brand_c",
+ /* palette= */ (s) -> s.primaryPalette,
+ /* tone= */ (s) -> s.isDark ? 50.0 : 60.0,
+ /* isBackground= */ false,
+ /* background= */ (s) -> mMdc.surfaceContainerLow(),
+ /* secondBackground= */ null,
+ /* contrastCurve= */ new ContrastCurve(1.0, 3.0, 4.0, 9.0),
+ /* toneDeltaPair= */
+ (s) -> new ToneDeltaPair(brandC(), brandD(), 10.0, TonePolarity.NEARER, false));
+ }
+
+ public DynamicColor brandD() {
+ return new DynamicColor(
+ /* name= */ "brand_d",
+ /* palette= */ (s) -> s.tertiaryPalette,
+ /* tone= */ (s) -> s.isDark ? 59.0 : 90.0,
+ /* isBackground= */ false,
+ /* background= */ (s) -> mMdc.surfaceContainerLow(),
+ /* secondBackground= */ null,
+ /* contrastCurve= */ new ContrastCurve(1.0, 3.0, 4.0, 13.0),
+ /* toneDeltaPair= */
+ (s) -> new ToneDeltaPair(brandD(), brandA(), 10.0, TonePolarity.NEARER, false));
+ }
+
+ // QUICK SETTING TIILES
+
+ public DynamicColor underSurface() {
+ return new DynamicColor(
+ /* name= */ "under_surface",
+ /* palette= */ (s) -> s.primaryPalette,
+ /* tone= */ (s) -> 0.0,
+ /* isBackground= */ true,
+ /* background= */ null,
+ /* secondBackground= */ null,
+ /* contrastCurve= */ null,
+ /* toneDeltaPair= */ null);
+ }
+
+ public DynamicColor shadeActive() {
+ return new DynamicColor(
+ /* name= */ "shade_active",
+ /* palette= */ (s) -> s.primaryPalette,
+ /* tone= */ (s) -> 90.0,
+ /* isBackground= */ false,
+ /* background= */ (s) -> underSurface(),
+ /* secondBackground= */ null,
+ /* contrastCurve= */ new ContrastCurve(1.0, 3.0, 4.5, 7.0),
+ /* toneDeltaPair= */
+ (s) -> new ToneDeltaPair(shadeActive(), shadeInactive(), 30.0, TonePolarity.LIGHTER,
+ false));
+ }
+
+ public DynamicColor onShadeActive() {
+ return new DynamicColor(
+ /* name= */ "on_shade_active",
+ /* palette= */ (s) -> s.primaryPalette,
+ /* tone= */ (s) -> 10.0,
+ /* isBackground= */ false,
+ /* background= */ (s) -> shadeActive(),
+ /* secondBackground= */ null,
+ /* contrastCurve= */ new ContrastCurve(1.0, 4.5, 7.0, 11.0),
+ /* toneDeltaPair= */
+ (s) -> new ToneDeltaPair(onShadeActive(), onShadeActiveVariant(), 20.0,
+ TonePolarity.NEARER, false));
+ }
+
+ public DynamicColor onShadeActiveVariant() {
+ return new DynamicColor(
+ /* name= */ "on_shade_active_variant",
+ /* palette= */ (s) -> s.primaryPalette,
+ /* tone= */ (s) -> 30.0,
+ /* isBackground= */ false,
+ /* background= */ (s) -> shadeActive(),
+ /* secondBackground= */ null,
+ /* contrastCurve= */ new ContrastCurve(1.0, 4.5, 7.0, 11.0),
+ /* toneDeltaPair= */
+ (s) -> new ToneDeltaPair(onShadeActiveVariant(), onShadeActive(), 20.0,
+ TonePolarity.NEARER, false));
+ }
+
+ public DynamicColor shadeInactive() {
+ return new DynamicColor(
+ /* name= */ "shade_inactive",
+ /* palette= */ (s) -> s.neutralPalette,
+ /* tone= */ (s) -> 20.0,
+ /* isBackground= */ true,
+ /* background= */ (s) -> underSurface(),
+ /* secondBackground= */ null,
+ /* contrastCurve= */ new ContrastCurve(1.0, 1.0, 1.0, 1.0),
+ /* toneDeltaPair= */(s) -> new ToneDeltaPair(shadeInactive(), shadeDisabled(), 15.0,
+ TonePolarity.LIGHTER, false));
+ }
+
+ public DynamicColor onShadeInactive() {
+ return new DynamicColor(
+ /* name= */ "on_shade_inactive",
+ /* palette= */ (s) -> s.neutralVariantPalette,
+ /* tone= */ (s) -> 90.0,
+ /* isBackground= */ true,
+ /* background= */ (s) -> shadeInactive(),
+ /* secondBackground= */ null,
+ /* contrastCurve= */ new ContrastCurve(1.0, 4.5, 7.0, 11.0),
+ /* toneDeltaPair= */
+ (s) -> new ToneDeltaPair(onShadeInactive(), onShadeInactiveVariant(), 10.0,
+ TonePolarity.NEARER, false));
+ }
+
+ public DynamicColor onShadeInactiveVariant() {
+ return new DynamicColor(
+ /* name= */ "on_shade_inactive_variant",
+ /* palette= */ (s) -> s.neutralVariantPalette,
+ /* tone= */ (s) -> 80.0,
+ /* isBackground= */ false,
+ /* background= */ (s) -> shadeInactive(),
+ /* secondBackground= */ null,
+ /* contrastCurve= */ new ContrastCurve(1.0, 4.5, 7.0, 11.0),
+ /* toneDeltaPair= */
+ (s) -> new ToneDeltaPair(onShadeInactiveVariant(), onShadeInactive(), 10.0,
+ TonePolarity.NEARER, false));
+ }
+
+ public DynamicColor shadeDisabled() {
+ return new DynamicColor(
+ /* name= */ "shade_disabled",
+ /* palette= */ (s) -> s.neutralPalette,
+ /* tone= */ (s) -> 4.0,
+ /* isBackground= */ false,
+ /* background= */ (s) -> underSurface(),
+ /* secondBackground= */ null,
+ /* contrastCurve= */ new ContrastCurve(1.0, 1.0, 1.0, 1.0),
+ /* toneDeltaPair= */ null);
+ }
+
+ public DynamicColor overviewBackground() {
+ return new DynamicColor(
+ /* name= */ "overview_background",
+ /* palette= */ (s) -> s.neutralVariantPalette,
+ /* tone= */ (s) -> s.isDark ? 80.0 : 35.0,
+ /* isBackground= */ true,
+ /* background= */ null,
+ /* secondBackground= */ null,
+ /* contrastCurve= */null,
+ /* toneDeltaPair= */ null);
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/theme/DynamicColors.kt b/packages/SystemUI/src/com/android/systemui/theme/DynamicColors.kt
index a983d2f..3518759 100644
--- a/packages/SystemUI/src/com/android/systemui/theme/DynamicColors.kt
+++ b/packages/SystemUI/src/com/android/systemui/theme/DynamicColors.kt
@@ -103,5 +103,33 @@
Pair.create("on_tertiary_fixed_variant", mdc.onTertiaryFixedVariant()),
)
}
+
+ @JvmStatic
+ fun getCustomColorsMapped(isExtendedFidelity: Boolean): List<Pair<String, DynamicColor>> {
+ val customMdc = CustomDynamicColors(isExtendedFidelity)
+ return arrayListOf(
+ Pair.create("widget_background", customMdc.widgetBackground()),
+ Pair.create("clock_hour", customMdc.clockHour()),
+ Pair.create("clock_minute", customMdc.clockMinute()),
+ Pair.create("clock_second", customMdc.weatherTemp()),
+ Pair.create("theme_app", customMdc.themeApp()),
+ Pair.create("on_theme_app", customMdc.onThemeApp()),
+ Pair.create("theme_app_ring", customMdc.themeAppRing()),
+ Pair.create("on_theme_app_ring", customMdc.themeNotif()),
+ Pair.create("brand_a", customMdc.brandA()),
+ Pair.create("brand_b", customMdc.brandB()),
+ Pair.create("brand_c", customMdc.brandC()),
+ Pair.create("brand_d", customMdc.brandD()),
+ Pair.create("under_surface", customMdc.underSurface()),
+ Pair.create("shade_active", customMdc.shadeActive()),
+ Pair.create("on_shade_active", customMdc.onShadeActive()),
+ Pair.create("on_shade_active_variant", customMdc.onShadeActiveVariant()),
+ Pair.create("shade_inactive", customMdc.shadeInactive()),
+ Pair.create("on_shade_inactive", customMdc.onShadeInactive()),
+ Pair.create("on_shade_inactive_variant", customMdc.onShadeInactiveVariant()),
+ Pair.create("shade_disabled", customMdc.shadeDisabled()),
+ Pair.create("overview_background", customMdc.overviewBackground())
+ )
+ }
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/theme/ThemeOverlayController.java b/packages/SystemUI/src/com/android/systemui/theme/ThemeOverlayController.java
index 5c3bbb7..d256c4a 100644
--- a/packages/SystemUI/src/com/android/systemui/theme/ThemeOverlayController.java
+++ b/packages/SystemUI/src/com/android/systemui/theme/ThemeOverlayController.java
@@ -56,6 +56,7 @@
import android.util.ArrayMap;
import android.util.ArraySet;
import android.util.Log;
+import android.util.Pair;
import android.util.SparseArray;
import android.util.SparseIntArray;
@@ -84,6 +85,7 @@
import com.android.systemui.util.kotlin.JavaAdapter;
import com.android.systemui.util.settings.SecureSettings;
+import com.google.ux.material.libmonet.dynamiccolor.DynamicColor;
import com.google.ux.material.libmonet.dynamiccolor.MaterialDynamicColors;
import org.json.JSONException;
@@ -631,29 +633,33 @@
protected FabricatedOverlay createDynamicOverlay() {
FabricatedOverlay overlay = newFabricatedOverlay("dynamic");
- assignDynamicPaletteToOverlay(overlay, true /* isDark */);
- assignDynamicPaletteToOverlay(overlay, false /* isDark */);
- assignFixedColorsToOverlay(overlay);
+ //Themed Colors
+ assignColorsToOverlay(overlay, DynamicColors.allDynamicColorsMapped(mIsFidelityEnabled),
+ false);
+ // Fixed Colors
+ assignColorsToOverlay(overlay, DynamicColors.getFixedColorsMapped(mIsFidelityEnabled),
+ true);
+ //Custom Colors
+ assignColorsToOverlay(overlay, DynamicColors.getCustomColorsMapped(mIsFidelityEnabled),
+ false);
return overlay;
}
- private void assignDynamicPaletteToOverlay(FabricatedOverlay overlay, boolean isDark) {
- String suffix = isDark ? "dark" : "light";
- ColorScheme scheme = isDark ? mDarkColorScheme : mLightColorScheme;
- DynamicColors.allDynamicColorsMapped(mIsFidelityEnabled).forEach(p -> {
- String resourceName = "android:color/system_" + p.first + "_" + suffix;
- int colorValue = p.second.getArgb(scheme.getMaterialScheme());
- overlay.setResourceValue(resourceName, TYPE_INT_COLOR_ARGB8, colorValue,
- null /* configuration */);
- });
- }
+ private void assignColorsToOverlay(FabricatedOverlay overlay,
+ List<Pair<String, DynamicColor>> colors, Boolean isFixed) {
+ colors.forEach(p -> {
+ String prefix = "android:color/system_" + p.first;
- private void assignFixedColorsToOverlay(FabricatedOverlay overlay) {
- DynamicColors.getFixedColorsMapped(mIsFidelityEnabled).forEach(p -> {
- String resourceName = "android:color/system_" + p.first;
- int colorValue = p.second.getArgb(mLightColorScheme.getMaterialScheme());
- overlay.setResourceValue(resourceName, TYPE_INT_COLOR_ARGB8, colorValue,
- null /* configuration */);
+ if (isFixed) {
+ overlay.setResourceValue(prefix, TYPE_INT_COLOR_ARGB8,
+ p.second.getArgb(mLightColorScheme.getMaterialScheme()), null);
+ return;
+ }
+
+ overlay.setResourceValue(prefix + "_light", TYPE_INT_COLOR_ARGB8,
+ p.second.getArgb(mLightColorScheme.getMaterialScheme()), null);
+ overlay.setResourceValue(prefix + "_dark", TYPE_INT_COLOR_ARGB8,
+ p.second.getArgb(mDarkColorScheme.getMaterialScheme()), null);
});
}
diff --git a/packages/SystemUI/src/com/android/systemui/unfold/FullscreenLightRevealAnimation.kt b/packages/SystemUI/src/com/android/systemui/unfold/FullscreenLightRevealAnimation.kt
index ca5ea3b..135edfc 100644
--- a/packages/SystemUI/src/com/android/systemui/unfold/FullscreenLightRevealAnimation.kt
+++ b/packages/SystemUI/src/com/android/systemui/unfold/FullscreenLightRevealAnimation.kt
@@ -32,6 +32,7 @@
import android.view.SurfaceSession
import android.view.WindowManager
import android.view.WindowlessWindowManager
+import androidx.annotation.WorkerThread
import com.android.app.tracing.traceSection
import com.android.systemui.dagger.qualifiers.Background
import com.android.systemui.dagger.qualifiers.Main
@@ -235,8 +236,10 @@
}
private inner class RotationWatcher : RotationChangeProvider.RotationListener {
+ @WorkerThread
override fun onRotationChanged(newRotation: Int) {
traceSection("$TAG#onRotationChanged") {
+ ensureInBackground()
if (currentRotation != newRotation) {
currentRotation = newRotation
scrimView?.revealEffect = lightRevealEffectFactory(currentRotation)
diff --git a/packages/SystemUI/src/com/android/systemui/user/domain/interactor/GuestUserInteractor.kt b/packages/SystemUI/src/com/android/systemui/user/domain/interactor/GuestUserInteractor.kt
index bfed0c4..0a1724c 100644
--- a/packages/SystemUI/src/com/android/systemui/user/domain/interactor/GuestUserInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/user/domain/interactor/GuestUserInteractor.kt
@@ -70,8 +70,10 @@
val isGuestUserResetting: Boolean = repository.isGuestUserResetting
init {
- resumeSessionReceiver.register()
- resetOrExitSessionReceiver.register()
+ if (applicationContext.userId == UserHandle.USER_SYSTEM) {
+ resumeSessionReceiver.register()
+ resetOrExitSessionReceiver.register()
+ }
}
/** Notifies that the device has finished booting. */
diff --git a/packages/SystemUI/src/com/android/systemui/user/domain/interactor/UserSwitcherInteractor.kt b/packages/SystemUI/src/com/android/systemui/user/domain/interactor/UserSwitcherInteractor.kt
index 9339651..516cb46 100644
--- a/packages/SystemUI/src/com/android/systemui/user/domain/interactor/UserSwitcherInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/user/domain/interactor/UserSwitcherInteractor.kt
@@ -533,7 +533,7 @@
targetUserId = targetUserId,
::showDialog,
::dismissDialog,
- ::selectUser,
+ ::switchUser
)
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/util/kotlin/JavaAdapter.kt b/packages/SystemUI/src/com/android/systemui/util/kotlin/JavaAdapter.kt
index 46ce5f2..1ec86a4 100644
--- a/packages/SystemUI/src/com/android/systemui/util/kotlin/JavaAdapter.kt
+++ b/packages/SystemUI/src/com/android/systemui/util/kotlin/JavaAdapter.kt
@@ -97,3 +97,12 @@
fun <A, B, R> combineFlows(flow1: Flow<A>, flow2: Flow<B>, bifunction: (A, B) -> R): Flow<R> {
return combine(flow1, flow2, bifunction)
}
+
+fun <A, B, C, R> combineFlows(
+ flow1: Flow<A>,
+ flow2: Flow<B>,
+ flow3: Flow<C>,
+ trifunction: (A, B, C) -> R
+): Flow<R> {
+ return combine(flow1, flow2, flow3, trifunction)
+}
diff --git a/packages/SystemUI/src/com/android/systemui/volume/dagger/MediaDevicesModule.kt b/packages/SystemUI/src/com/android/systemui/volume/dagger/MediaDevicesModule.kt
index 155102c9..3696108 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/dagger/MediaDevicesModule.kt
+++ b/packages/SystemUI/src/com/android/systemui/volume/dagger/MediaDevicesModule.kt
@@ -27,6 +27,8 @@
import com.android.systemui.dagger.qualifiers.Background
import com.android.systemui.volume.panel.component.mediaoutput.data.repository.LocalMediaRepositoryFactory
import com.android.systemui.volume.panel.component.mediaoutput.data.repository.LocalMediaRepositoryFactoryImpl
+import com.android.systemui.volume.panel.component.mediaoutput.domain.interactor.MediaControllerInteractor
+import com.android.systemui.volume.panel.component.mediaoutput.domain.interactor.MediaControllerInteractorImpl
import dagger.Binds
import dagger.Module
import dagger.Provides
@@ -41,6 +43,11 @@
impl: LocalMediaRepositoryFactoryImpl
): LocalMediaRepositoryFactory
+ @Binds
+ fun bindMediaControllerInteractor(
+ impl: MediaControllerInteractorImpl
+ ): MediaControllerInteractor
+
companion object {
@Provides
diff --git a/packages/SystemUI/src/com/android/systemui/volume/panel/component/anc/data/repository/AncSliceRepository.kt b/packages/SystemUI/src/com/android/systemui/volume/panel/component/anc/data/repository/AncSliceRepository.kt
index 3117abc..e1787e8 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/panel/component/anc/data/repository/AncSliceRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/volume/panel/component/anc/data/repository/AncSliceRepository.kt
@@ -21,22 +21,15 @@
import androidx.slice.Slice
import androidx.slice.SliceViewManager
import com.android.settingslib.bluetooth.BluetoothUtils
-import com.android.settingslib.media.BluetoothMediaDevice
-import com.android.systemui.dagger.qualifiers.Background
import com.android.systemui.dagger.qualifiers.Main
import com.android.systemui.slice.sliceForUri
-import com.android.systemui.volume.panel.component.mediaoutput.data.repository.LocalMediaRepositoryFactory
import dagger.assisted.Assisted
import dagger.assisted.AssistedFactory
import dagger.assisted.AssistedInject
import kotlin.coroutines.CoroutineContext
-import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.flow.Flow
-import kotlinx.coroutines.flow.distinctUntilChanged
-import kotlinx.coroutines.flow.flatMapLatest
import kotlinx.coroutines.flow.flowOf
import kotlinx.coroutines.flow.flowOn
-import kotlinx.coroutines.flow.map
/** Provides ANC slice data */
interface AncSliceRepository {
@@ -49,35 +42,30 @@
* - there is no supported device connected;
* - there is no slice provider for the uri;
*/
- fun ancSlice(width: Int, isCollapsed: Boolean, hideLabel: Boolean): Flow<Slice?>
+ fun ancSlice(
+ device: BluetoothDevice,
+ width: Int,
+ isCollapsed: Boolean,
+ hideLabel: Boolean
+ ): Flow<Slice?>
}
-@OptIn(ExperimentalCoroutinesApi::class)
class AncSliceRepositoryImpl
@AssistedInject
constructor(
- mediaRepositoryFactory: LocalMediaRepositoryFactory,
- @Background private val backgroundCoroutineContext: CoroutineContext,
@Main private val mainCoroutineContext: CoroutineContext,
@Assisted private val sliceViewManager: SliceViewManager,
) : AncSliceRepository {
- private val localMediaRepository = mediaRepositoryFactory.create(null)
-
- override fun ancSlice(width: Int, isCollapsed: Boolean, hideLabel: Boolean): Flow<Slice?> {
- return localMediaRepository.currentConnectedDevice
- .map {
- (it as? BluetoothMediaDevice)
- ?.cachedDevice
- ?.device
- ?.getExtraControlUri(width, isCollapsed, hideLabel)
- }
- .distinctUntilChanged()
- .flatMapLatest { sliceUri ->
- sliceUri ?: return@flatMapLatest flowOf(null)
- sliceViewManager.sliceForUri(sliceUri).flowOn(mainCoroutineContext)
- }
- .flowOn(backgroundCoroutineContext)
+ override fun ancSlice(
+ device: BluetoothDevice,
+ width: Int,
+ isCollapsed: Boolean,
+ hideLabel: Boolean
+ ): Flow<Slice?> {
+ val sliceUri =
+ device.getExtraControlUri(width, isCollapsed, hideLabel) ?: return flowOf(null)
+ return sliceViewManager.sliceForUri(sliceUri).flowOn(mainCoroutineContext)
}
private fun BluetoothDevice.getExtraControlUri(
diff --git a/packages/SystemUI/src/com/android/systemui/volume/panel/component/anc/domain/interactor/AncSliceInteractor.kt b/packages/SystemUI/src/com/android/systemui/volume/panel/component/anc/domain/interactor/AncSliceInteractor.kt
index cefa269..cfff457 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/panel/component/anc/domain/interactor/AncSliceInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/volume/panel/component/anc/domain/interactor/AncSliceInteractor.kt
@@ -19,6 +19,8 @@
import android.app.slice.Slice.HINT_ERROR
import android.app.slice.SliceItem.FORMAT_SLICE
import androidx.slice.Slice
+import com.android.systemui.volume.domain.interactor.AudioOutputInteractor
+import com.android.systemui.volume.domain.model.AudioOutputDevice
import com.android.systemui.volume.panel.component.anc.data.repository.AncSliceRepository
import com.android.systemui.volume.panel.component.anc.domain.model.AncSlices
import com.android.systemui.volume.panel.dagger.scope.VolumePanelScope
@@ -32,6 +34,7 @@
import kotlinx.coroutines.flow.combine
import kotlinx.coroutines.flow.filter
import kotlinx.coroutines.flow.flatMapLatest
+import kotlinx.coroutines.flow.flowOf
import kotlinx.coroutines.flow.stateIn
/** Provides a valid slice from [AncSliceRepository]. */
@@ -40,6 +43,7 @@
class AncSliceInteractor
@Inject
constructor(
+ private val audioOutputInteractor: AudioOutputInteractor,
private val ancSliceRepository: AncSliceRepository,
scope: CoroutineScope,
) {
@@ -70,9 +74,20 @@
* remove the labels from the [Slice].
*/
private fun ancSlice(width: Int, isCollapsed: Boolean, hideLabel: Boolean): Flow<Slice?> {
- return ancSliceRepository
- .ancSlice(width = width, isCollapsed = isCollapsed, hideLabel = hideLabel)
- .filter { it?.isValidSlice() != false }
+ return audioOutputInteractor.currentAudioDevice.flatMapLatest { outputDevice ->
+ if (outputDevice is AudioOutputDevice.Bluetooth) {
+ ancSliceRepository
+ .ancSlice(
+ device = outputDevice.cachedBluetoothDevice.device,
+ width = width,
+ isCollapsed = isCollapsed,
+ hideLabel = hideLabel,
+ )
+ .filter { it?.isValidSlice() != false }
+ } else {
+ flowOf(null)
+ }
+ }
}
private fun Slice.isValidSlice(): Boolean {
diff --git a/packages/SystemUI/src/com/android/systemui/volume/panel/component/mediaoutput/domain/interactor/MediaControllerInteractor.kt b/packages/SystemUI/src/com/android/systemui/volume/panel/component/mediaoutput/domain/interactor/MediaControllerInteractor.kt
new file mode 100644
index 0000000..4812765
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/volume/panel/component/mediaoutput/domain/interactor/MediaControllerInteractor.kt
@@ -0,0 +1,96 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.volume.panel.component.mediaoutput.domain.interactor
+
+import android.media.MediaMetadata
+import android.media.session.MediaController
+import android.media.session.MediaSession
+import android.media.session.PlaybackState
+import android.os.Bundle
+import android.os.Handler
+import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.dagger.qualifiers.Background
+import com.android.systemui.utils.coroutines.flow.conflatedCallbackFlow
+import com.android.systemui.volume.panel.component.mediaoutput.domain.model.MediaControllerChangeModel
+import javax.inject.Inject
+import kotlinx.coroutines.channels.ProducerScope
+import kotlinx.coroutines.channels.awaitClose
+import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.launch
+
+interface MediaControllerInteractor {
+
+ /** [MediaController.Callback] flow representation. */
+ fun stateChanges(mediaController: MediaController): Flow<MediaControllerChangeModel>
+}
+
+@SysUISingleton
+class MediaControllerInteractorImpl
+@Inject
+constructor(
+ @Background private val backgroundHandler: Handler,
+) : MediaControllerInteractor {
+
+ override fun stateChanges(mediaController: MediaController): Flow<MediaControllerChangeModel> {
+ return conflatedCallbackFlow {
+ val callback = MediaControllerCallbackProducer(this)
+ mediaController.registerCallback(callback, backgroundHandler)
+ awaitClose { mediaController.unregisterCallback(callback) }
+ }
+ }
+}
+
+private class MediaControllerCallbackProducer(
+ private val producingScope: ProducerScope<MediaControllerChangeModel>
+) : MediaController.Callback() {
+
+ override fun onSessionDestroyed() {
+ send(MediaControllerChangeModel.SessionDestroyed)
+ }
+
+ override fun onSessionEvent(event: String, extras: Bundle?) {
+ send(MediaControllerChangeModel.SessionEvent(event, extras))
+ }
+
+ override fun onPlaybackStateChanged(state: PlaybackState?) {
+ send(MediaControllerChangeModel.PlaybackStateChanged(state))
+ }
+
+ override fun onMetadataChanged(metadata: MediaMetadata?) {
+ send(MediaControllerChangeModel.MetadataChanged(metadata))
+ }
+
+ override fun onQueueChanged(queue: MutableList<MediaSession.QueueItem>?) {
+ send(MediaControllerChangeModel.QueueChanged(queue))
+ }
+
+ override fun onQueueTitleChanged(title: CharSequence?) {
+ send(MediaControllerChangeModel.QueueTitleChanged(title))
+ }
+
+ override fun onExtrasChanged(extras: Bundle?) {
+ send(MediaControllerChangeModel.ExtrasChanged(extras))
+ }
+
+ override fun onAudioInfoChanged(info: MediaController.PlaybackInfo?) {
+ send(MediaControllerChangeModel.AudioInfoChanged(info))
+ }
+
+ private fun send(change: MediaControllerChangeModel) {
+ producingScope.launch { producingScope.send(change) }
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/volume/panel/component/mediaoutput/domain/interactor/MediaDeviceSessionInteractor.kt b/packages/SystemUI/src/com/android/systemui/volume/panel/component/mediaoutput/domain/interactor/MediaDeviceSessionInteractor.kt
index dc73344..599bd73 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/panel/component/mediaoutput/domain/interactor/MediaDeviceSessionInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/volume/panel/component/mediaoutput/domain/interactor/MediaDeviceSessionInteractor.kt
@@ -18,11 +18,9 @@
import android.media.session.MediaController
import android.media.session.PlaybackState
-import android.os.Handler
-import com.android.settingslib.volume.data.repository.MediaControllerChange
import com.android.settingslib.volume.data.repository.MediaControllerRepository
-import com.android.settingslib.volume.data.repository.stateChanges
import com.android.systemui.dagger.qualifiers.Background
+import com.android.systemui.volume.panel.component.mediaoutput.domain.model.MediaControllerChangeModel
import com.android.systemui.volume.panel.component.mediaoutput.shared.model.MediaDeviceSession
import com.android.systemui.volume.panel.dagger.scope.VolumePanelScope
import javax.inject.Inject
@@ -45,38 +43,39 @@
@Inject
constructor(
@Background private val backgroundCoroutineContext: CoroutineContext,
- @Background private val backgroundHandler: Handler,
+ private val mediaControllerInteractor: MediaControllerInteractor,
private val mediaControllerRepository: MediaControllerRepository,
) {
/** [PlaybackState] changes for the [MediaDeviceSession]. */
fun playbackState(session: MediaDeviceSession): Flow<PlaybackState?> {
return stateChanges(session) {
- emit(MediaControllerChange.PlaybackStateChanged(it.playbackState))
+ emit(MediaControllerChangeModel.PlaybackStateChanged(it.playbackState))
}
- .filterIsInstance(MediaControllerChange.PlaybackStateChanged::class)
+ .filterIsInstance(MediaControllerChangeModel.PlaybackStateChanged::class)
.map { it.state }
}
/** [MediaController.PlaybackInfo] changes for the [MediaDeviceSession]. */
fun playbackInfo(session: MediaDeviceSession): Flow<MediaController.PlaybackInfo?> {
return stateChanges(session) {
- emit(MediaControllerChange.AudioInfoChanged(it.playbackInfo))
+ emit(MediaControllerChangeModel.AudioInfoChanged(it.playbackInfo))
}
- .filterIsInstance(MediaControllerChange.AudioInfoChanged::class)
+ .filterIsInstance(MediaControllerChangeModel.AudioInfoChanged::class)
.map { it.info }
}
private fun stateChanges(
session: MediaDeviceSession,
- onStart: suspend FlowCollector<MediaControllerChange>.(controller: MediaController) -> Unit,
- ): Flow<MediaControllerChange?> =
+ onStart:
+ suspend FlowCollector<MediaControllerChangeModel>.(controller: MediaController) -> Unit,
+ ): Flow<MediaControllerChangeModel?> =
mediaControllerRepository.activeSessions
.flatMapLatest { controllers ->
val controller: MediaController =
findControllerForSession(controllers, session)
?: return@flatMapLatest flowOf(null)
- controller.stateChanges(backgroundHandler).onStart { onStart(controller) }
+ mediaControllerInteractor.stateChanges(controller).onStart { onStart(controller) }
}
.flowOn(backgroundCoroutineContext)
diff --git a/packages/SystemUI/src/com/android/systemui/volume/panel/component/mediaoutput/domain/interactor/MediaOutputInteractor.kt b/packages/SystemUI/src/com/android/systemui/volume/panel/component/mediaoutput/domain/interactor/MediaOutputInteractor.kt
index b00829e..9fbd79a 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/panel/component/mediaoutput/domain/interactor/MediaOutputInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/volume/panel/component/mediaoutput/domain/interactor/MediaOutputInteractor.kt
@@ -19,12 +19,10 @@
import android.content.pm.PackageManager
import android.media.VolumeProvider
import android.media.session.MediaController
-import android.os.Handler
import android.util.Log
import com.android.settingslib.media.MediaDevice
import com.android.settingslib.volume.data.repository.LocalMediaRepository
import com.android.settingslib.volume.data.repository.MediaControllerRepository
-import com.android.settingslib.volume.data.repository.stateChanges
import com.android.systemui.dagger.qualifiers.Background
import com.android.systemui.volume.panel.component.mediaoutput.data.repository.LocalMediaRepositoryFactory
import com.android.systemui.volume.panel.component.mediaoutput.domain.model.MediaDeviceSessions
@@ -61,7 +59,7 @@
@VolumePanelScope private val coroutineScope: CoroutineScope,
@Background private val backgroundCoroutineContext: CoroutineContext,
mediaControllerRepository: MediaControllerRepository,
- @Background private val backgroundHandler: Handler,
+ private val mediaControllerInteractor: MediaControllerInteractor,
) {
private val activeMediaControllers: Flow<MediaControllers> =
@@ -194,7 +192,10 @@
return flowOf(null)
}
- return stateChanges(backgroundHandler).map { this }.onStart { emit(this@stateChanges) }
+ return mediaControllerInteractor
+ .stateChanges(this)
+ .map { this }
+ .onStart { emit(this@stateChanges) }
}
private data class MediaControllers(
diff --git a/packages/SystemUI/src/com/android/systemui/volume/panel/component/mediaoutput/domain/model/MediaControllerChangeModel.kt b/packages/SystemUI/src/com/android/systemui/volume/panel/component/mediaoutput/domain/model/MediaControllerChangeModel.kt
new file mode 100644
index 0000000..ef5a44a
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/volume/panel/component/mediaoutput/domain/model/MediaControllerChangeModel.kt
@@ -0,0 +1,45 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.volume.panel.component.mediaoutput.domain.model
+
+import android.media.MediaMetadata
+import android.media.session.MediaController
+import android.media.session.MediaSession
+import android.media.session.PlaybackState
+import android.os.Bundle
+
+/** Models particular change event received by [MediaController.Callback]. */
+sealed interface MediaControllerChangeModel {
+
+ data object SessionDestroyed : MediaControllerChangeModel
+
+ data class SessionEvent(val event: String, val extras: Bundle?) : MediaControllerChangeModel
+
+ data class PlaybackStateChanged(val state: PlaybackState?) : MediaControllerChangeModel
+
+ data class MetadataChanged(val metadata: MediaMetadata?) : MediaControllerChangeModel
+
+ data class QueueChanged(val queue: MutableList<MediaSession.QueueItem>?) :
+ MediaControllerChangeModel
+
+ data class QueueTitleChanged(val title: CharSequence?) : MediaControllerChangeModel
+
+ data class ExtrasChanged(val extras: Bundle?) : MediaControllerChangeModel
+
+ data class AudioInfoChanged(val info: MediaController.PlaybackInfo?) :
+ MediaControllerChangeModel
+}
diff --git a/packages/SystemUI/src/com/android/systemui/volume/panel/component/spatial/domain/interactor/SpatialAudioComponentInteractor.kt b/packages/SystemUI/src/com/android/systemui/volume/panel/component/spatial/domain/interactor/SpatialAudioComponentInteractor.kt
index 298ca67..9ca50d6 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/panel/component/spatial/domain/interactor/SpatialAudioComponentInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/volume/panel/component/spatial/domain/interactor/SpatialAudioComponentInteractor.kt
@@ -18,11 +18,9 @@
import android.media.AudioDeviceAttributes
import android.media.AudioDeviceInfo
-import com.android.settingslib.media.BluetoothMediaDevice
-import com.android.settingslib.media.MediaDevice
-import com.android.settingslib.media.PhoneMediaDevice
import com.android.settingslib.media.domain.interactor.SpatializerInteractor
-import com.android.systemui.volume.panel.component.mediaoutput.domain.interactor.MediaOutputInteractor
+import com.android.systemui.volume.domain.interactor.AudioOutputInteractor
+import com.android.systemui.volume.domain.model.AudioOutputDevice
import com.android.systemui.volume.panel.component.spatial.domain.model.SpatialAudioAvailabilityModel
import com.android.systemui.volume.panel.component.spatial.domain.model.SpatialAudioEnabledModel
import com.android.systemui.volume.panel.dagger.scope.VolumePanelScope
@@ -46,16 +44,20 @@
class SpatialAudioComponentInteractor
@Inject
constructor(
- mediaOutputInteractor: MediaOutputInteractor,
+ audioOutputInteractor: AudioOutputInteractor,
private val spatializerInteractor: SpatializerInteractor,
@VolumePanelScope private val coroutineScope: CoroutineScope,
) {
private val changes = MutableSharedFlow<Unit>()
private val currentAudioDeviceAttributes: StateFlow<AudioDeviceAttributes?> =
- mediaOutputInteractor.currentConnectedDevice
- .map { mediaDevice ->
- if (mediaDevice == null) builtinSpeaker else mediaDevice.getAudioDeviceAttributes()
+ audioOutputInteractor.currentAudioDevice
+ .map { audioDevice ->
+ if (audioDevice is AudioOutputDevice.Unknown) {
+ builtinSpeaker
+ } else {
+ audioDevice.getAudioDeviceAttributes()
+ }
}
.stateIn(coroutineScope, SharingStarted.WhileSubscribed(), builtinSpeaker)
@@ -135,36 +137,35 @@
changes.emit(Unit)
}
- private suspend fun MediaDevice.getAudioDeviceAttributes(): AudioDeviceAttributes? {
+ private suspend fun AudioOutputDevice.getAudioDeviceAttributes(): AudioDeviceAttributes? {
when (this) {
- is PhoneMediaDevice -> return builtinSpeaker
- is BluetoothMediaDevice -> {
- val device = cachedDevice ?: return null
+ is AudioOutputDevice.BuiltIn -> return builtinSpeaker
+ is AudioOutputDevice.Bluetooth -> {
return listOf(
AudioDeviceAttributes(
AudioDeviceAttributes.ROLE_OUTPUT,
AudioDeviceInfo.TYPE_BLE_HEADSET,
- device.address,
+ cachedBluetoothDevice.address,
),
AudioDeviceAttributes(
AudioDeviceAttributes.ROLE_OUTPUT,
AudioDeviceInfo.TYPE_BLE_SPEAKER,
- device.address,
+ cachedBluetoothDevice.address,
),
AudioDeviceAttributes(
AudioDeviceAttributes.ROLE_OUTPUT,
AudioDeviceInfo.TYPE_BLE_BROADCAST,
- device.address,
+ cachedBluetoothDevice.address,
),
AudioDeviceAttributes(
AudioDeviceAttributes.ROLE_OUTPUT,
AudioDeviceInfo.TYPE_BLUETOOTH_A2DP,
- device.address,
+ cachedBluetoothDevice.address,
),
AudioDeviceAttributes(
AudioDeviceAttributes.ROLE_OUTPUT,
AudioDeviceInfo.TYPE_HEARING_AID,
- device.address,
+ cachedBluetoothDevice.address,
)
)
.firstOrNull { spatializerInteractor.isSpatialAudioAvailable(it) }
diff --git a/packages/SystemUI/src/com/android/systemui/wallpapers/ImageWallpaper.java b/packages/SystemUI/src/com/android/systemui/wallpapers/ImageWallpaper.java
index 1568e8c0..2e29bbd 100644
--- a/packages/SystemUI/src/com/android/systemui/wallpapers/ImageWallpaper.java
+++ b/packages/SystemUI/src/com/android/systemui/wallpapers/ImageWallpaper.java
@@ -20,6 +20,7 @@
import static android.app.WallpaperManager.FLAG_SYSTEM;
import static android.app.WallpaperManager.SetWallpaperFlags;
+import static com.android.systemui.Flags.fixImageWallpaperCrashSurfaceAlreadyReleased;
import static com.android.window.flags.Flags.offloadColorExtraction;
import android.annotation.Nullable;
@@ -128,8 +129,17 @@
* and if the count is 0, unload the bitmap
*/
private int mBitmapUsages = 0;
+
+ /**
+ * Main lock for long operations (loading the bitmap or processing colors).
+ */
private final Object mLock = new Object();
+ /**
+ * Lock for SurfaceHolder operations. Should only be acquired after the main lock.
+ */
+ private final Object mSurfaceLock = new Object();
+
CanvasEngine() {
super();
setFixedSizeAllowed(true);
@@ -223,6 +233,12 @@
if (DEBUG) {
Log.i(TAG, "onSurfaceDestroyed");
}
+ if (fixImageWallpaperCrashSurfaceAlreadyReleased()) {
+ synchronized (mSurfaceLock) {
+ mSurfaceHolder = null;
+ }
+ return;
+ }
mLongExecutor.execute(this::onSurfaceDestroyedSynchronized);
}
@@ -259,7 +275,7 @@
}
private void drawFrameInternal() {
- if (mSurfaceHolder == null) {
+ if (mSurfaceHolder == null && !fixImageWallpaperCrashSurfaceAlreadyReleased()) {
Log.i(TAG, "attempt to draw a frame without a valid surface");
return;
}
@@ -268,6 +284,19 @@
if (!isBitmapLoaded()) {
loadWallpaperAndDrawFrameInternal();
} else {
+ if (fixImageWallpaperCrashSurfaceAlreadyReleased()) {
+ synchronized (mSurfaceLock) {
+ if (mSurfaceHolder == null) {
+ Log.i(TAG, "Surface released before the image could be drawn");
+ return;
+ }
+ mBitmapUsages++;
+ drawFrameOnCanvas(mBitmap);
+ reportEngineShown(false);
+ unloadBitmapIfNotUsedInternal();
+ return;
+ }
+ }
mBitmapUsages++;
drawFrameOnCanvas(mBitmap);
reportEngineShown(false);
@@ -328,9 +357,14 @@
mBitmap.recycle();
}
mBitmap = null;
-
- final Surface surface = getSurfaceHolder().getSurface();
- surface.hwuiDestroy();
+ if (fixImageWallpaperCrashSurfaceAlreadyReleased()) {
+ synchronized (mSurfaceLock) {
+ if (mSurfaceHolder != null) mSurfaceHolder.getSurface().hwuiDestroy();
+ }
+ } else {
+ final Surface surface = getSurfaceHolder().getSurface();
+ surface.hwuiDestroy();
+ }
mWallpaperManager.forgetLoadedWallpaper();
Trace.endSection();
}
diff --git a/packages/SystemUI/src/com/android/systemui/wmshell/WMShell.java b/packages/SystemUI/src/com/android/systemui/wmshell/WMShell.java
index b86a7c9..e073f7c 100644
--- a/packages/SystemUI/src/com/android/systemui/wmshell/WMShell.java
+++ b/packages/SystemUI/src/com/android/systemui/wmshell/WMShell.java
@@ -98,7 +98,7 @@
CoreStartable,
CommandQueue.Callbacks {
private static final String TAG = WMShell.class.getName();
- private static final int INVALID_SYSUI_STATE_MASK =
+ private static final long INVALID_SYSUI_STATE_MASK =
SYSUI_STATE_DIALOG_SHOWING
| SYSUI_STATE_STATUS_BAR_KEYGUARD_SHOWING
| SYSUI_STATE_STATUS_BAR_KEYGUARD_SHOWING_OCCLUDED
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/ClockEventControllerTest.kt b/packages/SystemUI/tests/src/com/android/keyguard/ClockEventControllerTest.kt
index 6f550ba..5702a8c 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/ClockEventControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/keyguard/ClockEventControllerTest.kt
@@ -21,14 +21,18 @@
import android.view.ViewTreeObserver
import android.widget.FrameLayout
import androidx.test.filters.SmallTest
-import com.android.systemui.Flags as AConfigFlags
import com.android.systemui.SysuiTestCase
import com.android.systemui.broadcast.BroadcastDispatcher
import com.android.systemui.flags.Flags
import com.android.systemui.keyguard.data.repository.FakeKeyguardRepository
import com.android.systemui.keyguard.domain.interactor.KeyguardInteractorFactory
import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor
-import com.android.systemui.keyguard.shared.model.KeyguardState
+import com.android.systemui.keyguard.shared.model.Edge
+import com.android.systemui.keyguard.shared.model.KeyguardState.AOD
+import com.android.systemui.keyguard.shared.model.KeyguardState.DOZING
+import com.android.systemui.keyguard.shared.model.KeyguardState.GONE
+import com.android.systemui.keyguard.shared.model.KeyguardState.LOCKSCREEN
+import com.android.systemui.keyguard.shared.model.KeyguardState.OCCLUDED
import com.android.systemui.keyguard.shared.model.TransitionState
import com.android.systemui.keyguard.shared.model.TransitionStep
import com.android.systemui.log.core.LogLevel
@@ -68,8 +72,9 @@
import org.mockito.Mockito.never
import org.mockito.Mockito.times
import org.mockito.Mockito.verify
-import org.mockito.Mockito.`when` as whenever
import org.mockito.junit.MockitoJUnit
+import com.android.systemui.Flags as AConfigFlags
+import org.mockito.Mockito.`when` as whenever
@RunWith(AndroidTestingRunner::class)
@SmallTest
@@ -319,26 +324,16 @@
fun listenForDozeAmountTransition_updatesClockDozeAmount() =
runBlocking(IMMEDIATE) {
val transitionStep = MutableStateFlow(TransitionStep())
- whenever(
- keyguardTransitionInteractor.transition(
- KeyguardState.LOCKSCREEN,
- KeyguardState.AOD
- )
- )
+ whenever(keyguardTransitionInteractor.transition(Edge.create(LOCKSCREEN, AOD)))
.thenReturn(transitionStep)
- whenever(
- keyguardTransitionInteractor.transition(
- KeyguardState.AOD,
- KeyguardState.LOCKSCREEN
- )
- )
+ whenever(keyguardTransitionInteractor.transition(Edge.create(AOD, LOCKSCREEN)))
.thenReturn(transitionStep)
val job = underTest.listenForDozeAmountTransition(this)
transitionStep.value =
TransitionStep(
- from = KeyguardState.LOCKSCREEN,
- to = KeyguardState.AOD,
+ from = LOCKSCREEN,
+ to = AOD,
value = 0.4f,
transitionState = TransitionState.RUNNING,
)
@@ -353,14 +348,14 @@
fun listenForTransitionToAodFromGone_updatesClockDozeAmountToOne() =
runBlocking(IMMEDIATE) {
val transitionStep = MutableStateFlow(TransitionStep())
- whenever(keyguardTransitionInteractor.transitionStepsToState(KeyguardState.AOD))
+ whenever(keyguardTransitionInteractor.transitionStepsToState(AOD))
.thenReturn(transitionStep)
val job = underTest.listenForAnyStateToAodTransition(this)
transitionStep.value =
TransitionStep(
- from = KeyguardState.GONE,
- to = KeyguardState.AOD,
+ from = GONE,
+ to = AOD,
transitionState = TransitionState.STARTED,
)
yield()
@@ -374,16 +369,16 @@
fun listenForTransitionToLSFromOccluded_updatesClockDozeAmountToZero() =
runBlocking(IMMEDIATE) {
val transitionStep = MutableStateFlow(TransitionStep())
- whenever(keyguardTransitionInteractor.transitionStepsToState(KeyguardState.LOCKSCREEN))
- .thenReturn(transitionStep)
+ whenever(keyguardTransitionInteractor.transitionStepsToState(LOCKSCREEN))
+ .thenReturn(transitionStep)
val job = underTest.listenForAnyStateToLockscreenTransition(this)
transitionStep.value =
- TransitionStep(
- from = KeyguardState.OCCLUDED,
- to = KeyguardState.LOCKSCREEN,
- transitionState = TransitionState.STARTED,
- )
+ TransitionStep(
+ from = OCCLUDED,
+ to = LOCKSCREEN,
+ transitionState = TransitionState.STARTED,
+ )
yield()
verify(animations, times(2)).doze(0f)
@@ -395,37 +390,37 @@
fun listenForTransitionToAodFromLockscreen_neverUpdatesClockDozeAmount() =
runBlocking(IMMEDIATE) {
val transitionStep = MutableStateFlow(TransitionStep())
- whenever(keyguardTransitionInteractor.transitionStepsToState(KeyguardState.AOD))
+ whenever(keyguardTransitionInteractor.transitionStepsToState(AOD))
.thenReturn(transitionStep)
val job = underTest.listenForAnyStateToAodTransition(this)
transitionStep.value =
TransitionStep(
- from = KeyguardState.LOCKSCREEN,
- to = KeyguardState.AOD,
+ from = LOCKSCREEN,
+ to = AOD,
transitionState = TransitionState.STARTED,
)
yield()
verify(animations, never()).doze(1f)
- job.cancel()
- }
+ job.cancel()
+ }
@Test
fun listenForAnyStateToLockscreenTransition_neverUpdatesClockDozeAmount() =
runBlocking(IMMEDIATE) {
val transitionStep = MutableStateFlow(TransitionStep())
- whenever(keyguardTransitionInteractor.transitionStepsToState(KeyguardState.LOCKSCREEN))
- .thenReturn(transitionStep)
+ whenever(keyguardTransitionInteractor.transitionStepsToState(LOCKSCREEN))
+ .thenReturn(transitionStep)
val job = underTest.listenForAnyStateToLockscreenTransition(this)
transitionStep.value =
- TransitionStep(
- from = KeyguardState.AOD,
- to = KeyguardState.LOCKSCREEN,
- transitionState = TransitionState.STARTED,
- )
+ TransitionStep(
+ from = AOD,
+ to = LOCKSCREEN,
+ transitionState = TransitionState.STARTED,
+ )
yield()
verify(animations, never()).doze(0f)
@@ -437,16 +432,16 @@
fun listenForAnyStateToDozingTransition_UpdatesClockDozeAmountToOne() =
runBlocking(IMMEDIATE) {
val transitionStep = MutableStateFlow(TransitionStep())
- whenever(keyguardTransitionInteractor.transitionStepsToState(KeyguardState.DOZING))
- .thenReturn(transitionStep)
+ whenever(keyguardTransitionInteractor.transitionStepsToState(DOZING))
+ .thenReturn(transitionStep)
val job = underTest.listenForAnyStateToDozingTransition(this)
transitionStep.value =
- TransitionStep(
- from = KeyguardState.LOCKSCREEN,
- to = KeyguardState.DOZING,
- transitionState = TransitionState.STARTED,
- )
+ TransitionStep(
+ from = LOCKSCREEN,
+ to = DOZING,
+ transitionState = TransitionState.STARTED,
+ )
yield()
verify(animations, times(2)).doze(1f)
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardAbsKeyInputViewControllerTest.java b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardAbsKeyInputViewControllerTest.java
index 51ceda3..f9fe5e7 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardAbsKeyInputViewControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardAbsKeyInputViewControllerTest.java
@@ -27,6 +27,7 @@
import static org.mockito.Mockito.when;
import android.os.SystemClock;
+import android.platform.test.annotations.EnableFlags;
import android.testing.AndroidTestingRunner;
import android.testing.TestableLooper.RunWithLooper;
import android.view.KeyEvent;
@@ -125,8 +126,8 @@
}
@Test
+ @EnableFlags(Flags.FLAG_REVAMPED_BOUNCER_MESSAGES)
public void withFeatureFlagOn_oldMessage_isHidden() {
- mSetFlagsRule.enableFlags(Flags.FLAG_REVAMPED_BOUNCER_MESSAGES);
KeyguardAbsKeyInputViewController underTest = createTestObject();
underTest.init();
diff --git a/packages/SystemUI/tests/src/com/android/systemui/accessibility/MagnificationTest.java b/packages/SystemUI/tests/src/com/android/systemui/accessibility/MagnificationTest.java
index 6dc5b72..bbdd805 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/accessibility/MagnificationTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/accessibility/MagnificationTest.java
@@ -27,6 +27,7 @@
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyBoolean;
import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.ArgumentMatchers.anyLong;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.ArgumentMatchers.isNull;
import static org.mockito.Mockito.doAnswer;
@@ -104,7 +105,7 @@
}).when(mAccessibilityManager).setMagnificationConnection(
any(IMagnificationConnection.class));
- when(mSysUiState.setFlag(anyInt(), anyBoolean())).thenReturn(mSysUiState);
+ when(mSysUiState.setFlag(anyLong(), anyBoolean())).thenReturn(mSysUiState);
doAnswer(invocation -> {
mMagnification.mMagnificationSettingsControllerCallback
diff --git a/packages/SystemUI/tests/src/com/android/systemui/accessibility/WindowMagnificationSizePrefsTest.java b/packages/SystemUI/tests/src/com/android/systemui/accessibility/WindowMagnificationFrameSizePrefsTest.java
similarity index 77%
rename from packages/SystemUI/tests/src/com/android/systemui/accessibility/WindowMagnificationSizePrefsTest.java
rename to packages/SystemUI/tests/src/com/android/systemui/accessibility/WindowMagnificationFrameSizePrefsTest.java
index 516b665..93c0eea 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/accessibility/WindowMagnificationSizePrefsTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/accessibility/WindowMagnificationFrameSizePrefsTest.java
@@ -39,9 +39,9 @@
@SmallTest
@RunWith(AndroidTestingRunner.class)
@TestableLooper.RunWithLooper
-public class WindowMagnificationSizePrefsTest extends SysuiTestCase {
+public class WindowMagnificationFrameSizePrefsTest extends SysuiTestCase {
- WindowMagnificationSizePrefs mWindowMagnificationSizePrefs;
+ WindowMagnificationFrameSizePrefs mWindowMagnificationFrameSizePrefs;
FakeSharedPreferences mSharedPreferences;
@Before
@@ -51,24 +51,24 @@
when(mContext.getSharedPreferences(
eq("window_magnification_preferences"), anyInt()))
.thenReturn(mSharedPreferences);
- mWindowMagnificationSizePrefs = new WindowMagnificationSizePrefs(mContext);
+ mWindowMagnificationFrameSizePrefs = new WindowMagnificationFrameSizePrefs(mContext);
}
@Test
public void saveSizeForCurrentDensity_getExpectedSize() {
Size testSize = new Size(500, 500);
- mWindowMagnificationSizePrefs.saveSizeForCurrentDensity(testSize);
+ mWindowMagnificationFrameSizePrefs.saveSizeForCurrentDensity(testSize);
- assertThat(mWindowMagnificationSizePrefs.getSizeForCurrentDensity())
+ assertThat(mWindowMagnificationFrameSizePrefs.getSizeForCurrentDensity())
.isEqualTo(testSize);
}
@Test
public void saveSizeForCurrentDensity_containsPreferenceForCurrentDensity() {
Size testSize = new Size(500, 500);
- mWindowMagnificationSizePrefs.saveSizeForCurrentDensity(testSize);
+ mWindowMagnificationFrameSizePrefs.saveSizeForCurrentDensity(testSize);
- assertThat(mWindowMagnificationSizePrefs.isPreferenceSavedForCurrentDensity())
+ assertThat(mWindowMagnificationFrameSizePrefs.isPreferenceSavedForCurrentDensity())
.isTrue();
}
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/MenuAnimationControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/MenuAnimationControllerTest.java
index e0df1e0..2d5e3a6 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/MenuAnimationControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/MenuAnimationControllerTest.java
@@ -26,7 +26,6 @@
import static org.mockito.Mockito.verifyZeroInteractions;
import android.graphics.PointF;
-import android.platform.test.annotations.EnableFlags;
import android.testing.AndroidTestingRunner;
import android.testing.TestableLooper;
import android.view.View;
@@ -40,7 +39,6 @@
import androidx.dynamicanimation.animation.SpringForce;
import androidx.test.filters.SmallTest;
-import com.android.systemui.Flags;
import com.android.systemui.Prefs;
import com.android.systemui.SysuiTestCase;
import com.android.systemui.accessibility.utils.TestUtils;
@@ -230,7 +228,6 @@
}
@Test
- @EnableFlags(Flags.FLAG_FLOATING_MENU_ANIMATED_TUCK)
public void tuck_animates() {
mMenuAnimationController.cancelAnimations();
mMenuAnimationController.moveToEdgeAndHide();
@@ -239,7 +236,6 @@
}
@Test
- @EnableFlags(Flags.FLAG_FLOATING_MENU_ANIMATED_TUCK)
public void untuck_animates() {
mMenuAnimationController.cancelAnimations();
mMenuAnimationController.moveOutEdgeAndShow();
diff --git a/packages/SystemUI/tests/src/com/android/systemui/accessibility/fontscaling/FontScalingDialogDelegateTest.kt b/packages/SystemUI/tests/src/com/android/systemui/accessibility/fontscaling/FontScalingDialogDelegateTest.kt
index bf6ca06..e371b39 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/accessibility/fontscaling/FontScalingDialogDelegateTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/accessibility/fontscaling/FontScalingDialogDelegateTest.kt
@@ -46,12 +46,12 @@
import org.junit.runner.RunWith
import org.mockito.ArgumentMatchers.any
import org.mockito.ArgumentMatchers.anyBoolean
-import org.mockito.ArgumentMatchers.anyInt
+import org.mockito.ArgumentMatchers.anyLong
import org.mockito.Mock
import org.mockito.Mockito.spy
import org.mockito.Mockito.verify
-import org.mockito.Mockito.`when` as whenever
import org.mockito.MockitoAnnotations
+import org.mockito.Mockito.`when` as whenever
private const val ON: Int = 1
private const val OFF: Int = 0
@@ -90,7 +90,7 @@
secureSettings = FakeSettings()
systemClock = FakeSystemClock()
backgroundDelayableExecutor = FakeExecutor(systemClock)
- whenever(sysuiState.setFlag(anyInt(), anyBoolean())).thenReturn(sysuiState)
+ whenever(sysuiState.setFlag(anyLong(), anyBoolean())).thenReturn(sysuiState)
fontScalingDialogDelegate =
spy(
diff --git a/packages/SystemUI/tests/src/com/android/systemui/accessibility/hearingaid/HearingDevicesDialogDelegateTest.java b/packages/SystemUI/tests/src/com/android/systemui/accessibility/hearingaid/HearingDevicesDialogDelegateTest.java
index ebb6b48..8895a5e 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/accessibility/hearingaid/HearingDevicesDialogDelegateTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/accessibility/hearingaid/HearingDevicesDialogDelegateTest.java
@@ -23,6 +23,7 @@
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyBoolean;
import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.ArgumentMatchers.anyLong;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
@@ -118,7 +119,7 @@
when(mLocalBluetoothManager.getCachedDeviceManager()).thenReturn(mCachedDeviceManager);
when(mCachedDeviceManager.getCachedDevicesCopy()).thenReturn(mDevices);
when(mLocalBluetoothManager.getEventManager()).thenReturn(mBluetoothEventManager);
- when(mSysUiState.setFlag(anyInt(), anyBoolean())).thenReturn(mSysUiState);
+ when(mSysUiState.setFlag(anyLong(), anyBoolean())).thenReturn(mSysUiState);
when(mCachedDevice.getAddress()).thenReturn(DEVICE_ADDRESS);
when(mHearingDeviceItem.getCachedBluetoothDevice()).thenReturn(mCachedDevice);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/animation/ActivityTransitionAnimatorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/animation/ActivityTransitionAnimatorTest.kt
index 8e4c155..fd37cad 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/animation/ActivityTransitionAnimatorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/animation/ActivityTransitionAnimatorTest.kt
@@ -8,6 +8,7 @@
import android.graphics.Point
import android.graphics.Rect
import android.os.Looper
+import android.platform.test.flag.junit.SetFlagsRule
import android.testing.AndroidTestingRunner
import android.testing.TestableLooper.RunWithLooper
import android.view.IRemoteAnimationFinishedCallback
@@ -17,15 +18,20 @@
import android.view.ViewGroup
import android.widget.FrameLayout
import android.widget.LinearLayout
+import android.window.RemoteTransition
+import android.window.TransitionFilter
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
+import com.android.systemui.shared.Flags
import com.android.systemui.util.mockito.any
+import com.android.wm.shell.shared.ShellTransitions
import junit.framework.Assert.assertFalse
import junit.framework.Assert.assertNotNull
import junit.framework.Assert.assertNull
import junit.framework.Assert.assertTrue
import junit.framework.AssertionFailedError
import kotlin.concurrent.thread
+import kotlin.test.assertEquals
import org.junit.After
import org.junit.Assert.assertThrows
import org.junit.Before
@@ -48,6 +54,7 @@
private val transitionContainer = LinearLayout(mContext)
private val mainExecutor = context.mainExecutor
private val testTransitionAnimator = fakeTransitionAnimator(mainExecutor)
+ private val testShellTransitions = FakeShellTransitions()
@Mock lateinit var callback: ActivityTransitionAnimator.Callback
@Mock lateinit var listener: ActivityTransitionAnimator.Listener
@Spy private val controller = TestTransitionAnimatorController(transitionContainer)
@@ -55,12 +62,16 @@
private lateinit var activityTransitionAnimator: ActivityTransitionAnimator
@get:Rule val rule = MockitoJUnit.rule()
+ @get:Rule val setFlagsRule = SetFlagsRule()
@Before
fun setup() {
activityTransitionAnimator =
ActivityTransitionAnimator(
mainExecutor,
+ ActivityTransitionAnimator.TransitionRegister.fromShellTransitions(
+ testShellTransitions
+ ),
testTransitionAnimator,
testTransitionAnimator,
disableWmTimeout = true,
@@ -164,6 +175,34 @@
}
@Test
+ fun registersReturnIffCookieIsPresent() {
+ setFlagsRule.enableFlags(Flags.FLAG_RETURN_ANIMATION_FRAMEWORK_LIBRARY)
+ `when`(callback.isOnKeyguard()).thenReturn(false)
+
+ startIntentWithAnimation(activityTransitionAnimator, controller) { _ ->
+ ActivityManager.START_DELIVERED_TO_TOP
+ }
+
+ waitForIdleSync()
+ assertTrue(testShellTransitions.remotes.isEmpty())
+ assertTrue(testShellTransitions.remotesForTakeover.isEmpty())
+
+ val controller =
+ object : DelegateTransitionAnimatorController(controller) {
+ override val transitionCookie
+ get() = ActivityTransitionAnimator.TransitionCookie("testCookie")
+ }
+
+ startIntentWithAnimation(activityTransitionAnimator, controller) { _ ->
+ ActivityManager.START_DELIVERED_TO_TOP
+ }
+
+ waitForIdleSync()
+ assertEquals(1, testShellTransitions.remotes.size)
+ assertTrue(testShellTransitions.remotesForTakeover.isEmpty())
+ }
+
+ @Test
fun doesNotStartIfAnimationIsCancelled() {
val runner = activityTransitionAnimator.createRunner(controller)
runner.onAnimationCancelled()
@@ -243,6 +282,35 @@
}
/**
+ * A fake implementation of [ShellTransitions] which saves filter-transition pairs locally and
+ * allows inspection.
+ */
+private class FakeShellTransitions : ShellTransitions {
+ val remotes = mutableMapOf<TransitionFilter, RemoteTransition>()
+ val remotesForTakeover = mutableMapOf<TransitionFilter, RemoteTransition>()
+
+ override fun registerRemote(filter: TransitionFilter, remoteTransition: RemoteTransition) {
+ remotes[filter] = remoteTransition
+ }
+
+ override fun registerRemoteForTakeover(
+ filter: TransitionFilter,
+ remoteTransition: RemoteTransition
+ ) {
+ remotesForTakeover[filter] = remoteTransition
+ }
+
+ override fun unregisterRemote(remoteTransition: RemoteTransition) {
+ while (remotes.containsValue(remoteTransition)) {
+ remotes.values.remove(remoteTransition)
+ }
+ while (remotesForTakeover.containsValue(remoteTransition)) {
+ remotesForTakeover.values.remove(remoteTransition)
+ }
+ }
+}
+
+/**
* A simple implementation of [ActivityTransitionAnimator.Controller] which throws if it is called
* outside of the main thread.
*/
diff --git a/packages/SystemUI/tests/src/com/android/systemui/animation/GhostedViewTransitionAnimatorControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/animation/GhostedViewTransitionAnimatorControllerTest.kt
index b31fe21..42fcd54 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/animation/GhostedViewTransitionAnimatorControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/animation/GhostedViewTransitionAnimatorControllerTest.kt
@@ -16,12 +16,16 @@
package com.android.systemui.animation
+import android.os.HandlerThread
import android.testing.AndroidTestingRunner
import android.testing.TestableLooper
+import android.view.View
import android.widget.FrameLayout
import androidx.test.filters.SmallTest
+import com.android.internal.jank.InteractionJankMonitor
import com.android.systemui.SysuiTestCase
import com.android.systemui.animation.view.LaunchableFrameLayout
+import com.google.common.truth.Truth.assertThat
import org.junit.Assert.assertThrows
import org.junit.Test
import org.junit.runner.RunWith
@@ -30,6 +34,13 @@
@RunWith(AndroidTestingRunner::class)
@TestableLooper.RunWithLooper
class GhostedViewTransitionAnimatorControllerTest : SysuiTestCase() {
+ companion object {
+ private const val LAUNCH_CUJ = 0
+ private const val RETURN_CUJ = 1
+ }
+
+ private val interactionJankMonitor = FakeInteractionJankMonitor()
+
@Test
fun animatingOrphanViewDoesNotCrash() {
val state = TransitionAnimator.State(top = 0, bottom = 0, left = 0, right = 0)
@@ -47,4 +58,63 @@
GhostedViewTransitionAnimatorController(FrameLayout(mContext))
}
}
+
+ @Test
+ fun cujsAreLoggedCorrectly() {
+ val parent = FrameLayout(mContext)
+
+ val launchView = LaunchableFrameLayout(mContext)
+ parent.addView((launchView))
+ val launchController =
+ GhostedViewTransitionAnimatorController(
+ launchView,
+ launchCujType = LAUNCH_CUJ,
+ returnCujType = RETURN_CUJ,
+ interactionJankMonitor = interactionJankMonitor
+ )
+ launchController.onTransitionAnimationStart(isExpandingFullyAbove = true)
+ assertThat(interactionJankMonitor.ongoing).containsExactly(LAUNCH_CUJ)
+ launchController.onTransitionAnimationEnd(isExpandingFullyAbove = true)
+ assertThat(interactionJankMonitor.ongoing).isEmpty()
+ assertThat(interactionJankMonitor.finished).containsExactly(LAUNCH_CUJ)
+
+ val returnView = LaunchableFrameLayout(mContext)
+ parent.addView((returnView))
+ val returnController =
+ object : GhostedViewTransitionAnimatorController(
+ returnView,
+ launchCujType = LAUNCH_CUJ,
+ returnCujType = RETURN_CUJ,
+ interactionJankMonitor = interactionJankMonitor
+ ) {
+ override val isLaunching = false
+ }
+ returnController.onTransitionAnimationStart(isExpandingFullyAbove = true)
+ assertThat(interactionJankMonitor.ongoing).containsExactly(RETURN_CUJ)
+ returnController.onTransitionAnimationEnd(isExpandingFullyAbove = true)
+ assertThat(interactionJankMonitor.ongoing).isEmpty()
+ assertThat(interactionJankMonitor.finished).containsExactly(LAUNCH_CUJ, RETURN_CUJ)
+ }
+
+ /**
+ * A fake implementation of [InteractionJankMonitor] which stores ongoing and finished CUJs and
+ * allows inspection.
+ */
+ private class FakeInteractionJankMonitor : InteractionJankMonitor(
+ HandlerThread("testThread")
+ ) {
+ val ongoing: MutableSet<Int> = mutableSetOf()
+ val finished: MutableSet<Int> = mutableSetOf()
+
+ override fun begin(v: View?, cujType: Int): Boolean {
+ ongoing.add(cujType)
+ return true
+ }
+
+ override fun end(cujType: Int): Boolean {
+ ongoing.remove(cujType)
+ finished.add(cujType)
+ return true
+ }
+ }
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/data/repository/PromptRepositoryImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/biometrics/data/repository/PromptRepositoryImplTest.kt
index 5e4272f..2682633 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/data/repository/PromptRepositoryImplTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/data/repository/PromptRepositoryImplTest.kt
@@ -44,6 +44,8 @@
import org.mockito.junit.MockitoJUnit
private const val USER_ID = 9
+private const val REQUEST_ID = 9L
+private const val WRONG_REQUEST_ID = 10L
private const val CHALLENGE = 90L
private const val OP_PACKAGE_NAME = "biometric.testapp"
@@ -105,6 +107,7 @@
repository.setPrompt(
PromptInfo().apply { isConfirmationRequested = case },
USER_ID,
+ REQUEST_ID,
CHALLENGE,
PromptKind.Biometric(),
OP_PACKAGE_NAME
@@ -124,6 +127,7 @@
repository.setPrompt(
PromptInfo().apply { isConfirmationRequested = case },
USER_ID,
+ REQUEST_ID,
CHALLENGE,
PromptKind.Biometric(),
OP_PACKAGE_NAME
@@ -134,12 +138,12 @@
}
@Test
- fun setsAndUnsetsPrompt() =
+ fun setsAndUnsetsPrompt_whenRequestIdMatches() =
testScope.runTest {
val kind = PromptKind.Pin
val promptInfo = PromptInfo()
- repository.setPrompt(promptInfo, USER_ID, CHALLENGE, kind, OP_PACKAGE_NAME)
+ repository.setPrompt(promptInfo, USER_ID, REQUEST_ID, CHALLENGE, kind, OP_PACKAGE_NAME)
assertThat(repository.promptKind.value).isEqualTo(kind)
assertThat(repository.userId.value).isEqualTo(USER_ID)
@@ -147,11 +151,33 @@
assertThat(repository.promptInfo.value).isSameInstanceAs(promptInfo)
assertThat(repository.opPackageName.value).isEqualTo(OP_PACKAGE_NAME)
- repository.unsetPrompt()
+ repository.unsetPrompt(REQUEST_ID)
assertThat(repository.promptInfo.value).isNull()
assertThat(repository.userId.value).isNull()
assertThat(repository.challenge.value).isNull()
assertThat(repository.opPackageName.value).isNull()
}
+
+ @Test
+ fun setsAndUnsetsPrompt_whenRequestIdDoesNotMatch() =
+ testScope.runTest {
+ val kind = PromptKind.Pin
+ val promptInfo = PromptInfo()
+
+ repository.setPrompt(promptInfo, USER_ID, REQUEST_ID, CHALLENGE, kind, OP_PACKAGE_NAME)
+
+ assertThat(repository.promptKind.value).isEqualTo(kind)
+ assertThat(repository.userId.value).isEqualTo(USER_ID)
+ assertThat(repository.challenge.value).isEqualTo(CHALLENGE)
+ assertThat(repository.promptInfo.value).isSameInstanceAs(promptInfo)
+ assertThat(repository.opPackageName.value).isEqualTo(OP_PACKAGE_NAME)
+
+ repository.unsetPrompt(WRONG_REQUEST_ID)
+
+ assertThat(repository.promptInfo.value).isNotNull()
+ assertThat(repository.userId.value).isNotNull()
+ assertThat(repository.challenge.value).isNotNull()
+ assertThat(repository.opPackageName.value).isNotNull()
+ }
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/domain/interactor/PromptCredentialInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/biometrics/domain/interactor/PromptCredentialInteractorTest.kt
index 8695c01..c4d0d23 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/domain/interactor/PromptCredentialInteractorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/domain/interactor/PromptCredentialInteractorTest.kt
@@ -33,6 +33,7 @@
import org.mockito.junit.MockitoJUnit
private const val USER_ID = 22
+private const val REQUEST_ID = 22L
private const val OPERATION_ID = 100L
private const val OP_PACKAGE_NAME = "biometric.testapp"
@@ -112,6 +113,7 @@
},
kind = PromptKind.Pin,
userId = USER_ID,
+ requestId = REQUEST_ID,
challenge = OPERATION_ID,
opPackageName = OP_PACKAGE_NAME
)
@@ -137,6 +139,7 @@
},
kind = PromptKind.Pin,
userId = USER_ID,
+ requestId = REQUEST_ID,
challenge = OPERATION_ID,
opPackageName = OP_PACKAGE_NAME
)
@@ -165,6 +168,7 @@
},
kind = PromptKind.Pin,
userId = USER_ID,
+ requestId = REQUEST_ID,
challenge = OPERATION_ID,
opPackageName = OP_PACKAGE_NAME
)
@@ -198,6 +202,7 @@
},
kind = kind,
userId = USER_ID,
+ requestId = REQUEST_ID,
challenge = OPERATION_ID,
opPackageName = OP_PACKAGE_NAME
)
@@ -223,7 +228,7 @@
assertThat(pattern.stealthMode).isEqualTo(isStealth)
}
- interactor.resetPrompt()
+ interactor.resetPrompt(REQUEST_ID)
assertThat(prompt).isNull()
}
@@ -346,12 +351,14 @@
promptInfo: PromptInfo,
kind: PromptKind,
userId: Int,
+ requestId: Long,
challenge: Long,
opPackageName: String,
) {
biometricPromptRepository.setPrompt(
promptInfo,
userId,
+ requestId,
challenge,
kind,
opPackageName,
@@ -359,8 +366,8 @@
}
/** Unset the current authentication request. */
- private fun PromptCredentialInteractor.resetPrompt() {
- biometricPromptRepository.unsetPrompt()
+ private fun PromptCredentialInteractor.resetPrompt(requestId: Long) {
+ biometricPromptRepository.unsetPrompt(requestId)
}
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/domain/interactor/PromptSelectorInteractorImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/biometrics/domain/interactor/PromptSelectorInteractorImplTest.kt
index 4068404..3102a84 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/domain/interactor/PromptSelectorInteractorImplTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/domain/interactor/PromptSelectorInteractorImplTest.kt
@@ -59,6 +59,7 @@
private const val NEGATIVE_TEXT = "escape"
private const val USER_ID = 8
+ private const val REQUEST_ID = 8L
private const val CHALLENGE = 999L
private const val OP_PACKAGE_NAME = "biometric.testapp"
private val componentNameOverriddenForConfirmDeviceCredentialActivity =
@@ -150,6 +151,7 @@
interactor.setPrompt(
info,
USER_ID,
+ REQUEST_ID,
modalities,
CHALLENGE,
OP_PACKAGE_NAME,
@@ -179,7 +181,7 @@
}
assertThat(isConfirmationRequired).isEqualTo(confirmationRequired)
- interactor.resetPrompt()
+ interactor.resetPrompt(REQUEST_ID)
verifyUnset()
}
@@ -206,6 +208,7 @@
interactor.setPrompt(
info,
USER_ID,
+ REQUEST_ID,
modalities,
CHALLENGE,
OP_PACKAGE_NAME,
@@ -214,7 +217,7 @@
assertThat(promptKind?.isBiometric()).isTrue()
- interactor.resetPrompt()
+ interactor.resetPrompt(REQUEST_ID)
verifyUnset()
}
@@ -230,6 +233,7 @@
interactor.setPrompt(
info,
USER_ID,
+ REQUEST_ID,
modalities,
CHALLENGE,
OP_PACKAGE_NAME,
@@ -238,7 +242,7 @@
assertThat(promptKind).isEqualTo(PromptKind.Password)
- interactor.resetPrompt()
+ interactor.resetPrompt(REQUEST_ID)
verifyUnset()
}
@@ -258,6 +262,7 @@
interactor.setPrompt(
info,
USER_ID,
+ REQUEST_ID,
modalities,
CHALLENGE,
OP_PACKAGE_NAME,
@@ -266,7 +271,7 @@
assertThat(promptKind).isEqualTo(PromptKind.Password)
- interactor.resetPrompt()
+ interactor.resetPrompt(REQUEST_ID)
verifyUnset()
}
@@ -290,6 +295,7 @@
interactor.setPrompt(
info,
USER_ID,
+ REQUEST_ID,
modalities,
CHALLENGE,
OP_PACKAGE_NAME,
@@ -298,7 +304,7 @@
assertThat(promptKind).isEqualTo(PromptKind.Password)
- interactor.resetPrompt()
+ interactor.resetPrompt(REQUEST_ID)
verifyUnset()
}
@@ -319,6 +325,7 @@
interactor.setPrompt(
info,
USER_ID,
+ REQUEST_ID,
modalities,
CHALLENGE,
OP_PACKAGE_NAME,
@@ -327,7 +334,7 @@
assertThat(promptKind?.isBiometric()).isTrue()
- interactor.resetPrompt()
+ interactor.resetPrompt(REQUEST_ID)
verifyUnset()
}
@@ -355,6 +362,7 @@
interactor.setPrompt(
info,
USER_ID,
+ REQUEST_ID,
BiometricModalities(),
CHALLENGE,
OP_PACKAGE_NAME,
@@ -365,7 +373,7 @@
assertThat(currentPrompt).isNull()
assertThat(credentialKind).isEqualTo(PromptKind.None)
- interactor.resetPrompt()
+ interactor.resetPrompt(REQUEST_ID)
verifyUnset()
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/ui/viewmodel/CredentialViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/biometrics/ui/viewmodel/CredentialViewModelTest.kt
index 3245020..9e804c1 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/ui/viewmodel/CredentialViewModelTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/ui/viewmodel/CredentialViewModelTest.kt
@@ -22,6 +22,7 @@
import org.junit.runners.JUnit4
private const val USER_ID = 9
+private const val REQUEST_ID = 9L
private const val OPERATION_ID = 10L
@OptIn(ExperimentalCoroutinesApi::class)
@@ -171,7 +172,7 @@
) =
runTest(dispatcher) {
init()
- promptRepository.setPrompt(promptInfo(), USER_ID, OPERATION_ID, kind)
+ promptRepository.setPrompt(promptInfo(), USER_ID, REQUEST_ID, OPERATION_ID, kind)
block()
}
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/ui/viewmodel/PromptViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/biometrics/ui/viewmodel/PromptViewModelTest.kt
index 97c3c42..53ccb90 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/ui/viewmodel/PromptViewModelTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/ui/viewmodel/PromptViewModelTest.kt
@@ -37,6 +37,7 @@
import android.hardware.face.FaceSensorPropertiesInternal
import android.hardware.fingerprint.FingerprintSensorProperties
import android.hardware.fingerprint.FingerprintSensorPropertiesInternal
+import android.platform.test.annotations.EnableFlags
import android.view.HapticFeedbackConstants
import android.view.MotionEvent
import androidx.test.filters.SmallTest
@@ -95,6 +96,7 @@
import org.mockito.junit.MockitoJUnit
private const val USER_ID = 4
+private const val REQUEST_ID = 4L
private const val CHALLENGE = 2L
private const val DELAY = 1000L
private const val OP_PACKAGE_NAME = "biometric.testapp"
@@ -1272,8 +1274,8 @@
}
@Test
+ @EnableFlags(FLAG_BP_TALKBACK)
fun hint_for_talkback_guidance() = runGenericTest {
- mSetFlagsRule.enableFlags(FLAG_BP_TALKBACK)
val hint by collectLastValue(viewModel.accessibilityHint)
// Touches should fall outside of sensor area
@@ -1295,10 +1297,9 @@
}
@Test
+ @EnableFlags(FLAG_CUSTOM_BIOMETRIC_PROMPT, FLAG_CONSTRAINT_BP)
fun descriptionOverriddenByVerticalListContentView() =
runGenericTest(contentView = promptContentView, description = "test description") {
- mSetFlagsRule.enableFlags(FLAG_CUSTOM_BIOMETRIC_PROMPT)
- mSetFlagsRule.enableFlags(FLAG_CONSTRAINT_BP)
val contentView by collectLastValue(viewModel.contentView)
val description by collectLastValue(viewModel.description)
@@ -1307,13 +1308,12 @@
}
@Test
+ @EnableFlags(FLAG_CUSTOM_BIOMETRIC_PROMPT, FLAG_CONSTRAINT_BP)
fun descriptionOverriddenByContentViewWithMoreOptionsButton() =
runGenericTest(
contentView = promptContentViewWithMoreOptionsButton,
description = "test description"
) {
- mSetFlagsRule.enableFlags(FLAG_CUSTOM_BIOMETRIC_PROMPT)
- mSetFlagsRule.enableFlags(FLAG_CONSTRAINT_BP)
val contentView by collectLastValue(viewModel.contentView)
val description by collectLastValue(viewModel.description)
@@ -1322,10 +1322,9 @@
}
@Test
+ @EnableFlags(FLAG_CUSTOM_BIOMETRIC_PROMPT, FLAG_CONSTRAINT_BP)
fun descriptionWithoutContentView() =
runGenericTest(description = "test description") {
- mSetFlagsRule.enableFlags(FLAG_CUSTOM_BIOMETRIC_PROMPT)
- mSetFlagsRule.enableFlags(FLAG_CONSTRAINT_BP)
val contentView by collectLastValue(viewModel.contentView)
val description by collectLastValue(viewModel.description)
@@ -1334,19 +1333,17 @@
}
@Test
+ @EnableFlags(FLAG_CUSTOM_BIOMETRIC_PROMPT, FLAG_CONSTRAINT_BP)
fun logo_nullIfPkgNameNotFound() =
runGenericTest(packageName = OP_PACKAGE_NAME_CAN_NOT_BE_FOUND) {
- mSetFlagsRule.enableFlags(FLAG_CUSTOM_BIOMETRIC_PROMPT)
- mSetFlagsRule.enableFlags(FLAG_CONSTRAINT_BP)
val logo by collectLastValue(viewModel.logo)
assertThat(logo).isNull()
}
@Test
+ @EnableFlags(FLAG_CUSTOM_BIOMETRIC_PROMPT, FLAG_CONSTRAINT_BP)
fun logo_defaultWithOverrides() =
runGenericTest(packageName = packageNameForLogoWithOverrides) {
- mSetFlagsRule.enableFlags(FLAG_CUSTOM_BIOMETRIC_PROMPT)
- mSetFlagsRule.enableFlags(FLAG_CONSTRAINT_BP)
val logo by collectLastValue(viewModel.logo)
// 1. PM.getApplicationInfo(packageNameForLogoWithOverrides) is set to return
@@ -1357,71 +1354,63 @@
}
@Test
+ @EnableFlags(FLAG_CUSTOM_BIOMETRIC_PROMPT, FLAG_CONSTRAINT_BP)
fun logo_defaultIsNull() =
runGenericTest(packageName = OP_PACKAGE_NAME_NO_ICON) {
- mSetFlagsRule.enableFlags(FLAG_CUSTOM_BIOMETRIC_PROMPT)
- mSetFlagsRule.enableFlags(FLAG_CONSTRAINT_BP)
val logo by collectLastValue(viewModel.logo)
assertThat(logo).isNull()
}
@Test
+ @EnableFlags(FLAG_CUSTOM_BIOMETRIC_PROMPT, FLAG_CONSTRAINT_BP)
fun logo_default() = runGenericTest {
- mSetFlagsRule.enableFlags(FLAG_CUSTOM_BIOMETRIC_PROMPT)
- mSetFlagsRule.enableFlags(FLAG_CONSTRAINT_BP)
val logo by collectLastValue(viewModel.logo)
assertThat(logo).isEqualTo(defaultLogoIcon)
}
@Test
+ @EnableFlags(FLAG_CUSTOM_BIOMETRIC_PROMPT, FLAG_CONSTRAINT_BP)
fun logo_resSetByApp() =
runGenericTest(logoRes = logoResFromApp) {
- mSetFlagsRule.enableFlags(FLAG_CUSTOM_BIOMETRIC_PROMPT)
- mSetFlagsRule.enableFlags(FLAG_CONSTRAINT_BP)
val logo by collectLastValue(viewModel.logo)
assertThat(logo).isEqualTo(logoFromApp)
}
@Test
+ @EnableFlags(FLAG_CUSTOM_BIOMETRIC_PROMPT, FLAG_CONSTRAINT_BP)
fun logo_bitmapSetByApp() =
runGenericTest(logoBitmap = logoBitmapFromApp) {
- mSetFlagsRule.enableFlags(FLAG_CUSTOM_BIOMETRIC_PROMPT)
- mSetFlagsRule.enableFlags(FLAG_CONSTRAINT_BP)
val logo by collectLastValue(viewModel.logo)
assertThat((logo as BitmapDrawable).bitmap).isEqualTo(logoBitmapFromApp)
}
@Test
+ @EnableFlags(FLAG_CUSTOM_BIOMETRIC_PROMPT, FLAG_CONSTRAINT_BP)
fun logoDescription_emptyIfPkgNameNotFound() =
runGenericTest(packageName = OP_PACKAGE_NAME_CAN_NOT_BE_FOUND) {
- mSetFlagsRule.enableFlags(FLAG_CUSTOM_BIOMETRIC_PROMPT)
- mSetFlagsRule.enableFlags(FLAG_CONSTRAINT_BP)
val logoDescription by collectLastValue(viewModel.logoDescription)
assertThat(logoDescription).isEqualTo("")
}
@Test
+ @EnableFlags(FLAG_CUSTOM_BIOMETRIC_PROMPT, FLAG_CONSTRAINT_BP)
fun logoDescription_defaultIsEmpty() =
runGenericTest(packageName = OP_PACKAGE_NAME_NO_ICON) {
- mSetFlagsRule.enableFlags(FLAG_CUSTOM_BIOMETRIC_PROMPT)
- mSetFlagsRule.enableFlags(FLAG_CONSTRAINT_BP)
val logoDescription by collectLastValue(viewModel.logoDescription)
assertThat(logoDescription).isEqualTo("")
}
@Test
+ @EnableFlags(FLAG_CUSTOM_BIOMETRIC_PROMPT, FLAG_CONSTRAINT_BP)
fun logoDescription_default() = runGenericTest {
- mSetFlagsRule.enableFlags(FLAG_CUSTOM_BIOMETRIC_PROMPT)
- mSetFlagsRule.enableFlags(FLAG_CONSTRAINT_BP)
val logoDescription by collectLastValue(viewModel.logoDescription)
assertThat(logoDescription).isEqualTo(defaultLogoDescription)
}
@Test
+ @EnableFlags(FLAG_CUSTOM_BIOMETRIC_PROMPT, FLAG_CONSTRAINT_BP)
fun logoDescription_setByApp() =
runGenericTest(logoDescription = logoDescriptionFromApp) {
- mSetFlagsRule.enableFlags(FLAG_CUSTOM_BIOMETRIC_PROMPT)
- mSetFlagsRule.enableFlags(FLAG_CONSTRAINT_BP)
val logoDescription by collectLastValue(viewModel.logoDescription)
assertThat(logoDescription).isEqualTo(logoDescriptionFromApp)
}
@@ -1469,7 +1458,7 @@
whenever(activityTaskManager.getTasks(1)).thenReturn(listOf(runningTaskInfo))
selector =
PromptSelectorInteractorImpl(fingerprintRepository, promptRepository, lockPatternUtils)
- selector.resetPrompt()
+ selector.resetPrompt(REQUEST_ID)
viewModel =
PromptViewModel(
@@ -1700,6 +1689,7 @@
setPrompt(
info,
USER_ID,
+ REQUEST_ID,
BiometricModalities(fingerprintProperties = fingerprint, faceProperties = face),
CHALLENGE,
packageName,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/bluetooth/BroadcastDialogDelegateTest.java b/packages/SystemUI/tests/src/com/android/systemui/bluetooth/BroadcastDialogDelegateTest.java
index a569cee..49f2043 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/bluetooth/BroadcastDialogDelegateTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/bluetooth/BroadcastDialogDelegateTest.java
@@ -21,7 +21,7 @@
import static com.google.common.truth.Truth.assertThat;
import static org.mockito.ArgumentMatchers.anyBoolean;
-import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.ArgumentMatchers.anyLong;
import static org.mockito.Mockito.any;
import static org.mockito.Mockito.eq;
import static org.mockito.Mockito.mock;
@@ -92,7 +92,7 @@
when(mLocalBluetoothManager.getProfileManager()).thenReturn(mLocalBluetoothProfileManager);
when(mLocalBluetoothProfileManager.getLeAudioBroadcastProfile()).thenReturn(null);
- when(mSysUiState.setFlag(anyInt(), anyBoolean())).thenReturn(mSysUiState);
+ when(mSysUiState.setFlag(anyLong(), anyBoolean())).thenReturn(mSysUiState);
when(mSystemUIDialogFactory.create(any(), any())).thenReturn(mDialog);
mBroadcastDialogDelegate = new BroadcastDialogDelegate(
diff --git a/packages/SystemUI/tests/src/com/android/systemui/bluetooth/qsdialog/BluetoothTileDialogDelegateTest.kt b/packages/SystemUI/tests/src/com/android/systemui/bluetooth/qsdialog/BluetoothTileDialogDelegateTest.kt
index 62c98b0..7215619 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/bluetooth/qsdialog/BluetoothTileDialogDelegateTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/bluetooth/qsdialog/BluetoothTileDialogDelegateTest.kt
@@ -50,7 +50,7 @@
import org.junit.Test
import org.junit.runner.RunWith
import org.mockito.ArgumentMatchers.anyBoolean
-import org.mockito.ArgumentMatchers.anyInt
+import org.mockito.ArgumentMatchers.anyLong
import org.mockito.Mock
import org.mockito.Mockito.`when`
import org.mockito.junit.MockitoJUnit
@@ -104,7 +104,7 @@
dispatcher = UnconfinedTestDispatcher(scheduler)
testScope = TestScope(dispatcher)
- whenever(sysuiState.setFlag(anyInt(), anyBoolean())).thenReturn(sysuiState)
+ whenever(sysuiState.setFlag(anyLong(), anyBoolean())).thenReturn(sysuiState)
mBluetoothTileDialogDelegate =
BluetoothTileDialogDelegate(
diff --git a/packages/SystemUI/tests/src/com/android/systemui/bluetooth/qsdialog/BluetoothTileDialogViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/bluetooth/qsdialog/BluetoothTileDialogViewModelTest.kt
index f62a55d..11f74c0 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/bluetooth/qsdialog/BluetoothTileDialogViewModelTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/bluetooth/qsdialog/BluetoothTileDialogViewModelTest.kt
@@ -17,6 +17,7 @@
package com.android.systemui.bluetooth.qsdialog
import android.bluetooth.BluetoothAdapter
+import android.platform.test.annotations.EnableFlags
import android.testing.AndroidTestingRunner
import android.testing.TestableLooper
import android.view.View
@@ -63,6 +64,7 @@
@SmallTest
@RunWith(AndroidTestingRunner::class)
@TestableLooper.RunWithLooper(setAsMainLooper = true)
+@EnableFlags(Flags.FLAG_BLUETOOTH_QS_TILE_DIALOG_AUTO_ON_TOGGLE)
class BluetoothTileDialogViewModelTest : SysuiTestCase() {
@get:Rule val mockitoRule: MockitoRule = MockitoJUnit.rule()
@@ -113,7 +115,6 @@
@Before
fun setUp() {
- mSetFlagsRule.enableFlags(Flags.FLAG_BLUETOOTH_QS_TILE_DIALOG_AUTO_ON_TOGGLE)
scheduler = TestCoroutineScheduler()
dispatcher = UnconfinedTestDispatcher(scheduler)
testScope = TestScope(dispatcher)
diff --git a/packages/SystemUI/tests/src/com/android/systemui/classifier/FalsingCollectorImplTest.java b/packages/SystemUI/tests/src/com/android/systemui/classifier/FalsingCollectorImplTest.java
index bc6c459..5361cef 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/classifier/FalsingCollectorImplTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/classifier/FalsingCollectorImplTest.java
@@ -35,9 +35,13 @@
import com.android.keyguard.KeyguardUpdateMonitor;
import com.android.systemui.SysuiTestCase;
import com.android.systemui.communal.domain.interactor.CommunalInteractor;
+import com.android.systemui.deviceentry.domain.interactor.DeviceEntryInteractor;
import com.android.systemui.dock.DockManager;
import com.android.systemui.dock.DockManagerFake;
+import com.android.systemui.flags.DisableSceneContainer;
+import com.android.systemui.flags.EnableSceneContainer;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
+import com.android.systemui.scene.domain.interactor.SceneContainerOcclusionInteractor;
import com.android.systemui.shade.domain.interactor.ShadeInteractor;
import com.android.systemui.statusbar.StatusBarState;
import com.android.systemui.statusbar.SysuiStatusBarStateController;
@@ -50,6 +54,7 @@
import com.android.systemui.util.sensors.ThresholdSensor;
import com.android.systemui.util.time.FakeSystemClock;
+import kotlinx.coroutines.flow.MutableStateFlow;
import kotlinx.coroutines.flow.StateFlowKt;
import org.junit.Before;
@@ -89,6 +94,14 @@
private SelectedUserInteractor mSelectedUserInteractor;
@Mock
private CommunalInteractor mCommunalInteractor;
+ @Mock
+ private DeviceEntryInteractor mDeviceEntryInteractor;
+ private final MutableStateFlow<Boolean> mIsDeviceEntered =
+ StateFlowKt.MutableStateFlow(false);
+ @Mock
+ private SceneContainerOcclusionInteractor mSceneContainerOcclusionInteractor;
+ private final MutableStateFlow<Boolean> mIsInvisibleDueToOcclusion =
+ StateFlowKt.MutableStateFlow(false);
private final DockManagerFake mDockManager = new DockManagerFake();
private final FakeSystemClock mFakeSystemClock = new FakeSystemClock();
private final FakeExecutor mFakeExecutor = new FakeExecutor(mFakeSystemClock);
@@ -99,15 +112,21 @@
when(mStatusBarStateController.getState()).thenReturn(StatusBarState.KEYGUARD);
when(mKeyguardStateController.isShowing()).thenReturn(true);
+ when(mKeyguardStateController.isOccluded()).thenReturn(false);
when(mShadeInteractor.isQsExpanded()).thenReturn(StateFlowKt.MutableStateFlow(false));
+ when(mDeviceEntryInteractor.isDeviceEntered()).thenReturn(mIsDeviceEntered);
+ when(mSceneContainerOcclusionInteractor.getInvisibleDueToOcclusion()).thenReturn(
+ mIsInvisibleDueToOcclusion);
+
mFalsingCollector = new FalsingCollectorImpl(mFalsingDataProvider, mFalsingManager,
mKeyguardUpdateMonitor, mHistoryTracker, mProximitySensor,
mStatusBarStateController, mKeyguardStateController,
() -> mShadeInteractor, mBatteryController,
mDockManager, mFakeExecutor,
mJavaAdapter, mFakeSystemClock, () -> mSelectedUserInteractor,
- () -> mCommunalInteractor
+ () -> mCommunalInteractor, () -> mDeviceEntryInteractor,
+ () -> mSceneContainerOcclusionInteractor
);
mFalsingCollector.init();
}
@@ -189,7 +208,8 @@
}
@Test
- public void testRegisterSensor_OccludingActivity() {
+ @DisableSceneContainer
+ public void testRegisterSensor_OccludingActivity_sceneContainerDisabled() {
when(mKeyguardStateController.isOccluded()).thenReturn(true);
ArgumentCaptor<StatusBarStateController.StateListener> stateListenerArgumentCaptor =
@@ -203,6 +223,21 @@
}
@Test
+ @EnableSceneContainer
+ public void testRegisterSensor_OccludingActivity_sceneContainerEnabled() {
+ mIsInvisibleDueToOcclusion.setValue(true);
+
+ ArgumentCaptor<StatusBarStateController.StateListener> stateListenerArgumentCaptor =
+ ArgumentCaptor.forClass(StatusBarStateController.StateListener.class);
+ verify(mStatusBarStateController).addCallback(stateListenerArgumentCaptor.capture());
+
+ mFalsingCollector.onScreenTurningOn();
+ reset(mProximitySensor);
+ stateListenerArgumentCaptor.getValue().onStateChanged(StatusBarState.SHADE);
+ verify(mProximitySensor).register(any(ThresholdSensor.Listener.class));
+ }
+
+ @Test
public void testPassThroughEnterKeyEvent() {
KeyEvent enterDown = KeyEvent.obtain(0, 0, MotionEvent.ACTION_DOWN, KeyEvent.KEYCODE_ENTER,
0, 0, 0, 0, 0, 0, 0, "");
@@ -280,7 +315,8 @@
}
@Test
- public void testAvoidUnlocked() {
+ @DisableSceneContainer
+ public void testAvoidUnlocked_sceneContainerDisabled() {
MotionEvent down = MotionEvent.obtain(0, 0, MotionEvent.ACTION_DOWN, 0, 0, 0);
MotionEvent up = MotionEvent.obtain(0, 0, MotionEvent.ACTION_UP, 0, 0, 0);
@@ -296,6 +332,23 @@
}
@Test
+ @EnableSceneContainer
+ public void testAvoidUnlocked_sceneContainerEnabled() {
+ MotionEvent down = MotionEvent.obtain(0, 0, MotionEvent.ACTION_DOWN, 0, 0, 0);
+ MotionEvent up = MotionEvent.obtain(0, 0, MotionEvent.ACTION_UP, 0, 0, 0);
+
+ mIsDeviceEntered.setValue(true);
+
+ // Nothing passed initially
+ mFalsingCollector.onTouchEvent(down);
+ verify(mFalsingDataProvider, never()).onMotionEvent(any(MotionEvent.class));
+
+ // Up event would normally flush the up event, but doesn't.
+ mFalsingCollector.onTouchEvent(up);
+ verify(mFalsingDataProvider, never()).onMotionEvent(any(MotionEvent.class));
+ }
+
+ @Test
public void testGestureWhenDozing() {
// We check the FalsingManager for taps during the transition to AoD (dozing=true,
// pulsing=false), so the FalsingCollector needs to continue to analyze events that occur
diff --git a/packages/SystemUI/tests/src/com/android/systemui/contrast/ContrastDialogDelegateTest.kt b/packages/SystemUI/tests/src/com/android/systemui/contrast/ContrastDialogDelegateTest.kt
deleted file mode 100644
index ab03465..0000000
--- a/packages/SystemUI/tests/src/com/android/systemui/contrast/ContrastDialogDelegateTest.kt
+++ /dev/null
@@ -1,112 +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.contrast
-
-import android.app.UiModeManager
-import android.app.UiModeManager.ContrastUtils.fromContrastLevel
-import android.os.Looper
-import android.provider.Settings
-import android.testing.AndroidTestingRunner
-import android.testing.TestableLooper.RunWithLooper
-import android.view.LayoutInflater
-import android.view.View
-import android.widget.FrameLayout
-import androidx.test.filters.SmallTest
-import com.android.systemui.SysuiTestCase
-import com.android.systemui.animation.DialogTransitionAnimator
-import com.android.systemui.flags.FakeFeatureFlags
-import com.android.systemui.flags.FeatureFlags
-import com.android.systemui.model.SysUiState
-import com.android.systemui.settings.UserTracker
-import com.android.systemui.statusbar.phone.SystemUIDialog
-import com.android.systemui.util.concurrency.FakeExecutor
-import com.android.systemui.util.mockito.any
-import com.android.systemui.util.mockito.whenever
-import com.android.systemui.util.settings.SecureSettings
-import com.android.systemui.util.time.FakeSystemClock
-import org.junit.Before
-import org.junit.Test
-import org.junit.runner.RunWith
-import org.mockito.ArgumentCaptor
-import org.mockito.ArgumentMatchers.anyInt
-import org.mockito.Mock
-import org.mockito.Mockito.eq
-import org.mockito.Mockito.verify
-import org.mockito.MockitoAnnotations
-
-/** Test the behaviour of buttons of the [ContrastDialogDelegate]. */
-@SmallTest
-@RunWith(AndroidTestingRunner::class)
-@RunWithLooper
-class ContrastDialogDelegateTest : SysuiTestCase() {
-
- private val mainExecutor = FakeExecutor(FakeSystemClock())
- private lateinit var mContrastDialogDelegate: ContrastDialogDelegate
- @Mock private lateinit var sysuiDialogFactory: SystemUIDialog.Factory
- @Mock private lateinit var sysuiDialog: SystemUIDialog
- @Mock private lateinit var mockUiModeManager: UiModeManager
- @Mock private lateinit var mockUserTracker: UserTracker
- @Mock private lateinit var mockSecureSettings: SecureSettings
- @Mock private lateinit var sysuiState: SysUiState
-
- @Before
- fun setUp() {
- MockitoAnnotations.initMocks(this)
- mDependency.injectTestDependency(FeatureFlags::class.java, FakeFeatureFlags())
- mDependency.injectTestDependency(SysUiState::class.java, sysuiState)
- mDependency.injectMockDependency(DialogTransitionAnimator::class.java)
- whenever(sysuiState.setFlag(any(), any())).thenReturn(sysuiState)
- whenever(sysuiDialogFactory.create(any(SystemUIDialog.Delegate::class.java)))
- .thenReturn(sysuiDialog)
- whenever(sysuiDialog.layoutInflater).thenReturn(LayoutInflater.from(mContext))
-
- whenever(mockUserTracker.userId).thenReturn(context.userId)
- if (Looper.myLooper() == null) Looper.prepare()
-
- mContrastDialogDelegate =
- ContrastDialogDelegate(
- sysuiDialogFactory,
- mainExecutor,
- mockUiModeManager,
- mockUserTracker,
- mockSecureSettings
- )
-
- mContrastDialogDelegate.createDialog()
- val viewCaptor = ArgumentCaptor.forClass(View::class.java)
- verify(sysuiDialog).setView(viewCaptor.capture())
- whenever(sysuiDialog.requireViewById(anyInt()) as View?).then {
- viewCaptor.value.requireViewById(it.getArgument(0))
- }
- }
-
- @Test
- fun testClickButtons_putsContrastInSettings() {
- mContrastDialogDelegate.onCreate(sysuiDialog, null)
-
- mContrastDialogDelegate.contrastButtons.forEach {
- (contrastLevel: Int, clickedButton: FrameLayout) ->
- clickedButton.performClick()
- mainExecutor.runAllReady()
- verify(mockSecureSettings)
- .putFloatForUser(
- eq(Settings.Secure.CONTRAST_LEVEL),
- eq(fromContrastLevel(contrastLevel)),
- eq(context.userId)
- )
- }
- }
-}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyboard/shortcut/ui/viewmodel/ShortcutHelperViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyboard/shortcut/ui/viewmodel/ShortcutHelperViewModelTest.kt
index 8653308..44a8904 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyboard/shortcut/ui/viewmodel/ShortcutHelperViewModelTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyboard/shortcut/ui/viewmodel/ShortcutHelperViewModelTest.kt
@@ -26,6 +26,8 @@
import com.android.systemui.kosmos.testCase
import com.android.systemui.kosmos.testDispatcher
import com.android.systemui.kosmos.testScope
+import com.android.systemui.model.sysUiState
+import com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_SHORTCUT_HELPER_SHOWING
import com.google.common.truth.Truth.assertThat
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.test.UnconfinedTestDispatcher
@@ -47,7 +49,7 @@
private val testScope = kosmos.testScope
private val testHelper = kosmos.shortcutHelperTestHelper
-
+ private val sysUiState = kosmos.sysUiState
private val viewModel = kosmos.shortcutHelperViewModel
@Test
@@ -90,12 +92,12 @@
}
@Test
- fun shouldShow_falseAfterViewDestroyed() =
+ fun shouldShow_falseAfterViewClosed() =
testScope.runTest {
val shouldShow by collectLastValue(viewModel.shouldShow)
testHelper.toggle(deviceId = 567)
- viewModel.onUserLeave()
+ viewModel.onViewClosed()
assertThat(shouldShow).isFalse()
}
@@ -108,7 +110,7 @@
testHelper.hideForSystem()
testHelper.toggle(deviceId = 987)
testHelper.showFromActivity()
- viewModel.onUserLeave()
+ viewModel.onViewClosed()
testHelper.hideFromActivity()
testHelper.hideForSystem()
testHelper.toggle(deviceId = 456)
@@ -127,4 +129,27 @@
val shouldShowNew by collectLastValue(viewModel.shouldShow)
assertThat(shouldShowNew).isEqualTo(shouldShow)
}
+
+ @Test
+ fun sysUiStateFlag_disabledByDefault() =
+ testScope.runTest {
+ assertThat(sysUiState.isFlagEnabled(SYSUI_STATE_SHORTCUT_HELPER_SHOWING)).isFalse()
+ }
+
+ @Test
+ fun sysUiStateFlag_trueAfterViewOpened() =
+ testScope.runTest {
+ viewModel.onViewOpened()
+
+ assertThat(sysUiState.isFlagEnabled(SYSUI_STATE_SHORTCUT_HELPER_SHOWING)).isTrue()
+ }
+
+ @Test
+ fun sysUiStateFlag_falseAfterViewClosed() =
+ testScope.runTest {
+ viewModel.onViewOpened()
+ viewModel.onViewClosed()
+
+ assertThat(sysUiState.isFlagEnabled(SYSUI_STATE_SHORTCUT_HELPER_SHOWING)).isFalse()
+ }
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ResourceTrimmerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ResourceTrimmerTest.kt
index b50d248..977116e 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ResourceTrimmerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ResourceTrimmerTest.kt
@@ -8,6 +8,8 @@
import android.testing.AndroidTestingRunner
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
+import com.android.systemui.flags.DisableSceneContainer
+import com.android.systemui.flags.EnableSceneContainer
import com.android.systemui.flags.Flags
import com.android.systemui.flags.fakeFeatureFlagsClassic
import com.android.systemui.keyguard.data.repository.fakeKeyguardRepository
@@ -19,6 +21,10 @@
import com.android.systemui.kosmos.testScope
import com.android.systemui.power.domain.interactor.PowerInteractor.Companion.setAsleepForTest
import com.android.systemui.power.domain.interactor.powerInteractor
+import com.android.systemui.scene.data.repository.Idle
+import com.android.systemui.scene.data.repository.setSceneTransition
+import com.android.systemui.scene.domain.interactor.sceneInteractor
+import com.android.systemui.scene.shared.model.Scenes
import com.android.systemui.testKosmos
import com.android.systemui.util.mockito.any
import com.android.systemui.utils.GlobalWindowManager
@@ -69,12 +75,13 @@
resourceTrimmer =
ResourceTrimmer(
keyguardInteractor,
- powerInteractor,
- kosmos.keyguardTransitionInteractor,
- globalWindowManager,
- testScope.backgroundScope,
- kosmos.testDispatcher,
- featureFlags
+ powerInteractor = powerInteractor,
+ keyguardTransitionInteractor = kosmos.keyguardTransitionInteractor,
+ globalWindowManager = globalWindowManager,
+ applicationScope = testScope.backgroundScope,
+ bgDispatcher = kosmos.testDispatcher,
+ featureFlags = featureFlags,
+ sceneInteractor = kosmos.sceneInteractor,
)
resourceTrimmer.start()
}
@@ -203,6 +210,7 @@
@Test
@EnableFlags(com.android.systemui.Flags.FLAG_TRIM_RESOURCES_WITH_BACKGROUND_TRIM_AT_LOCK)
+ @DisableSceneContainer
fun keyguardTransitionsToGone_trimsFontCache() =
testScope.runTest {
keyguardTransitionRepository.sendTransitionSteps(
@@ -218,6 +226,20 @@
@Test
@EnableFlags(com.android.systemui.Flags.FLAG_TRIM_RESOURCES_WITH_BACKGROUND_TRIM_AT_LOCK)
+ @EnableSceneContainer
+ fun keyguardTransitionsToGone_trimsFontCache_scene_container() =
+ testScope.runTest {
+ kosmos.setSceneTransition(Idle(Scenes.Gone))
+
+ verify(globalWindowManager, times(1))
+ .trimMemory(ComponentCallbacks2.TRIM_MEMORY_UI_HIDDEN)
+ verify(globalWindowManager, times(1)).trimCaches(HardwareRenderer.CACHE_TRIM_FONT)
+ verifyNoMoreInteractions(globalWindowManager)
+ }
+
+ @Test
+ @EnableFlags(com.android.systemui.Flags.FLAG_TRIM_RESOURCES_WITH_BACKGROUND_TRIM_AT_LOCK)
+ @DisableSceneContainer
fun keyguardTransitionsToGone_flagDisabled_doesNotTrimFontCache() =
testScope.runTest {
featureFlags.set(Flags.TRIM_FONT_CACHES_AT_UNLOCK, false)
@@ -231,4 +253,18 @@
.trimMemory(ComponentCallbacks2.TRIM_MEMORY_UI_HIDDEN)
verify(globalWindowManager, times(0)).trimCaches(any())
}
+
+ @Test
+ @EnableFlags(com.android.systemui.Flags.FLAG_TRIM_RESOURCES_WITH_BACKGROUND_TRIM_AT_LOCK)
+ @EnableSceneContainer
+ fun keyguardTransitionsToGone_flagDisabled_doesNotTrimFontCache_scene_container() =
+ testScope.runTest {
+ featureFlags.set(Flags.TRIM_FONT_CACHES_AT_UNLOCK, false)
+ kosmos.setSceneTransition(Idle(Scenes.Gone))
+
+ // Memory hidden should still be called.
+ verify(globalWindowManager, times(1))
+ .trimMemory(ComponentCallbacks2.TRIM_MEMORY_UI_HIDDEN)
+ verify(globalWindowManager, times(0)).trimCaches(any())
+ }
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/KeyguardTransitionRepositoryTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/KeyguardTransitionRepositoryTest.kt
index 53560d7..48a5df9 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/KeyguardTransitionRepositoryTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/KeyguardTransitionRepositoryTest.kt
@@ -25,16 +25,19 @@
import androidx.test.filters.SmallTest
import com.android.app.animation.Interpolators
import com.android.systemui.SysuiTestCase
+import com.android.systemui.coroutines.collectValues
import com.android.systemui.keyguard.shared.model.KeyguardState
import com.android.systemui.keyguard.shared.model.KeyguardState.AOD
import com.android.systemui.keyguard.shared.model.KeyguardState.DREAMING
import com.android.systemui.keyguard.shared.model.KeyguardState.LOCKSCREEN
+import com.android.systemui.keyguard.shared.model.KeyguardState.OCCLUDED
import com.android.systemui.keyguard.shared.model.KeyguardState.OFF
import com.android.systemui.keyguard.shared.model.TransitionInfo
import com.android.systemui.keyguard.shared.model.TransitionModeOnCanceled
import com.android.systemui.keyguard.shared.model.TransitionState
import com.android.systemui.keyguard.shared.model.TransitionStep
import com.android.systemui.keyguard.util.KeyguardTransitionRunner
+import com.android.systemui.kosmos.testDispatcher
import com.android.systemui.kosmos.testScope
import com.android.systemui.testKosmos
import com.google.common.truth.Truth.assertThat
@@ -45,6 +48,8 @@
import kotlinx.coroutines.flow.dropWhile
import kotlinx.coroutines.flow.launchIn
import kotlinx.coroutines.flow.onEach
+import kotlinx.coroutines.launch
+import kotlinx.coroutines.test.advanceTimeBy
import kotlinx.coroutines.test.runCurrent
import kotlinx.coroutines.test.runTest
import org.junit.After
@@ -372,6 +377,43 @@
assertThat(wtfHandler.failed).isTrue()
}
+ @Test
+ fun simulateRaceConditionIsProcessedInOrder() =
+ testScope.runTest {
+ val ktr = KeyguardTransitionRepositoryImpl(kosmos.testDispatcher)
+ val steps by collectValues(ktr.transitions.dropWhile { step -> step.from == OFF })
+
+ // Add a delay to the first transition in order to attempt to have the second transition
+ // be processed first
+ val info1 = TransitionInfo(OWNER_NAME, AOD, LOCKSCREEN, animator = null)
+ launch {
+ ktr.forceDelayForRaceConditionTest = true
+ ktr.startTransition(info1)
+ }
+ val info2 = TransitionInfo(OWNER_NAME, LOCKSCREEN, OCCLUDED, animator = null)
+ launch {
+ ktr.forceDelayForRaceConditionTest = false
+ ktr.startTransition(info2)
+ }
+
+ runCurrent()
+ assertThat(steps.isEmpty()).isTrue()
+
+ advanceTimeBy(60L)
+ assertThat(steps[0])
+ .isEqualTo(
+ TransitionStep(info1.from, info1.to, 0f, TransitionState.STARTED, OWNER_NAME)
+ )
+ assertThat(steps[1])
+ .isEqualTo(
+ TransitionStep(info1.from, info1.to, 0f, TransitionState.CANCELED, OWNER_NAME)
+ )
+ assertThat(steps[2])
+ .isEqualTo(
+ TransitionStep(info2.from, info2.to, 0f, TransitionState.STARTED, OWNER_NAME)
+ )
+ }
+
private fun listWithStep(
step: BigDecimal,
start: BigDecimal = BigDecimal.ZERO,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardDismissActionInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardDismissActionInteractorTest.kt
index 62855d7..974e3bb 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardDismissActionInteractorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardDismissActionInteractorTest.kt
@@ -21,12 +21,18 @@
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.coroutines.collectLastValue
+import com.android.systemui.flags.DisableSceneContainer
+import com.android.systemui.flags.EnableSceneContainer
import com.android.systemui.keyguard.data.repository.fakeKeyguardRepository
import com.android.systemui.keyguard.data.repository.fakeKeyguardTransitionRepository
import com.android.systemui.keyguard.shared.model.DismissAction
import com.android.systemui.keyguard.shared.model.KeyguardDone
import com.android.systemui.keyguard.shared.model.KeyguardState
import com.android.systemui.kosmos.testScope
+import com.android.systemui.scene.data.repository.Idle
+import com.android.systemui.scene.data.repository.setSceneTransition
+import com.android.systemui.scene.domain.interactor.sceneInteractor
+import com.android.systemui.scene.shared.model.Scenes
import com.android.systemui.testKosmos
import com.google.common.truth.Truth.assertThat
import kotlinx.coroutines.ExperimentalCoroutinesApi
@@ -65,10 +71,11 @@
underTest =
KeyguardDismissActionInteractor(
- keyguardRepository,
- kosmos.keyguardTransitionInteractor,
- dismissInteractorWithDependencies.interactor,
- testScope.backgroundScope,
+ repository = keyguardRepository,
+ transitionInteractor = kosmos.keyguardTransitionInteractor,
+ dismissInteractor = dismissInteractorWithDependencies.interactor,
+ applicationScope = testScope.backgroundScope,
+ sceneInteractor = kosmos.sceneInteractor,
)
}
@@ -153,6 +160,7 @@
}
@Test
+ @DisableSceneContainer
fun executeDismissAction_dismissKeyguardRequestWithoutImmediateDismissAction() =
testScope.runTest {
val executeDismissAction by collectLastValue(underTest.executeDismissAction)
@@ -179,6 +187,29 @@
}
@Test
+ @EnableSceneContainer
+ fun executeDismissAction_dismissKeyguardRequestWithoutImmediateDismissAction_scene_container() =
+ testScope.runTest {
+ val executeDismissAction by collectLastValue(underTest.executeDismissAction)
+
+ // WHEN a keyguard action will run after the keyguard is gone
+ val onDismissAction = {}
+ keyguardRepository.setDismissAction(
+ DismissAction.RunAfterKeyguardGone(
+ dismissAction = onDismissAction,
+ onCancelAction = {},
+ message = "message",
+ willAnimateOnLockscreen = true,
+ )
+ )
+ assertThat(executeDismissAction).isNull()
+
+ kosmos.setSceneTransition(Idle(Scenes.Gone))
+
+ assertThat(executeDismissAction).isNotNull()
+ }
+
+ @Test
fun resetDismissAction() =
testScope.runTest {
val resetDismissAction by collectLastValue(underTest.resetDismissAction)
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionScenariosTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionScenariosTest.kt
index ef15d21..fa3fe5c 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionScenariosTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionScenariosTest.kt
@@ -91,33 +91,40 @@
}
private val testScope = kosmos.testScope
- private val keyguardRepository = kosmos.fakeKeyguardRepository
- private val bouncerRepository = kosmos.fakeKeyguardBouncerRepository
+ private val keyguardRepository by lazy { kosmos.fakeKeyguardRepository }
+ private val bouncerRepository by lazy { kosmos.fakeKeyguardBouncerRepository }
private var commandQueue = kosmos.fakeCommandQueue
private val shadeTestUtil by lazy { kosmos.shadeTestUtil }
- private val transitionRepository = kosmos.fakeKeyguardTransitionRepository
+ private val transitionRepository by lazy { kosmos.fakeKeyguardTransitionRepository }
private lateinit var featureFlags: FakeFeatureFlags
// Used to verify transition requests for test output
@Mock private lateinit var keyguardSecurityModel: KeyguardSecurityModel
- private val fromLockscreenTransitionInteractor = kosmos.fromLockscreenTransitionInteractor
- private val fromDreamingTransitionInteractor = kosmos.fromDreamingTransitionInteractor
- private val fromDozingTransitionInteractor = kosmos.fromDozingTransitionInteractor
- private val fromOccludedTransitionInteractor = kosmos.fromOccludedTransitionInteractor
- private val fromGoneTransitionInteractor = kosmos.fromGoneTransitionInteractor
- private val fromAodTransitionInteractor = kosmos.fromAodTransitionInteractor
- private val fromAlternateBouncerTransitionInteractor =
+ private val fromLockscreenTransitionInteractor by lazy {
+ kosmos.fromLockscreenTransitionInteractor
+ }
+ private val fromDreamingTransitionInteractor by lazy { kosmos.fromDreamingTransitionInteractor }
+ private val fromDozingTransitionInteractor by lazy { kosmos.fromDozingTransitionInteractor }
+ private val fromOccludedTransitionInteractor by lazy { kosmos.fromOccludedTransitionInteractor }
+ private val fromGoneTransitionInteractor by lazy { kosmos.fromGoneTransitionInteractor }
+ private val fromAodTransitionInteractor by lazy { kosmos.fromAodTransitionInteractor }
+ private val fromAlternateBouncerTransitionInteractor by lazy {
kosmos.fromAlternateBouncerTransitionInteractor
- private val fromPrimaryBouncerTransitionInteractor =
+ }
+ private val fromPrimaryBouncerTransitionInteractor by lazy {
kosmos.fromPrimaryBouncerTransitionInteractor
- private val fromDreamingLockscreenHostedTransitionInteractor =
+ }
+ private val fromDreamingLockscreenHostedTransitionInteractor by lazy {
kosmos.fromDreamingLockscreenHostedTransitionInteractor
- private val fromGlanceableHubTransitionInteractor = kosmos.fromGlanceableHubTransitionInteractor
+ }
+ private val fromGlanceableHubTransitionInteractor by lazy {
+ kosmos.fromGlanceableHubTransitionInteractor
+ }
- private val powerInteractor = kosmos.powerInteractor
- private val communalInteractor = kosmos.communalInteractor
- private val dockManager = kosmos.fakeDockManager
+ private val powerInteractor by lazy { kosmos.powerInteractor }
+ private val communalInteractor by lazy { kosmos.communalInteractor }
+ private val dockManager by lazy { kosmos.fakeDockManager }
companion object {
@JvmStatic
@@ -633,7 +640,7 @@
// GIVEN a prior transition has run to DREAMING
keyguardRepository.setDreamingWithOverlay(true)
runTransitionAndSetWakefulness(KeyguardState.LOCKSCREEN, KeyguardState.DREAMING)
- runCurrent()
+ advanceTimeBy(60L)
// WHEN the device wakes up without a keyguard
keyguardRepository.setKeyguardShowing(false)
@@ -1662,7 +1669,9 @@
// THEN a transition from DOZING => OCCLUDED should occur
assertThat(transitionRepository)
.startedTransition(
- ownerName = "FromDozingTransitionInteractor",
+ ownerName =
+ "FromDozingTransitionInteractor" +
+ "(keyguardInteractor.onCameraLaunchDetected)",
from = KeyguardState.DOZING,
to = KeyguardState.OCCLUDED,
animatorAssertion = { it.isNotNull() },
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/WindowManagerLockscreenVisibilityInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/WindowManagerLockscreenVisibilityInteractorTest.kt
index a77169e..2b8a644 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/WindowManagerLockscreenVisibilityInteractorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/WindowManagerLockscreenVisibilityInteractorTest.kt
@@ -20,8 +20,11 @@
import androidx.test.filters.SmallTest
import com.android.compose.animation.scene.ObservableTransitionState
import com.android.systemui.SysuiTestCase
+import com.android.systemui.authentication.data.repository.FakeAuthenticationRepository
+import com.android.systemui.authentication.domain.interactor.authenticationInteractor
import com.android.systemui.coroutines.collectLastValue
import com.android.systemui.coroutines.collectValues
+import com.android.systemui.deviceentry.domain.interactor.deviceUnlockedInteractor
import com.android.systemui.flags.DisableSceneContainer
import com.android.systemui.flags.EnableSceneContainer
import com.android.systemui.keyguard.data.repository.fakeKeyguardTransitionRepository
@@ -30,6 +33,7 @@
import com.android.systemui.keyguard.shared.model.TransitionStep
import com.android.systemui.kosmos.testScope
import com.android.systemui.scene.data.repository.sceneContainerRepository
+import com.android.systemui.scene.domain.interactor.sceneInteractor
import com.android.systemui.scene.shared.model.Scenes
import com.android.systemui.testKosmos
import com.android.systemui.util.mockito.mock
@@ -38,6 +42,7 @@
import junit.framework.Assert.assertEquals
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.flowOf
+import kotlinx.coroutines.flow.map
import kotlinx.coroutines.test.runCurrent
import kotlinx.coroutines.test.runTest
import org.junit.Before
@@ -727,42 +732,48 @@
@Test
@EnableSceneContainer
- fun sceneContainer_lockscreenVisibility_visibleWhenNotGone() =
+ fun lockscreenVisibility() =
testScope.runTest {
- val lockscreenVisibility by collectLastValue(underTest.value.lockscreenVisibility)
+ val isDeviceUnlocked by
+ collectLastValue(
+ kosmos.deviceUnlockedInteractor.deviceUnlockStatus.map { it.isUnlocked }
+ )
+ assertThat(isDeviceUnlocked).isFalse()
- sceneTransitions.value = lsToGone
+ val currentScene by collectLastValue(kosmos.sceneInteractor.currentScene)
+ assertThat(currentScene).isEqualTo(Scenes.Lockscreen)
+
+ val lockscreenVisibility by collectLastValue(underTest.value.lockscreenVisibility)
assertThat(lockscreenVisibility).isTrue()
- sceneTransitions.value = ObservableTransitionState.Idle(Scenes.Gone)
- assertThat(lockscreenVisibility).isFalse()
-
- sceneTransitions.value = goneToLs
- assertThat(lockscreenVisibility).isFalse()
-
- sceneTransitions.value = ObservableTransitionState.Idle(Scenes.Lockscreen)
+ kosmos.sceneInteractor.changeScene(Scenes.Bouncer, "")
+ assertThat(currentScene).isEqualTo(Scenes.Bouncer)
assertThat(lockscreenVisibility).isTrue()
- }
- @Test
- @EnableSceneContainer
- fun sceneContainer_lockscreenVisibility_notVisibleWhenReturningToGone() =
- testScope.runTest {
- val lockscreenVisibility by collectLastValue(underTest.value.lockscreenVisibility)
-
- sceneTransitions.value = goneToLs
+ kosmos.authenticationInteractor.authenticate(FakeAuthenticationRepository.DEFAULT_PIN)
+ assertThat(isDeviceUnlocked).isTrue()
+ kosmos.sceneInteractor.changeScene(Scenes.Gone, "")
+ assertThat(currentScene).isEqualTo(Scenes.Gone)
assertThat(lockscreenVisibility).isFalse()
- sceneTransitions.value = lsToGone
+ kosmos.sceneInteractor.changeScene(Scenes.Shade, "")
+ assertThat(currentScene).isEqualTo(Scenes.Shade)
assertThat(lockscreenVisibility).isFalse()
- sceneTransitions.value = ObservableTransitionState.Idle(Scenes.Gone)
+ kosmos.sceneInteractor.changeScene(Scenes.QuickSettings, "")
+ assertThat(currentScene).isEqualTo(Scenes.QuickSettings)
assertThat(lockscreenVisibility).isFalse()
- sceneTransitions.value = goneToLs
+ kosmos.sceneInteractor.changeScene(Scenes.Shade, "")
+ assertThat(currentScene).isEqualTo(Scenes.Shade)
assertThat(lockscreenVisibility).isFalse()
- sceneTransitions.value = ObservableTransitionState.Idle(Scenes.Lockscreen)
+ kosmos.sceneInteractor.changeScene(Scenes.Gone, "")
+ assertThat(currentScene).isEqualTo(Scenes.Gone)
+ assertThat(lockscreenVisibility).isFalse()
+
+ kosmos.sceneInteractor.changeScene(Scenes.Lockscreen, "")
+ assertThat(currentScene).isEqualTo(Scenes.Lockscreen)
assertThat(lockscreenVisibility).isTrue()
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/scenetransition/LockscreenSceneTransitionInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/scenetransition/LockscreenSceneTransitionInteractorTest.kt
new file mode 100644
index 0000000..8a5af09
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/scenetransition/LockscreenSceneTransitionInteractorTest.kt
@@ -0,0 +1,1318 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.keyguard.domain.interactor.scenetransition
+
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.SmallTest
+import com.android.compose.animation.scene.ObservableTransitionState
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.coroutines.collectLastValue
+import com.android.systemui.coroutines.collectValues
+import com.android.systemui.keyguard.data.repository.keyguardTransitionRepository
+import com.android.systemui.keyguard.data.repository.realKeyguardTransitionRepository
+import com.android.systemui.keyguard.shared.model.KeyguardState
+import com.android.systemui.keyguard.shared.model.TransitionInfo
+import com.android.systemui.keyguard.shared.model.TransitionModeOnCanceled
+import com.android.systemui.keyguard.shared.model.TransitionState
+import com.android.systemui.keyguard.shared.model.TransitionStep
+import com.android.systemui.kosmos.testScope
+import com.android.systemui.scene.data.repository.sceneContainerRepository
+import com.android.systemui.scene.shared.model.Scenes
+import com.android.systemui.testKosmos
+import com.google.common.truth.Truth.assertThat
+import kotlin.test.Test
+import kotlinx.coroutines.flow.MutableStateFlow
+import kotlinx.coroutines.flow.flowOf
+import kotlinx.coroutines.launch
+import kotlinx.coroutines.test.runTest
+import org.junit.Before
+import org.junit.runner.RunWith
+
+@SmallTest
+@RunWith(AndroidJUnit4::class)
+class LockscreenSceneTransitionInteractorTest : SysuiTestCase() {
+ private val kosmos =
+ testKosmos().apply { keyguardTransitionRepository = realKeyguardTransitionRepository }
+
+ private val testScope = kosmos.testScope
+ private val underTest = kosmos.lockscreenSceneTransitionInteractor
+
+ private val progress = MutableStateFlow(0f)
+
+ private val sceneTransitions =
+ MutableStateFlow<ObservableTransitionState>(
+ ObservableTransitionState.Idle(Scenes.Lockscreen)
+ )
+
+ private val lsToGone =
+ ObservableTransitionState.Transition(
+ Scenes.Lockscreen,
+ Scenes.Gone,
+ flowOf(Scenes.Lockscreen),
+ progress,
+ false,
+ flowOf(false)
+ )
+
+ private val goneToLs =
+ ObservableTransitionState.Transition(
+ Scenes.Gone,
+ Scenes.Lockscreen,
+ flowOf(Scenes.Lockscreen),
+ progress,
+ false,
+ flowOf(false)
+ )
+
+ @Before
+ fun setUp() {
+ underTest.start()
+ kosmos.sceneContainerRepository.setTransitionState(sceneTransitions)
+ testScope.launch {
+ kosmos.realKeyguardTransitionRepository.emitInitialStepsFromOff(
+ KeyguardState.LOCKSCREEN
+ )
+ }
+ }
+
+ /** STL: Ls -> Gone, then settle with Idle(Gone). This is the default case. */
+ @Test
+ fun transition_from_ls_scene_end_in_gone() =
+ testScope.runTest {
+ sceneTransitions.value = lsToGone
+
+ val currentStep by collectLastValue(kosmos.realKeyguardTransitionRepository.transitions)
+ assertTransition(
+ step = currentStep!!,
+ from = KeyguardState.LOCKSCREEN,
+ to = KeyguardState.UNDEFINED,
+ state = TransitionState.RUNNING,
+ progress = 0f,
+ )
+
+ progress.value = 0.4f
+ assertTransition(
+ step = currentStep!!,
+ state = TransitionState.RUNNING,
+ progress = 0.4f,
+ )
+
+ sceneTransitions.value = ObservableTransitionState.Idle(Scenes.Gone)
+ assertTransition(
+ step = currentStep!!,
+ from = KeyguardState.LOCKSCREEN,
+ to = KeyguardState.UNDEFINED,
+ state = TransitionState.FINISHED,
+ progress = 1f,
+ )
+ }
+
+ /**
+ * STL: Ls -> Gone, then settle with Idle(Ls). KTF in this scenario needs to invert the
+ * transition LS -> UNDEFINED to UNDEFINED -> LS as there is no mechanism in KTF to
+ * finish/settle to progress 0.0f.
+ */
+ @Test
+ fun transition_from_ls_scene_end_in_ls() =
+ testScope.runTest {
+ sceneTransitions.value = lsToGone
+
+ val currentStep by collectLastValue(kosmos.realKeyguardTransitionRepository.transitions)
+ val allSteps by collectValues(kosmos.realKeyguardTransitionRepository.transitions)
+ assertTransition(
+ step = currentStep!!,
+ from = KeyguardState.LOCKSCREEN,
+ to = KeyguardState.UNDEFINED,
+ state = TransitionState.RUNNING,
+ progress = 0f,
+ )
+
+ progress.value = 0.4f
+ assertTransition(
+ step = currentStep!!,
+ state = TransitionState.RUNNING,
+ progress = 0.4f,
+ )
+
+ sceneTransitions.value = ObservableTransitionState.Idle(Scenes.Lockscreen)
+
+ assertTransition(
+ step = allSteps[allSteps.size - 3],
+ from = KeyguardState.LOCKSCREEN,
+ to = KeyguardState.UNDEFINED,
+ state = TransitionState.CANCELED,
+ progress = 0.4f,
+ )
+
+ assertTransition(
+ step = allSteps[allSteps.size - 2],
+ from = KeyguardState.UNDEFINED,
+ to = KeyguardState.LOCKSCREEN,
+ state = TransitionState.STARTED,
+ progress = 0.6f,
+ )
+
+ assertTransition(
+ step = allSteps[allSteps.size - 1],
+ from = KeyguardState.UNDEFINED,
+ to = KeyguardState.LOCKSCREEN,
+ state = TransitionState.FINISHED,
+ progress = 1f,
+ )
+ }
+
+ /**
+ * STL: Ls -> Gone, then settle with Idle(Ls). KTF starts in AOD and needs to inverse correctly
+ * back to AOD.
+ */
+ @Test
+ fun transition_from_ls_scene_on_aod_end_in_ls() =
+ testScope.runTest {
+ val currentStep by collectLastValue(kosmos.realKeyguardTransitionRepository.transitions)
+ val allSteps by collectValues(kosmos.realKeyguardTransitionRepository.transitions)
+
+ kosmos.realKeyguardTransitionRepository.startTransition(
+ TransitionInfo(
+ ownerName = this.javaClass.simpleName,
+ from = KeyguardState.LOCKSCREEN,
+ to = KeyguardState.AOD,
+ animator = null,
+ modeOnCanceled = TransitionModeOnCanceled.RESET
+ )
+ )
+ sceneTransitions.value = lsToGone
+
+ assertTransition(
+ step = currentStep!!,
+ from = KeyguardState.AOD,
+ to = KeyguardState.UNDEFINED,
+ state = TransitionState.RUNNING,
+ progress = 0f,
+ )
+
+ progress.value = 0.4f
+ assertTransition(
+ step = currentStep!!,
+ state = TransitionState.RUNNING,
+ progress = 0.4f,
+ )
+
+ sceneTransitions.value = ObservableTransitionState.Idle(Scenes.Lockscreen)
+
+ assertTransition(
+ step = allSteps[allSteps.size - 3],
+ from = KeyguardState.AOD,
+ to = KeyguardState.UNDEFINED,
+ state = TransitionState.CANCELED,
+ progress = 0.4f,
+ )
+
+ assertTransition(
+ step = allSteps[allSteps.size - 2],
+ from = KeyguardState.UNDEFINED,
+ to = KeyguardState.AOD,
+ state = TransitionState.STARTED,
+ progress = 0.6f,
+ )
+
+ assertTransition(
+ step = allSteps[allSteps.size - 1],
+ from = KeyguardState.UNDEFINED,
+ to = KeyguardState.AOD,
+ state = TransitionState.FINISHED,
+ progress = 1f,
+ )
+ }
+
+ /**
+ * STL: Gone -> Ls, then settle with Idle(Ls). This is the default case in the reverse
+ * direction.
+ */
+ @Test
+ fun transition_to_ls_scene_end_in_ls() =
+ testScope.runTest {
+ val currentStep by collectLastValue(kosmos.realKeyguardTransitionRepository.transitions)
+ sceneTransitions.value = goneToLs
+
+ assertTransition(
+ step = currentStep!!,
+ from = KeyguardState.UNDEFINED,
+ to = KeyguardState.LOCKSCREEN,
+ state = TransitionState.RUNNING,
+ progress = 0f,
+ )
+
+ progress.value = 0.4f
+ assertTransition(
+ step = currentStep!!,
+ state = TransitionState.RUNNING,
+ progress = 0.4f,
+ )
+
+ sceneTransitions.value = ObservableTransitionState.Idle(Scenes.Lockscreen)
+
+ assertTransition(
+ step = currentStep!!,
+ from = KeyguardState.UNDEFINED,
+ to = KeyguardState.LOCKSCREEN,
+ state = TransitionState.FINISHED,
+ progress = 1f,
+ )
+ }
+
+ /** STL: Gone -> Ls (AOD), will transition to AOD once */
+ @Test
+ fun transition_to_ls_scene_with_changed_next_scene_is_respected_just_once() =
+ testScope.runTest {
+ underTest.onSceneAboutToChange(Scenes.Lockscreen, KeyguardState.AOD)
+ sceneTransitions.value = goneToLs
+
+ val currentStep by collectLastValue(kosmos.realKeyguardTransitionRepository.transitions)
+ assertTransition(
+ step = currentStep!!,
+ from = KeyguardState.UNDEFINED,
+ to = KeyguardState.AOD,
+ state = TransitionState.RUNNING,
+ progress = 0f,
+ )
+
+ sceneTransitions.value =
+ ObservableTransitionState.Transition(
+ Scenes.Shade,
+ Scenes.Lockscreen,
+ flowOf(Scenes.Lockscreen),
+ progress,
+ false,
+ flowOf(false)
+ )
+
+ assertTransition(
+ step = currentStep!!,
+ from = KeyguardState.UNDEFINED,
+ to = KeyguardState.LOCKSCREEN,
+ state = TransitionState.RUNNING,
+ progress = 0f,
+ )
+ }
+
+ /**
+ * STL: Gone -> Ls, then settle with Idle(Gone). KTF in this scenario needs to invert the
+ * transition UNDEFINED -> LS to LS -> UNDEFINED as there is no mechanism in KTF to
+ * finish/settle to progress 0.0f.
+ */
+ @Test
+ fun transition_to_ls_scene_end_in_from_scene() =
+ testScope.runTest {
+ sceneTransitions.value = goneToLs
+
+ val currentStep by collectLastValue(kosmos.realKeyguardTransitionRepository.transitions)
+ val allSteps by collectValues(kosmos.realKeyguardTransitionRepository.transitions)
+ assertTransition(
+ step = currentStep!!,
+ from = KeyguardState.UNDEFINED,
+ to = KeyguardState.LOCKSCREEN,
+ state = TransitionState.RUNNING,
+ progress = 0f,
+ )
+
+ progress.value = 0.4f
+ assertTransition(
+ step = currentStep!!,
+ state = TransitionState.RUNNING,
+ progress = 0.4f,
+ )
+
+ sceneTransitions.value = ObservableTransitionState.Idle(Scenes.Gone)
+ val stepM3 = allSteps[allSteps.size - 3]
+ val stepM2 = allSteps[allSteps.size - 2]
+
+ assertTransition(
+ step = stepM3,
+ from = KeyguardState.UNDEFINED,
+ to = KeyguardState.LOCKSCREEN,
+ state = TransitionState.CANCELED,
+ progress = 0.4f,
+ )
+
+ assertTransition(
+ step = stepM2,
+ from = KeyguardState.LOCKSCREEN,
+ to = KeyguardState.UNDEFINED,
+ state = TransitionState.STARTED,
+ progress = 0.6f,
+ )
+
+ assertTransition(
+ step = currentStep!!,
+ from = KeyguardState.LOCKSCREEN,
+ to = KeyguardState.UNDEFINED,
+ state = TransitionState.FINISHED,
+ progress = 1f,
+ )
+ }
+
+ /**
+ * STL: Gone -> Ls, then interrupted by Shade -> Ls. KTF in this scenario needs to invert the
+ * transition UNDEFINED -> LS to LS -> UNDEFINED as there is no mechanism in KTF to
+ * finish/settle to progress 0.0f. Then restart a different transition UNDEFINED -> Ls.
+ */
+ @Test
+ fun transition_to_ls_scene_end_in_to_ls_transition() =
+ testScope.runTest {
+ val currentStep by collectLastValue(kosmos.realKeyguardTransitionRepository.transitions)
+ val allSteps by collectValues(kosmos.realKeyguardTransitionRepository.transitions)
+ sceneTransitions.value = goneToLs
+ progress.value = 0.4f
+
+ assertTransition(
+ step = currentStep!!,
+ from = KeyguardState.UNDEFINED,
+ to = KeyguardState.LOCKSCREEN,
+ state = TransitionState.RUNNING,
+ progress = 0.4f,
+ )
+
+ sceneTransitions.value =
+ ObservableTransitionState.Transition(
+ Scenes.Shade,
+ Scenes.Lockscreen,
+ flowOf(Scenes.Lockscreen),
+ progress,
+ false,
+ flowOf(false)
+ )
+
+ assertTransition(
+ step = allSteps[allSteps.size - 5],
+ from = KeyguardState.UNDEFINED,
+ to = KeyguardState.LOCKSCREEN,
+ state = TransitionState.CANCELED,
+ progress = 0.4f,
+ )
+
+ assertTransition(
+ step = allSteps[allSteps.size - 4],
+ from = KeyguardState.LOCKSCREEN,
+ to = KeyguardState.UNDEFINED,
+ state = TransitionState.STARTED,
+ progress = 0.6f,
+ )
+
+ assertTransition(
+ step = allSteps[allSteps.size - 3],
+ from = KeyguardState.LOCKSCREEN,
+ to = KeyguardState.UNDEFINED,
+ state = TransitionState.FINISHED,
+ progress = 1f,
+ )
+
+ assertTransition(
+ step = allSteps[allSteps.size - 2],
+ from = KeyguardState.UNDEFINED,
+ to = KeyguardState.LOCKSCREEN,
+ state = TransitionState.STARTED,
+ progress = 0f,
+ )
+
+ progress.value = 0.2f
+ assertTransition(
+ step = allSteps[allSteps.size - 1],
+ from = KeyguardState.UNDEFINED,
+ to = KeyguardState.LOCKSCREEN,
+ state = TransitionState.RUNNING,
+ progress = 0.2f,
+ )
+ }
+
+ /**
+ * STL: Gone -> Ls, then interrupted by Ls -> Shade. This is like continuing the transition from
+ * Ls before the transition before has properly settled. This can happen in STL e.g. with an
+ * accelerated swipe (quick successive fling gestures).
+ */
+ @Test
+ fun transition_to_ls_scene_end_in_from_ls_transition() =
+ testScope.runTest {
+ val currentStep by collectLastValue(kosmos.realKeyguardTransitionRepository.transitions)
+ val allSteps by collectValues(kosmos.realKeyguardTransitionRepository.transitions)
+ sceneTransitions.value = goneToLs
+ progress.value = 0.4f
+
+ assertTransition(
+ step = currentStep!!,
+ from = KeyguardState.UNDEFINED,
+ to = KeyguardState.LOCKSCREEN,
+ state = TransitionState.RUNNING,
+ progress = 0.4f,
+ )
+
+ sceneTransitions.value =
+ ObservableTransitionState.Transition(
+ Scenes.Lockscreen,
+ Scenes.Shade,
+ flowOf(Scenes.Lockscreen),
+ progress,
+ false,
+ flowOf(false)
+ )
+
+ assertTransition(
+ step = allSteps[allSteps.size - 3],
+ from = KeyguardState.UNDEFINED,
+ to = KeyguardState.LOCKSCREEN,
+ state = TransitionState.CANCELED,
+ progress = 0.4f,
+ )
+
+ assertTransition(
+ step = allSteps[allSteps.size - 2],
+ from = KeyguardState.LOCKSCREEN,
+ to = KeyguardState.UNDEFINED,
+ state = TransitionState.STARTED,
+ progress = 0.0f,
+ )
+
+ progress.value = 0.2f
+ assertTransition(
+ step = allSteps[allSteps.size - 1],
+ from = KeyguardState.LOCKSCREEN,
+ to = KeyguardState.UNDEFINED,
+ state = TransitionState.RUNNING,
+ progress = 0.2f,
+ )
+ }
+
+ /**
+ * STL: Gone -> Ls, then interrupted by Gone -> Shade. This is going back to Gone but starting a
+ * transition from Gone before settling in Gone. KTF needs to make sure the transition is
+ * properly inversed and settled in UNDEFINED.
+ */
+ @Test
+ fun transition_to_ls_scene_end_in_other_transition() =
+ testScope.runTest {
+ val currentStep by collectLastValue(kosmos.realKeyguardTransitionRepository.transitions)
+ val allSteps by collectValues(kosmos.realKeyguardTransitionRepository.transitions)
+ sceneTransitions.value = goneToLs
+ progress.value = 0.4f
+
+ assertTransition(
+ step = currentStep!!,
+ from = KeyguardState.UNDEFINED,
+ to = KeyguardState.LOCKSCREEN,
+ state = TransitionState.RUNNING,
+ progress = 0.4f,
+ )
+
+ sceneTransitions.value =
+ ObservableTransitionState.Transition(
+ Scenes.Gone,
+ Scenes.Shade,
+ flowOf(Scenes.Lockscreen),
+ progress,
+ false,
+ flowOf(false)
+ )
+
+ assertTransition(
+ step = allSteps[allSteps.size - 3],
+ from = KeyguardState.UNDEFINED,
+ to = KeyguardState.LOCKSCREEN,
+ state = TransitionState.CANCELED,
+ progress = 0.4f,
+ )
+
+ assertTransition(
+ step = allSteps[allSteps.size - 2],
+ from = KeyguardState.LOCKSCREEN,
+ to = KeyguardState.UNDEFINED,
+ state = TransitionState.STARTED,
+ progress = 0.6f,
+ )
+
+ assertTransition(
+ step = allSteps[allSteps.size - 1],
+ from = KeyguardState.LOCKSCREEN,
+ to = KeyguardState.UNDEFINED,
+ state = TransitionState.FINISHED,
+ progress = 1f,
+ )
+ }
+
+ /**
+ * STL: Gone -> Ls, then interrupt in KTF LS -> AOD, then stl still finishes in Ls. After a KTF
+ * transition is started (UNDEFINED -> LOCKSCREEN) KTF immediately considers the active scene to
+ * be LOCKSCREEN. This means that all listeners for LOCKSCREEN are active and may start a new
+ * transition LOCKSCREEN -> *. Here we test LS -> AOD.
+ *
+ * KTF is allowed to already start and play the other transition, while the STL transition may
+ * finish later (gesture completes much later). When we eventually settle the STL transition in
+ * Ls we do not want to force KTF back to its original destination (LOCKSCREEN). Instead, for
+ * this scenario the settle can be ignored.
+ */
+ @Test
+ fun transition_to_ls_scene_interrupted_by_ktf_transition_then_finish_in_lockscreen() =
+ testScope.runTest {
+ sceneTransitions.value = goneToLs
+
+ val currentStep by collectLastValue(kosmos.realKeyguardTransitionRepository.transitions)
+ assertTransition(
+ step = currentStep!!,
+ from = KeyguardState.UNDEFINED,
+ to = KeyguardState.LOCKSCREEN,
+ state = TransitionState.RUNNING,
+ progress = 0f,
+ )
+
+ progress.value = 0.4f
+ assertTransition(
+ step = currentStep!!,
+ state = TransitionState.RUNNING,
+ progress = 0.4f,
+ )
+
+ kosmos.realKeyguardTransitionRepository.startTransition(
+ TransitionInfo(
+ ownerName = this.javaClass.simpleName,
+ from = KeyguardState.LOCKSCREEN,
+ to = KeyguardState.AOD,
+ animator = null,
+ modeOnCanceled = TransitionModeOnCanceled.RESET
+ )
+ )
+
+ assertTransition(
+ step = currentStep!!,
+ from = KeyguardState.LOCKSCREEN,
+ to = KeyguardState.AOD,
+ state = TransitionState.STARTED,
+ progress = 0f,
+ )
+
+ // Scene progress should not affect KTF transition anymore
+ progress.value = 0.7f
+ assertTransition(currentStep!!, progress = 0f)
+
+ // Scene transition still finishes but should not impact KTF transition
+ sceneTransitions.value = ObservableTransitionState.Idle(Scenes.Lockscreen)
+
+ assertTransition(
+ step = currentStep!!,
+ from = KeyguardState.LOCKSCREEN,
+ to = KeyguardState.AOD,
+ state = TransitionState.STARTED,
+ progress = 0f,
+ )
+ }
+
+ /**
+ * STL: Gone -> Ls, then interrupt in KTF LS -> AOD, then stl finishes in Gone.
+ *
+ * Refers to: `transition_to_ls_scene_interrupted_by_ktf_transition_then_finish_in_lockscreen`
+ *
+ * This is similar to the previous scenario but the gesture may have gone back to its origin. In
+ * this case we can not ignore the settlement, because whatever KTF has done in the meantime it
+ * needs to immediately finish in UNDEFINED (there is a jump cut).
+ */
+ @Test
+ fun transition_to_ls_scene_interrupted_by_ktf_transition_then_finish_in_gone() =
+ testScope.runTest {
+ sceneTransitions.value = goneToLs
+
+ val currentStep by collectLastValue(kosmos.realKeyguardTransitionRepository.transitions)
+ assertTransition(
+ step = currentStep!!,
+ from = KeyguardState.UNDEFINED,
+ to = KeyguardState.LOCKSCREEN,
+ state = TransitionState.RUNNING,
+ progress = 0f,
+ )
+
+ progress.value = 0.4f
+ assertTransition(
+ step = currentStep!!,
+ state = TransitionState.RUNNING,
+ progress = 0.4f,
+ )
+
+ kosmos.realKeyguardTransitionRepository.startTransition(
+ TransitionInfo(
+ ownerName = this.javaClass.simpleName,
+ from = KeyguardState.LOCKSCREEN,
+ to = KeyguardState.AOD,
+ animator = null,
+ modeOnCanceled = TransitionModeOnCanceled.RESET
+ )
+ )
+
+ assertTransition(
+ step = currentStep!!,
+ from = KeyguardState.LOCKSCREEN,
+ to = KeyguardState.AOD,
+ state = TransitionState.STARTED,
+ progress = 0f,
+ )
+
+ progress.value = 0.7f
+ assertThat(currentStep?.value).isEqualTo(0f)
+
+ sceneTransitions.value = ObservableTransitionState.Idle(Scenes.Gone)
+
+ assertTransition(
+ step = currentStep!!,
+ from = KeyguardState.AOD,
+ to = KeyguardState.UNDEFINED,
+ state = TransitionState.FINISHED,
+ progress = 1f,
+ )
+ }
+
+ /**
+ * STL: Gone -> Ls, then interrupt in KTF LS -> AOD, then STL Gone -> Shade
+ *
+ * Refers to: `transition_to_ls_scene_interrupted_by_ktf_transition_then_finish_in_lockscreen`
+ *
+ * This is similar to the previous scenario but the gesture may have been interrupted by any
+ * other transition. KTF needs to immediately finish in UNDEFINED (there is a jump cut).
+ */
+ @Test
+ fun transition_to_ls_interrupted_by_ktf_transition_then_interrupted_by_other_transition() =
+ testScope.runTest {
+ sceneTransitions.value = goneToLs
+
+ val currentStep by collectLastValue(kosmos.realKeyguardTransitionRepository.transitions)
+ assertTransition(
+ step = currentStep!!,
+ from = KeyguardState.UNDEFINED,
+ to = KeyguardState.LOCKSCREEN,
+ state = TransitionState.RUNNING,
+ progress = 0f,
+ )
+
+ progress.value = 0.4f
+ assertTransition(
+ step = currentStep!!,
+ state = TransitionState.RUNNING,
+ progress = 0.4f,
+ )
+
+ kosmos.realKeyguardTransitionRepository.startTransition(
+ TransitionInfo(
+ ownerName = this.javaClass.simpleName,
+ from = KeyguardState.LOCKSCREEN,
+ to = KeyguardState.AOD,
+ animator = null,
+ modeOnCanceled = TransitionModeOnCanceled.RESET
+ )
+ )
+
+ assertTransition(
+ step = currentStep!!,
+ from = KeyguardState.LOCKSCREEN,
+ to = KeyguardState.AOD,
+ state = TransitionState.STARTED,
+ progress = 0f,
+ )
+
+ progress.value = 0.7f
+ assertTransition(currentStep!!, progress = 0f)
+
+ sceneTransitions.value =
+ ObservableTransitionState.Transition(
+ Scenes.Gone,
+ Scenes.Shade,
+ flowOf(Scenes.Lockscreen),
+ progress,
+ false,
+ flowOf(false)
+ )
+
+ assertTransition(
+ step = currentStep!!,
+ from = KeyguardState.AOD,
+ to = KeyguardState.UNDEFINED,
+ state = TransitionState.FINISHED,
+ progress = 1f,
+ )
+ }
+
+ /**
+ * STL: Gone -> Ls, then interrupt in KTF LS -> AOD, then STL Ls -> Shade
+ *
+ * In this scenario it is important that the last STL transition Ls -> Shade triggers a cancel
+ * of the * -> AOD transition but then also properly starts a transition AOD (not LOCKSCREEN) ->
+ * UNDEFINED transition.
+ */
+ @Test
+ fun transition_to_ls_interrupted_by_ktf_transition_then_interrupted_by_from_ls_transition() =
+ testScope.runTest {
+ val currentStep by collectLastValue(kosmos.realKeyguardTransitionRepository.transitions)
+ val allSteps by collectValues(kosmos.realKeyguardTransitionRepository.transitions)
+ sceneTransitions.value = goneToLs
+ progress.value = 0.4f
+
+ assertTransition(
+ step = currentStep!!,
+ from = KeyguardState.UNDEFINED,
+ to = KeyguardState.LOCKSCREEN,
+ state = TransitionState.RUNNING,
+ progress = 0.4f,
+ )
+
+ kosmos.realKeyguardTransitionRepository.startTransition(
+ TransitionInfo(
+ ownerName = this.javaClass.simpleName,
+ from = KeyguardState.LOCKSCREEN,
+ to = KeyguardState.AOD,
+ animator = null,
+ modeOnCanceled = TransitionModeOnCanceled.RESET
+ )
+ )
+
+ assertTransition(
+ step = currentStep!!,
+ from = KeyguardState.LOCKSCREEN,
+ to = KeyguardState.AOD,
+ state = TransitionState.STARTED,
+ progress = 0f,
+ )
+
+ progress.value = 0.7f
+ assertTransition(currentStep!!, progress = 0f)
+
+ sceneTransitions.value =
+ ObservableTransitionState.Transition(
+ Scenes.Lockscreen,
+ Scenes.Shade,
+ flowOf(Scenes.Lockscreen),
+ progress,
+ false,
+ flowOf(false)
+ )
+ allSteps[allSteps.size - 3]
+
+ assertTransition(
+ step = allSteps[allSteps.size - 3],
+ from = KeyguardState.LOCKSCREEN,
+ to = KeyguardState.AOD,
+ state = TransitionState.CANCELED,
+ progress = 0f,
+ )
+
+ assertTransition(
+ step = allSteps[allSteps.size - 2],
+ from = KeyguardState.AOD,
+ to = KeyguardState.UNDEFINED,
+ state = TransitionState.STARTED,
+ progress = 0f,
+ )
+
+ progress.value = 0.2f
+ assertTransition(
+ step = allSteps[allSteps.size - 1],
+ from = KeyguardState.AOD,
+ to = KeyguardState.UNDEFINED,
+ state = TransitionState.RUNNING,
+ progress = 0.2f,
+ )
+ }
+
+ /**
+ * STL: Gone -> Ls, then interrupt in KTF LS -> AOD, then STL Shade -> Ls
+ *
+ * In this scenario it is important KTF is brought back into a FINISHED UNDEFINED state
+ * considering the state is already on AOD from where a new UNDEFINED -> LOCKSCREEN transition
+ * can be started.
+ */
+ @Test
+ fun transition_to_ls_interrupted_by_ktf_transition_then_interrupted_by_to_ls_transition() =
+ testScope.runTest {
+ val currentStep by collectLastValue(kosmos.realKeyguardTransitionRepository.transitions)
+ val allSteps by collectValues(kosmos.realKeyguardTransitionRepository.transitions)
+ sceneTransitions.value = goneToLs
+ progress.value = 0.4f
+
+ assertTransition(
+ step = currentStep!!,
+ from = KeyguardState.UNDEFINED,
+ to = KeyguardState.LOCKSCREEN,
+ state = TransitionState.RUNNING,
+ progress = 0.4f,
+ )
+
+ kosmos.realKeyguardTransitionRepository.startTransition(
+ TransitionInfo(
+ ownerName = this.javaClass.simpleName,
+ from = KeyguardState.LOCKSCREEN,
+ to = KeyguardState.AOD,
+ animator = null,
+ modeOnCanceled = TransitionModeOnCanceled.RESET
+ )
+ )
+
+ assertTransition(
+ step = currentStep!!,
+ from = KeyguardState.LOCKSCREEN,
+ to = KeyguardState.AOD,
+ state = TransitionState.STARTED,
+ progress = 0f,
+ )
+
+ progress.value = 0.7f
+ assertTransition(currentStep!!, progress = 0f)
+
+ sceneTransitions.value =
+ ObservableTransitionState.Transition(
+ Scenes.Shade,
+ Scenes.Lockscreen,
+ flowOf(Scenes.Lockscreen),
+ progress,
+ false,
+ flowOf(false)
+ )
+
+ assertTransition(
+ step = allSteps[allSteps.size - 5],
+ from = KeyguardState.LOCKSCREEN,
+ to = KeyguardState.AOD,
+ state = TransitionState.CANCELED,
+ progress = 0f,
+ )
+
+ assertTransition(
+ step = allSteps[allSteps.size - 4],
+ from = KeyguardState.AOD,
+ to = KeyguardState.UNDEFINED,
+ state = TransitionState.STARTED,
+ progress = 1f,
+ )
+
+ assertTransition(
+ step = allSteps[allSteps.size - 3],
+ from = KeyguardState.AOD,
+ to = KeyguardState.UNDEFINED,
+ state = TransitionState.FINISHED,
+ progress = 1f,
+ )
+
+ assertTransition(
+ step = allSteps[allSteps.size - 2],
+ from = KeyguardState.UNDEFINED,
+ to = KeyguardState.LOCKSCREEN,
+ state = TransitionState.STARTED,
+ progress = 0f,
+ )
+
+ assertTransition(
+ step = allSteps[allSteps.size - 1],
+ from = KeyguardState.UNDEFINED,
+ to = KeyguardState.LOCKSCREEN,
+ state = TransitionState.RUNNING,
+ progress = 0.7f,
+ )
+ }
+
+ /**
+ * STL: Gone -> Ls, then interrupt multiple canceled KTF transitions, then STL Ls -> Shade
+ *
+ * Similar to
+ * `transition_to_ls_scene_interrupted_by_ktf_transition_then_interrupted_by_from_ls_transition`
+ * but here KTF is canceled multiple times such that in the end OCCLUDED -> UNDEFINED is
+ * properly started. (not from AOD or LOCKSCREEN)
+ *
+ * Note: there is no test which tests multiple cancels from the STL side, this is because all
+ * STL transitions trigger a response from LockscreenSceneTransitionInteractor which forces KTF
+ * into a specific state, so testing each pair is enough. Meanwhile KTF can move around without
+ * any reaction from LockscreenSceneTransitionInteractor.
+ */
+ @Test
+ fun transition_to_ls_interrupted_by_ktf_cancel_sequence_interrupted_by_from_ls_transition() =
+ testScope.runTest {
+ val currentStep by collectLastValue(kosmos.realKeyguardTransitionRepository.transitions)
+ val allSteps by collectValues(kosmos.realKeyguardTransitionRepository.transitions)
+ sceneTransitions.value = lsToGone
+ progress.value = 0.4f
+
+ assertTransition(
+ step = currentStep!!,
+ from = KeyguardState.LOCKSCREEN,
+ to = KeyguardState.UNDEFINED,
+ state = TransitionState.RUNNING,
+ progress = 0.4f,
+ )
+
+ kosmos.realKeyguardTransitionRepository.startTransition(
+ TransitionInfo(
+ ownerName = this.javaClass.simpleName,
+ from = KeyguardState.LOCKSCREEN,
+ to = KeyguardState.AOD,
+ animator = null,
+ modeOnCanceled = TransitionModeOnCanceled.RESET
+ )
+ )
+
+ assertTransition(
+ step = currentStep!!,
+ from = KeyguardState.LOCKSCREEN,
+ to = KeyguardState.AOD,
+ state = TransitionState.STARTED,
+ progress = 0f,
+ )
+
+ kosmos.realKeyguardTransitionRepository.startTransition(
+ TransitionInfo(
+ ownerName = this.javaClass.simpleName,
+ from = KeyguardState.AOD,
+ to = KeyguardState.DOZING,
+ animator = null,
+ modeOnCanceled = TransitionModeOnCanceled.RESET
+ )
+ )
+
+ assertTransition(
+ step = currentStep!!,
+ from = KeyguardState.AOD,
+ to = KeyguardState.DOZING,
+ state = TransitionState.STARTED,
+ progress = 0f,
+ )
+
+ kosmos.realKeyguardTransitionRepository.startTransition(
+ TransitionInfo(
+ ownerName = this.javaClass.simpleName,
+ from = KeyguardState.DOZING,
+ to = KeyguardState.OCCLUDED,
+ animator = null,
+ modeOnCanceled = TransitionModeOnCanceled.RESET
+ )
+ )
+
+ assertTransition(
+ step = currentStep!!,
+ from = KeyguardState.DOZING,
+ to = KeyguardState.OCCLUDED,
+ state = TransitionState.STARTED,
+ progress = 0f,
+ )
+
+ progress.value = 0.7f
+ assertTransition(currentStep!!, progress = 0f)
+
+ sceneTransitions.value =
+ ObservableTransitionState.Transition(
+ Scenes.Lockscreen,
+ Scenes.Shade,
+ flowOf(Scenes.Lockscreen),
+ progress,
+ false,
+ flowOf(false)
+ )
+
+ assertTransition(
+ step = allSteps[allSteps.size - 3],
+ from = KeyguardState.DOZING,
+ to = KeyguardState.OCCLUDED,
+ state = TransitionState.CANCELED,
+ progress = 0f,
+ )
+
+ assertTransition(
+ step = allSteps[allSteps.size - 2],
+ from = KeyguardState.OCCLUDED,
+ to = KeyguardState.UNDEFINED,
+ state = TransitionState.STARTED,
+ progress = 0f,
+ )
+
+ progress.value = 0.2f
+ assertTransition(
+ step = allSteps[allSteps.size - 1],
+ from = KeyguardState.OCCLUDED,
+ to = KeyguardState.UNDEFINED,
+ state = TransitionState.RUNNING,
+ progress = 0.2f,
+ )
+ }
+
+ /**
+ * STL: Gone -> Ls, then interrupted by KTF LS -> AOD which is FINISHED before STL Ls -> Shade
+ *
+ * Similar to
+ * `transition_to_ls_scene_interrupted_by_ktf_transition_then_interrupted_by_from_ls_transition`
+ * but here KTF is finishing the transition and only then gets interrupted. Should correctly
+ * start AOD -> UNDEFINED.
+ */
+ @Test
+ fun transition_to_ls_scene_interrupted_and_finished_by_ktf_interrupted_by_from_ls_transition() =
+ testScope.runTest {
+ val currentStep by collectLastValue(kosmos.realKeyguardTransitionRepository.transitions)
+ val allSteps by collectValues(kosmos.realKeyguardTransitionRepository.transitions)
+ sceneTransitions.value = lsToGone
+ progress.value = 0.4f
+
+ assertTransition(
+ step = currentStep!!,
+ from = KeyguardState.LOCKSCREEN,
+ to = KeyguardState.UNDEFINED,
+ state = TransitionState.RUNNING,
+ progress = 0.4f,
+ )
+
+ val ktfUuid =
+ kosmos.realKeyguardTransitionRepository.startTransition(
+ TransitionInfo(
+ ownerName = this.javaClass.simpleName,
+ from = KeyguardState.LOCKSCREEN,
+ to = KeyguardState.AOD,
+ animator = null,
+ modeOnCanceled = TransitionModeOnCanceled.RESET
+ )
+ )
+
+ assertTransition(
+ step = currentStep!!,
+ from = KeyguardState.LOCKSCREEN,
+ to = KeyguardState.AOD,
+ state = TransitionState.STARTED,
+ progress = 0f,
+ )
+
+ kosmos.realKeyguardTransitionRepository.updateTransition(
+ ktfUuid!!,
+ 1f,
+ TransitionState.FINISHED
+ )
+
+ assertTransition(
+ step = currentStep!!,
+ from = KeyguardState.LOCKSCREEN,
+ to = KeyguardState.AOD,
+ state = TransitionState.FINISHED,
+ progress = 1f,
+ )
+
+ sceneTransitions.value =
+ ObservableTransitionState.Transition(
+ Scenes.Lockscreen,
+ Scenes.Shade,
+ flowOf(Scenes.Lockscreen),
+ progress,
+ false,
+ flowOf(false)
+ )
+
+ assertTransition(
+ step = allSteps[allSteps.size - 3],
+ from = KeyguardState.LOCKSCREEN,
+ to = KeyguardState.AOD,
+ state = TransitionState.FINISHED,
+ progress = 1f,
+ )
+
+ assertTransition(
+ step = allSteps[allSteps.size - 2],
+ from = KeyguardState.AOD,
+ to = KeyguardState.UNDEFINED,
+ state = TransitionState.STARTED,
+ progress = 0f,
+ )
+
+ progress.value = 0.2f
+ assertTransition(
+ step = allSteps[allSteps.size - 1],
+ from = KeyguardState.AOD,
+ to = KeyguardState.UNDEFINED,
+ state = TransitionState.RUNNING,
+ progress = 0.2f,
+ )
+ }
+
+ /**
+ * STL: Ls -> Gone, then interrupted by Ls -> Bouncer. This happens when the next transition is
+ * immediately started from Gone without settling in Idle. This specifically happens when
+ * dragging down on Ls and then changing direction. The transition will switch from -> Shade to
+ * -> Bouncer without settling or signaling any cancellation as STL considers this to be the
+ * same gesture.
+ *
+ * In STL there is no guarantee that transitions settle in Idle before continuing.
+ */
+ @Test
+ fun transition_from_ls_scene_interrupted_by_other_from_ls_transition() =
+ testScope.runTest {
+ val currentStep by collectLastValue(kosmos.realKeyguardTransitionRepository.transitions)
+ val allSteps by collectValues(kosmos.realKeyguardTransitionRepository.transitions)
+ sceneTransitions.value = lsToGone
+
+ assertTransition(
+ step = currentStep!!,
+ from = KeyguardState.LOCKSCREEN,
+ to = KeyguardState.UNDEFINED,
+ state = TransitionState.RUNNING,
+ progress = 0f,
+ )
+
+ progress.value = 0.4f
+ sceneTransitions.value =
+ ObservableTransitionState.Transition(
+ Scenes.Lockscreen,
+ Scenes.Bouncer,
+ flowOf(Scenes.Lockscreen),
+ progress,
+ false,
+ flowOf(false)
+ )
+
+ assertTransition(
+ step = allSteps[allSteps.size - 5],
+ from = KeyguardState.LOCKSCREEN,
+ to = KeyguardState.UNDEFINED,
+ state = TransitionState.CANCELED,
+ progress = 0.4f,
+ )
+
+ assertTransition(
+ step = allSteps[allSteps.size - 4],
+ from = KeyguardState.UNDEFINED,
+ to = KeyguardState.LOCKSCREEN,
+ state = TransitionState.STARTED,
+ progress = 0.6f,
+ )
+
+ assertTransition(
+ step = allSteps[allSteps.size - 3],
+ from = KeyguardState.UNDEFINED,
+ to = KeyguardState.LOCKSCREEN,
+ state = TransitionState.FINISHED,
+ progress = 1f,
+ )
+
+ assertTransition(
+ step = allSteps[allSteps.size - 2],
+ from = KeyguardState.LOCKSCREEN,
+ to = KeyguardState.UNDEFINED,
+ state = TransitionState.STARTED,
+ progress = 0f,
+ )
+ }
+
+ /**
+ * STL: Ls -> Gone, then interrupted by Gone -> Ls. This happens when the next transition is
+ * immediately started from Gone without settling in Idle. In STL there is no guarantee that
+ * transitions settle in Idle before continuing.
+ */
+ @Test
+ fun transition_from_ls_scene_interrupted_by_to_ls_transition() =
+ testScope.runTest {
+ val currentStep by collectLastValue(kosmos.realKeyguardTransitionRepository.transitions)
+ val allSteps by collectValues(kosmos.realKeyguardTransitionRepository.transitions)
+ sceneTransitions.value = lsToGone
+ progress.value = 0.4f
+
+ assertTransition(
+ step = currentStep!!,
+ from = KeyguardState.LOCKSCREEN,
+ to = KeyguardState.UNDEFINED,
+ state = TransitionState.RUNNING,
+ progress = 0.4f,
+ )
+
+ sceneTransitions.value =
+ ObservableTransitionState.Transition(
+ Scenes.Gone,
+ Scenes.Lockscreen,
+ flowOf(Scenes.Lockscreen),
+ progress,
+ false,
+ flowOf(false)
+ )
+
+ assertTransition(
+ step = allSteps[allSteps.size - 3],
+ from = KeyguardState.LOCKSCREEN,
+ to = KeyguardState.UNDEFINED,
+ state = TransitionState.FINISHED,
+ progress = 1f,
+ )
+
+ assertTransition(
+ step = allSteps[allSteps.size - 2],
+ from = KeyguardState.UNDEFINED,
+ to = KeyguardState.LOCKSCREEN,
+ state = TransitionState.STARTED,
+ progress = 0f,
+ )
+
+ progress.value = 0.2f
+ assertTransition(
+ step = allSteps[allSteps.size - 1],
+ from = KeyguardState.UNDEFINED,
+ to = KeyguardState.LOCKSCREEN,
+ state = TransitionState.RUNNING,
+ progress = 0.2f,
+ )
+ }
+
+ /**
+ * STL: Ls -> Gone, then interrupted by Gone -> Bouncer. This happens when the next transition
+ * is immediately started from Gone without settling in Idle. In STL there is no guarantee that
+ * transitions settle in Idle before continuing.
+ */
+ @Test
+ fun transition_from_ls_scene_interrupted_by_other_stl_transition() =
+ testScope.runTest {
+ val currentStep by collectLastValue(kosmos.realKeyguardTransitionRepository.transitions)
+ sceneTransitions.value = lsToGone
+ progress.value = 0.4f
+
+ assertTransition(
+ step = currentStep!!,
+ from = KeyguardState.LOCKSCREEN,
+ to = KeyguardState.UNDEFINED,
+ state = TransitionState.RUNNING,
+ progress = 0.4f,
+ )
+
+ sceneTransitions.value =
+ ObservableTransitionState.Transition(
+ Scenes.Gone,
+ Scenes.Bouncer,
+ flowOf(Scenes.Lockscreen),
+ progress,
+ false,
+ flowOf(false)
+ )
+
+ assertTransition(
+ step = currentStep!!,
+ from = KeyguardState.LOCKSCREEN,
+ to = KeyguardState.UNDEFINED,
+ state = TransitionState.FINISHED,
+ progress = 1f,
+ )
+ }
+
+ private fun assertTransition(
+ step: TransitionStep,
+ from: KeyguardState? = null,
+ to: KeyguardState? = null,
+ state: TransitionState? = null,
+ progress: Float? = null
+ ) {
+ if (from != null) assertThat(step.from).isEqualTo(from)
+ if (to != null) assertThat(step.to).isEqualTo(to)
+ if (state != null) assertThat(step.transitionState).isEqualTo(state)
+ if (progress != null) assertThat(step.value).isEqualTo(progress)
+ }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/binder/WindowManagerLockscreenVisibilityManagerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/binder/WindowManagerLockscreenVisibilityManagerTest.kt
index 9b2db3e..1f13298 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/binder/WindowManagerLockscreenVisibilityManagerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/binder/WindowManagerLockscreenVisibilityManagerTest.kt
@@ -21,6 +21,7 @@
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.keyguard.WindowManagerLockscreenVisibilityManager
+import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor
import com.android.systemui.statusbar.policy.KeyguardStateController
import com.android.systemui.util.concurrency.FakeExecutor
import com.android.systemui.util.time.FakeSystemClock
@@ -41,10 +42,9 @@
private lateinit var executor: FakeExecutor
@Mock private lateinit var activityTaskManagerService: IActivityTaskManager
-
@Mock private lateinit var keyguardStateController: KeyguardStateController
-
@Mock private lateinit var keyguardSurfaceBehindAnimator: KeyguardSurfaceBehindParamsApplier
+ @Mock private lateinit var keyguardTransitionInteractor: KeyguardTransitionInteractor
@Before
fun setUp() {
@@ -57,6 +57,7 @@
activityTaskManagerService = activityTaskManagerService,
keyguardStateController = keyguardStateController,
keyguardSurfaceBehindAnimator = keyguardSurfaceBehindAnimator,
+ keyguardTransitionInteractor = keyguardTransitionInteractor,
)
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/AlternateBouncerWindowViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/AlternateBouncerWindowViewModelTest.kt
index 1396b20..391831a 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/AlternateBouncerWindowViewModelTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/AlternateBouncerWindowViewModelTest.kt
@@ -18,15 +18,19 @@
package com.android.systemui.keyguard.ui.viewmodel
import androidx.test.filters.SmallTest
+import com.android.keyguard.keyguardUpdateMonitor
import com.android.systemui.Flags
import com.android.systemui.SysuiTestCase
import com.android.systemui.biometrics.data.repository.fakeFingerprintPropertyRepository
+import com.android.systemui.bouncer.data.repository.fakeKeyguardBouncerRepository
import com.android.systemui.coroutines.collectLastValue
+import com.android.systemui.keyguard.data.repository.fakeBiometricSettingsRepository
import com.android.systemui.keyguard.data.repository.fakeKeyguardTransitionRepository
import com.android.systemui.keyguard.shared.model.KeyguardState
import com.android.systemui.keyguard.shared.model.TransitionState
import com.android.systemui.keyguard.shared.model.TransitionStep
import com.android.systemui.kosmos.testScope
+import com.android.systemui.statusbar.policy.keyguardStateController
import com.android.systemui.testKosmos
import com.google.common.truth.Truth.assertThat
import kotlinx.coroutines.ExperimentalCoroutinesApi
@@ -34,6 +38,7 @@
import org.junit.Test
import org.junit.runner.RunWith
import org.junit.runners.JUnit4
+import org.mockito.kotlin.whenever
@ExperimentalCoroutinesApi
@RunWith(JUnit4::class)
@@ -49,13 +54,35 @@
fun alternateBouncerTransition_alternateBouncerWindowRequiredTrue() =
testScope.runTest {
mSetFlagsRule.enableFlags(Flags.FLAG_DEVICE_ENTRY_UDFPS_REFACTOR)
+ val canShowAlternateBouncer by collectLastValue(underTest.canShowAlternateBouncer)
val alternateBouncerWindowRequired by
collectLastValue(underTest.alternateBouncerWindowRequired)
+ givenCanShowAlternateBouncer()
fingerprintPropertyRepository.supportsUdfps()
transitionRepository.sendTransitionSteps(
listOf(
+ stepToLockscreen(0f, TransitionState.STARTED),
+ stepToLockscreen(.4f),
+ stepToLockscreen(1f, TransitionState.FINISHED),
+ ),
+ testScope,
+ )
+ assertThat(canShowAlternateBouncer).isTrue()
+ transitionRepository.sendTransitionSteps(
+ listOf(
+ stepFromLockscreenToAlternateBouncer(0f, TransitionState.STARTED),
+ stepFromLockscreenToAlternateBouncer(.4f),
+ stepFromLockscreenToAlternateBouncer(.6f),
+ ),
+ testScope,
+ )
+ assertThat(canShowAlternateBouncer).isTrue()
+ assertThat(alternateBouncerWindowRequired).isTrue()
+
+ transitionRepository.sendTransitionSteps(
+ listOf(
stepFromAlternateBouncer(0f, TransitionState.STARTED),
- stepFromAlternateBouncer(.4f),
+ stepFromAlternateBouncer(.2f),
stepFromAlternateBouncer(.6f),
),
testScope,
@@ -77,13 +104,21 @@
mSetFlagsRule.disableFlags(Flags.FLAG_DEVICE_ENTRY_UDFPS_REFACTOR)
val alternateBouncerWindowRequired by
collectLastValue(underTest.alternateBouncerWindowRequired)
+ givenCanShowAlternateBouncer()
fingerprintPropertyRepository.supportsUdfps()
transitionRepository.sendTransitionSteps(
listOf(
- stepFromAlternateBouncer(0f, TransitionState.STARTED),
- stepFromAlternateBouncer(.4f),
- stepFromAlternateBouncer(.6f),
- stepFromAlternateBouncer(1f),
+ stepToLockscreen(0f, TransitionState.STARTED),
+ stepToLockscreen(.4f),
+ stepToLockscreen(1f, TransitionState.FINISHED),
+ ),
+ testScope,
+ )
+ transitionRepository.sendTransitionSteps(
+ listOf(
+ stepFromLockscreenToAlternateBouncer(0f, TransitionState.STARTED),
+ stepFromLockscreenToAlternateBouncer(.4f),
+ stepFromLockscreenToAlternateBouncer(.6f),
),
testScope,
)
@@ -96,13 +131,23 @@
mSetFlagsRule.enableFlags(Flags.FLAG_DEVICE_ENTRY_UDFPS_REFACTOR)
val alternateBouncerWindowRequired by
collectLastValue(underTest.alternateBouncerWindowRequired)
+ givenCanShowAlternateBouncer()
fingerprintPropertyRepository.supportsUdfps()
transitionRepository.sendTransitionSteps(
listOf(
+ stepFromLockscreenToDozing(0f, TransitionState.STARTED),
+ stepFromLockscreenToDozing(.4f),
+ stepFromLockscreenToDozing(.6f),
+ stepFromLockscreenToDozing(1f, TransitionState.FINISHED),
+ ),
+ testScope,
+ )
+ assertThat(alternateBouncerWindowRequired).isFalse()
+ transitionRepository.sendTransitionSteps(
+ listOf(
stepFromDozingToLockscreen(0f, TransitionState.STARTED),
stepFromDozingToLockscreen(.4f),
stepFromDozingToLockscreen(.6f),
- stepFromDozingToLockscreen(1f),
),
testScope,
)
@@ -115,19 +160,39 @@
mSetFlagsRule.enableFlags(Flags.FLAG_DEVICE_ENTRY_UDFPS_REFACTOR)
val alternateBouncerWindowRequired by
collectLastValue(underTest.alternateBouncerWindowRequired)
+ givenCanShowAlternateBouncer()
fingerprintPropertyRepository.supportsRearFps()
transitionRepository.sendTransitionSteps(
listOf(
- stepFromAlternateBouncer(0f, TransitionState.STARTED),
- stepFromAlternateBouncer(.4f),
- stepFromAlternateBouncer(.6f),
- stepFromAlternateBouncer(1f),
+ stepToLockscreen(0f, TransitionState.STARTED),
+ stepToLockscreen(.4f),
+ stepToLockscreen(1f, TransitionState.FINISHED),
+ ),
+ testScope,
+ )
+ transitionRepository.sendTransitionSteps(
+ listOf(
+ stepFromLockscreenToAlternateBouncer(0f, TransitionState.STARTED),
+ stepFromLockscreenToAlternateBouncer(.4f),
+ stepFromLockscreenToAlternateBouncer(.6f),
),
testScope,
)
assertThat(alternateBouncerWindowRequired).isFalse()
}
+ private fun stepToLockscreen(
+ value: Float,
+ state: TransitionState = TransitionState.RUNNING
+ ): TransitionStep {
+ return step(
+ from = KeyguardState.GONE,
+ to = KeyguardState.LOCKSCREEN,
+ value = value,
+ transitionState = state,
+ )
+ }
+
private fun stepFromAlternateBouncer(
value: Float,
state: TransitionState = TransitionState.RUNNING
@@ -140,6 +205,18 @@
)
}
+ private fun stepFromLockscreenToAlternateBouncer(
+ value: Float,
+ state: TransitionState = TransitionState.RUNNING
+ ): TransitionStep {
+ return step(
+ from = KeyguardState.LOCKSCREEN,
+ to = KeyguardState.ALTERNATE_BOUNCER,
+ value = value,
+ transitionState = state,
+ )
+ }
+
private fun stepFromDozingToLockscreen(
value: Float,
state: TransitionState = TransitionState.RUNNING
@@ -152,6 +229,18 @@
)
}
+ private fun stepFromLockscreenToDozing(
+ value: Float,
+ state: TransitionState = TransitionState.RUNNING
+ ): TransitionStep {
+ return step(
+ from = KeyguardState.LOCKSCREEN,
+ to = KeyguardState.DOZING,
+ value = value,
+ transitionState = state,
+ )
+ }
+
private fun step(
from: KeyguardState,
to: KeyguardState,
@@ -166,4 +255,16 @@
ownerName = "AlternateBouncerViewModelTest"
)
}
+
+ /**
+ * Given the alternate bouncer parameters are set so that the alternate bouncer can show, aside
+ * from the fingerprint modality.
+ */
+ private fun givenCanShowAlternateBouncer() {
+ kosmos.fakeKeyguardBouncerRepository.setPrimaryShow(false)
+ kosmos.fakeBiometricSettingsRepository.setIsFingerprintAuthEnrolledAndEnabled(true)
+ kosmos.fakeBiometricSettingsRepository.setIsFingerprintAuthCurrentlyAllowed(true)
+ whenever(kosmos.keyguardUpdateMonitor.isFingerprintLockedOut).thenReturn(false)
+ whenever(kosmos.keyguardStateController.isUnlocked).thenReturn(false)
+ }
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardClockViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardClockViewModelTest.kt
index 768d446..40663ce 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardClockViewModelTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardClockViewModelTest.kt
@@ -23,6 +23,7 @@
import com.android.systemui.Flags
import com.android.systemui.SysuiTestCase
import com.android.systemui.coroutines.collectLastValue
+import com.android.systemui.flags.BrokenWithSceneContainer
import com.android.systemui.flags.andSceneContainer
import com.android.systemui.keyguard.data.repository.fakeKeyguardClockRepository
import com.android.systemui.keyguard.data.repository.keyguardClockRepository
@@ -56,7 +57,7 @@
class KeyguardClockViewModelTest(flags: FlagsParameterization) : SysuiTestCase() {
val kosmos = testKosmos()
val testScope = kosmos.testScope
- val underTest = kosmos.keyguardClockViewModel
+ val underTest by lazy { kosmos.keyguardClockViewModel }
val res = context.resources
@Mock lateinit var clockController: ClockController
@@ -96,6 +97,7 @@
}
@Test
+ @BrokenWithSceneContainer(339465026)
fun currentClockLayout_splitShadeOn_clockNotCentered_largeClock_splitShadeLargeClock() =
testScope.runTest {
val currentClockLayout by collectLastValue(underTest.currentClockLayout)
@@ -110,6 +112,7 @@
}
@Test
+ @BrokenWithSceneContainer(339465026)
fun currentClockLayout_splitShadeOn_clockNotCentered_smallClock_splitShadeSmallClock() =
testScope.runTest {
val currentClockLayout by collectLastValue(underTest.currentClockLayout)
@@ -124,6 +127,7 @@
}
@Test
+ @BrokenWithSceneContainer(339465026)
fun currentClockLayout_singleShade_smallClock_smallClock() =
testScope.runTest {
val currentClockLayout by collectLastValue(underTest.currentClockLayout)
@@ -193,6 +197,7 @@
}
@Test
+ @BrokenWithSceneContainer(339465026)
fun testClockSize_dynamicClockSize() =
testScope.runTest {
with(kosmos) {
@@ -216,6 +221,7 @@
}
@Test
+ @BrokenWithSceneContainer(339465026)
fun isLargeClockVisible_whenSmallClockSize_isFalse() =
testScope.runTest {
val value by collectLastValue(underTest.isLargeClockVisible)
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/controls/domain/pipeline/LegacyMediaDataFilterImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/controls/domain/pipeline/LegacyMediaDataFilterImplTest.kt
index e56a253..5986f4a 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/controls/domain/pipeline/LegacyMediaDataFilterImplTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/controls/domain/pipeline/LegacyMediaDataFilterImplTest.kt
@@ -33,9 +33,6 @@
import com.android.systemui.media.controls.util.MediaUiEventLogger
import com.android.systemui.settings.UserTracker
import com.android.systemui.statusbar.NotificationLockscreenUserManager
-import com.android.systemui.util.mockito.any
-import com.android.systemui.util.mockito.eq
-import com.android.systemui.util.mockito.whenever
import com.android.systemui.util.time.FakeSystemClock
import com.google.common.truth.Truth.assertThat
import java.util.concurrent.Executor
@@ -50,6 +47,9 @@
import org.mockito.Mockito.reset
import org.mockito.Mockito.verify
import org.mockito.MockitoAnnotations
+import org.mockito.kotlin.any
+import org.mockito.kotlin.eq
+import org.mockito.kotlin.whenever
private const val KEY = "TEST_KEY"
private const val KEY_ALT = "TEST_KEY_2"
@@ -172,20 +172,20 @@
fun testOnRemovedForCurrent_callsListener() {
// GIVEN a media was removed for main user
mediaDataFilter.onMediaDataLoaded(KEY, null, dataMain)
- mediaDataFilter.onMediaDataRemoved(KEY)
+ mediaDataFilter.onMediaDataRemoved(KEY, false)
// THEN we should tell the listener
- verify(listener).onMediaDataRemoved(eq(KEY))
+ verify(listener).onMediaDataRemoved(eq(KEY), eq(false))
}
@Test
fun testOnRemovedForGuest_doesNotCallListener() {
// GIVEN a media was removed for guest user
mediaDataFilter.onMediaDataLoaded(KEY, null, dataGuest)
- mediaDataFilter.onMediaDataRemoved(KEY)
+ mediaDataFilter.onMediaDataRemoved(KEY, false)
// THEN we should NOT tell the listener
- verify(listener, never()).onMediaDataRemoved(eq(KEY))
+ verify(listener, never()).onMediaDataRemoved(eq(KEY), anyBoolean())
}
@Test
@@ -197,7 +197,7 @@
setUser(USER_GUEST)
// THEN we should remove the main user's media
- verify(listener).onMediaDataRemoved(eq(KEY))
+ verify(listener).onMediaDataRemoved(eq(KEY), eq(false))
}
@Test
@@ -230,7 +230,7 @@
setPrivateProfileUnavailable()
// THEN we should add the private profile media
- verify(listener).onMediaDataRemoved(eq(KEY_ALT))
+ verify(listener).onMediaDataRemoved(eq(KEY_ALT), eq(false))
}
@Test
@@ -360,7 +360,7 @@
@Test
fun testOnNotificationRemoved_doesntHaveMedia() {
mediaDataFilter.onMediaDataLoaded(KEY, oldKey = null, data = dataMain)
- mediaDataFilter.onMediaDataRemoved(KEY)
+ mediaDataFilter.onMediaDataRemoved(KEY, false)
assertThat(mediaDataFilter.hasAnyMediaOrRecommendation()).isFalse()
assertThat(mediaDataFilter.hasAnyMedia()).isFalse()
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/controls/domain/pipeline/LegacyMediaDataManagerImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/controls/domain/pipeline/LegacyMediaDataManagerImplTest.kt
index 5a2d22d..3372f06 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/controls/domain/pipeline/LegacyMediaDataManagerImplTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/controls/domain/pipeline/LegacyMediaDataManagerImplTest.kt
@@ -66,9 +66,6 @@
import com.android.systemui.statusbar.SbnBuilder
import com.android.systemui.tuner.TunerService
import com.android.systemui.util.concurrency.FakeExecutor
-import com.android.systemui.util.mockito.any
-import com.android.systemui.util.mockito.capture
-import com.android.systemui.util.mockito.eq
import com.android.systemui.util.time.FakeSystemClock
import com.google.common.truth.Truth.assertThat
import org.junit.After
@@ -90,6 +87,9 @@
import org.mockito.Mockito.`when` as whenever
import org.mockito.MockitoSession
import org.mockito.junit.MockitoJUnit
+import org.mockito.kotlin.any
+import org.mockito.kotlin.capture
+import org.mockito.kotlin.eq
import org.mockito.quality.Strictness
private const val KEY = "KEY"
@@ -346,7 +346,7 @@
// THEN it is removed and listeners are informed
foregroundExecutor.advanceClockToLast()
foregroundExecutor.runAllReady()
- verify(listener).onMediaDataRemoved(PACKAGE_NAME)
+ verify(listener).onMediaDataRemoved(PACKAGE_NAME, false)
}
@Test
@@ -532,7 +532,7 @@
addNotificationAndLoad()
val data = mediaDataCaptor.value
mediaDataManager.onNotificationRemoved(KEY)
- verify(listener).onMediaDataRemoved(eq(KEY))
+ verify(listener).onMediaDataRemoved(eq(KEY), eq(false))
verify(logger).logMediaRemoved(anyInt(), eq(PACKAGE_NAME), eq(data.instanceId))
}
@@ -777,7 +777,7 @@
eq(false)
)
assertThat(mediaDataCaptor.value.resumption).isTrue()
- verify(listener, never()).onMediaDataRemoved(eq(KEY))
+ verify(listener, never()).onMediaDataRemoved(eq(KEY), eq(false))
// WHEN the second is removed
mediaDataManager.onNotificationRemoved(KEY_2)
// THEN the data is for resumption and the second key is removed
@@ -791,7 +791,7 @@
eq(false)
)
assertThat(mediaDataCaptor.value.resumption).isTrue()
- verify(listener).onMediaDataRemoved(eq(KEY_2))
+ verify(listener).onMediaDataRemoved(eq(KEY_2), eq(false))
}
@Test
@@ -816,7 +816,7 @@
mediaDataManager.onNotificationRemoved(KEY)
// THEN the media data is removed
- verify(listener).onMediaDataRemoved(eq(KEY))
+ verify(listener).onMediaDataRemoved(eq(KEY), eq(false))
}
@Test
@@ -866,7 +866,7 @@
mediaDataManager.onNotificationRemoved(KEY)
// THEN the media data is removed
- verify(listener).onMediaDataRemoved(eq(KEY))
+ verify(listener).onMediaDataRemoved(eq(KEY), eq(false))
}
@Test
@@ -905,7 +905,7 @@
assertThat(mediaDataCaptor.value.isPlaying).isFalse()
// And the oldest resume control was removed
- verify(listener).onMediaDataRemoved(eq("0:$PACKAGE_NAME"))
+ verify(listener).onMediaDataRemoved(eq("0:$PACKAGE_NAME"), eq(false))
}
fun testOnNotificationRemoved_lockDownMode() {
@@ -915,7 +915,7 @@
val data = mediaDataCaptor.value
mediaDataManager.onNotificationRemoved(KEY)
- verify(listener, never()).onMediaDataRemoved(eq(KEY))
+ verify(listener, never()).onMediaDataRemoved(eq(KEY), anyBoolean())
verify(logger, never())
.logActiveConvertedToResume(anyInt(), eq(PACKAGE_NAME), eq(data.instanceId))
verify(logger).logMediaRemoved(anyInt(), eq(PACKAGE_NAME), eq(data.instanceId))
@@ -1148,7 +1148,7 @@
mediaDataManager.setMediaResumptionEnabled(false)
// THEN the resume controls are dismissed
- verify(listener).onMediaDataRemoved(eq(PACKAGE_NAME))
+ verify(listener).onMediaDataRemoved(eq(PACKAGE_NAME), eq(false))
verify(logger).logMediaRemoved(anyInt(), eq(PACKAGE_NAME), eq(data.instanceId))
}
@@ -1156,19 +1156,19 @@
fun testDismissMedia_listenerCalled() {
addNotificationAndLoad()
val data = mediaDataCaptor.value
- val removed = mediaDataManager.dismissMediaData(KEY, 0L)
+ val removed = mediaDataManager.dismissMediaData(KEY, 0L, true)
assertThat(removed).isTrue()
foregroundExecutor.advanceClockToLast()
foregroundExecutor.runAllReady()
- verify(listener).onMediaDataRemoved(eq(KEY))
+ verify(listener).onMediaDataRemoved(eq(KEY), eq(true))
verify(logger).logMediaRemoved(anyInt(), eq(PACKAGE_NAME), eq(data.instanceId))
}
@Test
fun testDismissMedia_keyDoesNotExist_returnsFalse() {
- val removed = mediaDataManager.dismissMediaData(KEY, 0L)
+ val removed = mediaDataManager.dismissMediaData(KEY, 0L, true)
assertThat(removed).isFalse()
}
@@ -2077,7 +2077,7 @@
sessionCallbackCaptor.value.invoke(KEY)
// It remains as a regular player
- verify(listener, never()).onMediaDataRemoved(eq(KEY))
+ verify(listener, never()).onMediaDataRemoved(eq(KEY), anyBoolean())
verify(listener, never())
.onMediaDataLoaded(eq(PACKAGE_NAME), any(), any(), anyBoolean(), anyInt(), anyBoolean())
}
@@ -2093,7 +2093,7 @@
mediaDataManager.onNotificationRemoved(KEY)
// It is fully removed
- verify(listener).onMediaDataRemoved(eq(KEY))
+ verify(listener).onMediaDataRemoved(eq(KEY), eq(false))
verify(logger).logMediaRemoved(anyInt(), eq(PACKAGE_NAME), eq(data.instanceId))
verify(listener, never())
.onMediaDataLoaded(eq(PACKAGE_NAME), any(), any(), anyBoolean(), anyInt(), anyBoolean())
@@ -2146,7 +2146,7 @@
mediaDataManager.onNotificationRemoved(KEY)
// It remains as a regular player
- verify(listener, never()).onMediaDataRemoved(eq(KEY))
+ verify(listener, never()).onMediaDataRemoved(eq(KEY), anyBoolean())
verify(listener, never())
.onMediaDataLoaded(eq(PACKAGE_NAME), any(), any(), anyBoolean(), anyInt(), anyBoolean())
}
@@ -2199,7 +2199,7 @@
sessionCallbackCaptor.value.invoke(KEY)
// It is fully removed
- verify(listener).onMediaDataRemoved(eq(KEY))
+ verify(listener).onMediaDataRemoved(eq(KEY), eq(false))
verify(logger).logMediaRemoved(anyInt(), eq(PACKAGE_NAME), eq(data.instanceId))
verify(listener, never())
.onMediaDataLoaded(eq(PACKAGE_NAME), any(), any(), anyBoolean(), anyInt(), anyBoolean())
@@ -2253,7 +2253,7 @@
sessionCallbackCaptor.value.invoke(KEY)
// It is fully removed.
- verify(listener).onMediaDataRemoved(eq(KEY))
+ verify(listener).onMediaDataRemoved(eq(KEY), eq(false))
verify(logger).logMediaRemoved(anyInt(), eq(PACKAGE_NAME), eq(data.instanceId))
verify(listener, never())
.onMediaDataLoaded(
@@ -2279,7 +2279,7 @@
sessionCallbackCaptor.value.invoke(KEY)
// It is fully removed
- verify(listener).onMediaDataRemoved(eq(KEY))
+ verify(listener).onMediaDataRemoved(eq(KEY), eq(false))
verify(logger).logMediaRemoved(anyInt(), eq(PACKAGE_NAME), eq(data.instanceId))
verify(listener, never())
.onMediaDataLoaded(eq(PACKAGE_NAME), any(), any(), anyBoolean(), anyInt(), anyBoolean())
@@ -2329,7 +2329,7 @@
mediaDataManager.onNotificationRemoved(KEY)
// We still make sure to remove it
- verify(listener).onMediaDataRemoved(eq(KEY))
+ verify(listener).onMediaDataRemoved(eq(KEY), eq(false))
}
@Test
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/controls/domain/pipeline/MediaDataCombineLatestTest.java b/packages/SystemUI/tests/src/com/android/systemui/media/controls/domain/pipeline/MediaDataCombineLatestTest.java
index bb5b572..dd05a0d 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/controls/domain/pipeline/MediaDataCombineLatestTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/controls/domain/pipeline/MediaDataCombineLatestTest.java
@@ -202,24 +202,24 @@
@Test
public void mediaDataRemoved() {
// WHEN media data is removed without first receiving device or data
- mManager.onMediaDataRemoved(KEY);
+ mManager.onMediaDataRemoved(KEY, false);
// THEN a removed event isn't emitted
- verify(mListener, never()).onMediaDataRemoved(eq(KEY));
+ verify(mListener, never()).onMediaDataRemoved(eq(KEY), anyBoolean());
}
@Test
public void mediaDataRemovedAfterMediaEvent() {
mManager.onMediaDataLoaded(KEY, null, mMediaData, true /* immediately */,
0 /* receivedSmartspaceCardLatency */, false /* isSsReactivated */);
- mManager.onMediaDataRemoved(KEY);
- verify(mListener).onMediaDataRemoved(eq(KEY));
+ mManager.onMediaDataRemoved(KEY, false);
+ verify(mListener).onMediaDataRemoved(eq(KEY), eq(false));
}
@Test
public void mediaDataRemovedAfterDeviceEvent() {
mManager.onMediaDeviceChanged(KEY, null, mDeviceData);
- mManager.onMediaDataRemoved(KEY);
- verify(mListener).onMediaDataRemoved(eq(KEY));
+ mManager.onMediaDataRemoved(KEY, false);
+ verify(mListener).onMediaDataRemoved(eq(KEY), eq(false));
}
@Test
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/controls/domain/pipeline/MediaDataFilterImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/controls/domain/pipeline/MediaDataFilterImplTest.kt
index 77ad263..caaa42f 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/controls/domain/pipeline/MediaDataFilterImplTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/controls/domain/pipeline/MediaDataFilterImplTest.kt
@@ -40,9 +40,6 @@
import com.android.systemui.settings.UserTracker
import com.android.systemui.statusbar.NotificationLockscreenUserManager
import com.android.systemui.testKosmos
-import com.android.systemui.util.mockito.any
-import com.android.systemui.util.mockito.eq
-import com.android.systemui.util.mockito.whenever
import com.android.systemui.util.time.FakeSystemClock
import com.google.common.truth.Truth.assertThat
import java.util.concurrent.Executor
@@ -60,6 +57,9 @@
import org.mockito.Mockito.never
import org.mockito.Mockito.verify
import org.mockito.MockitoAnnotations
+import org.mockito.kotlin.any
+import org.mockito.kotlin.eq
+import org.mockito.kotlin.whenever
private const val KEY = "TEST_KEY"
private const val KEY_ALT = "TEST_KEY_2"
@@ -205,9 +205,9 @@
assertThat(currentMedia).containsExactly(mediaCommonModel)
- mediaDataFilter.onMediaDataRemoved(KEY)
+ mediaDataFilter.onMediaDataRemoved(KEY, false)
- verify(listener).onMediaDataRemoved(eq(KEY))
+ verify(listener).onMediaDataRemoved(eq(KEY), eq(false))
assertThat(currentMedia).doesNotContain(mediaCommonModel)
}
@@ -218,9 +218,9 @@
// GIVEN a media was removed for guest user
mediaDataFilter.onMediaDataLoaded(KEY, null, dataGuest)
- mediaDataFilter.onMediaDataRemoved(KEY)
+ mediaDataFilter.onMediaDataRemoved(KEY, false)
- verify(listener, never()).onMediaDataRemoved(eq(KEY))
+ verify(listener, never()).onMediaDataRemoved(eq(KEY), eq(false))
assertThat(currentMedia).isEmpty()
}
@@ -239,7 +239,7 @@
setUser(USER_GUEST)
// THEN we should remove the main user's media
- verify(listener).onMediaDataRemoved(eq(KEY))
+ verify(listener).onMediaDataRemoved(eq(KEY), eq(false))
assertThat(currentMedia).isEmpty()
}
@@ -291,7 +291,7 @@
val mediaLoadedStatesModel = MediaDataLoadingModel.Loaded(dataMain.instanceId)
// THEN we should remove the private profile media
- verify(listener).onMediaDataRemoved(eq(KEY_ALT))
+ verify(listener).onMediaDataRemoved(eq(KEY_ALT), eq(false))
assertThat(currentMedia)
.containsExactly(MediaCommonModel.MediaControl(mediaLoadedStatesModel))
}
@@ -502,7 +502,7 @@
val smartspaceMediaData by collectLastValue(repository.smartspaceMediaData)
mediaDataFilter.onMediaDataLoaded(KEY, oldKey = null, data = dataMain)
- mediaDataFilter.onMediaDataRemoved(KEY)
+ mediaDataFilter.onMediaDataRemoved(KEY, false)
assertThat(hasAnyMediaOrRecommendation(selectedUserEntries, smartspaceMediaData))
.isFalse()
assertThat(hasAnyMedia(selectedUserEntries)).isFalse()
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/controls/domain/pipeline/MediaDataProcessorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/controls/domain/pipeline/MediaDataProcessorTest.kt
index 1de7ee3..3bf4173 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/controls/domain/pipeline/MediaDataProcessorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/controls/domain/pipeline/MediaDataProcessorTest.kt
@@ -71,10 +71,6 @@
import com.android.systemui.statusbar.SbnBuilder
import com.android.systemui.testKosmos
import com.android.systemui.util.concurrency.FakeExecutor
-import com.android.systemui.util.mockito.any
-import com.android.systemui.util.mockito.capture
-import com.android.systemui.util.mockito.eq
-import com.android.systemui.util.mockito.whenever
import com.android.systemui.util.settings.FakeSettings
import com.android.systemui.util.time.FakeSystemClock
import com.android.systemui.utils.os.FakeHandler
@@ -101,6 +97,10 @@
import org.mockito.Mockito.verifyNoMoreInteractions
import org.mockito.MockitoSession
import org.mockito.junit.MockitoJUnit
+import org.mockito.kotlin.any
+import org.mockito.kotlin.capture
+import org.mockito.kotlin.eq
+import org.mockito.kotlin.whenever
import org.mockito.quality.Strictness
private const val KEY = "KEY"
@@ -384,7 +384,7 @@
// THEN it is removed and listeners are informed
foregroundExecutor.advanceClockToLast()
foregroundExecutor.runAllReady()
- verify(listener).onMediaDataRemoved(PACKAGE_NAME)
+ verify(listener).onMediaDataRemoved(PACKAGE_NAME, false)
}
@Test
@@ -567,7 +567,7 @@
addNotificationAndLoad()
val data = mediaDataCaptor.value
mediaDataProcessor.onNotificationRemoved(KEY)
- verify(listener).onMediaDataRemoved(eq(KEY))
+ verify(listener).onMediaDataRemoved(eq(KEY), eq(false))
verify(logger).logMediaRemoved(anyInt(), eq(PACKAGE_NAME), eq(data.instanceId))
}
@@ -812,7 +812,7 @@
eq(false)
)
assertThat(mediaDataCaptor.value.resumption).isTrue()
- verify(listener, never()).onMediaDataRemoved(eq(KEY))
+ verify(listener, never()).onMediaDataRemoved(eq(KEY), anyBoolean())
// WHEN the second is removed
mediaDataProcessor.onNotificationRemoved(KEY_2)
// THEN the data is for resumption and the second key is removed
@@ -826,7 +826,7 @@
eq(false)
)
assertThat(mediaDataCaptor.value.resumption).isTrue()
- verify(listener).onMediaDataRemoved(eq(KEY_2))
+ verify(listener).onMediaDataRemoved(eq(KEY_2), eq(false))
}
@Test
@@ -851,7 +851,7 @@
mediaDataProcessor.onNotificationRemoved(KEY)
// THEN the media data is removed
- verify(listener).onMediaDataRemoved(eq(KEY))
+ verify(listener).onMediaDataRemoved(eq(KEY), eq(false))
}
@Test
@@ -901,7 +901,7 @@
mediaDataProcessor.onNotificationRemoved(KEY)
// THEN the media data is removed
- verify(listener).onMediaDataRemoved(eq(KEY))
+ verify(listener).onMediaDataRemoved(eq(KEY), eq(false))
}
@Test
@@ -940,7 +940,7 @@
assertThat(mediaDataCaptor.value.isPlaying).isFalse()
// And the oldest resume control was removed
- verify(listener).onMediaDataRemoved(eq("0:$PACKAGE_NAME"))
+ verify(listener).onMediaDataRemoved(eq("0:$PACKAGE_NAME"), eq(false))
}
fun testOnNotificationRemoved_lockDownMode() {
@@ -950,7 +950,7 @@
val data = mediaDataCaptor.value
mediaDataProcessor.onNotificationRemoved(KEY)
- verify(listener, never()).onMediaDataRemoved(eq(KEY))
+ verify(listener, never()).onMediaDataRemoved(eq(KEY), eq(false))
verify(logger, never())
.logActiveConvertedToResume(anyInt(), eq(PACKAGE_NAME), eq(data.instanceId))
verify(logger).logMediaRemoved(anyInt(), eq(PACKAGE_NAME), eq(data.instanceId))
@@ -1183,7 +1183,7 @@
mediaDataProcessor.setMediaResumptionEnabled(false)
// THEN the resume controls are dismissed
- verify(listener).onMediaDataRemoved(eq(PACKAGE_NAME))
+ verify(listener).onMediaDataRemoved(eq(PACKAGE_NAME), eq(false))
verify(logger).logMediaRemoved(anyInt(), eq(PACKAGE_NAME), eq(data.instanceId))
}
@@ -1191,19 +1191,19 @@
fun testDismissMedia_listenerCalled() {
addNotificationAndLoad()
val data = mediaDataCaptor.value
- val removed = mediaDataProcessor.dismissMediaData(KEY, 0L)
+ val removed = mediaDataProcessor.dismissMediaData(KEY, 0L, true)
assertThat(removed).isTrue()
foregroundExecutor.advanceClockToLast()
foregroundExecutor.runAllReady()
- verify(listener).onMediaDataRemoved(eq(KEY))
+ verify(listener).onMediaDataRemoved(eq(KEY), eq(true))
verify(logger).logMediaRemoved(anyInt(), eq(PACKAGE_NAME), eq(data.instanceId))
}
@Test
fun testDismissMedia_keyDoesNotExist_returnsFalse() {
- val removed = mediaDataProcessor.dismissMediaData(KEY, 0L)
+ val removed = mediaDataProcessor.dismissMediaData(KEY, 0L, true)
assertThat(removed).isFalse()
}
@@ -2102,7 +2102,7 @@
sessionCallbackCaptor.value.invoke(KEY)
// It remains as a regular player
- verify(listener, never()).onMediaDataRemoved(eq(KEY))
+ verify(listener, never()).onMediaDataRemoved(eq(KEY), anyBoolean())
verify(listener, never())
.onMediaDataLoaded(eq(PACKAGE_NAME), any(), any(), anyBoolean(), anyInt(), anyBoolean())
}
@@ -2118,7 +2118,7 @@
mediaDataProcessor.onNotificationRemoved(KEY)
// It is fully removed
- verify(listener).onMediaDataRemoved(eq(KEY))
+ verify(listener).onMediaDataRemoved(eq(KEY), eq(false))
verify(logger).logMediaRemoved(anyInt(), eq(PACKAGE_NAME), eq(data.instanceId))
verify(listener, never())
.onMediaDataLoaded(eq(PACKAGE_NAME), any(), any(), anyBoolean(), anyInt(), anyBoolean())
@@ -2171,7 +2171,7 @@
mediaDataProcessor.onNotificationRemoved(KEY)
// It remains as a regular player
- verify(listener, never()).onMediaDataRemoved(eq(KEY))
+ verify(listener, never()).onMediaDataRemoved(eq(KEY), anyBoolean())
verify(listener, never())
.onMediaDataLoaded(eq(PACKAGE_NAME), any(), any(), anyBoolean(), anyInt(), anyBoolean())
}
@@ -2224,7 +2224,7 @@
sessionCallbackCaptor.value.invoke(KEY)
// It is fully removed
- verify(listener).onMediaDataRemoved(eq(KEY))
+ verify(listener).onMediaDataRemoved(eq(KEY), eq(false))
verify(logger).logMediaRemoved(anyInt(), eq(PACKAGE_NAME), eq(data.instanceId))
verify(listener, never())
.onMediaDataLoaded(eq(PACKAGE_NAME), any(), any(), anyBoolean(), anyInt(), anyBoolean())
@@ -2278,7 +2278,7 @@
sessionCallbackCaptor.value.invoke(KEY)
// It is fully removed.
- verify(listener).onMediaDataRemoved(eq(KEY))
+ verify(listener).onMediaDataRemoved(eq(KEY), eq(false))
verify(logger).logMediaRemoved(anyInt(), eq(PACKAGE_NAME), eq(data.instanceId))
verify(listener, never())
.onMediaDataLoaded(
@@ -2304,7 +2304,7 @@
sessionCallbackCaptor.value.invoke(KEY)
// It is fully removed
- verify(listener).onMediaDataRemoved(eq(KEY))
+ verify(listener).onMediaDataRemoved(eq(KEY), eq(false))
verify(logger).logMediaRemoved(anyInt(), eq(PACKAGE_NAME), eq(data.instanceId))
verify(listener, never())
.onMediaDataLoaded(eq(PACKAGE_NAME), any(), any(), anyBoolean(), anyInt(), anyBoolean())
@@ -2354,7 +2354,7 @@
mediaDataProcessor.onNotificationRemoved(KEY)
// We still make sure to remove it
- verify(listener).onMediaDataRemoved(eq(KEY))
+ verify(listener).onMediaDataRemoved(eq(KEY), eq(false))
}
@Test
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/controls/domain/pipeline/MediaDeviceManagerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/controls/domain/pipeline/MediaDeviceManagerTest.kt
index a447e44..d2701dd 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/controls/domain/pipeline/MediaDeviceManagerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/controls/domain/pipeline/MediaDeviceManagerTest.kt
@@ -51,7 +51,6 @@
import com.android.systemui.res.R
import com.android.systemui.statusbar.policy.ConfigurationController
import com.android.systemui.util.concurrency.FakeExecutor
-import com.android.systemui.util.mockito.eq
import com.android.systemui.util.time.FakeSystemClock
import com.google.common.truth.Truth.assertThat
import org.junit.After
@@ -60,6 +59,7 @@
import org.junit.Test
import org.junit.runner.RunWith
import org.mockito.ArgumentCaptor
+import org.mockito.ArgumentMatchers.anyBoolean
import org.mockito.ArgumentMatchers.anyInt
import org.mockito.Mock
import org.mockito.Mockito.any
@@ -71,6 +71,7 @@
import org.mockito.Mockito.verifyNoMoreInteractions
import org.mockito.Mockito.`when` as whenever
import org.mockito.junit.MockitoJUnit
+import org.mockito.kotlin.eq
private const val KEY = "TEST_KEY"
private const val KEY_OLD = "TEST_KEY_OLD"
@@ -158,7 +159,8 @@
@Test
fun removeUnknown() {
- manager.onMediaDataRemoved("unknown")
+ manager.onMediaDataRemoved("unknown", false)
+ verify(listener, never()).onKeyRemoved(eq(KEY), anyBoolean())
}
@Test
@@ -170,7 +172,7 @@
@Test
fun loadAndRemoveMediaData() {
manager.onMediaDataLoaded(KEY, null, mediaData)
- manager.onMediaDataRemoved(KEY)
+ manager.onMediaDataRemoved(KEY, false)
fakeBgExecutor.runAllReady()
verify(lmm).unregisterCallback(any())
verify(muteAwaitManager).stopListening()
@@ -386,9 +388,9 @@
fun listenerReceivesKeyRemoved() {
manager.onMediaDataLoaded(KEY, null, mediaData)
// WHEN the notification is removed
- manager.onMediaDataRemoved(KEY)
+ manager.onMediaDataRemoved(KEY, true)
// THEN the listener receives key removed event
- verify(listener).onKeyRemoved(eq(KEY))
+ verify(listener).onKeyRemoved(eq(KEY), eq(true))
}
@Test
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/controls/domain/pipeline/MediaSessionBasedFilterTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/controls/domain/pipeline/MediaSessionBasedFilterTest.kt
index 5a3c220..31a2435 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/controls/domain/pipeline/MediaSessionBasedFilterTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/controls/domain/pipeline/MediaSessionBasedFilterTest.kt
@@ -27,7 +27,6 @@
import com.android.systemui.media.controls.MediaTestUtils
import com.android.systemui.media.controls.shared.model.MediaData
import com.android.systemui.util.concurrency.FakeExecutor
-import com.android.systemui.util.mockito.eq
import com.android.systemui.util.time.FakeSystemClock
import org.junit.After
import org.junit.Before
@@ -38,12 +37,13 @@
import org.mockito.ArgumentMatchers.anyBoolean
import org.mockito.ArgumentMatchers.anyInt
import org.mockito.Mock
-import org.mockito.Mockito.any
import org.mockito.Mockito.never
import org.mockito.Mockito.reset
import org.mockito.Mockito.verify
import org.mockito.Mockito.`when` as whenever
import org.mockito.junit.MockitoJUnit
+import org.mockito.kotlin.any
+import org.mockito.kotlin.eq
private const val PACKAGE = "PKG"
private const val KEY = "TEST_KEY"
@@ -165,10 +165,10 @@
@Test
fun noMediaSession_removedEventNotFiltered() {
- filter.onMediaDataRemoved(KEY)
+ filter.onMediaDataRemoved(KEY, false)
bgExecutor.runAllReady()
fgExecutor.runAllReady()
- verify(mediaListener).onMediaDataRemoved(eq(KEY))
+ verify(mediaListener).onMediaDataRemoved(eq(KEY), eq(false))
}
@Test
@@ -193,11 +193,11 @@
whenever(mediaSessionManager.getActiveSessions(any())).thenReturn(controllers)
sessionListener.onActiveSessionsChanged(controllers)
// WHEN a removed event is received
- filter.onMediaDataRemoved(KEY)
+ filter.onMediaDataRemoved(KEY, false)
bgExecutor.runAllReady()
fgExecutor.runAllReady()
// THEN the event is not filtered
- verify(mediaListener).onMediaDataRemoved(eq(KEY))
+ verify(mediaListener).onMediaDataRemoved(eq(KEY), eq(false))
}
@Test
@@ -294,7 +294,7 @@
anyBoolean()
)
// AND there should be a removed event for key2
- verify(mediaListener).onMediaDataRemoved(eq(key2))
+ verify(mediaListener).onMediaDataRemoved(eq(key2), eq(false))
}
@Test
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/controls/domain/pipeline/MediaTimeoutListenerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/controls/domain/pipeline/MediaTimeoutListenerTest.kt
index 3cc65c9..6ca0bef 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/controls/domain/pipeline/MediaTimeoutListenerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/controls/domain/pipeline/MediaTimeoutListenerTest.kt
@@ -31,10 +31,6 @@
import com.android.systemui.plugins.statusbar.StatusBarStateController
import com.android.systemui.statusbar.SysuiStatusBarStateController
import com.android.systemui.util.concurrency.FakeExecutor
-import com.android.systemui.util.mockito.any
-import com.android.systemui.util.mockito.capture
-import com.android.systemui.util.mockito.eq
-import com.android.systemui.util.mockito.whenever
import com.android.systemui.util.time.FakeSystemClock
import com.google.common.truth.Truth.assertThat
import org.junit.Before
@@ -52,6 +48,10 @@
import org.mockito.Mockito.never
import org.mockito.Mockito.verify
import org.mockito.junit.MockitoJUnit
+import org.mockito.kotlin.any
+import org.mockito.kotlin.capture
+import org.mockito.kotlin.eq
+import org.mockito.kotlin.whenever
private const val KEY = "KEY"
private const val PACKAGE = "PKG"
@@ -166,12 +166,12 @@
@Test
fun testOnMediaDataRemoved_unregistersPlaybackListener() {
mediaTimeoutListener.onMediaDataLoaded(KEY, null, mediaData)
- mediaTimeoutListener.onMediaDataRemoved(KEY)
+ mediaTimeoutListener.onMediaDataRemoved(KEY, false)
verify(mediaController).unregisterCallback(anyObject())
// Ignores duplicate requests
clearInvocations(mediaController)
- mediaTimeoutListener.onMediaDataRemoved(KEY)
+ mediaTimeoutListener.onMediaDataRemoved(KEY, false)
verify(mediaController, never()).unregisterCallback(anyObject())
}
@@ -181,7 +181,7 @@
mediaTimeoutListener.onMediaDataLoaded(KEY, null, mediaData)
assertThat(executor.numPending()).isEqualTo(1)
// WHEN the media is removed
- mediaTimeoutListener.onMediaDataRemoved(KEY)
+ mediaTimeoutListener.onMediaDataRemoved(KEY, false)
// THEN the timeout runnable is cancelled
assertThat(executor.numPending()).isEqualTo(0)
}
@@ -398,7 +398,7 @@
// WHEN we have a resume control
testOnMediaDataLoaded_resumption_registersTimeout()
// AND the media is removed
- mediaTimeoutListener.onMediaDataRemoved(PACKAGE)
+ mediaTimeoutListener.onMediaDataRemoved(PACKAGE, false)
// THEN the timeout runnable is cancelled
assertThat(executor.numPending()).isEqualTo(0)
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/controls/ui/controller/MediaCarouselControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/controls/ui/controller/MediaCarouselControllerTest.kt
index 0a5aace..7856f9b 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/controls/ui/controller/MediaCarouselControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/controls/ui/controller/MediaCarouselControllerTest.kt
@@ -33,6 +33,10 @@
import com.android.systemui.SysuiTestCase
import com.android.systemui.dagger.qualifiers.Main
import com.android.systemui.dump.DumpManager
+import com.android.systemui.flags.DisableSceneContainer
+import com.android.systemui.flags.EnableSceneContainer
+import com.android.systemui.flags.Flags
+import com.android.systemui.flags.fakeFeatureFlagsClassic
import com.android.systemui.keyguard.data.repository.fakeKeyguardTransitionRepository
import com.android.systemui.keyguard.domain.interactor.keyguardTransitionInteractor
import com.android.systemui.keyguard.shared.model.KeyguardState
@@ -52,15 +56,16 @@
import com.android.systemui.plugins.FalsingManager
import com.android.systemui.qs.PageIndicator
import com.android.systemui.res.R
+import com.android.systemui.scene.data.repository.Idle
+import com.android.systemui.scene.data.repository.setSceneTransition
+import com.android.systemui.scene.domain.interactor.sceneInteractor
+import com.android.systemui.scene.shared.model.Scenes
import com.android.systemui.statusbar.notification.collection.provider.OnReorderingAllowedListener
import com.android.systemui.statusbar.notification.collection.provider.VisualStabilityProvider
import com.android.systemui.statusbar.policy.ConfigurationController
import com.android.systemui.testKosmos
import com.android.systemui.util.concurrency.DelayableExecutor
import com.android.systemui.util.concurrency.FakeExecutor
-import com.android.systemui.util.mockito.any
-import com.android.systemui.util.mockito.capture
-import com.android.systemui.util.mockito.eq
import com.android.systemui.util.settings.FakeSettings
import com.android.systemui.util.settings.GlobalSettings
import com.android.systemui.util.settings.SecureSettings
@@ -74,12 +79,14 @@
import kotlinx.coroutines.test.TestDispatcher
import kotlinx.coroutines.test.UnconfinedTestDispatcher
import kotlinx.coroutines.test.runTest
+import org.junit.After
import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
import org.mockito.ArgumentCaptor
import org.mockito.Captor
import org.mockito.Mock
+import org.mockito.Mockito.anyLong
import org.mockito.Mockito.floatThat
import org.mockito.Mockito.mock
import org.mockito.Mockito.never
@@ -88,6 +95,9 @@
import org.mockito.Mockito.verify
import org.mockito.Mockito.`when` as whenever
import org.mockito.MockitoAnnotations
+import org.mockito.kotlin.any
+import org.mockito.kotlin.capture
+import org.mockito.kotlin.eq
private val DATA = MediaTestUtils.emptyMediaData
@@ -136,6 +146,9 @@
private lateinit var testDispatcher: TestDispatcher
private lateinit var mediaCarouselController: MediaCarouselController
+ private var originalResumeSetting =
+ Settings.Secure.getInt(context.contentResolver, Settings.Secure.MEDIA_CONTROLS_RESUME, 1)
+
@Before
fun setup() {
MockitoAnnotations.initMocks(this)
@@ -145,29 +158,30 @@
testDispatcher = UnconfinedTestDispatcher()
mediaCarouselController =
MediaCarouselController(
- context,
- mediaControlPanelFactory,
- visualStabilityProvider,
- mediaHostStatesManager,
- activityStarter,
- clock,
- kosmos.testDispatcher,
- executor,
- bgExecutor,
- testDispatcher,
- mediaDataManager,
- configurationController,
- falsingManager,
- dumpManager,
- logger,
- debugLogger,
- mediaFlags,
- keyguardUpdateMonitor,
- kosmos.keyguardTransitionInteractor,
- globalSettings,
- secureSettings,
- kosmos.mediaCarouselViewModel,
- mediaViewControllerFactory,
+ context = context,
+ mediaControlPanelFactory = mediaControlPanelFactory,
+ visualStabilityProvider = visualStabilityProvider,
+ mediaHostStatesManager = mediaHostStatesManager,
+ activityStarter = activityStarter,
+ systemClock = clock,
+ mainDispatcher = kosmos.testDispatcher,
+ executor = executor,
+ bgExecutor = bgExecutor,
+ backgroundDispatcher = testDispatcher,
+ mediaManager = mediaDataManager,
+ configurationController = configurationController,
+ falsingManager = falsingManager,
+ dumpManager = dumpManager,
+ logger = logger,
+ debugLogger = debugLogger,
+ mediaFlags = mediaFlags,
+ keyguardUpdateMonitor = keyguardUpdateMonitor,
+ keyguardTransitionInteractor = kosmos.keyguardTransitionInteractor,
+ globalSettings = globalSettings,
+ secureSettings = secureSettings,
+ mediaCarouselViewModel = kosmos.mediaCarouselViewModel,
+ mediaViewControllerFactory = mediaViewControllerFactory,
+ sceneInteractor = kosmos.sceneInteractor,
)
verify(configurationController).addCallback(capture(configListener))
verify(mediaDataManager).addListener(capture(listener))
@@ -186,6 +200,15 @@
)
}
+ @After
+ fun tearDown() {
+ Settings.Secure.putInt(
+ context.contentResolver,
+ Settings.Secure.MEDIA_CONTROLS_RESUME,
+ originalResumeSetting
+ )
+ }
+
@Test
fun testPlayerOrdering() {
// Test values: key, data, last active time
@@ -818,10 +841,12 @@
verify(mediaCarousel).visibility = View.VISIBLE
}
+ @DisableSceneContainer
@ExperimentalCoroutinesApi
@Test
fun testKeyguardGone_showMediaCarousel() =
kosmos.testScope.runTest {
+ kosmos.fakeFeatureFlagsClassic.set(Flags.MEDIA_RETAIN_RECOMMENDATIONS, false)
var updatedVisibility = false
mediaCarouselController.updateHostVisibility = { updatedVisibility = true }
mediaCarouselController.mediaCarousel = mediaCarousel
@@ -840,10 +865,30 @@
job.cancel()
}
+ @EnableSceneContainer
+ @ExperimentalCoroutinesApi
+ @Test
+ fun testKeyguardGone_showMediaCarousel_scene_container() =
+ kosmos.testScope.runTest {
+ kosmos.fakeFeatureFlagsClassic.set(Flags.MEDIA_RETAIN_RECOMMENDATIONS, false)
+ var updatedVisibility = false
+ mediaCarouselController.updateHostVisibility = { updatedVisibility = true }
+ mediaCarouselController.mediaCarousel = mediaCarousel
+
+ val job = mediaCarouselController.listenForAnyStateToGoneKeyguardTransition(this)
+ kosmos.setSceneTransition(Idle(Scenes.Gone))
+
+ verify(mediaCarousel).visibility = View.VISIBLE
+ assertEquals(true, updatedVisibility)
+
+ job.cancel()
+ }
+
@ExperimentalCoroutinesApi
@Test
fun keyguardShowing_notAllowedOnLockscreen_updateVisibility() {
kosmos.testScope.runTest {
+ kosmos.fakeFeatureFlagsClassic.set(Flags.MEDIA_RETAIN_RECOMMENDATIONS, false)
var updatedVisibility = false
mediaCarouselController.updateHostVisibility = { updatedVisibility = true }
mediaCarouselController.mediaCarousel = mediaCarousel
@@ -870,6 +915,7 @@
@Test
fun keyguardShowing_allowedOnLockscreen_updateVisibility() {
kosmos.testScope.runTest {
+ kosmos.fakeFeatureFlagsClassic.set(Flags.MEDIA_RETAIN_RECOMMENDATIONS, false)
var updatedVisibility = false
mediaCarouselController.updateHostVisibility = { updatedVisibility = true }
mediaCarouselController.mediaCarousel = mediaCarousel
@@ -968,6 +1014,45 @@
verify(panel).updateAnimatorDurationScale()
}
+ @Test
+ fun swipeToDismiss_pausedAndResumeOff_userInitiated() {
+ // When resumption is disabled, paused media should be dismissed after being swiped away
+ Settings.Secure.putInt(context.contentResolver, Settings.Secure.MEDIA_CONTROLS_RESUME, 0)
+
+ val pausedMedia = DATA.copy(isPlaying = false)
+ listener.value.onMediaDataLoaded(PAUSED_LOCAL, PAUSED_LOCAL, pausedMedia)
+ mediaCarouselController.onSwipeToDismiss()
+
+ // When it can be removed immediately on update
+ whenever(visualStabilityProvider.isReorderingAllowed).thenReturn(true)
+ val inactiveMedia = pausedMedia.copy(active = false)
+ listener.value.onMediaDataLoaded(PAUSED_LOCAL, PAUSED_LOCAL, inactiveMedia)
+
+ // This is processed as a user-initiated dismissal
+ verify(debugLogger).logMediaRemoved(eq(PAUSED_LOCAL), eq(true))
+ verify(mediaDataManager).dismissMediaData(eq(PAUSED_LOCAL), anyLong(), eq(true))
+ }
+
+ @Test
+ fun swipeToDismiss_pausedAndResumeOff_delayed_userInitiated() {
+ // When resumption is disabled, paused media should be dismissed after being swiped away
+ Settings.Secure.putInt(context.contentResolver, Settings.Secure.MEDIA_CONTROLS_RESUME, 0)
+ mediaCarouselController.updateHostVisibility = {}
+
+ val pausedMedia = DATA.copy(isPlaying = false)
+ listener.value.onMediaDataLoaded(PAUSED_LOCAL, PAUSED_LOCAL, pausedMedia)
+ mediaCarouselController.onSwipeToDismiss()
+
+ // When it can't be removed immediately on update
+ whenever(visualStabilityProvider.isReorderingAllowed).thenReturn(false)
+ val inactiveMedia = pausedMedia.copy(active = false)
+ listener.value.onMediaDataLoaded(PAUSED_LOCAL, PAUSED_LOCAL, inactiveMedia)
+ visualStabilityCallback.value.onReorderingAllowed()
+
+ // This is processed as a user-initiated dismissal
+ verify(mediaDataManager).dismissMediaData(eq(PAUSED_LOCAL), anyLong(), eq(true))
+ }
+
/**
* Helper method when a configuration change occurs.
*
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/controls/ui/controller/MediaControlPanelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/controls/ui/controller/MediaControlPanelTest.kt
index 83e4d31..6d7976e 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/controls/ui/controller/MediaControlPanelTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/controls/ui/controller/MediaControlPanelTest.kt
@@ -98,11 +98,6 @@
import com.android.systemui.surfaceeffects.turbulencenoise.TurbulenceNoiseView
import com.android.systemui.util.animation.TransitionLayout
import com.android.systemui.util.concurrency.FakeExecutor
-import com.android.systemui.util.mockito.KotlinArgumentCaptor
-import com.android.systemui.util.mockito.any
-import com.android.systemui.util.mockito.argumentCaptor
-import com.android.systemui.util.mockito.eq
-import com.android.systemui.util.mockito.withArgCaptor
import com.android.systemui.util.settings.GlobalSettings
import com.android.systemui.util.time.FakeSystemClock
import com.google.common.truth.Truth.assertThat
@@ -125,6 +120,9 @@
import org.mockito.Mockito.verify
import org.mockito.Mockito.`when` as whenever
import org.mockito.junit.MockitoJUnit
+import org.mockito.kotlin.any
+import org.mockito.kotlin.argumentCaptor
+import org.mockito.kotlin.eq
private const val KEY = "TEST_KEY"
private const val PACKAGE = "PKG"
@@ -247,8 +245,7 @@
// Set up package manager mocks
val icon = context.getDrawable(R.drawable.ic_android)
whenever(packageManager.getApplicationIcon(anyString())).thenReturn(icon)
- whenever(packageManager.getApplicationIcon(any(ApplicationInfo::class.java)))
- .thenReturn(icon)
+ whenever(packageManager.getApplicationIcon(any<ApplicationInfo>())).thenReturn(icon)
whenever(packageManager.getApplicationInfo(eq(PACKAGE), anyInt()))
.thenReturn(applicationInfo)
whenever(packageManager.getApplicationLabel(any())).thenReturn(PACKAGE)
@@ -644,7 +641,7 @@
bgExecutor.runAllReady()
mainExecutor.runAllReady()
- verify(albumView).setImageDrawable(any(Drawable::class.java))
+ verify(albumView).setImageDrawable(any<Drawable>())
}
@Test
@@ -657,7 +654,7 @@
bgExecutor.runAllReady()
mainExecutor.runAllReady()
- verify(albumView).setImageDrawable(any(Drawable::class.java))
+ verify(albumView).setImageDrawable(any<Drawable>())
}
@Test
@@ -675,12 +672,12 @@
player.bindPlayer(state0, PACKAGE)
bgExecutor.runAllReady()
mainExecutor.runAllReady()
- verify(albumView).setImageDrawable(any(Drawable::class.java))
+ verify(albumView).setImageDrawable(any<Drawable>())
// Run Metadata update so that later states don't update
val captor = argumentCaptor<Animator.AnimatorListener>()
verify(mockAnimator, times(2)).addListener(captor.capture())
- captor.value.onAnimationEnd(mockAnimator)
+ captor.lastValue.onAnimationEnd(mockAnimator)
assertThat(titleText.getText()).isEqualTo(TITLE)
assertThat(artistText.getText()).isEqualTo(ARTIST)
@@ -696,13 +693,13 @@
player.bindPlayer(state2, PACKAGE)
bgExecutor.runAllReady()
mainExecutor.runAllReady()
- verify(albumView, times(2)).setImageDrawable(any(Drawable::class.java))
+ verify(albumView, times(2)).setImageDrawable(any<Drawable>())
// Fourth binding to new image runs transition due to color scheme change
player.bindPlayer(state3, PACKAGE)
bgExecutor.runAllReady()
mainExecutor.runAllReady()
- verify(albumView, times(3)).setImageDrawable(any(Drawable::class.java))
+ verify(albumView, times(3)).setImageDrawable(any<Drawable>())
}
@Test
@@ -974,7 +971,7 @@
val captor = argumentCaptor<SeekBarObserver>()
verify(seekBarData).observeForever(captor.capture())
- val seekBarObserver = captor.value!!
+ val seekBarObserver = captor.lastValue
// Then the seekbar is set to animate
assertThat(seekBarObserver.animationEnabled).isTrue()
@@ -1086,27 +1083,19 @@
whenever(mockAvd0.isRunning()).thenReturn(false)
val captor = ArgumentCaptor.forClass(Animatable2.AnimationCallback::class.java)
verify(mockAvd0, times(1)).registerAnimationCallback(captor.capture())
- verify(mockAvd1, never())
- .registerAnimationCallback(any(Animatable2.AnimationCallback::class.java))
- verify(mockAvd2, never())
- .registerAnimationCallback(any(Animatable2.AnimationCallback::class.java))
+ verify(mockAvd1, never()).registerAnimationCallback(any<Animatable2.AnimationCallback>())
+ verify(mockAvd2, never()).registerAnimationCallback(any<Animatable2.AnimationCallback>())
captor.getValue().onAnimationEnd(mockAvd0)
// Validate correct state was bound
assertThat(actionPlayPause.contentDescription).isEqualTo("loading")
assertThat(actionPlayPause.getBackground()).isNull()
- verify(mockAvd0, times(1))
- .registerAnimationCallback(any(Animatable2.AnimationCallback::class.java))
- verify(mockAvd1, times(1))
- .registerAnimationCallback(any(Animatable2.AnimationCallback::class.java))
- verify(mockAvd2, times(1))
- .registerAnimationCallback(any(Animatable2.AnimationCallback::class.java))
- verify(mockAvd0, times(1))
- .unregisterAnimationCallback(any(Animatable2.AnimationCallback::class.java))
- verify(mockAvd1, times(1))
- .unregisterAnimationCallback(any(Animatable2.AnimationCallback::class.java))
- verify(mockAvd2, never())
- .unregisterAnimationCallback(any(Animatable2.AnimationCallback::class.java))
+ verify(mockAvd0, times(1)).registerAnimationCallback(any<Animatable2.AnimationCallback>())
+ verify(mockAvd1, times(1)).registerAnimationCallback(any<Animatable2.AnimationCallback>())
+ verify(mockAvd2, times(1)).registerAnimationCallback(any<Animatable2.AnimationCallback>())
+ verify(mockAvd0, times(1)).unregisterAnimationCallback(any<Animatable2.AnimationCallback>())
+ verify(mockAvd1, times(1)).unregisterAnimationCallback(any<Animatable2.AnimationCallback>())
+ verify(mockAvd2, never()).unregisterAnimationCallback(any<Animatable2.AnimationCallback>())
}
@Test
@@ -1118,7 +1107,7 @@
// Capture animation handler
val captor = argumentCaptor<Animator.AnimatorListener>()
verify(mockAnimator, times(2)).addListener(captor.capture())
- val handler = captor.value
+ val handler = captor.lastValue
// Validate text views unchanged but animation started
assertThat(titleText.getText()).isEqualTo("")
@@ -1147,7 +1136,7 @@
// Capture animation handler
val captor = argumentCaptor<Animator.AnimatorListener>()
verify(mockAnimator, times(2)).addListener(captor.capture())
- val handler = captor.value
+ val handler = captor.lastValue
// Validate text views unchanged but animation started
assertThat(titleText.getText()).isEqualTo("")
@@ -1179,7 +1168,7 @@
// Capture animation handler
val captor = argumentCaptor<Animator.AnimatorListener>()
verify(mockAnimator, times(2)).addListener(captor.capture())
- val handler = captor.value
+ val handler = captor.lastValue
handler.onAnimationEnd(mockAnimator)
assertThat(artistText.getText()).isEqualTo("ARTIST_0")
@@ -1344,7 +1333,7 @@
assertThat(dismiss.isEnabled).isEqualTo(true)
dismiss.callOnClick()
verify(logger).logLongPressDismiss(anyInt(), eq(PACKAGE), eq(instanceId))
- verify(mediaDataManager).dismissMediaData(eq(mediaKey), anyLong())
+ verify(mediaDataManager).dismissMediaData(eq(mediaKey), anyLong(), eq(true))
}
@Test
@@ -1360,7 +1349,8 @@
@Test
fun player_dismissButtonClick_notInManager() {
val mediaKey = "key for dismissal"
- whenever(mediaDataManager.dismissMediaData(eq(mediaKey), anyLong())).thenReturn(false)
+ whenever(mediaDataManager.dismissMediaData(eq(mediaKey), anyLong(), eq(true)))
+ .thenReturn(false)
player.attachPlayer(viewHolder)
val state = mediaData.copy(notificationKey = KEY)
@@ -1369,8 +1359,8 @@
assertThat(dismiss.isEnabled).isEqualTo(true)
dismiss.callOnClick()
- verify(mediaDataManager).dismissMediaData(eq(mediaKey), anyLong())
- verify(mediaCarouselController).removePlayer(eq(mediaKey), eq(false), eq(false))
+ verify(mediaDataManager).dismissMediaData(eq(mediaKey), anyLong(), eq(true))
+ verify(mediaCarouselController).removePlayer(eq(mediaKey), eq(false), eq(false), eq(true))
}
@Test
@@ -1774,10 +1764,9 @@
player.attachPlayer(viewHolder)
player.bindPlayer(mediaData, KEY)
- val callback: () -> Unit = {}
- val captor = KotlinArgumentCaptor(callback::class.java)
+ val captor = argumentCaptor<() -> Unit>()
verify(seekBarViewModel).logSeek = captor.capture()
- captor.value.invoke()
+ captor.lastValue.invoke()
verify(logger).logSeek(anyInt(), eq(PACKAGE), eq(instanceId))
}
@@ -1800,7 +1789,7 @@
// THEN it sends the PendingIntent without dismissing keyguard first,
// and does not use the Intent directly (see b/271845008)
captor.value.onClick(viewHolder.player)
- verify(pendingIntent).send(any(Bundle::class.java))
+ verify(pendingIntent).send(any<Bundle>())
verify(pendingIntent, never()).getIntent()
verify(activityStarter, never()).postStartActivityDismissingKeyguard(eq(clickIntent), any())
}
@@ -2218,8 +2207,8 @@
mainExecutor.runAllReady()
verify(recCardTitle).setTextColor(any<Int>())
- verify(recAppIconItem, times(3)).setImageDrawable(any(Drawable::class.java))
- verify(coverItem, times(3)).setImageDrawable(any(Drawable::class.java))
+ verify(recAppIconItem, times(3)).setImageDrawable(any<Drawable>())
+ verify(coverItem, times(3)).setImageDrawable(any<Drawable>())
verify(coverItem, times(3)).imageMatrix = any()
}
@@ -2546,7 +2535,7 @@
seamless.callOnClick()
// Then we send the pending intent as is, without modifying the original intent
- verify(pendingIntent).send(any(Bundle::class.java))
+ verify(pendingIntent).send(any<Bundle>())
verify(pendingIntent, never()).getIntent()
}
@@ -2578,13 +2567,16 @@
return Icon.createWithBitmap(bmp)
}
- private fun getScrubbingChangeListener(): SeekBarViewModel.ScrubbingChangeListener =
- withArgCaptor {
- verify(seekBarViewModel).setScrubbingChangeListener(capture())
- }
+ private fun getScrubbingChangeListener(): SeekBarViewModel.ScrubbingChangeListener {
+ val captor = argumentCaptor<SeekBarViewModel.ScrubbingChangeListener>()
+ verify(seekBarViewModel).setScrubbingChangeListener(captor.capture())
+ return captor.lastValue
+ }
- private fun getEnabledChangeListener(): SeekBarViewModel.EnabledChangeListener = withArgCaptor {
- verify(seekBarViewModel).setEnabledChangeListener(capture())
+ private fun getEnabledChangeListener(): SeekBarViewModel.EnabledChangeListener {
+ val captor = argumentCaptor<SeekBarViewModel.EnabledChangeListener>()
+ verify(seekBarViewModel).setEnabledChangeListener(captor.capture())
+ return captor.lastValue
}
/**
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputDialogReceiverTest.java b/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputDialogReceiverTest.java
index 3b6a88a..5dbfe47 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputDialogReceiverTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputDialogReceiverTest.java
@@ -25,6 +25,8 @@
import static org.mockito.Mockito.verify;
import android.content.Intent;
+import android.platform.test.annotations.DisableFlags;
+import android.platform.test.annotations.EnableFlags;
import android.testing.AndroidTestingRunner;
import androidx.test.filters.SmallTest;
@@ -91,8 +93,8 @@
}
@Test
+ @DisableFlags(Flags.FLAG_LEGACY_LE_AUDIO_SHARING)
public void launchMediaOutputBroadcastDialog_flagOff_broadcastDialogFactoryNotCalled() {
- mSetFlagsRule.disableFlags(Flags.FLAG_LEGACY_LE_AUDIO_SHARING);
Intent intent = new Intent(
MediaOutputConstants.ACTION_LAUNCH_MEDIA_OUTPUT_BROADCAST_DIALOG);
intent.putExtra(MediaOutputConstants.EXTRA_PACKAGE_NAME, getContext().getPackageName());
@@ -105,8 +107,8 @@
}
@Test
+ @EnableFlags(Flags.FLAG_LEGACY_LE_AUDIO_SHARING)
public void launchMediaOutputBroadcastDialog_ExtraPackageName_BroadcastDialogFactoryCalled() {
- mSetFlagsRule.enableFlags(Flags.FLAG_LEGACY_LE_AUDIO_SHARING);
Intent intent = new Intent(
MediaOutputConstants.ACTION_LAUNCH_MEDIA_OUTPUT_BROADCAST_DIALOG);
intent.putExtra(MediaOutputConstants.EXTRA_PACKAGE_NAME, getContext().getPackageName());
@@ -119,8 +121,8 @@
}
@Test
+ @EnableFlags(Flags.FLAG_LEGACY_LE_AUDIO_SHARING)
public void launchMediaOutputBroadcastDialog_WrongExtraKey_DialogBroadcastFactoryNotCalled() {
- mSetFlagsRule.enableFlags(Flags.FLAG_LEGACY_LE_AUDIO_SHARING);
Intent intent = new Intent(
MediaOutputConstants.ACTION_LAUNCH_MEDIA_OUTPUT_BROADCAST_DIALOG);
intent.putExtra("Wrong Package Name Key", getContext().getPackageName());
@@ -133,8 +135,8 @@
}
@Test
+ @EnableFlags(Flags.FLAG_LEGACY_LE_AUDIO_SHARING)
public void launchMediaOutputBroadcastDialog_NoExtra_BroadcastDialogFactoryNotCalled() {
- mSetFlagsRule.enableFlags(Flags.FLAG_LEGACY_LE_AUDIO_SHARING);
Intent intent = new Intent(
MediaOutputConstants.ACTION_LAUNCH_MEDIA_OUTPUT_BROADCAST_DIALOG);
mMediaOutputDialogReceiver.onReceive(getContext(), intent);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/dream/MediaDreamSentinelTest.java b/packages/SystemUI/tests/src/com/android/systemui/media/dream/MediaDreamSentinelTest.java
index ff7c970..8f8630e 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/dream/MediaDreamSentinelTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/dream/MediaDreamSentinelTest.java
@@ -104,11 +104,11 @@
listener.onMediaDataLoaded(mKey, mOldKey, mData, /* immediately= */true,
/* receivedSmartspaceCardLatency= */0, /* isSsReactived= */ false);
- listener.onMediaDataRemoved(mKey);
+ listener.onMediaDataRemoved(mKey, false);
verify(mDreamOverlayStateController, never()).removeComplication(any());
when(mMediaDataManager.hasActiveMedia()).thenReturn(false);
- listener.onMediaDataRemoved(mKey);
+ listener.onMediaDataRemoved(mKey, false);
verify(mDreamOverlayStateController).removeComplication(eq(mMediaEntryComplication));
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/mediaprojection/appselector/data/ActivityTaskManagerThumbnailLoaderTest.kt b/packages/SystemUI/tests/src/com/android/systemui/mediaprojection/appselector/data/ActivityTaskManagerThumbnailLoaderTest.kt
index db275ec..db36131 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/mediaprojection/appselector/data/ActivityTaskManagerThumbnailLoaderTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/mediaprojection/appselector/data/ActivityTaskManagerThumbnailLoaderTest.kt
@@ -52,7 +52,7 @@
val taskId = 123
val isLowResolution = false
val snapshot = createTaskSnapshot()
- val thumbnailData = ThumbnailData(snapshot)
+ val thumbnailData = ThumbnailData.fromSnapshot(snapshot)
whenever(activityManager.getTaskThumbnail(taskId, isLowResolution))
.thenReturn(thumbnailData)
@@ -74,7 +74,7 @@
fun captureThumbnail_thumbnailAvailable_returnsThumbnailData() =
testScope.runTest {
val taskId = 321
- val thumbnailData = ThumbnailData(createTaskSnapshot())
+ val thumbnailData = ThumbnailData.fromSnapshot(createTaskSnapshot())
whenever(activityManager.takeTaskThumbnail(taskId)).thenReturn(thumbnailData)
diff --git a/packages/SystemUI/tests/src/com/android/systemui/model/SysUiStateExtTest.kt b/packages/SystemUI/tests/src/com/android/systemui/model/SysUiStateExtTest.kt
index 8e05410..c06a28e 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/model/SysUiStateExtTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/model/SysUiStateExtTest.kt
@@ -42,13 +42,13 @@
fun updateFlags() {
underTest.updateFlags(
Display.DEFAULT_DISPLAY,
- 1 to true,
- 2 to false,
- 3 to true,
+ 1L to true,
+ 2L to false,
+ 3L to true,
)
- assertThat(underTest.flags and 1).isNotEqualTo(0)
- assertThat(underTest.flags and 2).isEqualTo(0)
- assertThat(underTest.flags and 3).isNotEqualTo(0)
+ assertThat(underTest.flags and 1L).isNotEqualTo(0L)
+ assertThat(underTest.flags and 2L).isEqualTo(0L)
+ assertThat(underTest.flags and 3L).isNotEqualTo(0L)
}
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/monet/ColorSchemeTest.kt b/packages/SystemUI/tests/src/com/android/systemui/monet/ColorSchemeTest.kt
index 9f0e67b..85cc88d 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/monet/ColorSchemeTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/monet/ColorSchemeTest.kt
@@ -15,11 +15,13 @@
*/
package com.android.systemui.monet
-import androidx.test.filters.SmallTest
import android.testing.AndroidTestingRunner
import android.util.Log
+import android.util.Pair
+import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.theme.DynamicColors
+import com.google.ux.material.libmonet.dynamiccolor.DynamicColor
import com.google.ux.material.libmonet.hct.Hct
import com.google.ux.material.libmonet.scheme.SchemeTonalSpot
import java.io.File
@@ -81,6 +83,10 @@
@SmallTest
@RunWith(AndroidTestingRunner::class)
class ColorSchemeTest : SysuiTestCase() {
+ private val defaultContrast = 0.0
+ private val defaultIsDark = false
+ private val defaultIsFidelity = false
+
@Test
fun generateThemeStyles() {
val document = buildDoc<Any>()
@@ -107,7 +113,7 @@
}
val style = document.createElement(styleValue.name.lowercase())
- val colorScheme = ColorScheme(sourceColor.toInt(), false, styleValue)
+ val colorScheme = ColorScheme(sourceColor.toInt(), defaultIsDark, styleValue)
style.appendChild(
document.createTextNode(
@@ -139,7 +145,7 @@
document.appendWithBreak(resources)
// shade colors
- val colorScheme = ColorScheme(GOOGLE_BLUE, false)
+ val colorScheme = ColorScheme(GOOGLE_BLUE, defaultIsDark)
arrayOf(
Triple("accent1", "Primary", colorScheme.accent1),
Triple("accent2", "Secondary", colorScheme.accent2),
@@ -162,24 +168,35 @@
resources.appendWithBreak(document.createComment(commentRoles), 2)
- // dynamic colors
- arrayOf(false, true).forEach { isDark ->
- val suffix = if (isDark) "_dark" else "_light"
- val dynamicScheme = SchemeTonalSpot(Hct.fromInt(GOOGLE_BLUE), isDark, 0.5)
- DynamicColors.allDynamicColorsMapped(false).forEach {
- resources.createColorEntry(
- "system_${it.first}$suffix",
- it.second.getArgb(dynamicScheme)
- )
+ fun generateDynamic(pairs: List<Pair<String, DynamicColor>>) {
+ arrayOf(false, true).forEach { isDark ->
+ val suffix = if (isDark) "_dark" else "_light"
+ val dynamicScheme =
+ SchemeTonalSpot(Hct.fromInt(GOOGLE_BLUE), isDark, defaultContrast)
+ pairs.forEach {
+ resources.createColorEntry(
+ "system_${it.first}$suffix",
+ it.second.getArgb(dynamicScheme)
+ )
+ }
}
}
+ // dynamic colors
+ generateDynamic(DynamicColors.allDynamicColorsMapped(defaultIsFidelity))
+
// fixed colors
- val dynamicScheme = SchemeTonalSpot(Hct.fromInt(GOOGLE_BLUE), false, 0.5)
- DynamicColors.getFixedColorsMapped(false).forEach {
+ val dynamicScheme =
+ SchemeTonalSpot(Hct.fromInt(GOOGLE_BLUE), defaultIsDark, defaultContrast)
+ DynamicColors.getFixedColorsMapped(defaultIsFidelity).forEach {
resources.createColorEntry("system_${it.first}", it.second.getArgb(dynamicScheme))
}
+ resources.appendWithBreak(document.createComment(commentRoles), 2)
+
+ // custom colors
+ generateDynamic(DynamicColors.getCustomColorsMapped(defaultIsFidelity))
+
saveFile(document, "role_values.xml")
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/navigationbar/NavBarHelperTest.java b/packages/SystemUI/tests/src/com/android/systemui/navigationbar/NavBarHelperTest.java
index 224e755..2ff660f 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/navigationbar/NavBarHelperTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/navigationbar/NavBarHelperTest.java
@@ -125,7 +125,7 @@
private AccessibilityManager.AccessibilityServicesStateChangeListener
mAccessibilityServicesStateChangeListener;
- private static final int ACCESSIBILITY_BUTTON_CLICKABLE_STATE =
+ private static final long ACCESSIBILITY_BUTTON_CLICKABLE_STATE =
SYSUI_STATE_A11Y_BUTTON_CLICKABLE | SYSUI_STATE_A11Y_BUTTON_LONG_CLICKABLE;
private NavBarHelper mNavBarHelper;
diff --git a/packages/SystemUI/tests/src/com/android/systemui/navigationbar/NavigationBarTest.java b/packages/SystemUI/tests/src/com/android/systemui/navigationbar/NavigationBarTest.java
index 0e7a215..6cea1e8 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/navigationbar/NavigationBarTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/navigationbar/NavigationBarTest.java
@@ -38,7 +38,7 @@
import static org.junit.Assert.assertTrue;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyBoolean;
-import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.ArgumentMatchers.anyLong;
import static org.mockito.Matchers.eq;
import static org.mockito.Mockito.doNothing;
import static org.mockito.Mockito.doReturn;
@@ -300,7 +300,7 @@
doNothing().when(mWindowManager).addView(any(), any());
doNothing().when(mWindowManager).removeViewImmediate(any());
mMockSysUiState = mock(SysUiState.class);
- when(mMockSysUiState.setFlag(anyInt(), anyBoolean())).thenReturn(mMockSysUiState);
+ when(mMockSysUiState.setFlag(anyLong(), anyBoolean())).thenReturn(mMockSysUiState);
mContext.addMockSystemService(WindowManager.class, mWindowManager);
mSysuiTestableContextExternal.addMockSystemService(WindowManager.class, mWindowManager);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/navigationbar/TaskbarDelegateTest.kt b/packages/SystemUI/tests/src/com/android/systemui/navigationbar/TaskbarDelegateTest.kt
index 8d01e80d..bba275e 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/navigationbar/TaskbarDelegateTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/navigationbar/TaskbarDelegateTest.kt
@@ -16,18 +16,18 @@
import com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager
import com.android.wm.shell.back.BackAnimation
import com.android.wm.shell.pip.Pip
+import java.util.Optional
import org.junit.Before
import org.junit.Test
import org.mockito.ArgumentMatchers
import org.mockito.Mock
-import org.mockito.Mockito.`when`
import org.mockito.Mockito.any
import org.mockito.Mockito.anyBoolean
-import org.mockito.Mockito.anyInt
+import org.mockito.Mockito.anyLong
import org.mockito.Mockito.times
import org.mockito.Mockito.verify
+import org.mockito.Mockito.`when`
import org.mockito.MockitoAnnotations
-import java.util.Optional
@SmallTest
class TaskbarDelegateTest : SysuiTestCase() {
@@ -74,7 +74,7 @@
`when`(mNavBarHelper.edgeBackGestureHandler).thenReturn(mEdgeBackGestureHandler)
`when`(mLightBarControllerFactory.create(any())).thenReturn(mLightBarTransitionController)
`when`(mNavBarHelper.currentSysuiState).thenReturn(mCurrentSysUiState)
- `when`(mSysUiState.setFlag(anyInt(), anyBoolean())).thenReturn(mSysUiState)
+ `when`(mSysUiState.setFlag(anyLong(), anyBoolean())).thenReturn(mSysUiState)
mTaskStackChangeListeners = TaskStackChangeListeners.getTestInstance()
mTaskbarDelegate = TaskbarDelegate(context, mLightBarControllerFactory,
mStatusBarKeyguardViewManager)
diff --git a/packages/SystemUI/tests/src/com/android/systemui/people/widget/PeopleSpaceWidgetManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/people/widget/PeopleSpaceWidgetManagerTest.java
index 890e1e0..0998c0c 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/people/widget/PeopleSpaceWidgetManagerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/people/widget/PeopleSpaceWidgetManagerTest.java
@@ -94,6 +94,8 @@
import android.os.Bundle;
import android.os.UserHandle;
import android.os.UserManager;
+import android.platform.test.annotations.DisableFlags;
+import android.platform.test.annotations.EnableFlags;
import android.service.notification.ConversationChannelWrapper;
import android.service.notification.StatusBarNotification;
import android.service.notification.ZenModeConfig;
@@ -1576,17 +1578,19 @@
}
@Test
+ @DisableFlags({
+ android.appwidget.flags.Flags.FLAG_GENERATED_PREVIEWS,
+ android.appwidget.flags.Flags.FLAG_DRAW_DATA_PARCEL
+ })
public void testUpdateGeneratedPreview_flagDisabled() {
- mSetFlagsRule.disableFlags(android.appwidget.flags.Flags.FLAG_GENERATED_PREVIEWS);
- mSetFlagsRule.disableFlags(android.appwidget.flags.Flags.FLAG_DRAW_DATA_PARCEL);
mManager.updateGeneratedPreviewForUser(mUserTracker.getUserHandle());
verify(mAppWidgetManager, times(0)).setWidgetPreview(any(), anyInt(), any());
}
@Test
+ @EnableFlags(android.appwidget.flags.Flags.FLAG_GENERATED_PREVIEWS)
+ @DisableFlags(android.appwidget.flags.Flags.FLAG_DRAW_DATA_PARCEL)
public void testUpdateGeneratedPreview_userLocked() {
- mSetFlagsRule.enableFlags(android.appwidget.flags.Flags.FLAG_GENERATED_PREVIEWS);
- mSetFlagsRule.disableFlags(android.appwidget.flags.Flags.FLAG_DRAW_DATA_PARCEL);
when(mUserManager.isUserUnlocked(mUserTracker.getUserHandle())).thenReturn(false);
mManager.updateGeneratedPreviewForUser(mUserTracker.getUserHandle());
@@ -1594,9 +1598,9 @@
}
@Test
+ @EnableFlags(android.appwidget.flags.Flags.FLAG_GENERATED_PREVIEWS)
+ @DisableFlags(android.appwidget.flags.Flags.FLAG_DRAW_DATA_PARCEL)
public void testUpdateGeneratedPreview_userUnlocked() {
- mSetFlagsRule.enableFlags(android.appwidget.flags.Flags.FLAG_GENERATED_PREVIEWS);
- mSetFlagsRule.disableFlags(android.appwidget.flags.Flags.FLAG_DRAW_DATA_PARCEL);
when(mUserManager.isUserUnlocked(mUserTracker.getUserHandle())).thenReturn(true);
when(mAppWidgetManager.setWidgetPreview(any(), anyInt(), any())).thenReturn(true);
@@ -1605,9 +1609,9 @@
}
@Test
+ @EnableFlags(android.appwidget.flags.Flags.FLAG_GENERATED_PREVIEWS)
+ @DisableFlags(android.appwidget.flags.Flags.FLAG_DRAW_DATA_PARCEL)
public void testUpdateGeneratedPreview_doesNotSetTwice() {
- mSetFlagsRule.enableFlags(android.appwidget.flags.Flags.FLAG_GENERATED_PREVIEWS);
- mSetFlagsRule.disableFlags(android.appwidget.flags.Flags.FLAG_DRAW_DATA_PARCEL);
when(mUserManager.isUserUnlocked(mUserTracker.getUserHandle())).thenReturn(true);
when(mAppWidgetManager.setWidgetPreview(any(), anyInt(), any())).thenReturn(true);
@@ -1617,9 +1621,11 @@
}
@Test
+ @EnableFlags({
+ android.appwidget.flags.Flags.FLAG_GENERATED_PREVIEWS,
+ android.appwidget.flags.Flags.FLAG_DRAW_DATA_PARCEL
+ })
public void testUpdateGeneratedPreviewWithDataParcel_userLocked() throws InterruptedException {
- mSetFlagsRule.enableFlags(android.appwidget.flags.Flags.FLAG_GENERATED_PREVIEWS);
- mSetFlagsRule.enableFlags(android.appwidget.flags.Flags.FLAG_DRAW_DATA_PARCEL);
when(mUserManager.isUserUnlocked(mUserTracker.getUserHandle())).thenReturn(false);
mManager.updateGeneratedPreviewForUser(mUserTracker.getUserHandle());
@@ -1628,10 +1634,12 @@
}
@Test
+ @EnableFlags({
+ android.appwidget.flags.Flags.FLAG_GENERATED_PREVIEWS,
+ android.appwidget.flags.Flags.FLAG_DRAW_DATA_PARCEL
+ })
public void testUpdateGeneratedPreviewWithDataParcel_userUnlocked()
throws InterruptedException {
- mSetFlagsRule.enableFlags(android.appwidget.flags.Flags.FLAG_GENERATED_PREVIEWS);
- mSetFlagsRule.enableFlags(android.appwidget.flags.Flags.FLAG_DRAW_DATA_PARCEL);
when(mUserManager.isUserUnlocked(mUserTracker.getUserHandle())).thenReturn(true);
when(mAppWidgetManager.setWidgetPreview(any(), anyInt(), any())).thenReturn(true);
@@ -1641,10 +1649,12 @@
}
@Test
+ @EnableFlags({
+ android.appwidget.flags.Flags.FLAG_GENERATED_PREVIEWS,
+ android.appwidget.flags.Flags.FLAG_DRAW_DATA_PARCEL
+ })
public void testUpdateGeneratedPreviewWithDataParcel_doesNotSetTwice()
throws InterruptedException {
- mSetFlagsRule.enableFlags(android.appwidget.flags.Flags.FLAG_GENERATED_PREVIEWS);
- mSetFlagsRule.enableFlags(android.appwidget.flags.Flags.FLAG_DRAW_DATA_PARCEL);
when(mUserManager.isUserUnlocked(mUserTracker.getUserHandle())).thenReturn(true);
when(mAppWidgetManager.setWidgetPreview(any(), anyInt(), any())).thenReturn(true);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/TileStateToProtoTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/TileStateToProtoTest.kt
index 629c663..bc947fb 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/TileStateToProtoTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/TileStateToProtoTest.kt
@@ -3,6 +3,7 @@
import android.content.ComponentName
import android.service.quicksettings.Tile
import android.testing.AndroidTestingRunner
+import android.widget.Switch
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.plugins.qs.QSTile
@@ -66,15 +67,19 @@
assertThat(proto?.hasBooleanState()).isFalse()
}
+ /**
+ * The [QSTile.AdapterState.expandedAccessibilityClassName] setting to [Switch] results in the
+ * proto having a booleanState. The value of that boolean is true iff the tile is active.
+ */
@Test
- fun booleanState_ACTIVE() {
+ fun adapterState_ACTIVE() {
val state =
- QSTile.BooleanState().apply {
+ QSTile.AdapterState().apply {
spec = TEST_SPEC
label = TEST_LABEL
secondaryLabel = TEST_SUBTITLE
state = Tile.STATE_ACTIVE
- value = true
+ expandedAccessibilityClassName = Switch::class.java.name
}
val proto = state.toProto()
@@ -89,6 +94,33 @@
assertThat(proto?.booleanState).isTrue()
}
+ /**
+ * Similar to [adapterState_ACTIVE], the use of
+ * [QSTile.AdapterState.expandedAccessibilityClassName] signals that the tile is toggleable.
+ */
+ @Test
+ fun adapterState_INACTIVE() {
+ val state =
+ QSTile.AdapterState().apply {
+ spec = TEST_SPEC
+ label = TEST_LABEL
+ secondaryLabel = TEST_SUBTITLE
+ state = Tile.STATE_INACTIVE
+ expandedAccessibilityClassName = Switch::class.java.name
+ }
+ val proto = state.toProto()
+
+ assertThat(proto).isNotNull()
+ assertThat(proto?.hasSpec()).isTrue()
+ assertThat(proto?.spec).isEqualTo(TEST_SPEC)
+ assertThat(proto?.hasComponentName()).isFalse()
+ assertThat(proto?.label).isEqualTo(TEST_LABEL)
+ assertThat(proto?.secondaryLabel).isEqualTo(TEST_SUBTITLE)
+ assertThat(proto?.state).isEqualTo(Tile.STATE_INACTIVE)
+ assertThat(proto?.hasBooleanState()).isTrue()
+ assertThat(proto?.booleanState).isFalse()
+ }
+
@Test
fun noSpec_returnsNull() {
val state =
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/panels/domain/interactor/GridConsistencyInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/panels/domain/interactor/GridConsistencyInteractorTest.kt
index db752dd..d15cfbf 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/panels/domain/interactor/GridConsistencyInteractorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/panels/domain/interactor/GridConsistencyInteractorTest.kt
@@ -20,7 +20,6 @@
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.kosmos.testScope
-import com.android.systemui.qs.panels.data.repository.GridLayoutTypeRepository
import com.android.systemui.qs.panels.data.repository.IconTilesRepository
import com.android.systemui.qs.panels.data.repository.gridLayoutTypeRepository
import com.android.systemui.qs.panels.data.repository.iconTilesRepository
@@ -48,9 +47,6 @@
data object TestGridLayoutType : GridLayoutType
- private val gridLayout: MutableStateFlow<GridLayoutType> =
- MutableStateFlow(InfiniteGridLayoutType)
-
private val iconOnlyTiles =
MutableStateFlow(
setOf(
@@ -74,17 +70,13 @@
Pair(InfiniteGridLayoutType, infiniteGridConsistencyInteractor),
Pair(TestGridLayoutType, noopGridConsistencyInteractor)
)
- gridLayoutTypeRepository =
- object : GridLayoutTypeRepository {
- override val layout: StateFlow<GridLayoutType> = gridLayout.asStateFlow()
- }
}
private val underTest = with(kosmos) { gridConsistencyInteractor }
@Before
fun setUp() {
- gridLayout.value = InfiniteGridLayoutType
+ with(kosmos) { gridLayoutTypeRepository.setLayout(InfiniteGridLayoutType) }
underTest.start()
}
@@ -94,7 +86,7 @@
with(kosmos) {
testScope.runTest {
// Using the no-op grid consistency interactor
- gridLayout.value = TestGridLayoutType
+ gridLayoutTypeRepository.setLayout(TestGridLayoutType)
// Setting an invalid layout with holes
// [ Large A ] [ sa ]
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/AirplaneModeTileTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/AirplaneModeTileTest.kt
index e2a3fac6..ad87315 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/AirplaneModeTileTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/AirplaneModeTileTest.kt
@@ -22,7 +22,7 @@
import android.testing.TestableLooper
import androidx.test.filters.SmallTest
import com.android.internal.logging.MetricsLogger
-import com.android.systemui.res.R
+import com.android.internal.telephony.flags.Flags
import com.android.systemui.SysuiTestCase
import com.android.systemui.broadcast.BroadcastDispatcher
import com.android.systemui.classifier.FalsingManagerFake
@@ -33,10 +33,12 @@
import com.android.systemui.qs.QsEventLogger
import com.android.systemui.qs.logging.QSLogger
import com.android.systemui.qs.tileimpl.QSTileImpl
+import com.android.systemui.res.R
import com.android.systemui.settings.UserTracker
import com.android.systemui.util.settings.GlobalSettings
import com.google.common.truth.Truth.assertThat
import dagger.Lazy
+import kotlinx.coroutines.Job
import org.junit.After
import org.junit.Before
import org.junit.Test
@@ -44,11 +46,15 @@
import org.mockito.Mock
import org.mockito.Mockito
import org.mockito.MockitoAnnotations
+import org.mockito.kotlin.any
+import org.mockito.kotlin.times
+import org.mockito.kotlin.verify
@RunWith(AndroidTestingRunner::class)
@TestableLooper.RunWithLooper(setAsMainLooper = true)
@SmallTest
class AirplaneModeTileTest : SysuiTestCase() {
+
@Mock
private lateinit var mHost: QSHost
@Mock
@@ -62,7 +68,9 @@
@Mock
private lateinit var mBroadcastDispatcher: BroadcastDispatcher
@Mock
- private lateinit var mConnectivityManager: Lazy<ConnectivityManager>
+ private lateinit var mLazyConnectivityManager: Lazy<ConnectivityManager>
+ @Mock
+ private lateinit var mConnectivityManager: ConnectivityManager
@Mock
private lateinit var mGlobalSettings: GlobalSettings
@Mock
@@ -72,13 +80,15 @@
private lateinit var mTestableLooper: TestableLooper
private lateinit var mTile: AirplaneModeTile
+ @Mock
+ private lateinit var mClickJob: Job
@Before
fun setUp() {
MockitoAnnotations.initMocks(this)
mTestableLooper = TestableLooper.get(this)
Mockito.`when`(mHost.context).thenReturn(mContext)
Mockito.`when`(mHost.userContext).thenReturn(mContext)
-
+ Mockito.`when`(mLazyConnectivityManager.get()).thenReturn(mConnectivityManager)
mTile = AirplaneModeTile(
mHost,
mUiEventLogger,
@@ -90,7 +100,7 @@
mActivityStarter,
mQsLogger,
mBroadcastDispatcher,
- mConnectivityManager,
+ mLazyConnectivityManager,
mGlobalSettings,
mUserTracker)
}
@@ -120,4 +130,24 @@
assertThat(state.icon)
.isEqualTo(QSTileImpl.ResourceIcon.get(R.drawable.qs_airplane_icon_on))
}
+
+ @Test
+ fun handleClick_noSatelliteFeature_directSetAirplaneMode() {
+ mSetFlagsRule.disableFlags(Flags.FLAG_OEM_ENABLED_SATELLITE_FLAG)
+
+ mTile.handleClick(null)
+
+ verify(mConnectivityManager).setAirplaneMode(any())
+ }
+
+ @Test
+ fun handleClick_hasSatelliteFeatureButClickIsProcessing_doNothing() {
+ mSetFlagsRule.enableFlags(Flags.FLAG_OEM_ENABLED_SATELLITE_FLAG)
+ Mockito.`when`(mClickJob.isCompleted).thenReturn(false)
+ mTile.mClickJob = mClickJob
+
+ mTile.handleClick(null)
+
+ verify(mConnectivityManager, times(0)).setAirplaneMode(any())
+ }
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/BluetoothTileTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/BluetoothTileTest.kt
index 830f08a..1ffbb7b 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/BluetoothTileTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/BluetoothTileTest.kt
@@ -9,10 +9,11 @@
import android.testing.TestableLooper.RunWithLooper
import androidx.test.filters.SmallTest
import com.android.internal.logging.MetricsLogger
+import com.android.internal.telephony.flags.Flags
import com.android.settingslib.Utils
import com.android.settingslib.bluetooth.CachedBluetoothDevice
-import com.android.systemui.res.R
import com.android.systemui.SysuiTestCase
+import com.android.systemui.bluetooth.qsdialog.BluetoothTileDialogViewModel
import com.android.systemui.classifier.FalsingManagerFake
import com.android.systemui.flags.FeatureFlagsClassic
import com.android.systemui.plugins.ActivityStarter
@@ -23,13 +24,14 @@
import com.android.systemui.qs.QsEventLogger
import com.android.systemui.qs.logging.QSLogger
import com.android.systemui.qs.tileimpl.QSTileImpl
-import com.android.systemui.bluetooth.qsdialog.BluetoothTileDialogViewModel
+import com.android.systemui.res.R
import com.android.systemui.statusbar.policy.BluetoothController
import com.android.systemui.util.mockito.any
import com.android.systemui.util.mockito.eq
import com.android.systemui.util.mockito.mock
import com.android.systemui.util.mockito.whenever
import com.google.common.truth.Truth.assertThat
+import kotlinx.coroutines.Job
import org.junit.After
import org.junit.Before
import org.junit.Test
@@ -37,6 +39,7 @@
import org.mockito.Mock
import org.mockito.Mockito.times
import org.mockito.Mockito.verify
+import org.mockito.Mockito.`when`
import org.mockito.MockitoAnnotations
@RunWith(AndroidTestingRunner::class)
@@ -54,7 +57,7 @@
@Mock private lateinit var uiEventLogger: QsEventLogger
@Mock private lateinit var featureFlags: FeatureFlagsClassic
@Mock private lateinit var bluetoothTileDialogViewModel: BluetoothTileDialogViewModel
-
+ @Mock private lateinit var clickJob: Job
private lateinit var testableLooper: TestableLooper
private lateinit var tile: FakeBluetoothTile
@@ -191,6 +194,41 @@
}
@Test
+ fun handleClick_hasSatelliteFeatureButNoQsTileDialogAndClickIsProcessing_doNothing() {
+ mSetFlagsRule.enableFlags(Flags.FLAG_OEM_ENABLED_SATELLITE_FLAG)
+ `when`(featureFlags.isEnabled(com.android.systemui.flags.Flags.BLUETOOTH_QS_TILE_DIALOG))
+ .thenReturn(false)
+ `when`(clickJob.isCompleted).thenReturn(false)
+ tile.mClickJob = clickJob
+
+ tile.handleClick(null)
+
+ verify(bluetoothController, times(0)).setBluetoothEnabled(any())
+ }
+
+ @Test
+ fun handleClick_noSatelliteFeatureAndNoQsTileDialog_directSetBtEnable() {
+ mSetFlagsRule.disableFlags(Flags.FLAG_OEM_ENABLED_SATELLITE_FLAG)
+ `when`(featureFlags.isEnabled(com.android.systemui.flags.Flags.BLUETOOTH_QS_TILE_DIALOG))
+ .thenReturn(false)
+
+ tile.handleClick(null)
+
+ verify(bluetoothController).setBluetoothEnabled(any())
+ }
+
+ @Test
+ fun handleClick_noSatelliteFeatureButHasQsTileDialog_showDialog() {
+ mSetFlagsRule.disableFlags(Flags.FLAG_OEM_ENABLED_SATELLITE_FLAG)
+ `when`(featureFlags.isEnabled(com.android.systemui.flags.Flags.BLUETOOTH_QS_TILE_DIALOG))
+ .thenReturn(true)
+
+ tile.handleClick(null)
+
+ verify(bluetoothTileDialogViewModel).showDialog(null)
+ }
+
+ @Test
fun testMetadataListener_whenDisconnected_isUnregistered() {
val state = QSTile.BooleanState()
val cachedDevice = mock<CachedBluetoothDevice>()
diff --git a/packages/SystemUI/tests/src/com/android/systemui/reardisplay/RearDisplayDialogControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/reardisplay/RearDisplayDialogControllerTest.java
index f88a5a0..b75b318 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/reardisplay/RearDisplayDialogControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/reardisplay/RearDisplayDialogControllerTest.java
@@ -20,7 +20,7 @@
import static junit.framework.Assert.assertNotSame;
import static org.mockito.ArgumentMatchers.anyBoolean;
-import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.ArgumentMatchers.anyLong;
import static org.mockito.Mockito.reset;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
@@ -81,7 +81,7 @@
public void setup() {
MockitoAnnotations.initMocks(this);
- when(mSysUiState.setFlag(anyInt(), anyBoolean())).thenReturn(mSysUiState);
+ when(mSysUiState.setFlag(anyLong(), anyBoolean())).thenReturn(mSysUiState);
when(mSystemUIDialogFactory.create()).thenReturn(mSystemUIDialog);
when(mSystemUIDialog.getContext()).thenReturn(mContext);
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/recents/OverviewProxyServiceTest.kt b/packages/SystemUI/tests/src/com/android/systemui/recents/OverviewProxyServiceTest.kt
index effae5f..74deae3 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/recents/OverviewProxyServiceTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/recents/OverviewProxyServiceTest.kt
@@ -72,7 +72,7 @@
import org.mockito.Mockito.anyInt
import org.mockito.Mockito.atLeast
import org.mockito.Mockito.clearInvocations
-import org.mockito.Mockito.intThat
+import org.mockito.Mockito.longThat
import org.mockito.Mockito.mock
import org.mockito.Mockito.spy
import org.mockito.Mockito.times
@@ -162,7 +162,7 @@
verify(overviewProxy)
.onSystemUiStateChanged(
- intThat { it and SYSUI_STATE_WAKEFULNESS_MASK == WAKEFULNESS_AWAKE }
+ longThat { it and SYSUI_STATE_WAKEFULNESS_MASK == WAKEFULNESS_AWAKE }
)
}
@@ -172,7 +172,7 @@
verify(overviewProxy)
.onSystemUiStateChanged(
- intThat { it and SYSUI_STATE_WAKEFULNESS_MASK == WAKEFULNESS_WAKING }
+ longThat { it and SYSUI_STATE_WAKEFULNESS_MASK == WAKEFULNESS_WAKING }
)
}
@@ -182,7 +182,7 @@
verify(overviewProxy)
.onSystemUiStateChanged(
- intThat { it and SYSUI_STATE_WAKEFULNESS_MASK == WAKEFULNESS_ASLEEP }
+ longThat { it and SYSUI_STATE_WAKEFULNESS_MASK == WAKEFULNESS_ASLEEP }
)
}
@@ -194,7 +194,7 @@
verify(overviewProxy)
.onSystemUiStateChanged(
- intThat { it and SYSUI_STATE_WAKEFULNESS_MASK == WAKEFULNESS_GOING_TO_SLEEP }
+ longThat { it and SYSUI_STATE_WAKEFULNESS_MASK == WAKEFULNESS_GOING_TO_SLEEP }
)
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/recordissue/RecordIssueDialogDelegateTest.kt b/packages/SystemUI/tests/src/com/android/systemui/recordissue/RecordIssueDialogDelegateTest.kt
index 6846c72..fcc6b4f 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/recordissue/RecordIssueDialogDelegateTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/recordissue/RecordIssueDialogDelegateTest.kt
@@ -55,6 +55,7 @@
import org.junit.runner.RunWith
import org.mockito.ArgumentMatchers.anyBoolean
import org.mockito.ArgumentMatchers.anyInt
+import org.mockito.ArgumentMatchers.anyLong
import org.mockito.Mock
import org.mockito.Mockito.never
import org.mockito.Mockito.spy
@@ -94,7 +95,7 @@
fun setup() {
MockitoAnnotations.initMocks(this)
whenever(dprLazy.get()).thenReturn(devicePolicyResolver)
- whenever(sysuiState.setFlag(anyInt(), anyBoolean())).thenReturn(sysuiState)
+ whenever(sysuiState.setFlag(anyLong(), anyBoolean())).thenReturn(sysuiState)
whenever(screenCaptureDisabledDialogDelegate.createSysUIDialog())
.thenReturn(screenCaptureDisabledDialog)
whenever(
diff --git a/packages/SystemUI/tests/src/com/android/systemui/screenshot/ActionExecutorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/screenshot/ActionExecutorTest.kt
index 5e7d8fb..a10d81f 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/screenshot/ActionExecutorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/screenshot/ActionExecutorTest.kt
@@ -21,7 +21,6 @@
import android.os.Bundle
import android.os.UserHandle
import android.testing.AndroidTestingRunner
-import android.view.View
import android.view.Window
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
@@ -49,7 +48,7 @@
private val intentExecutor = mock<ActionIntentExecutor>()
private val window = mock<Window>()
- private val view = mock<View>()
+ private val viewProxy = mock<ScreenshotShelfViewProxy>()
private val onDismiss = mock<(() -> Unit)>()
private val pendingIntent = mock<PendingIntent>()
@@ -70,16 +69,16 @@
}
@Test
- fun sendPendingIntent_dismisses() = runTest {
+ fun sendPendingIntent_requestsDismissal() = runTest {
actionExecutor = createActionExecutor()
actionExecutor.sendPendingIntent(pendingIntent)
verify(pendingIntent).send(any(Bundle::class.java))
- verify(onDismiss).invoke()
+ verify(viewProxy).requestDismissal(null)
}
private fun createActionExecutor(): ActionExecutor {
- return ActionExecutor(intentExecutor, testScope, window, view, onDismiss)
+ return ActionExecutor(intentExecutor, testScope, window, viewProxy, onDismiss)
}
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/screenshot/ActionIntentExecutorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/screenshot/ActionIntentExecutorTest.kt
index 5e53fe1..5cd3f66 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/screenshot/ActionIntentExecutorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/screenshot/ActionIntentExecutorTest.kt
@@ -23,17 +23,18 @@
import android.testing.TestableContext
import com.android.systemui.Flags
import com.android.systemui.SysuiTestCase
+import com.android.systemui.screenshot.proxy.SystemUiProxy
import com.android.systemui.settings.DisplayTracker
import com.android.systemui.shared.system.ActivityManagerWrapper
import com.android.systemui.statusbar.phone.CentralSurfaces
-import com.android.systemui.util.mockito.mock
import kotlinx.coroutines.test.StandardTestDispatcher
import kotlinx.coroutines.test.TestCoroutineScheduler
import kotlinx.coroutines.test.TestScope
import kotlinx.coroutines.test.runTest
import org.junit.Test
import org.junit.runner.RunWith
-import org.mockito.Mockito.verify
+import org.mockito.kotlin.mock
+import org.mockito.kotlin.verify
@RunWith(AndroidTestingRunner::class)
class ActionIntentExecutorTest : SysuiTestCase() {
@@ -44,8 +45,9 @@
private val testableContext = TestableContext(mContext)
private val activityManagerWrapper = mock<ActivityManagerWrapper>()
+ private val systemUiProxy = mock<SystemUiProxy>()
+
private val displayTracker = mock<DisplayTracker>()
- private val keyguardController = mock<ScreenshotKeyguardController>()
private val actionIntentExecutor =
ActionIntentExecutor(
@@ -53,12 +55,12 @@
activityManagerWrapper,
testScope,
mainDispatcher,
+ systemUiProxy,
displayTracker,
- keyguardController,
)
@Test
- @EnableFlags(Flags.FLAG_SCREENSHOT_ACTION_DISMISS_SYSTEM_WINDOWS)
+ @EnableFlags(Flags.FLAG_FIX_SCREENSHOT_ACTION_DISMISS_SYSTEM_WINDOWS)
fun launchIntent_callsCloseSystemWindows() =
testScope.runTest {
val intent = Intent(Intent.ACTION_EDIT).apply { flags = Intent.FLAG_ACTIVITY_NEW_TASK }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationPanelViewControllerBaseTest.java b/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationPanelViewControllerBaseTest.java
index 5b47c94..4d32cc4 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationPanelViewControllerBaseTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationPanelViewControllerBaseTest.java
@@ -40,6 +40,7 @@
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
+import android.animation.Animator;
import android.annotation.IdRes;
import android.content.ContentResolver;
import android.content.res.Configuration;
@@ -207,12 +208,15 @@
import org.junit.After;
import org.junit.Before;
+import org.junit.Rule;
import org.mockito.ArgumentCaptor;
import org.mockito.Captor;
import org.mockito.Mock;
-import org.mockito.MockitoAnnotations;
+import org.mockito.junit.MockitoJUnit;
+import org.mockito.junit.MockitoRule;
import org.mockito.stubbing.Answer;
+import java.util.HashSet;
import java.util.List;
import java.util.Optional;
@@ -387,9 +391,11 @@
protected FragmentHostManager.FragmentListener mFragmentListener;
+ @Rule(order = 200)
+ public MockitoRule mMockitoRule = MockitoJUnit.rule();
+
@Before
public void setup() {
- MockitoAnnotations.initMocks(this);
mFeatureFlags.set(Flags.LOCKSCREEN_ENABLE_LANDSCAPE, false);
mFeatureFlags.set(Flags.QS_USER_DETAIL_SHORTCUT, false);
@@ -547,6 +553,8 @@
}).when(mView).setOnTouchListener(any(NotificationPanelViewController.TouchHandler.class));
// Dreaming->Lockscreen
+ when(mKeyguardTransitionInteractor.transition(any()))
+ .thenReturn(emptyFlow());
when(mKeyguardTransitionInteractor.transition(any(), any()))
.thenReturn(emptyFlow());
when(mDreamingToLockscreenTransitionViewModel.getLockscreenAlpha())
@@ -655,7 +663,7 @@
when(mView.getParent()).thenReturn(mViewParent);
when(mQs.getHeader()).thenReturn(mQsHeader);
when(mDownMotionEvent.getAction()).thenReturn(MotionEvent.ACTION_DOWN);
- when(mSysUiState.setFlag(anyInt(), anyBoolean())).thenReturn(mSysUiState);
+ when(mSysUiState.setFlag(anyLong(), anyBoolean())).thenReturn(mSysUiState);
mMainHandler = new Handler(Looper.getMainLooper());
@@ -759,6 +767,9 @@
@Override
public void onOpenStarted() {}
});
+ // Create a set to which the class will add all animators used, so that we can
+ // verify that they are all stopped.
+ mNotificationPanelViewController.mTestSetOfAnimatorsUsed = new HashSet<>();
ArgumentCaptor<View.OnAttachStateChangeListener> onAttachStateChangeListenerArgumentCaptor =
ArgumentCaptor.forClass(View.OnAttachStateChangeListener.class);
verify(mView, atLeast(1)).addOnAttachStateChangeListener(
@@ -820,13 +831,20 @@
@After
public void tearDown() {
+ List<Animator> leakedAnimators = null;
if (mNotificationPanelViewController != null) {
mNotificationPanelViewController.mBottomAreaShadeAlphaAnimator.cancel();
mNotificationPanelViewController.cancelHeightAnimator();
+ leakedAnimators = mNotificationPanelViewController.mTestSetOfAnimatorsUsed.stream()
+ .filter(Animator::isRunning).toList();
+ mNotificationPanelViewController.mTestSetOfAnimatorsUsed.forEach(Animator::cancel);
}
if (mMainHandler != null) {
mMainHandler.removeCallbacksAndMessages(null);
}
+ if (leakedAnimators != null) {
+ assertThat(leakedAnimators).isEmpty();
+ }
}
protected void setBottomPadding(int stackBottom, int lockIconPadding, int indicationPadding,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationPanelViewControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationPanelViewControllerTest.java
index e1cdda4..6536405 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationPanelViewControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationPanelViewControllerTest.java
@@ -705,6 +705,7 @@
}
@Test
+ @Ignore("b/341163515 - fails to clean up animators correctly")
public void testSwipeWhileLocked_notifiesKeyguardState() {
mStatusBarStateController.setState(KEYGUARD);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationShadeWindowViewControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationShadeWindowViewControllerTest.kt
index 45d0102..4a867a8 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationShadeWindowViewControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationShadeWindowViewControllerTest.kt
@@ -46,6 +46,7 @@
import com.android.systemui.keyevent.domain.interactor.SysUIKeyEventHandler
import com.android.systemui.keyguard.KeyguardUnlockAnimationController
import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor
+import com.android.systemui.keyguard.shared.model.Edge
import com.android.systemui.keyguard.shared.model.KeyguardState.DREAMING
import com.android.systemui.keyguard.shared.model.KeyguardState.LOCKSCREEN
import com.android.systemui.keyguard.shared.model.TransitionStep
@@ -173,7 +174,7 @@
.thenReturn(keyguardBouncerComponent)
whenever(keyguardBouncerComponent.securityContainerController)
.thenReturn(keyguardSecurityContainerController)
- whenever(keyguardTransitionInteractor.transition(LOCKSCREEN, DREAMING))
+ whenever(keyguardTransitionInteractor.transition(Edge.create(LOCKSCREEN, DREAMING)))
.thenReturn(emptyFlow<TransitionStep>())
featureFlagsClassic = FakeFeatureFlagsClassic()
@@ -518,46 +519,6 @@
}
@Test
- fun handleExternalTouch_intercepted_sendsOnTouch() {
- // Accept dispatch and also intercept.
- whenever(view.dispatchTouchEvent(any())).thenReturn(true)
- whenever(view.onInterceptTouchEvent(any())).thenReturn(true)
-
- underTest.handleExternalTouch(DOWN_EVENT)
- underTest.handleExternalTouch(MOVE_EVENT)
-
- // Once intercepted, both events are sent to the view.
- verify(view).onTouchEvent(DOWN_EVENT)
- verify(view).onTouchEvent(MOVE_EVENT)
- }
-
- @Test
- fun handleExternalTouch_notDispatched_interceptNotCalled() {
- // Don't accept dispatch
- whenever(view.dispatchTouchEvent(any())).thenReturn(false)
-
- underTest.handleExternalTouch(DOWN_EVENT)
-
- // Interception is not offered.
- verify(view, never()).onInterceptTouchEvent(any())
- }
-
- @Test
- fun handleExternalTouch_notIntercepted_onTouchNotSent() {
- // Accept dispatch, but don't dispatch
- whenever(view.dispatchTouchEvent(any())).thenReturn(true)
- whenever(view.onInterceptTouchEvent(any())).thenReturn(false)
-
- underTest.handleExternalTouch(DOWN_EVENT)
- underTest.handleExternalTouch(MOVE_EVENT)
-
- // Interception offered for both events, but onTouchEvent is never called.
- verify(view).onInterceptTouchEvent(DOWN_EVENT)
- verify(view).onInterceptTouchEvent(MOVE_EVENT)
- verify(view, never()).onTouchEvent(any())
- }
-
- @Test
fun testGetKeyguardMessageArea() =
testScope.runTest {
underTest.keyguardMessageArea
diff --git a/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationShadeWindowViewTest.kt b/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationShadeWindowViewTest.kt
index f380b6c..e83a46b 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationShadeWindowViewTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationShadeWindowViewTest.kt
@@ -36,6 +36,7 @@
import com.android.systemui.keyevent.domain.interactor.SysUIKeyEventHandler
import com.android.systemui.keyguard.KeyguardUnlockAnimationController
import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor
+import com.android.systemui.keyguard.shared.model.Edge
import com.android.systemui.keyguard.shared.model.KeyguardState.DREAMING
import com.android.systemui.keyguard.shared.model.KeyguardState.LOCKSCREEN
import com.android.systemui.res.R
@@ -151,7 +152,7 @@
whenever(statusBarStateController.isDozing).thenReturn(false)
mDependency.injectTestDependency(ShadeController::class.java, shadeController)
whenever(dockManager.isDocked).thenReturn(false)
- whenever(keyguardTransitionInteractor.transition(LOCKSCREEN, DREAMING))
+ whenever(keyguardTransitionInteractor.transition(Edge.create(LOCKSCREEN, DREAMING)))
.thenReturn(emptyFlow())
val featureFlags = FakeFeatureFlags()
diff --git a/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationsQSContainerControllerLegacyTest.kt b/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationsQSContainerControllerLegacyTest.kt
index 81d0e06..2c2fcbe 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationsQSContainerControllerLegacyTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationsQSContainerControllerLegacyTest.kt
@@ -16,6 +16,8 @@
package com.android.systemui.shade
+import android.platform.test.annotations.DisableFlags
+import android.platform.test.annotations.EnableFlags
import android.testing.AndroidTestingRunner
import android.testing.TestableLooper
import android.view.View
@@ -26,8 +28,8 @@
import androidx.constraintlayout.widget.ConstraintLayout
import androidx.constraintlayout.widget.ConstraintSet
import androidx.test.filters.SmallTest
-import com.android.systemui.Flags as AConfigFlags
import com.android.systemui.Flags.FLAG_CENTRALIZED_STATUS_BAR_HEIGHT_FIX
+import com.android.systemui.Flags.FLAG_MIGRATE_CLOCKS_TO_BLUEPRINT
import com.android.systemui.SysuiTestCase
import com.android.systemui.fragments.FragmentHostManager
import com.android.systemui.fragments.FragmentService
@@ -164,10 +166,10 @@
}
@Test
+ @DisableFlags(FLAG_CENTRALIZED_STATUS_BAR_HEIGHT_FIX)
fun testLargeScreen_updateResources_refactorFlagOff_splitShadeHeightIsSetBasedOnResource() {
val headerResourceHeight = 20
val headerHelperHeight = 30
- mSetFlagsRule.disableFlags(FLAG_CENTRALIZED_STATUS_BAR_HEIGHT_FIX)
whenever(largeScreenHeaderHelper.getLargeScreenHeaderHeight())
.thenReturn(headerHelperHeight)
overrideResource(R.bool.config_use_large_screen_shade_header, true)
@@ -187,10 +189,10 @@
}
@Test
+ @EnableFlags(FLAG_CENTRALIZED_STATUS_BAR_HEIGHT_FIX)
fun testLargeScreen_updateResources_refactorFlagOn_splitShadeHeightIsSetBasedOnHelper() {
val headerResourceHeight = 20
val headerHelperHeight = 30
- mSetFlagsRule.enableFlags(FLAG_CENTRALIZED_STATUS_BAR_HEIGHT_FIX)
whenever(largeScreenHeaderHelper.getLargeScreenHeaderHeight())
.thenReturn(headerHelperHeight)
overrideResource(R.bool.config_use_large_screen_shade_header, true)
@@ -400,8 +402,8 @@
}
@Test
+ @DisableFlags(FLAG_MIGRATE_CLOCKS_TO_BLUEPRINT)
fun testSplitShadeLayout_isAlignedToGuideline() {
- mSetFlagsRule.disableFlags(AConfigFlags.FLAG_MIGRATE_CLOCKS_TO_BLUEPRINT)
enableSplitShade()
underTest.updateResources()
assertThat(getConstraintSetLayout(R.id.qs_frame).endToEnd).isEqualTo(R.id.qs_edge_guideline)
@@ -410,8 +412,8 @@
}
@Test
+ @DisableFlags(FLAG_MIGRATE_CLOCKS_TO_BLUEPRINT)
fun testSinglePaneLayout_childrenHaveEqualMargins() {
- mSetFlagsRule.disableFlags(AConfigFlags.FLAG_MIGRATE_CLOCKS_TO_BLUEPRINT)
disableSplitShade()
underTest.updateResources()
val qsStartMargin = getConstraintSetLayout(R.id.qs_frame).startMargin
@@ -427,8 +429,8 @@
}
@Test
+ @DisableFlags(FLAG_MIGRATE_CLOCKS_TO_BLUEPRINT)
fun testSplitShadeLayout_childrenHaveInsideMarginsOfZero() {
- mSetFlagsRule.disableFlags(AConfigFlags.FLAG_MIGRATE_CLOCKS_TO_BLUEPRINT)
enableSplitShade()
underTest.updateResources()
assertThat(getConstraintSetLayout(R.id.qs_frame).endMargin).isEqualTo(0)
@@ -445,9 +447,8 @@
}
@Test
+ @DisableFlags(FLAG_MIGRATE_CLOCKS_TO_BLUEPRINT, FLAG_CENTRALIZED_STATUS_BAR_HEIGHT_FIX)
fun testLargeScreenLayout_refactorFlagOff_qsAndNotifsTopMarginIsOfHeaderHeightResource() {
- mSetFlagsRule.disableFlags(AConfigFlags.FLAG_MIGRATE_CLOCKS_TO_BLUEPRINT)
- mSetFlagsRule.disableFlags(FLAG_CENTRALIZED_STATUS_BAR_HEIGHT_FIX)
setLargeScreen()
val largeScreenHeaderResourceHeight = 100
val largeScreenHeaderHelperHeight = 200
@@ -468,9 +469,9 @@
}
@Test
+ @DisableFlags(FLAG_MIGRATE_CLOCKS_TO_BLUEPRINT)
+ @EnableFlags(FLAG_CENTRALIZED_STATUS_BAR_HEIGHT_FIX)
fun testLargeScreenLayout_refactorFlagOn_qsAndNotifsTopMarginIsOfHeaderHeightHelper() {
- mSetFlagsRule.disableFlags(AConfigFlags.FLAG_MIGRATE_CLOCKS_TO_BLUEPRINT)
- mSetFlagsRule.enableFlags(FLAG_CENTRALIZED_STATUS_BAR_HEIGHT_FIX)
setLargeScreen()
val largeScreenHeaderResourceHeight = 100
val largeScreenHeaderHelperHeight = 200
@@ -491,8 +492,8 @@
}
@Test
+ @DisableFlags(FLAG_MIGRATE_CLOCKS_TO_BLUEPRINT)
fun testSmallScreenLayout_qsAndNotifsTopMarginIsZero() {
- mSetFlagsRule.disableFlags(AConfigFlags.FLAG_MIGRATE_CLOCKS_TO_BLUEPRINT)
setSmallScreen()
underTest.updateResources()
assertThat(getConstraintSetLayout(R.id.qs_frame).topMargin).isEqualTo(0)
@@ -512,8 +513,8 @@
}
@Test
+ @DisableFlags(FLAG_MIGRATE_CLOCKS_TO_BLUEPRINT)
fun testSinglePaneShadeLayout_isAlignedToParent() {
- mSetFlagsRule.disableFlags(AConfigFlags.FLAG_MIGRATE_CLOCKS_TO_BLUEPRINT)
disableSplitShade()
underTest.updateResources()
assertThat(getConstraintSetLayout(R.id.qs_frame).endToEnd)
diff --git a/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationsQSContainerControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationsQSContainerControllerTest.kt
index 4ae751b..f21def3 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationsQSContainerControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationsQSContainerControllerTest.kt
@@ -16,6 +16,8 @@
package com.android.systemui.shade
+import android.platform.test.annotations.DisableFlags
+import android.platform.test.annotations.EnableFlags
import android.testing.AndroidTestingRunner
import android.testing.TestableLooper
import android.view.View
@@ -27,6 +29,7 @@
import androidx.constraintlayout.widget.ConstraintSet
import androidx.test.filters.SmallTest
import com.android.systemui.Flags.FLAG_CENTRALIZED_STATUS_BAR_HEIGHT_FIX
+import com.android.systemui.Flags.FLAG_MIGRATE_CLOCKS_TO_BLUEPRINT
import com.android.systemui.SysuiTestCase
import com.android.systemui.fragments.FragmentHostManager
import com.android.systemui.fragments.FragmentService
@@ -67,6 +70,7 @@
@RunWith(AndroidTestingRunner::class)
@TestableLooper.RunWithLooper(setAsMainLooper = true)
@SmallTest
+@EnableFlags(FLAG_MIGRATE_CLOCKS_TO_BLUEPRINT)
class NotificationsQSContainerControllerTest : SysuiTestCase() {
private val view = mock<NotificationsQuickSettingsContainer>()
@@ -99,7 +103,6 @@
MockitoAnnotations.initMocks(this)
fakeSystemClock = FakeSystemClock()
delayableExecutor = FakeExecutor(fakeSystemClock)
- mSetFlagsRule.enableFlags(com.android.systemui.Flags.FLAG_MIGRATE_CLOCKS_TO_BLUEPRINT)
mContext.ensureTestableResources()
whenever(view.context).thenReturn(mContext)
whenever(view.resources).thenReturn(mContext.resources)
@@ -161,8 +164,8 @@
}
@Test
+ @DisableFlags(FLAG_CENTRALIZED_STATUS_BAR_HEIGHT_FIX)
fun testLargeScreen_updateResources_refactorFlagOff_splitShadeHeightIsSet_basedOnResource() {
- mSetFlagsRule.disableFlags(FLAG_CENTRALIZED_STATUS_BAR_HEIGHT_FIX)
val helperHeight = 30
val resourceHeight = 20
whenever(largeScreenHeaderHelper.getLargeScreenHeaderHeight()).thenReturn(helperHeight)
@@ -182,8 +185,8 @@
}
@Test
+ @EnableFlags(FLAG_CENTRALIZED_STATUS_BAR_HEIGHT_FIX)
fun testLargeScreen_updateResources_refactorFlagOn_splitShadeHeightIsSet_basedOnHelper() {
- mSetFlagsRule.enableFlags(FLAG_CENTRALIZED_STATUS_BAR_HEIGHT_FIX)
val helperHeight = 30
val resourceHeight = 20
whenever(largeScreenHeaderHelper.getLargeScreenHeaderHeight()).thenReturn(helperHeight)
@@ -424,8 +427,8 @@
}
@Test
+ @DisableFlags(FLAG_CENTRALIZED_STATUS_BAR_HEIGHT_FIX)
fun testLargeScreenLayout_refactorFlagOff_qsAndNotifsTopMarginIsOfHeaderResourceHeight() {
- mSetFlagsRule.disableFlags(FLAG_CENTRALIZED_STATUS_BAR_HEIGHT_FIX)
setLargeScreen()
val largeScreenHeaderHelperHeight = 200
val largeScreenHeaderResourceHeight = 100
@@ -444,8 +447,8 @@
}
@Test
+ @EnableFlags(FLAG_CENTRALIZED_STATUS_BAR_HEIGHT_FIX)
fun testLargeScreenLayout_refactorFlagOn_qsAndNotifsTopMarginIsOfHeaderHelperHeight() {
- mSetFlagsRule.enableFlags(FLAG_CENTRALIZED_STATUS_BAR_HEIGHT_FIX)
setLargeScreen()
val largeScreenHeaderHelperHeight = 200
val largeScreenHeaderResourceHeight = 100
diff --git a/packages/SystemUI/tests/src/com/android/systemui/shade/QuickSettingsControllerImplBaseTest.java b/packages/SystemUI/tests/src/com/android/systemui/shade/QuickSettingsControllerImplBaseTest.java
index 04fa590..845744a 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/shade/QuickSettingsControllerImplBaseTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/shade/QuickSettingsControllerImplBaseTest.java
@@ -42,13 +42,10 @@
import com.android.systemui.deviceentry.domain.interactor.DeviceEntryFaceAuthInteractor;
import com.android.systemui.deviceentry.domain.interactor.DeviceEntryUdfpsInteractor;
import com.android.systemui.dump.DumpManager;
-import com.android.systemui.flags.FakeFeatureFlagsClassic;
import com.android.systemui.flags.FeatureFlags;
import com.android.systemui.fragments.FragmentHostManager;
import com.android.systemui.keyguard.data.repository.FakeCommandQueue;
import com.android.systemui.keyguard.data.repository.FakeKeyguardRepository;
-import com.android.systemui.keyguard.domain.interactor.FromLockscreenTransitionInteractor;
-import com.android.systemui.keyguard.domain.interactor.FromPrimaryBouncerTransitionInteractor;
import com.android.systemui.keyguard.domain.interactor.KeyguardInteractor;
import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor;
import com.android.systemui.kosmos.KosmosJavaAdapter;
@@ -59,9 +56,7 @@
import com.android.systemui.power.domain.interactor.PowerInteractor;
import com.android.systemui.qs.QSFragmentLegacy;
import com.android.systemui.res.R;
-import com.android.systemui.scene.data.repository.SceneContainerRepository;
import com.android.systemui.scene.domain.interactor.SceneInteractor;
-import com.android.systemui.scene.shared.logger.SceneLogger;
import com.android.systemui.screenrecord.RecordingController;
import com.android.systemui.shade.data.repository.FakeShadeRepository;
import com.android.systemui.shade.domain.interactor.ShadeInteractor;
@@ -176,12 +171,7 @@
protected Handler mMainHandler;
protected LockscreenShadeTransitionController.Callback mLockscreenShadeTransitionCallback;
- protected final ShadeExpansionStateManager mShadeExpansionStateManager =
- new ShadeExpansionStateManager();
-
protected FragmentHostManager.FragmentListener mFragmentListener;
- private FromLockscreenTransitionInteractor mFromLockscreenTransitionInteractor;
- private FromPrimaryBouncerTransitionInteractor mFromPrimaryBouncerTransitionInteractor;
@Before
public void setup() {
@@ -190,19 +180,11 @@
mStatusBarStateController = mKosmos.getStatusBarStateController();
mKosmos.getFakeDeviceProvisioningRepository().setDeviceProvisioned(true);
- FakeFeatureFlagsClassic featureFlags = new FakeFeatureFlagsClassic();
FakeConfigurationRepository configurationRepository = new FakeConfigurationRepository();
PowerInteractor powerInteractor = mKosmos.getPowerInteractor();
- SceneInteractor sceneInteractor = new SceneInteractor(
- mTestScope.getBackgroundScope(),
- new SceneContainerRepository(
- mTestScope.getBackgroundScope(),
- mKosmos.getFakeSceneContainerConfig(),
- mKosmos.getSceneDataSource()),
- mock(SceneLogger.class),
- mKosmos.getDeviceUnlockedInteractor());
+ SceneInteractor sceneInteractor = mKosmos.getSceneInteractor();
KeyguardTransitionInteractor keyguardTransitionInteractor =
mKosmos.getKeyguardTransitionInteractor();
@@ -220,10 +202,6 @@
() -> mKosmos.getSharedNotificationContainerInteractor(),
mTestScope);
- mFromLockscreenTransitionInteractor = mKosmos.getFromLockscreenTransitionInteractor();
- mFromPrimaryBouncerTransitionInteractor =
- mKosmos.getFromPrimaryBouncerTransitionInteractor();
-
ResourcesSplitShadeStateController splitShadeStateController =
new ResourcesSplitShadeStateController();
diff --git a/packages/SystemUI/tests/src/com/android/systemui/shade/QuickSettingsControllerImplWithCoroutinesTest.kt b/packages/SystemUI/tests/src/com/android/systemui/shade/QuickSettingsControllerImplWithCoroutinesTest.kt
index 2c453a7..dfd7a71 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/shade/QuickSettingsControllerImplWithCoroutinesTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/shade/QuickSettingsControllerImplWithCoroutinesTest.kt
@@ -21,6 +21,7 @@
import com.android.systemui.statusbar.disableflags.data.model.DisableFlagsModel
import com.google.common.truth.Truth.assertThat
import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.cancelChildren
import kotlinx.coroutines.test.runCurrent
import kotlinx.coroutines.test.runTest
import org.junit.Test
@@ -36,6 +37,8 @@
runCurrent()
assertThat(mQsController.isExpansionEnabled).isFalse()
+
+ coroutineContext.cancelChildren()
}
@Test
@@ -45,6 +48,8 @@
runCurrent()
assertThat(mQsController.isExpansionEnabled).isTrue()
+
+ coroutineContext.cancelChildren()
}
@Test
@@ -58,6 +63,8 @@
runCurrent()
assertThat(mQsController.isExpansionEnabled).isFalse()
+
+ coroutineContext.cancelChildren()
}
@Test
@@ -71,6 +78,8 @@
runCurrent()
assertThat(mQsController.isExpansionEnabled).isFalse()
+
+ coroutineContext.cancelChildren()
}
@Test
@@ -81,5 +90,7 @@
runCurrent()
assertThat(mQsController.isExpansionEnabled).isTrue()
+
+ coroutineContext.cancelChildren()
}
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/DragDownHelperTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/DragDownHelperTest.kt
index 9ec9b69..05d9495 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/DragDownHelperTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/DragDownHelperTest.kt
@@ -17,8 +17,8 @@
package com.android.systemui.statusbar
-import android.testing.AndroidTestingRunner
import android.testing.TestableLooper
+import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.systemui.ExpandHelper
import com.android.systemui.SysuiTestCase
@@ -39,7 +39,7 @@
@SmallTest
@TestableLooper.RunWithLooper
-@RunWith(AndroidTestingRunner::class)
+@RunWith(AndroidJUnit4::class)
class DragDownHelperTest : SysuiTestCase() {
private lateinit var dragDownHelper: DragDownHelper
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/KeyguardIndicationControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/KeyguardIndicationControllerTest.java
index 1504d4c..995b538 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/KeyguardIndicationControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/KeyguardIndicationControllerTest.java
@@ -65,9 +65,9 @@
import android.hardware.biometrics.BiometricSourceType;
import android.os.BatteryManager;
import android.os.RemoteException;
-import android.testing.AndroidTestingRunner;
import android.testing.TestableLooper;
+import androidx.test.ext.junit.runners.AndroidJUnit4;
import androidx.test.filters.FlakyTest;
import androidx.test.filters.SmallTest;
@@ -88,7 +88,7 @@
import java.util.Set;
@SmallTest
-@RunWith(AndroidTestingRunner.class)
+@RunWith(AndroidJUnit4.class)
@TestableLooper.RunWithLooper
public class KeyguardIndicationControllerTest extends KeyguardIndicationControllerBaseTest {
@Test
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/KeyguardIndicationControllerWithCoroutinesTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/KeyguardIndicationControllerWithCoroutinesTest.kt
index cdc7520..4a14f88 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/KeyguardIndicationControllerWithCoroutinesTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/KeyguardIndicationControllerWithCoroutinesTest.kt
@@ -16,9 +16,9 @@
package com.android.systemui.statusbar
-import android.testing.AndroidTestingRunner
import android.testing.TestableLooper
import android.view.View
+import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.systemui.flags.Flags.LOCKSCREEN_WALLPAPER_DREAM_ENABLED
import kotlinx.coroutines.Dispatchers
@@ -28,7 +28,7 @@
import org.mockito.Mockito.times
import org.mockito.Mockito.verify
-@RunWith(AndroidTestingRunner::class)
+@RunWith(AndroidJUnit4::class)
@SmallTest
@TestableLooper.RunWithLooper(setAsMainLooper = true)
class KeyguardIndicationControllerWithCoroutinesTest : KeyguardIndicationControllerBaseTest() {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/LSShadeTransitionLoggerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/LSShadeTransitionLoggerTest.kt
index 8cb530c..948a732 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/LSShadeTransitionLoggerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/LSShadeTransitionLoggerTest.kt
@@ -1,7 +1,7 @@
package com.android.systemui.statusbar
-import android.testing.AndroidTestingRunner
import android.util.DisplayMetrics
+import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.log.LogBuffer
@@ -16,7 +16,7 @@
import org.mockito.Mock
import org.mockito.junit.MockitoJUnit
-@RunWith(AndroidTestingRunner::class)
+@RunWith(AndroidJUnit4::class)
@SmallTest
class LSShadeTransitionLoggerTest : SysuiTestCase() {
lateinit var logger: LSShadeTransitionLogger
@@ -41,4 +41,4 @@
// log a non-null, non row, ensure no crash
logger.logDragDownStarted(view)
}
-}
\ No newline at end of file
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/LightRevealScrimTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/LightRevealScrimTest.kt
index d3befb4..fe2dd6d 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/LightRevealScrimTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/LightRevealScrimTest.kt
@@ -16,8 +16,8 @@
package com.android.systemui.statusbar
-import android.testing.AndroidTestingRunner
import android.view.View
+import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
import com.google.common.truth.Truth.assertThat
@@ -28,7 +28,7 @@
import org.junit.runner.RunWith
import java.util.function.Consumer
-@RunWith(AndroidTestingRunner::class)
+@RunWith(AndroidJUnit4::class)
@SmallTest
class LightRevealScrimTest : SysuiTestCase() {
@@ -85,4 +85,4 @@
private const val DEFAULT_WIDTH = 42
private const val DEFAULT_HEIGHT = 24
}
-}
\ No newline at end of file
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/LockscreenShadeQsTransitionControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/LockscreenShadeQsTransitionControllerTest.kt
index 402d9aa..e48242a 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/LockscreenShadeQsTransitionControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/LockscreenShadeQsTransitionControllerTest.kt
@@ -16,7 +16,7 @@
package com.android.systemui.statusbar
-import android.testing.AndroidTestingRunner
+import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.dump.DumpManager
@@ -36,7 +36,7 @@
import org.mockito.MockitoAnnotations
@SmallTest
-@RunWith(AndroidTestingRunner::class)
+@RunWith(AndroidJUnit4::class)
class LockscreenShadeQsTransitionControllerTest : SysuiTestCase() {
private val configurationController = FakeConfigurationController()
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/LockscreenShadeTransitionControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/LockscreenShadeTransitionControllerTest.kt
index a92cf8c..69e8f47 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/LockscreenShadeTransitionControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/LockscreenShadeTransitionControllerTest.kt
@@ -1,9 +1,9 @@
package com.android.systemui.statusbar
import android.app.StatusBarManager.DISABLE2_NOTIFICATION_SHADE
-import android.testing.AndroidTestingRunner
import android.testing.TestableLooper
import android.testing.TestableLooper.RunWithLooper
+import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.systemui.ExpandHelper
import com.android.systemui.SysUITestModule
@@ -74,7 +74,7 @@
@SmallTest
@RunWithLooper(setAsMainLooper = true)
-@RunWith(AndroidTestingRunner::class)
+@RunWith(AndroidJUnit4::class)
@OptIn(ExperimentalCoroutinesApi::class)
class LockscreenShadeTransitionControllerTest : SysuiTestCase() {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationListenerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationListenerTest.java
index d3febf5..ef1c927 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationListenerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationListenerTest.java
@@ -32,8 +32,8 @@
import android.service.notification.NotificationListenerService.Ranking;
import android.service.notification.NotificationListenerService.RankingMap;
import android.service.notification.StatusBarNotification;
-import android.testing.AndroidTestingRunner;
+import androidx.test.ext.junit.runners.AndroidJUnit4;
import androidx.test.filters.SmallTest;
import com.android.systemui.SysuiTestCase;
@@ -52,7 +52,7 @@
import org.mockito.MockitoAnnotations;
@SmallTest
-@RunWith(AndroidTestingRunner.class)
+@RunWith(AndroidJUnit4.class)
public class NotificationListenerTest extends SysuiTestCase {
private static final String TEST_PACKAGE_NAME = "test";
private static final int TEST_UID = 0;
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationRemoteInputManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationRemoteInputManagerTest.java
index d3850be..c9d910c 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationRemoteInputManagerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationRemoteInputManagerTest.java
@@ -27,9 +27,9 @@
import android.content.Context;
import android.os.SystemClock;
import android.os.UserHandle;
-import android.testing.AndroidTestingRunner;
import android.testing.TestableLooper;
+import androidx.test.ext.junit.runners.AndroidJUnit4;
import androidx.test.filters.SmallTest;
import com.android.systemui.SysuiTestCase;
@@ -52,7 +52,7 @@
import org.mockito.MockitoAnnotations;
@SmallTest
-@RunWith(AndroidTestingRunner.class)
+@RunWith(AndroidJUnit4.class)
@TestableLooper.RunWithLooper
public class NotificationRemoteInputManagerTest extends SysuiTestCase {
private static final String TEST_PACKAGE_NAME = "test";
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationShadeDepthControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationShadeDepthControllerTest.kt
index fc0c85e..9f94cff 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationShadeDepthControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationShadeDepthControllerTest.kt
@@ -17,11 +17,11 @@
package com.android.systemui.statusbar
import android.os.IBinder
-import android.testing.AndroidTestingRunner
import android.testing.TestableLooper.RunWithLooper
import android.view.Choreographer
import android.view.View
import android.view.ViewRootImpl
+import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.animation.ShadeInterpolation
@@ -59,7 +59,7 @@
import org.mockito.Mockito.`when`
import org.mockito.junit.MockitoJUnit
-@RunWith(AndroidTestingRunner::class)
+@RunWith(AndroidJUnit4::class)
@RunWithLooper
@SmallTest
class NotificationShadeDepthControllerTest : SysuiTestCase() {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/PulseExpansionHandlerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/PulseExpansionHandlerTest.kt
index 49e5c45..9907740 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/PulseExpansionHandlerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/PulseExpansionHandlerTest.kt
@@ -17,8 +17,8 @@
package com.android.systemui.statusbar
-import android.testing.AndroidTestingRunner
import android.testing.TestableLooper
+import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.dump.DumpManager
@@ -43,7 +43,7 @@
@SmallTest
@TestableLooper.RunWithLooper
-@RunWith(AndroidTestingRunner::class)
+@RunWith(AndroidJUnit4::class)
class PulseExpansionHandlerTest : SysuiTestCase() {
private lateinit var pulseExpansionHandler: PulseExpansionHandler
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/RemoteInputNotificationRebuilderTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/RemoteInputNotificationRebuilderTest.java
index ce11d6a..58943ea 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/RemoteInputNotificationRebuilderTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/RemoteInputNotificationRebuilderTest.java
@@ -27,9 +27,9 @@
import android.net.Uri;
import android.os.UserHandle;
import android.service.notification.StatusBarNotification;
-import android.testing.AndroidTestingRunner;
import android.testing.TestableLooper;
+import androidx.test.ext.junit.runners.AndroidJUnit4;
import androidx.test.filters.SmallTest;
import com.android.systemui.SysuiTestCase;
@@ -44,7 +44,7 @@
import org.mockito.MockitoAnnotations;
@SmallTest
-@RunWith(AndroidTestingRunner.class)
+@RunWith(AndroidJUnit4.class)
@TestableLooper.RunWithLooper
public class RemoteInputNotificationRebuilderTest extends SysuiTestCase {
private static final String TEST_PACKAGE_NAME = "test";
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/SingleShadeLockScreenOverScrollerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/SingleShadeLockScreenOverScrollerTest.kt
index 2606be5..6b9a19a 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/SingleShadeLockScreenOverScrollerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/SingleShadeLockScreenOverScrollerTest.kt
@@ -1,7 +1,7 @@
package com.android.systemui.statusbar
import org.mockito.Mockito.`when` as whenever
-import android.testing.AndroidTestingRunner
+import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayoutController
@@ -14,7 +14,7 @@
import org.mockito.Mockito.verify
import org.mockito.MockitoAnnotations
-@RunWith(AndroidTestingRunner::class)
+@RunWith(AndroidJUnit4::class)
@SmallTest
class SingleShadeLockScreenOverScrollerTest : SysuiTestCase() {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/SmartReplyControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/SmartReplyControllerTest.java
index 775dc3c..3346e19 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/SmartReplyControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/SmartReplyControllerTest.java
@@ -29,9 +29,9 @@
import android.os.RemoteException;
import android.os.UserHandle;
import android.service.notification.StatusBarNotification;
-import android.testing.AndroidTestingRunner;
import android.testing.TestableLooper;
+import androidx.test.ext.junit.runners.AndroidJUnit4;
import androidx.test.filters.SmallTest;
import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
@@ -52,7 +52,7 @@
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
-@RunWith(AndroidTestingRunner.class)
+@RunWith(AndroidJUnit4.class)
@TestableLooper.RunWithLooper
@SmallTest
public class SmartReplyControllerTest extends SysuiTestCase {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/SplitShadeLockScreenOverScrollerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/SplitShadeLockScreenOverScrollerTest.kt
index 700fb1e..58473c4 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/SplitShadeLockScreenOverScrollerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/SplitShadeLockScreenOverScrollerTest.kt
@@ -1,7 +1,7 @@
package com.android.systemui.statusbar
-import android.testing.AndroidTestingRunner
import android.testing.TestableLooper
+import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.dump.DumpManager
@@ -23,7 +23,7 @@
import org.mockito.Mockito.`when` as whenever
import org.mockito.MockitoAnnotations
-@RunWith(AndroidTestingRunner::class)
+@RunWith(AndroidJUnit4::class)
@SmallTest
@TestableLooper.RunWithLooper
class SplitShadeLockScreenOverScrollerTest : SysuiTestCase() {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/StatusBarStateEventTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/StatusBarStateEventTest.kt
index 79a2008..26692c9 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/StatusBarStateEventTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/StatusBarStateEventTest.kt
@@ -16,7 +16,7 @@
package com.android.systemui.statusbar
-import android.testing.AndroidTestingRunner
+import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
import org.junit.Assert.assertEquals
@@ -24,7 +24,7 @@
import org.junit.runner.RunWith
@SmallTest
-@RunWith(AndroidTestingRunner::class)
+@RunWith(AndroidJUnit4::class)
class StatusBarStateEventTest : SysuiTestCase() {
@Test
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/VibratorHelperTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/VibratorHelperTest.kt
index b905825..78c1887 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/VibratorHelperTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/VibratorHelperTest.kt
@@ -5,9 +5,9 @@
import android.os.VibrationAttributes
import android.os.VibrationEffect
import android.os.Vibrator
-import android.testing.AndroidTestingRunner
import android.view.HapticFeedbackConstants
import android.view.View
+import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.util.mockito.eq
@@ -26,7 +26,7 @@
import org.mockito.junit.MockitoJUnit
import java.util.concurrent.Executor
-@RunWith(AndroidTestingRunner::class)
+@RunWith(AndroidJUnit4::class)
@SmallTest
class VibratorHelperTest : SysuiTestCase() {
@@ -120,4 +120,4 @@
return verify(vibrator)
}
-}
\ No newline at end of file
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ongoingcall/OngoingCallBackgroundContainerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/chips/ui/view/ChipBackgroundContainerTest.kt
similarity index 86%
rename from packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ongoingcall/OngoingCallBackgroundContainerTest.kt
rename to packages/SystemUI/tests/src/com/android/systemui/statusbar/chips/ui/view/ChipBackgroundContainerTest.kt
index f0a457e..7d2b463 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ongoingcall/OngoingCallBackgroundContainerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/chips/ui/view/ChipBackgroundContainerTest.kt
@@ -14,15 +14,15 @@
* limitations under the License.
*/
-package com.android.systemui.statusbar.phone.ongoingcall
+package com.android.systemui.statusbar.chips.ui.view
import android.testing.AndroidTestingRunner
import android.testing.TestableLooper
import android.view.LayoutInflater
import android.view.View
import androidx.test.filters.SmallTest
-import com.android.systemui.res.R
import com.android.systemui.SysuiTestCase
+import com.android.systemui.res.R
import com.google.common.truth.Truth.assertThat
import org.junit.Before
import org.junit.Test
@@ -31,16 +31,17 @@
@SmallTest
@RunWith(AndroidTestingRunner::class)
@TestableLooper.RunWithLooper
-class OngoingCallBackgroundContainerTest : SysuiTestCase() {
+class ChipBackgroundContainerTest : SysuiTestCase() {
- private lateinit var underTest: OngoingCallBackgroundContainer
+ private lateinit var underTest: ChipBackgroundContainer
@Before
fun setUp() {
allowTestableLooperAsMainThread()
TestableLooper.get(this).runWithLooper {
- val chipView = LayoutInflater.from(context).inflate(R.layout.ongoing_call_chip, null)
- underTest = chipView.requireViewById(R.id.ongoing_call_chip_background)
+ val chipView =
+ LayoutInflater.from(context).inflate(R.layout.ongoing_activity_chip, null)
+ underTest = chipView.requireViewById(R.id.ongoing_activity_chip_background)
}
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ongoingcall/OngoingCallChronometerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/chips/ui/view/ChipChronometerTest.kt
similarity index 89%
rename from packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ongoingcall/OngoingCallChronometerTest.kt
rename to packages/SystemUI/tests/src/com/android/systemui/statusbar/chips/ui/view/ChipChronometerTest.kt
index 7e25aa3..b8d4e47 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ongoingcall/OngoingCallChronometerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/chips/ui/view/ChipChronometerTest.kt
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2021 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.
@@ -14,15 +14,15 @@
* limitations under the License.
*/
-package com.android.systemui.statusbar.phone.ongoingcall
+package com.android.systemui.statusbar.chips.ui.view
import android.testing.AndroidTestingRunner
import android.testing.TestableLooper
import android.view.LayoutInflater
import android.view.View
import androidx.test.filters.SmallTest
-import com.android.systemui.res.R
import com.android.systemui.SysuiTestCase
+import com.android.systemui.res.R
import com.google.common.truth.Truth.assertThat
import org.junit.Before
import org.junit.Test
@@ -38,17 +38,18 @@
@SmallTest
@RunWith(AndroidTestingRunner::class)
@TestableLooper.RunWithLooper
-class OngoingCallChronometerTest : SysuiTestCase() {
+class ChipChronometerTest : SysuiTestCase() {
- private lateinit var textView: OngoingCallChronometer
+ private lateinit var textView: ChipChronometer
private lateinit var doesNotFitText: String
@Before
fun setUp() {
allowTestableLooperAsMainThread()
TestableLooper.get(this).runWithLooper {
- val chipView = LayoutInflater.from(mContext).inflate(R.layout.ongoing_call_chip, null)
- textView = chipView.findViewById(R.id.ongoing_call_chip_time)!!
+ val chipView =
+ LayoutInflater.from(mContext).inflate(R.layout.ongoing_activity_chip, null)
+ textView = chipView.findViewById(R.id.ongoing_activity_chip_time)!!
measureTextView()
calculateDoesNotFixText()
}
@@ -159,8 +160,8 @@
private fun measureTextView() {
textView.measure(
- View.MeasureSpec.makeMeasureSpec(TEXT_VIEW_MAX_WIDTH, View.MeasureSpec.AT_MOST),
- View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED)
+ View.MeasureSpec.makeMeasureSpec(TEXT_VIEW_MAX_WIDTH, View.MeasureSpec.AT_MOST),
+ View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED)
)
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/connectivity/AccessPointControllerImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/connectivity/AccessPointControllerImplTest.kt
index 7e88ae0..643acdb 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/connectivity/AccessPointControllerImplTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/connectivity/AccessPointControllerImplTest.kt
@@ -17,8 +17,8 @@
package com.android.systemui.statusbar.connectivity
import android.os.UserManager
+import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
-import android.testing.AndroidTestingRunner
import android.testing.TestableLooper.RunWithLooper
import androidx.lifecycle.Lifecycle
import com.android.systemui.SysuiTestCase
@@ -42,7 +42,7 @@
import java.util.concurrent.Executor
@SmallTest
-@RunWith(AndroidTestingRunner::class)
+@RunWith(AndroidJUnit4::class)
@RunWithLooper(setAsMainLooper = true)
class AccessPointControllerImplTest : SysuiTestCase() {
@@ -244,4 +244,4 @@
verify(wifiEntryOther).connect(any())
verify(callback, never()).onSettingsActivityTriggered(any())
}
-}
\ No newline at end of file
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/connectivity/MobileStateTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/connectivity/MobileStateTest.kt
index 7aed4f7..40f81e2 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/connectivity/MobileStateTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/connectivity/MobileStateTest.kt
@@ -15,7 +15,7 @@
*/
package com.android.systemui.statusbar.connectivity
-import android.testing.AndroidTestingRunner
+import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.settingslib.mobile.TelephonyIcons
import com.android.systemui.SysuiTestCase
@@ -26,7 +26,7 @@
import org.junit.runner.RunWith
@SmallTest
-@RunWith(AndroidTestingRunner::class)
+@RunWith(AndroidJUnit4::class)
class MobileStateTest : SysuiTestCase() {
private val state = MobileState()
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/connectivity/NetworkControllerDataTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/connectivity/NetworkControllerDataTest.java
index 461d804..4241254 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/connectivity/NetworkControllerDataTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/connectivity/NetworkControllerDataTest.java
@@ -39,10 +39,10 @@
import android.telephony.NetworkRegistrationInfo;
import android.telephony.ServiceState;
import android.telephony.TelephonyManager;
-import android.testing.AndroidTestingRunner;
import android.testing.TestableLooper;
import android.testing.TestableLooper.RunWithLooper;
+import androidx.test.ext.junit.runners.AndroidJUnit4;
import androidx.test.filters.SmallTest;
import com.android.settingslib.SignalIcon.MobileIconGroup;
@@ -60,7 +60,7 @@
import java.util.HashMap;
@SmallTest
-@RunWith(AndroidTestingRunner.class)
+@RunWith(AndroidJUnit4.class)
@RunWithLooper
public class NetworkControllerDataTest extends NetworkControllerBaseTest {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/connectivity/NetworkControllerEthernetTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/connectivity/NetworkControllerEthernetTest.java
index 3bbf06d..521cb4f 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/connectivity/NetworkControllerEthernetTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/connectivity/NetworkControllerEthernetTest.java
@@ -19,9 +19,9 @@
import static junit.framework.Assert.assertEquals;
import android.net.NetworkCapabilities;
-import android.testing.AndroidTestingRunner;
import android.testing.TestableLooper.RunWithLooper;
+import androidx.test.ext.junit.runners.AndroidJUnit4;
import androidx.test.filters.SmallTest;
import org.junit.Test;
@@ -30,7 +30,7 @@
import org.mockito.Mockito;
@SmallTest
-@RunWith(AndroidTestingRunner.class)
+@RunWith(AndroidJUnit4.class)
@RunWithLooper
public class NetworkControllerEthernetTest extends NetworkControllerBaseTest {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/connectivity/NetworkControllerSignalTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/connectivity/NetworkControllerSignalTest.java
index 35609a5..22f0e9d 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/connectivity/NetworkControllerSignalTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/connectivity/NetworkControllerSignalTest.java
@@ -33,10 +33,10 @@
import android.telephony.SubscriptionInfo;
import android.telephony.SubscriptionManager;
import android.telephony.TelephonyManager;
-import android.testing.AndroidTestingRunner;
import android.testing.TestableLooper;
import android.testing.TestableLooper.RunWithLooper;
+import androidx.test.ext.junit.runners.AndroidJUnit4;
import androidx.test.filters.SmallTest;
import com.android.settingslib.graph.SignalDrawable;
@@ -59,7 +59,7 @@
import java.util.List;
@SmallTest
-@RunWith(AndroidTestingRunner.class)
+@RunWith(AndroidJUnit4.class)
@RunWithLooper
public class NetworkControllerSignalTest extends NetworkControllerBaseTest {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/connectivity/NetworkControllerWifiTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/connectivity/NetworkControllerWifiTest.java
index 44a1c50..6c80a97 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/connectivity/NetworkControllerWifiTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/connectivity/NetworkControllerWifiTest.java
@@ -34,9 +34,9 @@
import android.net.vcn.VcnTransportInfo;
import android.net.wifi.WifiInfo;
import android.net.wifi.WifiManager;
-import android.testing.AndroidTestingRunner;
import android.testing.TestableLooper.RunWithLooper;
+import androidx.test.ext.junit.runners.AndroidJUnit4;
import androidx.test.filters.SmallTest;
import com.android.settingslib.mobile.TelephonyIcons;
@@ -50,7 +50,7 @@
import java.util.Collections;
@SmallTest
-@RunWith(AndroidTestingRunner.class)
+@RunWith(AndroidJUnit4.class)
@RunWithLooper
public class NetworkControllerWifiTest extends NetworkControllerBaseTest {
// These match the constants in WifiManager and need to be kept up to date.
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/connectivity/NetworkTypeResIdCacheTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/connectivity/NetworkTypeResIdCacheTest.kt
index 5bf0a94..3eeed73 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/connectivity/NetworkTypeResIdCacheTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/connectivity/NetworkTypeResIdCacheTest.kt
@@ -16,7 +16,7 @@
package com.android.systemui.statusbar.connectivity
-import android.testing.AndroidTestingRunner
+import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.settingslib.SignalIcon.MobileIconGroup
import com.android.systemui.SysuiTestCase
@@ -26,7 +26,7 @@
import org.junit.runner.RunWith
@SmallTest
-@RunWith(AndroidTestingRunner::class)
+@RunWith(AndroidJUnit4::class)
class NetworkTypeResIdCacheTest : SysuiTestCase() {
private lateinit var cache: NetworkTypeResIdCache
private var overrides = MobileIconCarrierIdOverridesFake()
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/events/SystemEventChipAnimationControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/events/SystemEventChipAnimationControllerTest.kt
index 452302d..984bda1 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/events/SystemEventChipAnimationControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/events/SystemEventChipAnimationControllerTest.kt
@@ -19,11 +19,11 @@
import android.content.Context
import android.graphics.Insets
import android.graphics.Rect
-import android.testing.AndroidTestingRunner
import android.testing.TestableLooper
import android.view.Gravity
import android.view.View
import android.widget.FrameLayout
+import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.animation.AnimatorTestRule
@@ -46,7 +46,7 @@
import org.mockito.MockitoAnnotations
@SmallTest
-@RunWith(AndroidTestingRunner::class)
+@RunWith(AndroidJUnit4::class)
@TestableLooper.RunWithLooper
class SystemEventChipAnimationControllerTest : SysuiTestCase() {
private lateinit var controller: SystemEventChipAnimationController
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/events/SystemEventCoordinatorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/events/SystemEventCoordinatorTest.kt
index ae84df5..742494b 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/events/SystemEventCoordinatorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/events/SystemEventCoordinatorTest.kt
@@ -15,8 +15,8 @@
*/
package com.android.systemui.statusbar.events
-import android.testing.AndroidTestingRunner
import android.testing.TestableLooper
+import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.display.domain.interactor.ConnectedDisplayInteractor
@@ -40,7 +40,7 @@
import org.mockito.Mockito.verifyNoMoreInteractions
import org.mockito.MockitoAnnotations
-@RunWith(AndroidTestingRunner::class)
+@RunWith(AndroidJUnit4::class)
@TestableLooper.RunWithLooper(setAsMainLooper = true)
@SmallTest
@OptIn(ExperimentalCoroutinesApi::class)
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/events/SystemStatusAnimationSchedulerImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/events/SystemStatusAnimationSchedulerImplTest.kt
index cacfa8d..376873d 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/events/SystemStatusAnimationSchedulerImplTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/events/SystemStatusAnimationSchedulerImplTest.kt
@@ -19,10 +19,10 @@
import android.graphics.Insets
import android.graphics.Rect
import android.os.Process
-import android.testing.AndroidTestingRunner
import android.testing.TestableLooper.RunWithLooper
import android.view.View
import android.widget.FrameLayout
+import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.animation.AnimatorTestRule
@@ -54,7 +54,7 @@
import org.mockito.Mockito.verify
import org.mockito.MockitoAnnotations
-@RunWith(AndroidTestingRunner::class)
+@RunWith(AndroidJUnit4::class)
@RunWithLooper(setAsMainLooper = true)
@OptIn(ExperimentalCoroutinesApi::class)
@SmallTest
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/gesture/GenericGestureDetectorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/gesture/GenericGestureDetectorTest.kt
index d3f5ade..0f58990 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/gesture/GenericGestureDetectorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/gesture/GenericGestureDetectorTest.kt
@@ -1,9 +1,9 @@
package com.android.systemui.statusbar.gesture
-import android.testing.AndroidTestingRunner
import android.testing.TestableLooper
import android.view.InputEvent
import android.view.MotionEvent
+import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.settings.FakeDisplayTracker
@@ -13,7 +13,7 @@
import org.junit.runner.RunWith
@SmallTest
-@RunWith(AndroidTestingRunner::class)
+@RunWith(AndroidJUnit4::class)
@TestableLooper.RunWithLooper
class GenericGestureDetectorTest : SysuiTestCase() {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/AboveShelfObserverTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/AboveShelfObserverTest.java
index 6b2ee76..01a0fd0 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/AboveShelfObserverTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/AboveShelfObserverTest.java
@@ -19,11 +19,11 @@
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.verify;
-import android.testing.AndroidTestingRunner;
import android.testing.TestableLooper;
import android.testing.TestableLooper.RunWithLooper;
import android.widget.FrameLayout;
+import androidx.test.ext.junit.runners.AndroidJUnit4;
import androidx.test.filters.SmallTest;
import com.android.systemui.SysuiTestCase;
@@ -36,7 +36,7 @@
import org.junit.runner.RunWith;
@SmallTest
-@RunWith(AndroidTestingRunner.class)
+@RunWith(AndroidJUnit4.class)
@RunWithLooper
public class AboveShelfObserverTest extends SysuiTestCase {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/AssistantFeedbackControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/AssistantFeedbackControllerTest.java
index fc4702c..d66b010 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/AssistantFeedbackControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/AssistantFeedbackControllerTest.java
@@ -42,9 +42,9 @@
import android.os.UserHandle;
import android.provider.DeviceConfig;
import android.service.notification.StatusBarNotification;
-import android.testing.AndroidTestingRunner;
import android.testing.TestableLooper;
+import androidx.test.ext.junit.runners.AndroidJUnit4;
import androidx.test.filters.SmallTest;
import com.android.internal.config.sysui.SystemUiDeviceConfigFlags;
@@ -58,7 +58,7 @@
import org.junit.runner.RunWith;
@SmallTest
-@RunWith(AndroidTestingRunner.class)
+@RunWith(AndroidJUnit4.class)
@TestableLooper.RunWithLooper
public class AssistantFeedbackControllerTest extends SysuiTestCase {
private static final String TEST_PACKAGE_NAME = "test_package";
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/DynamicChildBindControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/DynamicChildBindControllerTest.java
index 0103564..77fd067 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/DynamicChildBindControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/DynamicChildBindControllerTest.java
@@ -25,12 +25,12 @@
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
-import android.testing.AndroidTestingRunner;
import android.testing.TestableLooper;
import android.util.ArrayMap;
import android.view.LayoutInflater;
import android.view.View;
+import androidx.test.ext.junit.runners.AndroidJUnit4;
import androidx.test.filters.SmallTest;
import com.android.systemui.SysuiTestCase;
@@ -56,7 +56,7 @@
import java.util.Map;
@SmallTest
-@RunWith(AndroidTestingRunner.class)
+@RunWith(AndroidJUnit4.class)
@TestableLooper.RunWithLooper
public class DynamicChildBindControllerTest extends SysuiTestCase {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/DynamicPrivacyControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/DynamicPrivacyControllerTest.java
index 5b72ca0..d879fce 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/DynamicPrivacyControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/DynamicPrivacyControllerTest.java
@@ -25,9 +25,9 @@
import static org.mockito.Mockito.verifyNoMoreInteractions;
import static org.mockito.Mockito.when;
-import android.testing.AndroidTestingRunner;
import android.testing.TestableLooper;
+import androidx.test.ext.junit.runners.AndroidJUnit4;
import androidx.test.filters.SmallTest;
import com.android.systemui.SysuiTestCase;
@@ -38,9 +38,10 @@
import org.junit.Assert;
import org.junit.Before;
import org.junit.Test;
+import org.junit.runner.RunWith;
@SmallTest
-@org.junit.runner.RunWith(AndroidTestingRunner.class)
+@RunWith(AndroidJUnit4.class)
@TestableLooper.RunWithLooper
public class DynamicPrivacyControllerTest extends SysuiTestCase {
@@ -127,4 +128,4 @@
mDynamicPrivacyController.onUnlockedChanged();
verifyNoMoreInteractions(mListener);
}
-}
\ No newline at end of file
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationSectionsFeatureManagerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationSectionsFeatureManagerTest.kt
index 1cce3b5..9e733be 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationSectionsFeatureManagerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationSectionsFeatureManagerTest.kt
@@ -16,15 +16,17 @@
package com.android.systemui.statusbar.notification
+import android.platform.test.annotations.DisableFlags
import android.provider.DeviceConfig
import android.provider.Settings
-import android.testing.AndroidTestingRunner
+import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.dx.mockito.inline.extended.ExtendedMockito
import com.android.internal.config.sysui.SystemUiDeviceConfigFlags.NOTIFICATIONS_USE_PEOPLE_FILTERING
import com.android.systemui.SysuiTestCase
+import com.android.systemui.statusbar.notification.shared.PriorityPeopleSection
import com.android.systemui.util.DeviceConfigProxyFake
import com.android.systemui.util.Utils
import com.android.systemui.util.mockito.any
@@ -39,8 +41,9 @@
import org.mockito.MockitoSession
import org.mockito.quality.Strictness
-@RunWith(AndroidTestingRunner::class)
+@RunWith(AndroidJUnit4::class)
@SmallTest
+@DisableFlags(PriorityPeopleSection.FLAG_NAME) // this class has no logic with the flag enabled
class NotificationSectionsFeatureManagerTest : SysuiTestCase() {
var manager: NotificationSectionsFeatureManager? = null
val proxyFake = DeviceConfigProxyFake()
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationTransitionAnimatorControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationTransitionAnimatorControllerTest.kt
index 3b3f05d..3abdf62 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationTransitionAnimatorControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationTransitionAnimatorControllerTest.kt
@@ -1,9 +1,9 @@
package com.android.systemui.statusbar.notification
import android.app.Notification.GROUP_ALERT_SUMMARY
-import android.testing.AndroidTestingRunner
import android.testing.TestableLooper
import android.testing.TestableLooper.RunWithLooper
+import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.coroutines.collectLastValue
@@ -35,7 +35,7 @@
import org.mockito.junit.MockitoJUnit
@SmallTest
-@RunWith(AndroidTestingRunner::class)
+@RunWith(AndroidJUnit4::class)
@RunWithLooper
class NotificationTransitionAnimatorControllerTest : SysuiTestCase() {
@Mock lateinit var notificationListContainer: NotificationListContainer
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationWakeUpCoordinatorLoggerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationWakeUpCoordinatorLoggerTest.kt
index 1aac515..a5206f5 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationWakeUpCoordinatorLoggerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationWakeUpCoordinatorLoggerTest.kt
@@ -16,7 +16,7 @@
package com.android.systemui.statusbar.notification
-import android.testing.AndroidTestingRunner
+import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.log.LogBuffer
@@ -28,7 +28,7 @@
import org.junit.Test
import org.junit.runner.RunWith
-@RunWith(AndroidTestingRunner::class)
+@RunWith(AndroidJUnit4::class)
@SmallTest
class NotificationWakeUpCoordinatorLoggerTest : SysuiTestCase() {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationWakeUpCoordinatorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationWakeUpCoordinatorTest.kt
index 67b540c..0906d8e 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationWakeUpCoordinatorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationWakeUpCoordinatorTest.kt
@@ -16,8 +16,8 @@
package com.android.systemui.statusbar.notification
-import android.testing.AndroidTestingRunner
import android.testing.TestableLooper
+import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.compose.animation.scene.ObservableTransitionState
import com.android.systemui.SysuiTestCase
@@ -60,7 +60,7 @@
import org.mockito.Mockito.verifyNoMoreInteractions
@OptIn(ExperimentalCoroutinesApi::class)
-@RunWith(AndroidTestingRunner::class)
+@RunWith(AndroidJUnit4::class)
@SmallTest
@TestableLooper.RunWithLooper(setAsMainLooper = true)
class NotificationWakeUpCoordinatorTest : SysuiTestCase() {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/RoundableTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/RoundableTest.kt
index 7d8cf36..382b307 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/RoundableTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/RoundableTest.kt
@@ -2,6 +2,7 @@
import android.platform.test.annotations.EnableFlags
import android.view.View
+import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.statusbar.notification.shared.NotificationsImprovedHunAnimation
@@ -10,13 +11,12 @@
import org.junit.Assert.assertEquals
import org.junit.Test
import org.junit.runner.RunWith
-import org.junit.runners.JUnit4
import org.mockito.Mockito.atLeastOnce
import org.mockito.Mockito.times
import org.mockito.Mockito.verify
@SmallTest
-@RunWith(JUnit4::class)
+@RunWith(AndroidJUnit4::class)
class RoundableTest : SysuiTestCase() {
private val targetView: View = mock()
private val roundable = FakeRoundable(targetView = targetView)
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/HighPriorityProviderTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/HighPriorityProviderTest.java
index 2d044fe..8e95ac5 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/HighPriorityProviderTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/HighPriorityProviderTest.java
@@ -30,8 +30,8 @@
import android.app.Notification;
import android.app.NotificationChannel;
-import android.testing.AndroidTestingRunner;
+import androidx.test.ext.junit.runners.AndroidJUnit4;
import androidx.test.filters.SmallTest;
import com.android.systemui.SysuiTestCase;
@@ -51,7 +51,7 @@
import java.util.List;
@SmallTest
-@RunWith(AndroidTestingRunner.class)
+@RunWith(AndroidJUnit4.class)
public class HighPriorityProviderTest extends SysuiTestCase {
@Mock private PeopleNotificationIdentifier mPeopleNotificationIdentifier;
@Mock private GroupMembershipManager mGroupMembershipManager;
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/NotifLiveDataImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/NotifLiveDataImplTest.kt
index 892575a..2a58751 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/NotifLiveDataImplTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/NotifLiveDataImplTest.kt
@@ -16,9 +16,9 @@
package com.android.systemui.statusbar.notification.collection
-import android.testing.AndroidTestingRunner
import android.testing.TestableLooper.RunWithLooper
import androidx.lifecycle.Observer
+import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.util.concurrency.FakeExecutor
@@ -34,7 +34,7 @@
import org.mockito.Mockito.verifyNoMoreInteractions
@SmallTest
-@RunWith(AndroidTestingRunner::class)
+@RunWith(AndroidJUnit4::class)
@RunWithLooper
class NotifLiveDataImplTest : SysuiTestCase() {
@@ -164,4 +164,4 @@
assertThat(executor.runAllReady()).isEqualTo(2)
verifyNoMoreInteractions(syncObserver, asyncObserver)
}
-}
\ No newline at end of file
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/NotifLiveDataStoreImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/NotifLiveDataStoreImplTest.kt
index 9c8ac5c..d87f827 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/NotifLiveDataStoreImplTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/NotifLiveDataStoreImplTest.kt
@@ -16,8 +16,8 @@
package com.android.systemui.statusbar.notification.collection
-import android.testing.AndroidTestingRunner
import android.testing.TestableLooper.RunWithLooper
+import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.util.concurrency.FakeExecutor
@@ -30,7 +30,7 @@
import java.lang.UnsupportedOperationException
@SmallTest
-@RunWith(AndroidTestingRunner::class)
+@RunWith(AndroidJUnit4::class)
@RunWithLooper
class NotifLiveDataStoreImplTest : SysuiTestCase() {
@@ -102,4 +102,4 @@
liveDataStoreImpl.setActiveNotifList(mutableListOf(entry1, entry2))
executor.runAllReady()
}
-}
\ No newline at end of file
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/NotifPipelineChoreographerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/NotifPipelineChoreographerTest.kt
index 3b908b4..f1da22f 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/NotifPipelineChoreographerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/NotifPipelineChoreographerTest.kt
@@ -16,8 +16,8 @@
package com.android.systemui.statusbar.notification.collection
-import android.testing.AndroidTestingRunner
import android.view.Choreographer
+import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.dagger.SysUISingleton
@@ -36,7 +36,7 @@
import org.mockito.Mockito.`when` as whenever
@SmallTest
-@RunWith(AndroidTestingRunner::class)
+@RunWith(AndroidJUnit4::class)
class NotifPipelineChoreographerTest : SysuiTestCase() {
val viewChoreographer: Choreographer = mock()
@@ -118,4 +118,4 @@
@BindsInstance @Main executor: DelayableExecutor
): NotifPipelineChoreographerTestComponent
}
-}
\ No newline at end of file
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/NotificationEntryTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/NotificationEntryTest.java
index 8a48fe1..72d1db3 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/NotificationEntryTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/NotificationEntryTest.java
@@ -46,8 +46,8 @@
import android.service.notification.NotificationListenerService.Ranking;
import android.service.notification.SnoozeCriterion;
import android.service.notification.StatusBarNotification;
-import android.testing.AndroidTestingRunner;
+import androidx.test.ext.junit.runners.AndroidJUnit4;
import androidx.test.filters.SmallTest;
import com.android.systemui.SysuiTestCase;
@@ -64,7 +64,7 @@
import java.util.ArrayList;
@SmallTest
-@RunWith(AndroidTestingRunner.class)
+@RunWith(AndroidJUnit4.class)
public class NotificationEntryTest extends SysuiTestCase {
private static final String TEST_PACKAGE_NAME = "test";
private static final int TEST_UID = 0;
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/SectionStyleProviderTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/SectionStyleProviderTest.kt
index ab55a7d..1fd6b04 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/SectionStyleProviderTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/SectionStyleProviderTest.kt
@@ -20,7 +20,7 @@
import android.platform.test.annotations.EnableFlags
import android.platform.test.flag.junit.SetFlagsRule
import android.service.notification.StatusBarNotification
-import android.testing.AndroidTestingRunner
+import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.statusbar.notification.collection.listbuilder.NotifSection
@@ -47,7 +47,7 @@
import org.mockito.Mockito.`when` as whenever
@SmallTest
-@RunWith(AndroidTestingRunner::class)
+@RunWith(AndroidJUnit4::class)
class SectionStyleProviderTest : SysuiTestCase() {
@Rule @JvmField public val setFlagsRule = SetFlagsRule()
@@ -118,4 +118,4 @@
override fun getSection(): NotifSection? = NotifSection(inputSectioner, 1)
}
}
-}
\ No newline at end of file
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/TargetSdkResolverTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/TargetSdkResolverTest.kt
index 4708350..2ad3c9e 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/TargetSdkResolverTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/TargetSdkResolverTest.kt
@@ -25,7 +25,7 @@
import android.os.UserHandle
import android.service.notification.NotificationListenerService.Ranking
import android.service.notification.StatusBarNotification
-import android.testing.AndroidTestingRunner
+import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.statusbar.notification.collection.notifcollection.CommonNotifCollection
@@ -48,7 +48,7 @@
private const val USER_ID = -1
@SmallTest
-@RunWith(AndroidTestingRunner::class)
+@RunWith(AndroidJUnit4::class)
class TargetSdkResolverTest : SysuiTestCase() {
private val packageManager: PackageManager = mock()
private val applicationInfo = ApplicationInfo().apply { targetSdkVersion = SDK_VERSION }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coalescer/GroupCoalescerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coalescer/GroupCoalescerTest.java
index b1180ae..f029a2c 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coalescer/GroupCoalescerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coalescer/GroupCoalescerTest.java
@@ -30,8 +30,8 @@
import android.service.notification.NotificationListenerService.Ranking;
import android.service.notification.NotificationListenerService.RankingMap;
import android.service.notification.StatusBarNotification;
-import android.testing.AndroidTestingRunner;
+import androidx.test.ext.junit.runners.AndroidJUnit4;
import androidx.test.filters.SmallTest;
import com.android.systemui.SysuiTestCase;
@@ -57,7 +57,7 @@
import java.util.Collections;
@SmallTest
-@RunWith(AndroidTestingRunner.class)
+@RunWith(AndroidJUnit4.class)
public class GroupCoalescerTest extends SysuiTestCase {
private GroupCoalescer mCoalescer;
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/ColorizedFgsCoordinatorTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/ColorizedFgsCoordinatorTest.java
index f2207af..1f29255 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/ColorizedFgsCoordinatorTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/ColorizedFgsCoordinatorTest.java
@@ -30,9 +30,9 @@
import android.content.Intent;
import android.graphics.Color;
import android.os.UserHandle;
-import android.testing.AndroidTestingRunner;
import android.testing.TestableLooper;
+import androidx.test.ext.junit.runners.AndroidJUnit4;
import androidx.test.filters.SmallTest;
import com.android.systemui.SysuiTestCase;
@@ -47,7 +47,7 @@
import org.mockito.MockitoAnnotations;
@SmallTest
-@RunWith(AndroidTestingRunner.class)
+@RunWith(AndroidJUnit4.class)
@TestableLooper.RunWithLooper
public class ColorizedFgsCoordinatorTest extends SysuiTestCase {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/DataStoreCoordinatorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/DataStoreCoordinatorTest.kt
index 59fc591..e72109d 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/DataStoreCoordinatorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/DataStoreCoordinatorTest.kt
@@ -15,8 +15,8 @@
*/
package com.android.systemui.statusbar.notification.collection.coordinator
-import android.testing.AndroidTestingRunner
import android.testing.TestableLooper.RunWithLooper
+import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.statusbar.notification.collection.GroupEntryBuilder
@@ -39,7 +39,7 @@
import org.mockito.MockitoAnnotations.initMocks
@SmallTest
-@RunWith(AndroidTestingRunner::class)
+@RunWith(AndroidJUnit4::class)
@RunWithLooper
class DataStoreCoordinatorTest : SysuiTestCase() {
private lateinit var coordinator: DataStoreCoordinator
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/DismissibilityCoordinatorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/DismissibilityCoordinatorTest.kt
index f91e5a8..543f0c7 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/DismissibilityCoordinatorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/DismissibilityCoordinatorTest.kt
@@ -17,7 +17,7 @@
package com.android.systemui.statusbar.notification.collection.coordinator
import android.app.Notification
-import android.testing.AndroidTestingRunner
+import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.dump.DumpManager
@@ -38,7 +38,7 @@
import org.mockito.Mockito.`when` as whenever
@SmallTest
-@RunWith(AndroidTestingRunner::class)
+@RunWith(AndroidJUnit4::class)
class DismissibilityCoordinatorTest : SysuiTestCase() {
private lateinit var coordinator: DismissibilityCoordinator
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/DreamCoordinatorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/DreamCoordinatorTest.kt
index a544cad..4d5ea92 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/DreamCoordinatorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/DreamCoordinatorTest.kt
@@ -17,7 +17,7 @@
package com.android.systemui.statusbar.notification.collection.coordinator
-import android.testing.AndroidTestingRunner
+import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.keyguard.data.repository.FakeKeyguardRepository
@@ -49,7 +49,7 @@
import org.mockito.MockitoAnnotations
@SmallTest
-@RunWith(AndroidTestingRunner::class)
+@RunWith(AndroidJUnit4::class)
class DreamCoordinatorTest : SysuiTestCase() {
@Mock private lateinit var statusBarStateController: SysuiStatusBarStateController
@Mock private lateinit var notifPipeline: NotifPipeline
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/GroupCountCoordinatorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/GroupCountCoordinatorTest.kt
index 929c3d4..7b688d4 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/GroupCountCoordinatorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/GroupCountCoordinatorTest.kt
@@ -15,8 +15,8 @@
*/
package com.android.systemui.statusbar.notification.collection.coordinator
-import android.testing.AndroidTestingRunner
import android.testing.TestableLooper.RunWithLooper
+import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.statusbar.notification.collection.GroupEntryBuilder
@@ -36,7 +36,7 @@
import org.mockito.MockitoAnnotations.initMocks
@SmallTest
-@RunWith(AndroidTestingRunner::class)
+@RunWith(AndroidJUnit4::class)
@RunWithLooper
class GroupCountCoordinatorTest : SysuiTestCase() {
private lateinit var coordinator: GroupCountCoordinator
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/GroupWhenCoordinatorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/GroupWhenCoordinatorTest.kt
index eac0e29..3f14026 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/GroupWhenCoordinatorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/GroupWhenCoordinatorTest.kt
@@ -16,8 +16,8 @@
package com.android.systemui.statusbar.notification.collection.coordinator
import android.app.Notification
-import android.testing.AndroidTestingRunner
import android.testing.TestableLooper
+import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.statusbar.SbnBuilder
@@ -45,7 +45,7 @@
import org.mockito.MockitoAnnotations.initMocks
@SmallTest
-@RunWith(AndroidTestingRunner::class)
+@RunWith(AndroidJUnit4::class)
@TestableLooper.RunWithLooper
class GroupWhenCoordinatorTest : SysuiTestCase() {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/GutsCoordinatorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/GutsCoordinatorTest.kt
index a652ad6..7fe97d2 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/GutsCoordinatorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/GutsCoordinatorTest.kt
@@ -15,8 +15,8 @@
*/
package com.android.systemui.statusbar.notification.collection.coordinator
-import android.testing.AndroidTestingRunner
import android.testing.TestableLooper.RunWithLooper
+import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.dump.DumpManager
@@ -42,7 +42,7 @@
import org.mockito.MockitoAnnotations.initMocks
@SmallTest
-@RunWith(AndroidTestingRunner::class)
+@RunWith(AndroidJUnit4::class)
@RunWithLooper
class GutsCoordinatorTest : SysuiTestCase() {
private lateinit var coordinator: GutsCoordinator
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/HeadsUpCoordinatorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/HeadsUpCoordinatorTest.kt
index cd75e08..8e9323f 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/HeadsUpCoordinatorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/HeadsUpCoordinatorTest.kt
@@ -17,8 +17,8 @@
import android.app.Notification.GROUP_ALERT_ALL
import android.app.Notification.GROUP_ALERT_SUMMARY
-import android.testing.AndroidTestingRunner
import android.testing.TestableLooper.RunWithLooper
+import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.log.logcatLogBuffer
@@ -70,7 +70,7 @@
import org.mockito.MockitoAnnotations
@SmallTest
-@RunWith(AndroidTestingRunner::class)
+@RunWith(AndroidJUnit4::class)
@RunWithLooper
class HeadsUpCoordinatorTest : SysuiTestCase() {
private lateinit var coordinator: HeadsUpCoordinator
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/HideNotifsForOtherUsersCoordinatorTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/HideNotifsForOtherUsersCoordinatorTest.java
index 27542a4..5dcad4b 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/HideNotifsForOtherUsersCoordinatorTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/HideNotifsForOtherUsersCoordinatorTest.java
@@ -24,9 +24,9 @@
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
-import android.testing.AndroidTestingRunner;
import android.util.SparseArray;
+import androidx.test.ext.junit.runners.AndroidJUnit4;
import androidx.test.filters.SmallTest;
import com.android.systemui.SysuiTestCase;
@@ -47,7 +47,7 @@
import org.mockito.MockitoAnnotations;
@SmallTest
-@RunWith(AndroidTestingRunner.class)
+@RunWith(AndroidJUnit4.class)
public class HideNotifsForOtherUsersCoordinatorTest extends SysuiTestCase {
@Mock private NotificationLockscreenUserManager mLockscreenUserManager;
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/KeyguardCoordinatorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/KeyguardCoordinatorTest.kt
index 5ff7353..25533d8 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/KeyguardCoordinatorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/KeyguardCoordinatorTest.kt
@@ -20,7 +20,7 @@
import android.app.Notification
import android.os.UserHandle
import android.provider.Settings
-import android.testing.AndroidTestingRunner
+import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.dump.DumpManager
@@ -67,7 +67,7 @@
import org.mockito.Mockito.`when` as whenever
@SmallTest
-@RunWith(AndroidTestingRunner::class)
+@RunWith(AndroidJUnit4::class)
class KeyguardCoordinatorTest : SysuiTestCase() {
private val headsUpManager: HeadsUpManager = mock()
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/MediaCoordinatorTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/MediaCoordinatorTest.java
index e90a3ac8..07c29a0 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/MediaCoordinatorTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/MediaCoordinatorTest.java
@@ -33,8 +33,8 @@
import android.platform.test.annotations.DisableFlags;
import android.platform.test.annotations.EnableFlags;
import android.service.notification.NotificationListenerService;
-import android.testing.AndroidTestingRunner;
+import androidx.test.ext.junit.runners.AndroidJUnit4;
import androidx.test.filters.SmallTest;
import com.android.internal.statusbar.IStatusBarService;
@@ -58,7 +58,7 @@
import org.mockito.MockitoAnnotations;
@SmallTest
-@RunWith(AndroidTestingRunner.class)
+@RunWith(AndroidJUnit4.class)
public final class MediaCoordinatorTest extends SysuiTestCase {
private MediaSession mMediaSession;
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/NotificationStatsLoggerCoordinatorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/NotificationStatsLoggerCoordinatorTest.kt
index c29ff41..501bca2 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/NotificationStatsLoggerCoordinatorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/NotificationStatsLoggerCoordinatorTest.kt
@@ -18,7 +18,7 @@
import android.platform.test.annotations.EnableFlags
import android.service.notification.NotificationListenerService.REASON_CANCEL
-import android.testing.AndroidTestingRunner
+import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.statusbar.notification.collection.NotifPipeline
@@ -36,7 +36,7 @@
import org.mockito.Mockito.verify
@SmallTest
-@RunWith(AndroidTestingRunner::class)
+@RunWith(AndroidJUnit4::class)
@EnableFlags(NotificationsLiveDataStoreRefactor.FLAG_NAME)
class NotificationStatsLoggerCoordinatorTest : SysuiTestCase() {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/PreparationCoordinatorTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/PreparationCoordinatorTest.java
index cceaaea..8012768 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/PreparationCoordinatorTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/PreparationCoordinatorTest.java
@@ -39,10 +39,10 @@
import android.database.ContentObserver;
import android.os.Handler;
import android.os.RemoteException;
-import android.testing.AndroidTestingRunner;
import android.testing.TestableLooper;
import androidx.annotation.NonNull;
+import androidx.test.ext.junit.runners.AndroidJUnit4;
import androidx.test.filters.SmallTest;
import com.android.internal.statusbar.IStatusBarService;
@@ -88,7 +88,7 @@
import java.util.Map;
@SmallTest
-@RunWith(AndroidTestingRunner.class)
+@RunWith(AndroidJUnit4.class)
@TestableLooper.RunWithLooper
public class PreparationCoordinatorTest extends SysuiTestCase {
private NotifCollectionListener mCollectionListener;
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/RankingCoordinatorTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/RankingCoordinatorTest.java
index 3d1253e..c05b131 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/RankingCoordinatorTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/RankingCoordinatorTest.java
@@ -35,9 +35,9 @@
import android.app.Notification;
import android.app.NotificationChannel;
import android.app.NotificationManager;
-import android.testing.AndroidTestingRunner;
import androidx.annotation.Nullable;
+import androidx.test.ext.junit.runners.AndroidJUnit4;
import androidx.test.filters.SmallTest;
import com.android.systemui.SysuiTestCase;
@@ -67,7 +67,7 @@
import java.util.Arrays;
@SmallTest
-@RunWith(AndroidTestingRunner.class)
+@RunWith(AndroidJUnit4.class)
public class RankingCoordinatorTest extends SysuiTestCase {
@Mock private StatusBarStateController mStatusBarStateController;
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/RemoteInputCoordinatorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/RemoteInputCoordinatorTest.kt
index d3df48e9..deb3fc1 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/RemoteInputCoordinatorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/RemoteInputCoordinatorTest.kt
@@ -23,8 +23,8 @@
import android.platform.test.annotations.DisableFlags
import android.platform.test.annotations.EnableFlags
import android.service.notification.StatusBarNotification
-import android.testing.AndroidTestingRunner
import android.testing.TestableLooper.RunWithLooper
+import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.dump.DumpManager
@@ -54,7 +54,7 @@
import org.mockito.MockitoAnnotations.initMocks
@SmallTest
-@RunWith(AndroidTestingRunner::class)
+@RunWith(AndroidJUnit4::class)
@RunWithLooper
class RemoteInputCoordinatorTest : SysuiTestCase() {
private lateinit var coordinator: RemoteInputCoordinator
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/RowAlertTimeCoordinatorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/RowAlertTimeCoordinatorTest.kt
index 7daadb0..1b7ec53 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/RowAlertTimeCoordinatorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/RowAlertTimeCoordinatorTest.kt
@@ -15,8 +15,8 @@
*/
package com.android.systemui.statusbar.notification.collection.coordinator
-import android.testing.AndroidTestingRunner
import android.testing.TestableLooper.RunWithLooper
+import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.statusbar.notification.collection.GroupEntryBuilder
@@ -37,7 +37,7 @@
import org.mockito.MockitoAnnotations.initMocks
@SmallTest
-@RunWith(AndroidTestingRunner::class)
+@RunWith(AndroidJUnit4::class)
@RunWithLooper
class RowAlertTimeCoordinatorTest : SysuiTestCase() {
private lateinit var coordinator: RowAlertTimeCoordinator
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/RowAppearanceCoordinatorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/RowAppearanceCoordinatorTest.kt
index a66f8ce..5b231e2 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/RowAppearanceCoordinatorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/RowAppearanceCoordinatorTest.kt
@@ -15,8 +15,8 @@
*/
package com.android.systemui.statusbar.notification.collection.coordinator
-import android.testing.AndroidTestingRunner
import android.testing.TestableLooper.RunWithLooper
+import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.statusbar.notification.AssistantFeedbackController
@@ -41,7 +41,7 @@
import org.mockito.MockitoAnnotations.initMocks
@SmallTest
-@RunWith(AndroidTestingRunner::class)
+@RunWith(AndroidJUnit4::class)
@RunWithLooper
class RowAppearanceCoordinatorTest : SysuiTestCase() {
private lateinit var coordinator: RowAppearanceCoordinator
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/ShadeEventCoordinatorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/ShadeEventCoordinatorTest.kt
index 56f16f3..ccf7cdd 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/ShadeEventCoordinatorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/ShadeEventCoordinatorTest.kt
@@ -17,8 +17,8 @@
import android.service.notification.NotificationListenerService.REASON_APP_CANCEL
import android.service.notification.NotificationListenerService.REASON_CANCEL
-import android.testing.AndroidTestingRunner
import android.testing.TestableLooper.RunWithLooper
+import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.log.logcatLogBuffer
@@ -40,7 +40,7 @@
import org.mockito.Mockito.`when` as whenever
@SmallTest
-@RunWith(AndroidTestingRunner::class)
+@RunWith(AndroidJUnit4::class)
@RunWithLooper
class ShadeEventCoordinatorTest : SysuiTestCase() {
private lateinit var coordinator: ShadeEventCoordinator
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/StackCoordinatorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/StackCoordinatorTest.kt
index ea4f692..c7513de 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/StackCoordinatorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/StackCoordinatorTest.kt
@@ -17,8 +17,8 @@
import android.platform.test.annotations.DisableFlags
import android.platform.test.annotations.EnableFlags
-import android.testing.AndroidTestingRunner
import android.testing.TestableLooper.RunWithLooper
+import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.server.notification.Flags.FLAG_SCREENSHARE_NOTIFICATION_HIDING
import com.android.systemui.Flags.FLAG_SCREENSHARE_NOTIFICATION_HIDING_BUG_FIX
@@ -51,7 +51,7 @@
import org.mockito.Mockito.`when` as whenever
@SmallTest
-@RunWith(AndroidTestingRunner::class)
+@RunWith(AndroidJUnit4::class)
@RunWithLooper
class StackCoordinatorTest : SysuiTestCase() {
private lateinit var coordinator: StackCoordinator
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/ViewConfigCoordinatorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/ViewConfigCoordinatorTest.kt
index b1d2ea21..c8fbe61 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/ViewConfigCoordinatorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/ViewConfigCoordinatorTest.kt
@@ -15,8 +15,8 @@
*/
package com.android.systemui.statusbar.notification.collection.coordinator
-import android.testing.AndroidTestingRunner
import android.testing.TestableLooper.RunWithLooper
+import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.keyguard.KeyguardUpdateMonitor
import com.android.keyguard.KeyguardUpdateMonitorCallback
@@ -40,7 +40,7 @@
import org.mockito.Mockito.`when` as whenever
@SmallTest
-@RunWith(AndroidTestingRunner::class)
+@RunWith(AndroidJUnit4::class)
@RunWithLooper
class ViewConfigCoordinatorTest : SysuiTestCase() {
private lateinit var coordinator: ViewConfigCoordinator
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/inflation/NotifUiAdjustmentProviderTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/inflation/NotifUiAdjustmentProviderTest.kt
index 8e6cecc..7943872 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/inflation/NotifUiAdjustmentProviderTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/inflation/NotifUiAdjustmentProviderTest.kt
@@ -20,8 +20,8 @@
import android.platform.test.annotations.DisableFlags
import android.platform.test.annotations.EnableFlags
import android.provider.Settings.Secure.SHOW_NOTIFICATION_SNOOZE
-import android.testing.AndroidTestingRunner
import android.testing.TestableLooper.RunWithLooper
+import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.server.notification.Flags.FLAG_SCREENSHARE_NOTIFICATION_HIDING
import com.android.systemui.SysuiTestCase
@@ -54,7 +54,7 @@
import org.mockito.Mockito.`when` as whenever
@SmallTest
-@RunWith(AndroidTestingRunner::class)
+@RunWith(AndroidJUnit4::class)
@RunWithLooper
class NotifUiAdjustmentProviderTest : SysuiTestCase() {
private val lockscreenUserManager: NotificationLockscreenUserManager = mock()
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/listbuilder/SemiStableSortTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/listbuilder/SemiStableSortTest.kt
index 1cdd023..d205770 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/listbuilder/SemiStableSortTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/listbuilder/SemiStableSortTest.kt
@@ -15,9 +15,9 @@
*/
package com.android.systemui.statusbar.notification.collection.listbuilder
-import android.testing.AndroidTestingRunner
import android.testing.TestableLooper.RunWithLooper
import android.util.Log
+import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
import org.junit.Assert.assertFalse
@@ -27,7 +27,7 @@
import org.junit.runner.RunWith
@SmallTest
-@RunWith(AndroidTestingRunner::class)
+@RunWith(AndroidJUnit4::class)
@RunWithLooper
class SemiStableSortTest : SysuiTestCase() {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/listbuilder/ShadeListBuilderHelperTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/listbuilder/ShadeListBuilderHelperTest.kt
index 2036954..49f836f 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/listbuilder/ShadeListBuilderHelperTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/listbuilder/ShadeListBuilderHelperTest.kt
@@ -15,8 +15,8 @@
*/
package com.android.systemui.statusbar.notification.collection.listbuilder
-import android.testing.AndroidTestingRunner
import android.testing.TestableLooper.RunWithLooper
+import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.statusbar.notification.collection.listbuilder.ShadeListBuilderHelper.getContiguousSubLists
@@ -25,7 +25,7 @@
import org.junit.runner.RunWith
@SmallTest
-@RunWith(AndroidTestingRunner::class)
+@RunWith(AndroidJUnit4::class)
@RunWithLooper
class ShadeListBuilderHelperTest : SysuiTestCase() {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/notifcollection/NotifCollectionInconsistencyTrackerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/notifcollection/NotifCollectionInconsistencyTrackerTest.kt
index 22f6bdc..341a51e 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/notifcollection/NotifCollectionInconsistencyTrackerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/notifcollection/NotifCollectionInconsistencyTrackerTest.kt
@@ -17,8 +17,8 @@
package com.android.systemui.statusbar.notification.collection.notifcollection
import android.service.notification.NotificationListenerService.RankingMap
-import android.testing.AndroidTestingRunner
import android.testing.TestableLooper.RunWithLooper
+import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.log.logcatLogBuffer
@@ -34,7 +34,7 @@
import org.mockito.Mockito.verifyNoMoreInteractions
@SmallTest
-@RunWith(AndroidTestingRunner::class)
+@RunWith(AndroidJUnit4::class)
@RunWithLooper
class NotifCollectionInconsistencyTrackerTest : SysuiTestCase() {
private val logger = spy(NotifCollectionLogger(logcatLogBuffer()))
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/notifcollection/SelfTrackingLifetimeExtenderTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/notifcollection/SelfTrackingLifetimeExtenderTest.kt
index a09f3a3..99e55a8 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/notifcollection/SelfTrackingLifetimeExtenderTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/notifcollection/SelfTrackingLifetimeExtenderTest.kt
@@ -16,8 +16,8 @@
package com.android.systemui.statusbar.notification.collection.notifcollection
import android.os.Handler
-import android.testing.AndroidTestingRunner
import android.testing.TestableLooper.RunWithLooper
+import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.statusbar.notification.collection.NotificationEntry
@@ -41,7 +41,7 @@
import java.util.function.Predicate
@SmallTest
-@RunWith(AndroidTestingRunner::class)
+@RunWith(AndroidJUnit4::class)
@RunWithLooper
class SelfTrackingLifetimeExtenderTest : SysuiTestCase() {
private lateinit var extender: TestableSelfTrackingLifetimeExtender
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/provider/VisualStabilityProviderTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/provider/VisualStabilityProviderTest.kt
index b56f8e9..586b947 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/provider/VisualStabilityProviderTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/provider/VisualStabilityProviderTest.kt
@@ -15,7 +15,7 @@
*/
package com.android.systemui.statusbar.notification.collection.provider
-import android.testing.AndroidTestingRunner
+import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.util.mockito.mock
@@ -29,7 +29,7 @@
import org.mockito.Mockito.verifyNoMoreInteractions
@SmallTest
-@RunWith(AndroidTestingRunner::class)
+@RunWith(AndroidJUnit4::class)
class VisualStabilityProviderTest : SysuiTestCase() {
private val visualStabilityProvider = VisualStabilityProvider()
private val listener: OnReorderingAllowedListener = mock()
@@ -148,4 +148,4 @@
visualStabilityProvider.isReorderingAllowed = true
verify(selfAddingListener, times(2)).onReorderingAllowed()
}
-}
\ No newline at end of file
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/render/ShadeViewDifferTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/render/ShadeViewDifferTest.kt
index eeabc74..9d3e2e8 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/render/ShadeViewDifferTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/render/ShadeViewDifferTest.kt
@@ -16,10 +16,10 @@
package com.android.systemui.statusbar.notification.collection.render
import android.content.Context
-import android.testing.AndroidTestingRunner
import android.view.View
import android.view.ViewGroup
import android.widget.FrameLayout
+import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.log.logcatLogBuffer
@@ -34,7 +34,7 @@
import org.mockito.Mockito.verify
@SmallTest
-@RunWith(AndroidTestingRunner::class)
+@RunWith(AndroidJUnit4::class)
class ShadeViewDifferTest : SysuiTestCase() {
private lateinit var differ: ShadeViewDiffer
private val rootController = FakeController(mContext, "RootController")
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/domain/interactor/SeenNotificationsInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/domain/interactor/SeenNotificationsInteractorTest.kt
index 2a3c1a5..3908529 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/domain/interactor/SeenNotificationsInteractorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/domain/interactor/SeenNotificationsInteractorTest.kt
@@ -15,7 +15,7 @@
package com.android.systemui.statusbar.notification.domain.interactor
-import android.testing.AndroidTestingRunner
+import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.coroutines.collectLastValue
@@ -25,7 +25,7 @@
import org.junit.Test
import org.junit.runner.RunWith
-@RunWith(AndroidTestingRunner::class)
+@RunWith(AndroidJUnit4::class)
@SmallTest
class SeenNotificationsInteractorTest : SysuiTestCase() {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/icon/IconManagerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/icon/IconManagerTest.kt
index 4ac9dc2..bfa816e 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/icon/IconManagerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/icon/IconManagerTest.kt
@@ -30,8 +30,8 @@
import android.os.Bundle
import android.os.SystemClock
import android.os.UserHandle
-import android.testing.AndroidTestingRunner
import androidx.test.InstrumentationRegistry
+import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.controls.controller.AuxiliaryPersistenceWrapperTest.Companion.any
@@ -53,7 +53,7 @@
import org.mockito.MockitoAnnotations
@SmallTest
-@RunWith(AndroidTestingRunner::class)
+@RunWith(AndroidJUnit4::class)
class IconManagerTest : SysuiTestCase() {
companion object {
private const val TEST_PACKAGE_NAME = "test"
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/icon/domain/interactor/NotificationIconsInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/icon/domain/interactor/NotificationIconsInteractorTest.kt
index b410b33..c9f2add 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/icon/domain/interactor/NotificationIconsInteractorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/icon/domain/interactor/NotificationIconsInteractorTest.kt
@@ -15,7 +15,7 @@
package com.android.systemui.statusbar.notification.icon.domain.interactor
-import android.testing.AndroidTestingRunner
+import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.systemui.SysUITestComponent
import com.android.systemui.SysUITestModule
@@ -52,7 +52,7 @@
import org.junit.runner.RunWith
@SmallTest
-@RunWith(AndroidTestingRunner::class)
+@RunWith(AndroidJUnit4::class)
class NotificationIconsInteractorTest : SysuiTestCase() {
private val bubbles: Bubbles = mock()
@@ -151,7 +151,7 @@
}
@SmallTest
-@RunWith(AndroidTestingRunner::class)
+@RunWith(AndroidJUnit4::class)
class AlwaysOnDisplayNotificationIconsInteractorTest : SysuiTestCase() {
private val bubbles: Bubbles = mock()
@@ -256,7 +256,7 @@
}
@SmallTest
-@RunWith(AndroidTestingRunner::class)
+@RunWith(AndroidJUnit4::class)
class StatusBarNotificationIconsInteractorTest : SysuiTestCase() {
private val bubbles: Bubbles = mock()
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/interruption/HeadsUpViewBinderTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/interruption/HeadsUpViewBinderTest.java
index 60eea9b..af2789b 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/interruption/HeadsUpViewBinderTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/interruption/HeadsUpViewBinderTest.java
@@ -26,9 +26,9 @@
import static org.mockito.Mockito.verifyNoMoreInteractions;
import static org.mockito.Mockito.when;
-import android.testing.AndroidTestingRunner;
import androidx.core.os.CancellationSignal;
+import androidx.test.ext.junit.runners.AndroidJUnit4;
import androidx.test.filters.SmallTest;
import com.android.internal.util.NotificationMessagingUtil;
@@ -47,7 +47,7 @@
import java.util.concurrent.atomic.AtomicReference;
-@RunWith(AndroidTestingRunner.class)
+@RunWith(AndroidJUnit4.class)
@SmallTest
public class HeadsUpViewBinderTest extends SysuiTestCase {
private HeadsUpViewBinder mViewBinder;
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/interruption/KeyguardNotificationVisibilityProviderTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/interruption/KeyguardNotificationVisibilityProviderTest.java
index 8662048..19214fb 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/interruption/KeyguardNotificationVisibilityProviderTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/interruption/KeyguardNotificationVisibilityProviderTest.java
@@ -42,9 +42,9 @@
import android.os.Handler;
import android.os.UserHandle;
import android.provider.Settings;
-import android.testing.AndroidTestingRunner;
import android.testing.TestableLooper;
+import androidx.test.ext.junit.runners.AndroidJUnit4;
import androidx.test.filters.SmallTest;
import com.android.keyguard.KeyguardUpdateMonitor;
@@ -86,7 +86,7 @@
import java.util.function.Consumer;
@SmallTest
-@RunWith(AndroidTestingRunner.class)
+@RunWith(AndroidJUnit4.class)
@TestableLooper.RunWithLooper
public class KeyguardNotificationVisibilityProviderTest extends SysuiTestCase {
private static final int NOTIF_USER_ID = 0;
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/interruption/NotificationInterruptStateProviderImplTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/interruption/NotificationInterruptStateProviderImplTest.java
index 7ade053..3e8461a 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/interruption/NotificationInterruptStateProviderImplTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/interruption/NotificationInterruptStateProviderImplTest.java
@@ -60,8 +60,8 @@
import android.os.PowerManager;
import android.os.RemoteException;
import android.platform.test.annotations.DisableFlags;
-import android.testing.AndroidTestingRunner;
+import androidx.test.ext.junit.runners.AndroidJUnit4;
import androidx.test.filters.SmallTest;
import com.android.internal.logging.testing.UiEventLoggerFake;
@@ -96,7 +96,7 @@
* Tests for the interruption state provider which understands whether the system & notification
* is in a state allowing a particular notification to hun, pulse, or bubble.
*/
-@RunWith(AndroidTestingRunner.class)
+@RunWith(AndroidJUnit4.class)
@SmallTest
public class NotificationInterruptStateProviderImplTest extends SysuiTestCase {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/interruption/NotificationInterruptStateProviderWrapperTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/interruption/NotificationInterruptStateProviderWrapperTest.kt
index 7ed3312..a6177e8 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/interruption/NotificationInterruptStateProviderWrapperTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/interruption/NotificationInterruptStateProviderWrapperTest.kt
@@ -17,7 +17,7 @@
package com.android.systemui.statusbar.notification.interruption
import android.platform.test.annotations.DisableFlags
-import android.testing.AndroidTestingRunner
+import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.systemui.statusbar.notification.collection.NotificationEntryBuilder
import com.android.systemui.statusbar.notification.interruption.NotificationInterruptStateProvider.FullScreenIntentDecision
@@ -34,7 +34,7 @@
import org.junit.runner.RunWith
@SmallTest
-@RunWith(AndroidTestingRunner::class)
+@RunWith(AndroidJUnit4::class)
@DisableFlags(VisualInterruptionRefactor.FLAG_NAME)
class NotificationInterruptStateProviderWrapperTest : VisualInterruptionDecisionProviderTestBase() {
override val provider by lazy {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/interruption/VisualInterruptionDecisionProviderImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/interruption/VisualInterruptionDecisionProviderImplTest.kt
index edab9d9..eeb51a6 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/interruption/VisualInterruptionDecisionProviderImplTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/interruption/VisualInterruptionDecisionProviderImplTest.kt
@@ -16,11 +16,13 @@
package com.android.systemui.statusbar.notification.interruption
+import android.Manifest.permission
import android.app.Notification.CATEGORY_EVENT
import android.app.Notification.CATEGORY_REMINDER
import android.app.NotificationManager
+import android.content.pm.PackageManager.PERMISSION_GRANTED
import android.platform.test.annotations.EnableFlags
-import android.testing.AndroidTestingRunner
+import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.systemui.statusbar.notification.collection.NotificationEntry
import com.android.systemui.statusbar.notification.interruption.VisualInterruptionType.BUBBLE
@@ -28,9 +30,11 @@
import com.android.systemui.statusbar.notification.interruption.VisualInterruptionType.PULSE
import org.junit.Test
import org.junit.runner.RunWith
+import org.mockito.Mockito.anyString
+import org.mockito.Mockito.`when`
@SmallTest
-@RunWith(AndroidTestingRunner::class)
+@RunWith(AndroidJUnit4::class)
@EnableFlags(VisualInterruptionRefactor.FLAG_NAME)
class VisualInterruptionDecisionProviderImplTest : VisualInterruptionDecisionProviderTestBase() {
override val provider by lazy {
@@ -51,7 +55,8 @@
uiEventLogger,
userTracker,
avalancheProvider,
- systemSettings
+ systemSettings,
+ packageManager
)
}
@@ -83,14 +88,18 @@
fun testAvalancheFilter_duringAvalanche_allowConversationFromAfterEvent() {
avalancheProvider.startTime = whenAgo(10)
- withFilter(AvalancheSuppressor(avalancheProvider, systemClock, systemSettings)) {
+ withFilter(
+ AvalancheSuppressor(avalancheProvider, systemClock, systemSettings, packageManager)
+ ) {
ensurePeekState()
- assertShouldHeadsUp(buildEntry {
- importance = NotificationManager.IMPORTANCE_HIGH
- isConversation = true
- isImportantConversation = false
- whenMs = whenAgo(5)
- })
+ assertShouldHeadsUp(
+ buildEntry {
+ importance = NotificationManager.IMPORTANCE_HIGH
+ isConversation = true
+ isImportantConversation = false
+ whenMs = whenAgo(5)
+ }
+ )
}
}
@@ -98,14 +107,18 @@
fun testAvalancheFilter_duringAvalanche_suppressConversationFromBeforeEvent() {
avalancheProvider.startTime = whenAgo(10)
- withFilter(AvalancheSuppressor(avalancheProvider, systemClock, systemSettings)) {
+ withFilter(
+ AvalancheSuppressor(avalancheProvider, systemClock, systemSettings, packageManager)
+ ) {
ensurePeekState()
- assertShouldNotHeadsUp(buildEntry {
- importance = NotificationManager.IMPORTANCE_DEFAULT
- isConversation = true
- isImportantConversation = false
- whenMs = whenAgo(15)
- })
+ assertShouldNotHeadsUp(
+ buildEntry {
+ importance = NotificationManager.IMPORTANCE_DEFAULT
+ isConversation = true
+ isImportantConversation = false
+ whenMs = whenAgo(15)
+ }
+ )
}
}
@@ -113,12 +126,16 @@
fun testAvalancheFilter_duringAvalanche_allowHighPriorityConversation() {
avalancheProvider.startTime = whenAgo(10)
- withFilter(AvalancheSuppressor(avalancheProvider, systemClock, systemSettings)) {
+ withFilter(
+ AvalancheSuppressor(avalancheProvider, systemClock, systemSettings, packageManager)
+ ) {
ensurePeekState()
- assertShouldHeadsUp(buildEntry {
- importance = NotificationManager.IMPORTANCE_HIGH
- isImportantConversation = true
- })
+ assertShouldHeadsUp(
+ buildEntry {
+ importance = NotificationManager.IMPORTANCE_HIGH
+ isImportantConversation = true
+ }
+ )
}
}
@@ -126,12 +143,16 @@
fun testAvalancheFilter_duringAvalanche_allowCall() {
avalancheProvider.startTime = whenAgo(10)
- withFilter(AvalancheSuppressor(avalancheProvider, systemClock, systemSettings)) {
+ withFilter(
+ AvalancheSuppressor(avalancheProvider, systemClock, systemSettings, packageManager)
+ ) {
ensurePeekState()
- assertShouldHeadsUp(buildEntry {
- importance = NotificationManager.IMPORTANCE_HIGH
- isCall = true
- })
+ assertShouldHeadsUp(
+ buildEntry {
+ importance = NotificationManager.IMPORTANCE_HIGH
+ isCall = true
+ }
+ )
}
}
@@ -139,12 +160,16 @@
fun testAvalancheFilter_duringAvalanche_allowCategoryReminder() {
avalancheProvider.startTime = whenAgo(10)
- withFilter(AvalancheSuppressor(avalancheProvider, systemClock, systemSettings)) {
+ withFilter(
+ AvalancheSuppressor(avalancheProvider, systemClock, systemSettings, packageManager)
+ ) {
ensurePeekState()
- assertShouldHeadsUp(buildEntry {
- importance = NotificationManager.IMPORTANCE_HIGH
- category = CATEGORY_REMINDER
- })
+ assertShouldHeadsUp(
+ buildEntry {
+ importance = NotificationManager.IMPORTANCE_HIGH
+ category = CATEGORY_REMINDER
+ }
+ )
}
}
@@ -152,12 +177,16 @@
fun testAvalancheFilter_duringAvalanche_allowCategoryEvent() {
avalancheProvider.startTime = whenAgo(10)
- withFilter(AvalancheSuppressor(avalancheProvider, systemClock, systemSettings)) {
+ withFilter(
+ AvalancheSuppressor(avalancheProvider, systemClock, systemSettings, packageManager)
+ ) {
ensurePeekState()
- assertShouldHeadsUp(buildEntry {
- importance = NotificationManager.IMPORTANCE_HIGH
- category = CATEGORY_EVENT
- })
+ assertShouldHeadsUp(
+ buildEntry {
+ importance = NotificationManager.IMPORTANCE_HIGH
+ category = CATEGORY_EVENT
+ }
+ )
}
}
@@ -165,7 +194,9 @@
fun testAvalancheFilter_duringAvalanche_allowFsi() {
avalancheProvider.startTime = whenAgo(10)
- withFilter(AvalancheSuppressor(avalancheProvider, systemClock, systemSettings)) {
+ withFilter(
+ AvalancheSuppressor(avalancheProvider, systemClock, systemSettings, packageManager)
+ ) {
assertFsiNotSuppressed()
}
}
@@ -174,16 +205,44 @@
fun testAvalancheFilter_duringAvalanche_allowColorized() {
avalancheProvider.startTime = whenAgo(10)
- withFilter(AvalancheSuppressor(avalancheProvider, systemClock, systemSettings)) {
+ withFilter(
+ AvalancheSuppressor(avalancheProvider, systemClock, systemSettings, packageManager)
+ ) {
ensurePeekState()
- assertShouldHeadsUp(buildEntry {
- importance = NotificationManager.IMPORTANCE_HIGH
- isColorized = true
- })
+ assertShouldHeadsUp(
+ buildEntry {
+ importance = NotificationManager.IMPORTANCE_HIGH
+ isColorized = true
+ }
+ )
}
}
@Test
+ fun testAvalancheFilter_duringAvalanche_allowEmergency() {
+ avalancheProvider.startTime = whenAgo(10)
+
+ `when`(
+ packageManager.checkPermission(
+ org.mockito.Mockito.eq(permission.RECEIVE_EMERGENCY_BROADCAST),
+ anyString()
+ )
+ ).thenReturn(PERMISSION_GRANTED)
+
+ withFilter(
+ AvalancheSuppressor(avalancheProvider, systemClock, systemSettings, packageManager)
+ ) {
+ ensurePeekState()
+ assertShouldHeadsUp(
+ buildEntry {
+ importance = NotificationManager.IMPORTANCE_HIGH
+ }
+ )
+ }
+ }
+
+
+ @Test
fun testPeekCondition_suppressesOnlyPeek() {
withCondition(TestCondition(types = setOf(PEEK)) { true }) {
assertPeekSuppressed()
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/interruption/VisualInterruptionDecisionProviderTestBase.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/interruption/VisualInterruptionDecisionProviderTestBase.kt
index 3b979a7..71e7dc5 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/interruption/VisualInterruptionDecisionProviderTestBase.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/interruption/VisualInterruptionDecisionProviderTestBase.kt
@@ -42,6 +42,7 @@
import android.app.PendingIntent.FLAG_MUTABLE
import android.content.Context
import android.content.Intent
+import android.content.pm.PackageManager
import android.content.pm.UserInfo
import android.graphics.drawable.Icon
import android.hardware.display.FakeAmbientDisplayConfiguration
@@ -129,6 +130,7 @@
protected val userTracker = FakeUserTracker()
protected val avalancheProvider: AvalancheProvider = mock()
lateinit var systemSettings: SystemSettings
+ protected val packageManager: PackageManager = mock()
protected abstract val provider: VisualInterruptionDecisionProvider
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/interruption/VisualInterruptionDecisionProviderTestUtil.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/interruption/VisualInterruptionDecisionProviderTestUtil.kt
index 60aaa64..61c008b 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/interruption/VisualInterruptionDecisionProviderTestUtil.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/interruption/VisualInterruptionDecisionProviderTestUtil.kt
@@ -15,11 +15,11 @@
*/
package com.android.systemui.statusbar.notification.interruption
+import android.content.pm.PackageManager
import android.hardware.display.AmbientDisplayConfiguration
import android.os.Handler
import android.os.PowerManager
import com.android.internal.logging.UiEventLogger
-import com.android.systemui.broadcast.BroadcastDispatcher
import com.android.systemui.dagger.qualifiers.Main
import com.android.systemui.plugins.statusbar.StatusBarStateController
import com.android.systemui.settings.UserTracker
@@ -53,7 +53,8 @@
uiEventLogger: UiEventLogger,
userTracker: UserTracker,
avalancheProvider: AvalancheProvider,
- systemSettings: SystemSettings
+ systemSettings: SystemSettings,
+ packageManager: PackageManager,
): VisualInterruptionDecisionProvider {
return if (VisualInterruptionRefactor.isEnabled) {
VisualInterruptionDecisionProviderImpl(
@@ -73,7 +74,8 @@
uiEventLogger,
userTracker,
avalancheProvider,
- systemSettings
+ systemSettings,
+ packageManager
)
} else {
NotificationInterruptStateProviderWrapper(
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/logging/ExpansionStateLoggerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/logging/ExpansionStateLoggerTest.java
index 2662c80..5974171 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/logging/ExpansionStateLoggerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/logging/ExpansionStateLoggerTest.java
@@ -22,9 +22,9 @@
import static org.mockito.Mockito.verify;
import android.os.RemoteException;
-import android.testing.AndroidTestingRunner;
import android.testing.TestableLooper;
+import androidx.test.ext.junit.runners.AndroidJUnit4;
import androidx.test.filters.SmallTest;
import com.android.internal.statusbar.IStatusBarService;
@@ -44,7 +44,7 @@
import java.util.Collections;
@SmallTest
-@RunWith(AndroidTestingRunner.class)
+@RunWith(AndroidJUnit4.class)
@TestableLooper.RunWithLooper
public class ExpansionStateLoggerTest extends SysuiTestCase {
private static final String NOTIFICATION_KEY = "notin_key";
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/logging/NotificationLoggerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/logging/NotificationLoggerTest.java
index 1113091..a8929a6 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/logging/NotificationLoggerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/logging/NotificationLoggerTest.java
@@ -35,9 +35,9 @@
import android.os.Looper;
import android.os.UserHandle;
import android.platform.test.annotations.DisableFlags;
-import android.testing.AndroidTestingRunner;
import android.testing.TestableLooper;
+import androidx.test.ext.junit.runners.AndroidJUnit4;
import androidx.test.filters.SmallTest;
import com.android.internal.logging.InstanceId;
@@ -87,7 +87,7 @@
import java.util.concurrent.Executor;
@SmallTest
-@RunWith(AndroidTestingRunner.class)
+@RunWith(AndroidJUnit4.class)
@TestableLooper.RunWithLooper
@DisableFlags(NotificationsLiveDataStoreRefactor.FLAG_NAME)
public class NotificationLoggerTest extends SysuiTestCase {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/logging/NotificationMemoryLoggerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/logging/NotificationMemoryLoggerTest.kt
index 4b0b4b8..3ea7732 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/logging/NotificationMemoryLoggerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/logging/NotificationMemoryLoggerTest.kt
@@ -21,8 +21,8 @@
import android.graphics.Bitmap
import android.graphics.drawable.Icon
import android.stats.sysui.NotificationEnums
-import android.testing.AndroidTestingRunner
import android.util.StatsEvent
+import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.log.assertLogsWtf
@@ -46,7 +46,7 @@
import org.mockito.MockitoAnnotations
@SmallTest
-@RunWith(AndroidTestingRunner::class)
+@RunWith(AndroidJUnit4::class)
class NotificationMemoryLoggerTest : SysuiTestCase() {
@Rule @JvmField val expect = Expect.create()
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/logging/NotificationMemoryMeterTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/logging/NotificationMemoryMeterTest.kt
index 072a497..f10a52a 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/logging/NotificationMemoryMeterTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/logging/NotificationMemoryMeterTest.kt
@@ -24,8 +24,8 @@
import android.graphics.Bitmap
import android.graphics.drawable.Icon
import android.stats.sysui.NotificationEnums
-import android.testing.AndroidTestingRunner
import android.widget.RemoteViews
+import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.statusbar.notification.NotificationUtils
@@ -36,7 +36,7 @@
import org.junit.runner.RunWith
@SmallTest
-@RunWith(AndroidTestingRunner::class)
+@RunWith(AndroidJUnit4::class)
class NotificationMemoryMeterTest : SysuiTestCase() {
@Test
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/logging/NotificationMemoryViewWalkerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/logging/NotificationMemoryViewWalkerTest.kt
index 4bb28ae..d7dde96 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/logging/NotificationMemoryViewWalkerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/logging/NotificationMemoryViewWalkerTest.kt
@@ -3,9 +3,9 @@
import android.app.Notification
import android.graphics.Bitmap
import android.graphics.drawable.Icon
-import android.testing.AndroidTestingRunner
import android.testing.TestableLooper
import android.widget.RemoteViews
+import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.statusbar.notification.row.NotificationRowContentBinder
@@ -17,7 +17,7 @@
import org.junit.runner.RunWith
@SmallTest
-@RunWith(AndroidTestingRunner::class)
+@RunWith(AndroidJUnit4::class)
@TestableLooper.RunWithLooper
class NotificationMemoryViewWalkerTest : SysuiTestCase() {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/ActivatableNotificationViewTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/ActivatableNotificationViewTest.kt
index 9b9cb82..9f98fd4 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/ActivatableNotificationViewTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/ActivatableNotificationViewTest.kt
@@ -17,9 +17,9 @@
import android.annotation.ColorInt
import android.graphics.Color
-import android.testing.AndroidTestingRunner
import android.testing.TestableLooper.RunWithLooper
import android.view.View
+import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.settingslib.Utils
import com.android.systemui.res.R
@@ -34,7 +34,7 @@
import org.junit.runner.RunWith
@SmallTest
-@RunWith(AndroidTestingRunner::class)
+@RunWith(AndroidJUnit4::class)
@RunWithLooper
class ActivatableNotificationViewTest : SysuiTestCase() {
private val mContentView: View = mock()
@@ -98,4 +98,4 @@
assertThat(mView.topRoundness).isEqualTo(1f)
assertThat(mView.roundableState.hashCode()).isEqualTo(roundableState.hashCode())
}
-}
\ No newline at end of file
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/BigPictureIconManagerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/BigPictureIconManagerTest.kt
index 0eae5fc..fda5cd2 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/BigPictureIconManagerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/BigPictureIconManagerTest.kt
@@ -20,8 +20,8 @@
import android.graphics.drawable.Drawable
import android.graphics.drawable.Icon
import android.net.Uri
-import android.testing.AndroidTestingRunner
import android.testing.TestableLooper.RunWithLooper
+import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.internal.widget.NotificationDrawableConsumer
import com.android.systemui.SysuiTestCase
@@ -50,7 +50,7 @@
@OptIn(ExperimentalCoroutinesApi::class)
@SmallTest
@RunWithLooper
-@RunWith(AndroidTestingRunner::class)
+@RunWith(AndroidJUnit4::class)
class BigPictureIconManagerTest : SysuiTestCase() {
private val testDispatcher = StandardTestDispatcher()
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/ChannelEditorDialogControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/ChannelEditorDialogControllerTest.kt
index 7dcbd80..c5b19ab 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/ChannelEditorDialogControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/ChannelEditorDialogControllerTest.kt
@@ -25,8 +25,8 @@
import android.graphics.Color
import android.graphics.drawable.ColorDrawable
import android.print.PrintManager.PRINT_SPOOLER_PACKAGE_NAME
+import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
-import android.testing.AndroidTestingRunner
import android.testing.TestableLooper
import android.view.View
@@ -47,7 +47,7 @@
import org.mockito.MockitoAnnotations
@SmallTest
-@RunWith(AndroidTestingRunner::class)
+@RunWith(AndroidJUnit4::class)
@TestableLooper.RunWithLooper
class ChannelEditorDialogControllerTest : SysuiTestCase() {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowControllerTest.kt
index 210b1a7..e738b61 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowControllerTest.kt
@@ -21,8 +21,8 @@
import android.net.Uri
import android.os.UserHandle
import android.os.UserHandle.USER_ALL
-import android.testing.AndroidTestingRunner
import android.testing.TestableLooper
+import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.internal.logging.MetricsLogger
import com.android.internal.statusbar.IStatusBarService
@@ -71,7 +71,7 @@
import org.mockito.Mockito.`when` as whenever
@SmallTest
-@RunWith(AndroidTestingRunner::class)
+@RunWith(AndroidJUnit4::class)
@TestableLooper.RunWithLooper
class ExpandableNotificationRowControllerTest : SysuiTestCase() {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowDragControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowDragControllerTest.java
index 9d2f32d..1c5f37c 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowDragControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowDragControllerTest.java
@@ -31,10 +31,10 @@
import android.app.Notification;
import android.app.PendingIntent;
import android.content.Context;
-import android.testing.AndroidTestingRunner;
import android.testing.TestableLooper;
import android.testing.TestableLooper.RunWithLooper;
+import androidx.test.ext.junit.runners.AndroidJUnit4;
import androidx.test.filters.SmallTest;
import com.android.systemui.SysuiTestCase;
@@ -48,7 +48,7 @@
import org.junit.runner.RunWith;
@SmallTest
-@RunWith(AndroidTestingRunner.class)
+@RunWith(AndroidJUnit4.class)
@RunWithLooper
public class ExpandableNotificationRowDragControllerTest extends SysuiTestCase {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowTest.java
index aa79c23..7304bd6 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowTest.java
@@ -46,13 +46,13 @@
import android.graphics.drawable.Drawable;
import android.os.UserHandle;
import android.platform.test.annotations.EnableFlags;
-import android.testing.AndroidTestingRunner;
import android.testing.TestableLooper;
import android.testing.TestableLooper.RunWithLooper;
import android.util.DisplayMetrics;
import android.view.View;
import android.widget.ImageView;
+import androidx.test.ext.junit.runners.AndroidJUnit4;
import androidx.test.filters.SmallTest;
import com.android.internal.R;
@@ -87,7 +87,7 @@
import java.util.function.Consumer;
@SmallTest
-@RunWith(AndroidTestingRunner.class)
+@RunWith(AndroidJUnit4.class)
@RunWithLooper
public class ExpandableNotificationRowTest extends SysuiTestCase {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/FeedbackInfoTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/FeedbackInfoTest.java
index ffb8646..d04d6fc 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/FeedbackInfoTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/FeedbackInfoTest.java
@@ -45,13 +45,13 @@
import android.graphics.drawable.Drawable;
import android.os.UserHandle;
import android.service.notification.StatusBarNotification;
-import android.testing.AndroidTestingRunner;
import android.testing.UiThreadTest;
import android.view.LayoutInflater;
import android.view.View;
import android.widget.ImageView;
import android.widget.TextView;
+import androidx.test.ext.junit.runners.AndroidJUnit4;
import androidx.test.filters.SmallTest;
import com.android.internal.statusbar.IStatusBarService;
@@ -72,7 +72,7 @@
import java.util.Locale;
@SmallTest
-@RunWith(AndroidTestingRunner.class)
+@RunWith(AndroidJUnit4.class)
@UiThreadTest
public class FeedbackInfoTest extends SysuiTestCase {
private static final String TEST_PACKAGE_NAME = "test_package";
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/HeadsUpStyleProviderImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/HeadsUpStyleProviderImplTest.kt
index 5e50af3..c325791 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/HeadsUpStyleProviderImplTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/HeadsUpStyleProviderImplTest.kt
@@ -20,7 +20,7 @@
import android.platform.test.annotations.DisableFlags
import android.platform.test.annotations.EnableFlags
import android.platform.test.flag.junit.SetFlagsRule
-import android.testing.AndroidTestingRunner
+import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.statusbar.data.repository.FakeStatusBarModeRepository
@@ -31,7 +31,7 @@
import org.junit.runner.RunWith
@SmallTest
-@RunWith(AndroidTestingRunner::class)
+@RunWith(AndroidJUnit4::class)
class HeadsUpStyleProviderImplTest : SysuiTestCase() {
@Rule @JvmField val setFlagsRule = SetFlagsRule()
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotifBindPipelineTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotifBindPipelineTest.java
index 25172de..18fd42d 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotifBindPipelineTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotifBindPipelineTest.java
@@ -22,11 +22,11 @@
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.verify;
-import android.testing.AndroidTestingRunner;
import android.testing.TestableLooper;
import androidx.annotation.NonNull;
import androidx.core.os.CancellationSignal;
+import androidx.test.ext.junit.runners.AndroidJUnit4;
import androidx.test.filters.SmallTest;
import com.android.systemui.SysuiTestCase;
@@ -48,7 +48,7 @@
import java.util.List;
@SmallTest
-@RunWith(AndroidTestingRunner.class)
+@RunWith(AndroidJUnit4.class)
@TestableLooper.RunWithLooper
public class NotifBindPipelineTest extends SysuiTestCase {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotifInflationErrorManagerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotifInflationErrorManagerTest.kt
index e38adeb..29252b2 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotifInflationErrorManagerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotifInflationErrorManagerTest.kt
@@ -16,8 +16,8 @@
package com.android.systemui.statusbar.notification.row
-import android.testing.AndroidTestingRunner
import android.testing.TestableLooper
+import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.statusbar.notification.collection.NotificationEntry
@@ -33,7 +33,7 @@
import org.mockito.Mockito.verify
@SmallTest
-@RunWith(AndroidTestingRunner::class)
+@RunWith(AndroidJUnit4::class)
@TestableLooper.RunWithLooper
class NotifInflationErrorManagerTest : SysuiTestCase() {
private lateinit var manager: NotifInflationErrorManager
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotifRemoteViewCacheImplTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotifRemoteViewCacheImplTest.java
index 20cc01a..8b1c95b 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotifRemoteViewCacheImplTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotifRemoteViewCacheImplTest.java
@@ -26,9 +26,9 @@
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.verify;
-import android.testing.AndroidTestingRunner;
import android.widget.RemoteViews;
+import androidx.test.ext.junit.runners.AndroidJUnit4;
import androidx.test.filters.SmallTest;
import com.android.systemui.SysuiTestCase;
@@ -45,7 +45,7 @@
import org.mockito.MockitoAnnotations;
@SmallTest
-@RunWith(AndroidTestingRunner.class)
+@RunWith(AndroidJUnit4.class)
public class NotifRemoteViewCacheImplTest extends SysuiTestCase {
private NotifRemoteViewCacheImpl mNotifRemoteViewCache;
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationContentInflaterTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationContentInflaterTest.java
index 03a8403..a355cd1 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationContentInflaterTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationContentInflaterTest.java
@@ -40,7 +40,6 @@
import android.os.CancellationSignal;
import android.os.Handler;
import android.os.Looper;
-import android.testing.AndroidTestingRunner;
import android.testing.TestableLooper;
import android.testing.TestableLooper.RunWithLooper;
import android.util.TypedValue;
@@ -49,6 +48,7 @@
import android.widget.RemoteViews;
import android.widget.TextView;
+import androidx.test.ext.junit.runners.AndroidJUnit4;
import androidx.test.filters.SmallTest;
import androidx.test.filters.Suppress;
@@ -79,7 +79,7 @@
import java.util.concurrent.TimeUnit;
@SmallTest
-@RunWith(AndroidTestingRunner.class)
+@RunWith(AndroidJUnit4.class)
@RunWithLooper(setAsMainLooper = true)
@Suppress
public class NotificationContentInflaterTest extends SysuiTestCase {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationContentViewTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationContentViewTest.kt
index 7332bc3..2bb610a 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationContentViewTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationContentViewTest.kt
@@ -20,7 +20,6 @@
import android.content.res.Resources
import android.os.UserHandle
import android.service.notification.StatusBarNotification
-import android.testing.AndroidTestingRunner
import android.testing.TestableLooper
import android.testing.ViewUtils
import android.view.NotificationHeaderView
@@ -29,6 +28,7 @@
import android.widget.FrameLayout
import android.widget.ImageView
import android.widget.LinearLayout
+import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.internal.R
import com.android.internal.widget.NotificationActionListLayout
@@ -60,7 +60,7 @@
import org.mockito.MockitoAnnotations.initMocks
@SmallTest
-@RunWith(AndroidTestingRunner::class)
+@RunWith(AndroidJUnit4::class)
@TestableLooper.RunWithLooper
class NotificationContentViewTest : SysuiTestCase() {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationConversationInfoTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationConversationInfoTest.java
index 97cb11e2..be89ab8 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationConversationInfoTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationConversationInfoTest.java
@@ -65,13 +65,13 @@
import android.os.UserHandle;
import android.os.UserManager;
import android.service.notification.StatusBarNotification;
-import android.testing.AndroidTestingRunner;
import android.testing.TestableLooper;
import android.view.LayoutInflater;
import android.view.View;
import android.widget.ImageView;
import android.widget.TextView;
+import androidx.test.ext.junit.runners.AndroidJUnit4;
import androidx.test.filters.SmallTest;
import com.android.internal.logging.MetricsLogger;
@@ -103,7 +103,7 @@
import java.util.concurrent.CountDownLatch;
@SmallTest
-@RunWith(AndroidTestingRunner.class)
+@RunWith(AndroidJUnit4.class)
@TestableLooper.RunWithLooper
public class NotificationConversationInfoTest extends SysuiTestCase {
private static final String TEST_PACKAGE_NAME = "test_package";
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationGutsManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationGutsManagerTest.java
index 907649b..625963f 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationGutsManagerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationGutsManagerTest.java
@@ -57,12 +57,12 @@
import android.os.UserManager;
import android.provider.Settings;
import android.service.notification.StatusBarNotification;
-import android.testing.AndroidTestingRunner;
import android.testing.TestableLooper;
import android.util.ArraySet;
import android.view.View;
import android.view.accessibility.AccessibilityManager;
+import androidx.test.ext.junit.runners.AndroidJUnit4;
import androidx.test.filters.SmallTest;
import com.android.internal.logging.MetricsLogger;
@@ -115,7 +115,7 @@
* Tests for {@link NotificationGutsManager}.
*/
@SmallTest
-@RunWith(AndroidTestingRunner.class)
+@RunWith(AndroidJUnit4.class)
@TestableLooper.RunWithLooper
public class NotificationGutsManagerTest extends SysuiTestCase {
private static final String TEST_CHANNEL_ID = "NotificationManagerServiceTestChannelId";
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationGutsManagerWithScenesTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationGutsManagerWithScenesTest.kt
index 1b85dfa..0b5f8d5 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationGutsManagerWithScenesTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationGutsManagerWithScenesTest.kt
@@ -31,11 +31,11 @@
import android.os.userManager
import android.provider.Settings
import android.service.notification.NotificationListenerService.Ranking
-import android.testing.AndroidTestingRunner
import android.testing.TestableLooper.RunWithLooper
import android.util.ArraySet
import android.view.View
import android.view.accessibility.accessibilityManager
+import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.compose.animation.scene.ObservableTransitionState
import com.android.internal.logging.MetricsLogger
@@ -91,7 +91,7 @@
/** Tests for [NotificationGutsManager] with the scene container enabled. */
@SmallTest
-@RunWith(AndroidTestingRunner::class)
+@RunWith(AndroidJUnit4::class)
@RunWithLooper
@EnableSceneContainer
class NotificationGutsManagerWithScenesTest : SysuiTestCase() {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationGutsTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationGutsTest.kt
index 7f9471e..350f90d 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationGutsTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationGutsTest.kt
@@ -16,11 +16,11 @@
package com.android.systemui.statusbar.notification.row
-import android.testing.AndroidTestingRunner
import android.testing.TestableLooper.RunWithLooper
import android.testing.ViewUtils
import android.view.LayoutInflater
import android.view.View
+import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.systemui.res.R
import com.android.systemui.SysuiTestCase
@@ -34,7 +34,7 @@
import org.mockito.MockitoAnnotations
@SmallTest
-@RunWith(AndroidTestingRunner::class)
+@RunWith(AndroidJUnit4::class)
@RunWithLooper
class NotificationGutsTest : SysuiTestCase() {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationInfoTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationInfoTest.java
index 13ced92..245a6a0 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationInfoTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationInfoTest.java
@@ -55,13 +55,13 @@
import android.os.UserHandle;
import android.service.notification.StatusBarNotification;
import android.telecom.TelecomManager;
-import android.testing.AndroidTestingRunner;
import android.testing.TestableLooper;
import android.view.LayoutInflater;
import android.view.View;
import android.widget.ImageView;
import android.widget.TextView;
+import androidx.test.ext.junit.runners.AndroidJUnit4;
import androidx.test.filters.SmallTest;
import com.android.internal.logging.MetricsLogger;
@@ -85,7 +85,7 @@
import java.util.concurrent.CountDownLatch;
@SmallTest
-@RunWith(AndroidTestingRunner.class)
+@RunWith(AndroidJUnit4.class)
@TestableLooper.RunWithLooper
public class NotificationInfoTest extends SysuiTestCase {
private static final String TEST_PACKAGE_NAME = "test_package";
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationMenuRowTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationMenuRowTest.java
index e929028..027e899 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationMenuRowTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationMenuRowTest.java
@@ -29,13 +29,13 @@
import static org.mockito.Mockito.when;
import android.provider.Settings;
-import android.testing.AndroidTestingRunner;
import android.testing.TestableLooper;
import android.testing.TestableLooper.RunWithLooper;
import android.testing.ViewUtils;
import android.view.View;
import android.view.ViewGroup;
+import androidx.test.ext.junit.runners.AndroidJUnit4;
import androidx.test.filters.SmallTest;
import com.android.systemui.plugins.statusbar.NotificationMenuRowPlugin;
@@ -49,7 +49,7 @@
import org.junit.runner.RunWith;
import org.mockito.Mockito;
-@RunWith(AndroidTestingRunner.class)
+@RunWith(AndroidJUnit4.class)
@RunWithLooper
@SmallTest
public class NotificationMenuRowTest extends LeakCheckedTest {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationSettingsControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationSettingsControllerTest.kt
index 8261c1c..352b79f 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationSettingsControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationSettingsControllerTest.kt
@@ -22,8 +22,8 @@
import android.net.Uri
import android.os.Handler
import android.provider.Settings.Secure
-import android.testing.AndroidTestingRunner
import android.testing.TestableLooper
+import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.dump.DumpManager
@@ -54,7 +54,7 @@
import org.mockito.MockitoAnnotations
@SmallTest
-@RunWith(AndroidTestingRunner::class)
+@RunWith(AndroidJUnit4::class)
@TestableLooper.RunWithLooper
class NotificationSettingsControllerTest : SysuiTestCase() {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationSnoozeTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationSnoozeTest.java
index 4a91cd2..22f1e46 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationSnoozeTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationSnoozeTest.java
@@ -23,11 +23,11 @@
import static org.mockito.Mockito.mock;
import android.provider.Settings;
-import android.testing.AndroidTestingRunner;
import android.testing.TestableResources;
import android.testing.UiThreadTest;
import android.util.KeyValueListParser;
+import androidx.test.ext.junit.runners.AndroidJUnit4;
import androidx.test.filters.SmallTest;
import com.android.systemui.SysuiTestCase;
@@ -41,7 +41,7 @@
import java.util.ArrayList;
@SmallTest
-@RunWith(AndroidTestingRunner.class)
+@RunWith(AndroidJUnit4.class)
@UiThreadTest
public class NotificationSnoozeTest extends SysuiTestCase {
private static final int RES_DEFAULT = 2;
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/PartialConversationInfoTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/PartialConversationInfoTest.java
index 51665d9..57b0f3f 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/PartialConversationInfoTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/PartialConversationInfoTest.java
@@ -41,7 +41,6 @@
import android.graphics.drawable.Icon;
import android.os.UserHandle;
import android.service.notification.StatusBarNotification;
-import android.testing.AndroidTestingRunner;
import android.testing.TestableLooper;
import android.text.SpannableString;
import android.view.LayoutInflater;
@@ -49,6 +48,7 @@
import android.widget.ImageView;
import android.widget.TextView;
+import androidx.test.ext.junit.runners.AndroidJUnit4;
import androidx.test.filters.SmallTest;
import com.android.internal.logging.MetricsLogger;
@@ -69,7 +69,7 @@
import java.util.concurrent.CountDownLatch;
@SmallTest
-@RunWith(AndroidTestingRunner.class)
+@RunWith(AndroidJUnit4.class)
@TestableLooper.RunWithLooper
public class PartialConversationInfoTest extends SysuiTestCase {
private static final String TEST_PACKAGE_NAME = "test_package";
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/RowContentBindStageTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/RowContentBindStageTest.java
index 1534c84..841cb4a 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/RowContentBindStageTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/RowContentBindStageTest.java
@@ -34,10 +34,10 @@
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.verify;
-import android.testing.AndroidTestingRunner;
import android.testing.TestableLooper;
import android.util.Log;
+import androidx.test.ext.junit.runners.AndroidJUnit4;
import androidx.test.filters.SmallTest;
import com.android.systemui.SysuiTestCase;
@@ -52,7 +52,7 @@
import org.mockito.MockitoAnnotations;
@SmallTest
-@RunWith(AndroidTestingRunner.class)
+@RunWith(AndroidJUnit4.class)
@TestableLooper.RunWithLooper
public class RowContentBindStageTest extends SysuiTestCase {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/SingleLineConversationViewBinderTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/SingleLineConversationViewBinderTest.kt
index 1c959af..53a1198 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/SingleLineConversationViewBinderTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/SingleLineConversationViewBinderTest.kt
@@ -18,8 +18,8 @@
import android.app.Notification
import android.app.Person
import android.platform.test.annotations.EnableFlags
-import android.testing.AndroidTestingRunner
import android.testing.TestableLooper
+import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.internal.R
import com.android.systemui.SysuiTestCase
@@ -34,7 +34,7 @@
import org.junit.runner.RunWith
@SmallTest
-@RunWith(AndroidTestingRunner::class)
+@RunWith(AndroidJUnit4::class)
@TestableLooper.RunWithLooper
class SingleLineConversationViewBinderTest : SysuiTestCase() {
private lateinit var notificationBuilder: Notification.Builder
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/SingleLineViewBinderTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/SingleLineViewBinderTest.kt
index f0fc349..ee819c4 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/SingleLineViewBinderTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/SingleLineViewBinderTest.kt
@@ -17,8 +17,8 @@
import android.app.Notification
import android.platform.test.annotations.EnableFlags
-import android.testing.AndroidTestingRunner
import android.testing.TestableLooper
+import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.internal.R
import com.android.systemui.SysuiTestCase
@@ -33,7 +33,7 @@
import org.junit.runner.RunWith
@SmallTest
-@RunWith(AndroidTestingRunner::class)
+@RunWith(AndroidJUnit4::class)
@TestableLooper.RunWithLooper
class SingleLineViewBinderTest : SysuiTestCase() {
private lateinit var notificationBuilder: Notification.Builder
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/SingleLineViewInflaterTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/SingleLineViewInflaterTest.kt
index b67153a..e025d3d 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/SingleLineViewInflaterTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/SingleLineViewInflaterTest.kt
@@ -27,9 +27,9 @@
import android.graphics.drawable.Drawable
import android.graphics.drawable.Icon
import android.platform.test.annotations.EnableFlags
-import android.testing.AndroidTestingRunner
import android.testing.TestableLooper
import androidx.core.graphics.drawable.toBitmap
+import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.res.R
@@ -48,7 +48,7 @@
import org.junit.runner.RunWith
@SmallTest
-@RunWith(AndroidTestingRunner::class)
+@RunWith(AndroidJUnit4::class)
@TestableLooper.RunWithLooper
@EnableFlags(AsyncHybridViewInflation.FLAG_NAME)
class SingleLineViewInflaterTest : SysuiTestCase() {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/TextPrecomputerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/TextPrecomputerTest.kt
index d46763d..f8533a5 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/TextPrecomputerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/TextPrecomputerTest.kt
@@ -16,11 +16,11 @@
package com.android.systemui.statusbar.notification.row
-import android.testing.AndroidTestingRunner
import android.testing.TestableLooper.RunWithLooper
import android.text.PrecomputedText
import android.text.TextPaint
import android.widget.TextView
+import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
import com.google.common.truth.Truth.assertThat
@@ -29,7 +29,7 @@
import org.junit.runner.RunWith
@SmallTest
-@RunWith(AndroidTestingRunner::class)
+@RunWith(AndroidJUnit4::class)
@RunWithLooper
class TextPrecomputerTest : SysuiTestCase() {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/ui/viewmodel/ActivatableNotificationViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/ui/viewmodel/ActivatableNotificationViewModelTest.kt
index c960230..0dc871a 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/ui/viewmodel/ActivatableNotificationViewModelTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/ui/viewmodel/ActivatableNotificationViewModelTest.kt
@@ -18,7 +18,7 @@
package com.android.systemui.statusbar.notification.row.ui.viewmodel
-import android.testing.AndroidTestingRunner
+import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.accessibility.data.repository.FakeAccessibilityRepository
@@ -31,7 +31,7 @@
import org.junit.Test
import org.junit.runner.RunWith
-@RunWith(AndroidTestingRunner::class)
+@RunWith(AndroidJUnit4::class)
@SmallTest
class ActivatableNotificationViewModelTest : SysuiTestCase() {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationBigPictureTemplateViewWrapperTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationBigPictureTemplateViewWrapperTest.java
index a15b4cd..ec280a1 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationBigPictureTemplateViewWrapperTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationBigPictureTemplateViewWrapperTest.java
@@ -24,10 +24,10 @@
import android.graphics.drawable.AnimatedImageDrawable;
import android.graphics.drawable.Icon;
import android.os.Bundle;
-import android.testing.AndroidTestingRunner;
import android.view.LayoutInflater;
import android.view.View;
+import androidx.test.ext.junit.runners.AndroidJUnit4;
import androidx.test.filters.SmallTest;
import com.android.internal.R;
@@ -40,7 +40,7 @@
import org.junit.Test;
import org.junit.runner.RunWith;
-@RunWith(AndroidTestingRunner.class)
+@RunWith(AndroidJUnit4.class)
@SmallTest
public class NotificationBigPictureTemplateViewWrapperTest extends SysuiTestCase {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationConversationTemplateViewWrapperTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationConversationTemplateViewWrapperTest.kt
index fe2971c..9d990b1 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationConversationTemplateViewWrapperTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationConversationTemplateViewWrapperTest.kt
@@ -17,8 +17,8 @@
package com.android.systemui.statusbar.notification.row.wrapper
import android.graphics.drawable.AnimatedImageDrawable
-import android.testing.AndroidTestingRunner
import android.view.View
+import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.internal.R
import com.android.internal.widget.CachingIconView
@@ -38,7 +38,7 @@
import org.mockito.Mockito.`when` as whenever
@SmallTest
-@RunWith(AndroidTestingRunner::class)
+@RunWith(AndroidJUnit4::class)
class NotificationConversationTemplateViewWrapperTest : SysuiTestCase() {
private lateinit var mRow: ExpandableNotificationRow
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationCustomViewWrapperTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationCustomViewWrapperTest.java
index 2d72c7e..f9a9704 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationCustomViewWrapperTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationCustomViewWrapperTest.java
@@ -16,10 +16,10 @@
package com.android.systemui.statusbar.notification.row.wrapper;
-import android.testing.AndroidTestingRunner;
import android.view.View;
import android.widget.RemoteViews;
+import androidx.test.ext.junit.runners.AndroidJUnit4;
import androidx.test.filters.SmallTest;
import com.android.systemui.SysuiTestCase;
@@ -33,7 +33,7 @@
import org.junit.runner.RunWith;
@SmallTest
-@RunWith(AndroidTestingRunner.class)
+@RunWith(AndroidJUnit4.class)
public class NotificationCustomViewWrapperTest extends SysuiTestCase {
private ExpandableNotificationRow mRow;
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationMessagingTemplateViewWrapperTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationMessagingTemplateViewWrapperTest.kt
index f26c18b..fc829d5 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationMessagingTemplateViewWrapperTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationMessagingTemplateViewWrapperTest.kt
@@ -17,8 +17,8 @@
package com.android.systemui.statusbar.notification.row.wrapper
import android.graphics.drawable.AnimatedImageDrawable
-import android.testing.AndroidTestingRunner
import android.view.View
+import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.internal.widget.MessagingGroup
import com.android.internal.widget.MessagingImageMessage
@@ -36,7 +36,7 @@
import org.mockito.Mockito.`when` as whenever
@SmallTest
-@RunWith(AndroidTestingRunner::class)
+@RunWith(AndroidJUnit4::class)
class NotificationMessagingTemplateViewWrapperTest : SysuiTestCase() {
private lateinit var mRow: ExpandableNotificationRow
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationTemplateViewWrapperTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationTemplateViewWrapperTest.kt
index 54eed26..1ce3bad 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationTemplateViewWrapperTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationTemplateViewWrapperTest.kt
@@ -19,7 +19,6 @@
import android.app.PendingIntent
import android.app.PendingIntent.CancelListener
import android.content.Intent
-import android.testing.AndroidTestingRunner
import android.testing.TestableLooper
import android.testing.TestableLooper.RunWithLooper
import android.testing.ViewUtils
@@ -27,6 +26,7 @@
import android.view.View
import android.view.ViewGroup
import android.widget.FrameLayout
+import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.internal.R
import com.android.systemui.SysuiTestCase
@@ -46,7 +46,7 @@
import org.mockito.Mockito.verify
@SmallTest
-@RunWith(AndroidTestingRunner::class)
+@RunWith(AndroidJUnit4::class)
@RunWithLooper
class NotificationTemplateViewWrapperTest : SysuiTestCase() {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationViewWrapperTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationViewWrapperTest.java
index fad85f53..d17c8db 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationViewWrapperTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationViewWrapperTest.java
@@ -20,11 +20,11 @@
import static org.mockito.Mockito.when;
import android.content.Context;
-import android.testing.AndroidTestingRunner;
import android.view.View;
import android.widget.LinearLayout;
import android.widget.TextView;
+import androidx.test.ext.junit.runners.AndroidJUnit4;
import androidx.test.filters.SmallTest;
import com.android.systemui.SysuiTestCase;
@@ -35,7 +35,7 @@
import org.junit.Test;
import org.junit.runner.RunWith;
-@RunWith(AndroidTestingRunner.class)
+@RunWith(AndroidJUnit4.class)
@SmallTest
public class NotificationViewWrapperTest extends SysuiTestCase {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/shelf/domain/interactor/NotificationShelfInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/shelf/domain/interactor/NotificationShelfInteractorTest.kt
index 59d98c2..4c6e25a 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/shelf/domain/interactor/NotificationShelfInteractorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/shelf/domain/interactor/NotificationShelfInteractorTest.kt
@@ -19,7 +19,7 @@
package com.android.systemui.statusbar.notification.shelf.domain.interactor
import android.os.PowerManager
-import android.testing.AndroidTestingRunner
+import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.coroutines.collectLastValue
@@ -41,7 +41,7 @@
import org.mockito.Mockito.isNull
import org.mockito.Mockito.verify
-@RunWith(AndroidTestingRunner::class)
+@RunWith(AndroidJUnit4::class)
@SmallTest
class NotificationShelfInteractorTest : SysuiTestCase() {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/shelf/ui/viewmodel/NotificationShelfViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/shelf/ui/viewmodel/NotificationShelfViewModelTest.kt
index 917569c..e2fb3ba 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/shelf/ui/viewmodel/NotificationShelfViewModelTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/shelf/ui/viewmodel/NotificationShelfViewModelTest.kt
@@ -17,7 +17,7 @@
package com.android.systemui.statusbar.notification.shelf.ui.viewmodel
import android.os.PowerManager
-import android.testing.AndroidTestingRunner
+import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.systemui.SysUITestComponent
import com.android.systemui.SysUITestModule
@@ -44,7 +44,7 @@
import org.mockito.Mockito
import org.mockito.Mockito.verify
-@RunWith(AndroidTestingRunner::class)
+@RunWith(AndroidJUnit4::class)
@SmallTest
class NotificationShelfViewModelTest : SysuiTestCase() {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/AmbientStateTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/AmbientStateTest.kt
index fb15948..2349c25 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/AmbientStateTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/AmbientStateTest.kt
@@ -16,7 +16,7 @@
package com.android.systemui.statusbar.notification.stack
-import android.testing.AndroidTestingRunner
+import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.dump.DumpManager
@@ -33,7 +33,7 @@
private const val MAX_PULSE_HEIGHT = 100000f
-@RunWith(AndroidTestingRunner::class)
+@RunWith(AndroidJUnit4::class)
@SmallTest
class AmbientStateTest : SysuiTestCase() {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/MediaContainerViewTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/MediaContainerViewTest.kt
index f4e236e..3a77d82 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/MediaContainerViewTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/MediaContainerViewTest.kt
@@ -1,8 +1,8 @@
package com.android.systemui.statusbar.notification.stack
-import android.testing.AndroidTestingRunner
import android.testing.TestableLooper
import android.view.LayoutInflater
+import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.res.R
@@ -15,7 +15,7 @@
* Tests for {@link MediaContainView}.
*/
@SmallTest
-@RunWith(AndroidTestingRunner::class)
+@RunWith(AndroidJUnit4::class)
@TestableLooper.RunWithLooper
class MediaContainerViewTest : SysuiTestCase() {
@@ -35,4 +35,4 @@
mediaContainerView.updateClipping()
assertTrue(mediaContainerView.clipHeight == 10)
}
-}
\ No newline at end of file
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationChildrenContainerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationChildrenContainerTest.java
index 3b16f14..14bbd38 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationChildrenContainerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationChildrenContainerTest.java
@@ -21,13 +21,13 @@
import android.app.Notification;
import android.platform.test.annotations.DisableFlags;
import android.platform.test.annotations.EnableFlags;
-import android.testing.AndroidTestingRunner;
import android.testing.TestableLooper;
import android.testing.TestableLooper.RunWithLooper;
import android.view.NotificationHeaderView;
import android.view.View;
import android.widget.RemoteViews;
+import androidx.test.ext.junit.runners.AndroidJUnit4;
import androidx.test.filters.SmallTest;
import com.android.systemui.SysuiTestCase;
@@ -45,7 +45,7 @@
import java.util.List;
@SmallTest
-@RunWith(AndroidTestingRunner.class)
+@RunWith(AndroidJUnit4.class)
@RunWithLooper
//@DisableFlags(AsyncGroupHeaderViewInflation.FLAG_NAME)
public class NotificationChildrenContainerTest extends SysuiTestCase {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationShelfTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationShelfTest.kt
index 745d20d..48e8f88 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationShelfTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationShelfTest.kt
@@ -1,10 +1,11 @@
package com.android.systemui.statusbar.notification.stack
+import android.platform.test.annotations.DisableFlags
import android.service.notification.StatusBarNotification
-import android.testing.AndroidTestingRunner
import android.testing.TestableLooper.RunWithLooper
import android.view.LayoutInflater
import android.widget.FrameLayout
+import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.keyguard.BouncerPanelExpansionCalculator.aboutToShowBouncerProgress
import com.android.systemui.SysuiTestCase
@@ -34,7 +35,7 @@
/** Tests for {@link NotificationShelf}. */
@SmallTest
-@RunWith(AndroidTestingRunner::class)
+@RunWith(AndroidJUnit4::class)
@RunWithLooper
open class NotificationShelfTest : SysuiTestCase() {
@@ -69,8 +70,8 @@
}
@Test
+ @DisableFlags(NotificationIconContainerRefactor.FLAG_NAME)
fun testShadeWidth_BasedOnFractionToShade() {
- mSetFlagsRule.disableFlags(NotificationIconContainerRefactor.FLAG_NAME)
setFractionToShade(0f)
setOnLockscreen(true)
@@ -85,8 +86,8 @@
}
@Test
+ @DisableFlags(NotificationIconContainerRefactor.FLAG_NAME)
fun testShelfIsLong_WhenNotOnLockscreen() {
- mSetFlagsRule.disableFlags(NotificationIconContainerRefactor.FLAG_NAME)
setFractionToShade(0f)
setOnLockscreen(false)
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutControllerTest.java
index f262df1..ce2491b 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutControllerTest.java
@@ -42,12 +42,12 @@
import android.metrics.LogMaker;
import android.platform.test.annotations.DisableFlags;
import android.platform.test.annotations.EnableFlags;
-import android.testing.AndroidTestingRunner;
import android.testing.TestableLooper;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewTreeObserver;
+import androidx.test.ext.junit.runners.AndroidJUnit4;
import androidx.test.filters.SmallTest;
import com.android.internal.logging.MetricsLogger;
@@ -126,7 +126,7 @@
*/
@SmallTest
@TestableLooper.RunWithLooper(setAsMainLooper = true)
-@RunWith(AndroidTestingRunner.class)
+@RunWith(AndroidJUnit4.class)
public class NotificationStackScrollLayoutControllerTest extends SysuiTestCase {
protected KosmosJavaAdapter mKosmos = new KosmosJavaAdapter(this);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutTest.java
index 0c0a2a5..f461e2f 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutTest.java
@@ -54,7 +54,6 @@
import android.os.SystemClock;
import android.platform.test.annotations.DisableFlags;
import android.platform.test.annotations.EnableFlags;
-import android.testing.AndroidTestingRunner;
import android.testing.TestableLooper;
import android.testing.TestableResources;
import android.util.MathUtils;
@@ -65,6 +64,7 @@
import android.view.WindowInsetsAnimation;
import android.widget.TextView;
+import androidx.test.ext.junit.runners.AndroidJUnit4;
import androidx.test.filters.SmallTest;
import com.android.keyguard.BouncerPanelExpansionCalculator;
@@ -114,7 +114,7 @@
* Tests for {@link NotificationStackScrollLayout}.
*/
@SmallTest
-@RunWith(AndroidTestingRunner.class)
+@RunWith(AndroidJUnit4.class)
@TestableLooper.RunWithLooper
public class NotificationStackScrollLayoutTest extends SysuiTestCase {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackSizeCalculatorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackSizeCalculatorTest.kt
index 6fec9ad..dae5542 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackSizeCalculatorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackSizeCalculatorTest.kt
@@ -18,8 +18,8 @@
import android.annotation.DimenRes
import android.service.notification.StatusBarNotification
-import android.testing.AndroidTestingRunner
import android.view.View.VISIBLE
+import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.systemui.res.R
import com.android.systemui.SysuiTestCase
@@ -44,7 +44,7 @@
import org.mockito.MockitoAnnotations
@SmallTest
-@RunWith(AndroidTestingRunner::class)
+@RunWith(AndroidJUnit4::class)
class NotificationStackSizeCalculatorTest : SysuiTestCase() {
@Mock private lateinit var sysuiStatusBarStateController: SysuiStatusBarStateController
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationSwipeHelperTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationSwipeHelperTest.java
index 85a2bdd..2d11917 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationSwipeHelperTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationSwipeHelperTest.java
@@ -37,12 +37,12 @@
import android.animation.ValueAnimator.AnimatorUpdateListener;
import android.os.Handler;
import android.service.notification.StatusBarNotification;
-import android.testing.AndroidTestingRunner;
import android.testing.TestableLooper;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewConfiguration;
+import androidx.test.ext.junit.runners.AndroidJUnit4;
import androidx.test.filters.SmallTest;
import com.android.systemui.SysuiTestCase;
@@ -70,7 +70,7 @@
* Tests for {@link NotificationSwipeHelper}.
*/
@SmallTest
-@RunWith(AndroidTestingRunner.class)
+@RunWith(AndroidJUnit4.class)
@TestableLooper.RunWithLooper()
public class NotificationSwipeHelperTest extends SysuiTestCase {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationTargetsHelperTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationTargetsHelperTest.kt
index e30947c..660eb30 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationTargetsHelperTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationTargetsHelperTest.kt
@@ -1,8 +1,8 @@
package com.android.systemui.statusbar.notification.stack
-import android.testing.AndroidTestingRunner
import android.testing.TestableLooper
import android.testing.TestableLooper.RunWithLooper
+import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.flags.FakeFeatureFlags
@@ -15,7 +15,7 @@
/** Tests for {@link NotificationTargetsHelper}. */
@SmallTest
-@RunWith(AndroidTestingRunner::class)
+@RunWith(AndroidJUnit4::class)
@RunWithLooper
class NotificationTargetsHelperTest : SysuiTestCase() {
private val featureFlags = FakeFeatureFlags()
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/StackStateAnimatorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/StackStateAnimatorTest.kt
index 926c35f..798465e 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/StackStateAnimatorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/StackStateAnimatorTest.kt
@@ -17,8 +17,8 @@
package com.android.systemui.statusbar.notification.stack
import android.platform.test.annotations.EnableFlags
-import android.testing.AndroidTestingRunner
import android.testing.TestableLooper.RunWithLooper
+import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.animation.AnimatorTestRule
@@ -48,7 +48,7 @@
private const val HEADS_UP_ABOVE_SCREEN = 80
@SmallTest
-@RunWith(AndroidTestingRunner::class)
+@RunWith(AndroidJUnit4::class)
@RunWithLooper
class StackStateAnimatorTest : SysuiTestCase() {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/ViewStateTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/ViewStateTest.kt
index cd6bb5f..e493420 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/ViewStateTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/ViewStateTest.kt
@@ -16,7 +16,7 @@
package com.android.systemui.statusbar.notification.stack
-import android.testing.AndroidTestingRunner
+import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.log.assertDoesNotLogWtf
@@ -27,7 +27,7 @@
import org.junit.Test
import org.junit.runner.RunWith
-@RunWith(AndroidTestingRunner::class)
+@RunWith(AndroidJUnit4::class)
@SmallTest
class ViewStateTest : SysuiTestCase() {
private val viewState = ViewState()
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/domain/interactor/HideNotificationsInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/domain/interactor/HideNotificationsInteractorTest.kt
index e2ac203..e46906f 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/domain/interactor/HideNotificationsInteractorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/domain/interactor/HideNotificationsInteractorTest.kt
@@ -17,10 +17,10 @@
import android.content.res.Configuration
import android.graphics.Rect
-import android.testing.AndroidTestingRunner
import android.view.Surface
import android.view.Surface.ROTATION_0
import android.view.Surface.ROTATION_90
+import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.common.ui.data.repository.ConfigurationRepositoryImpl
@@ -52,7 +52,7 @@
@OptIn(ExperimentalCoroutinesApi::class)
@SmallTest
-@RunWith(AndroidTestingRunner::class)
+@RunWith(AndroidJUnit4::class)
open class HideNotificationsInteractorTest : SysuiTestCase() {
private val testScope = TestScope()
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/AutoTileManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/AutoTileManagerTest.java
index 84cd518..f0bc655 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/AutoTileManagerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/AutoTileManagerTest.java
@@ -45,10 +45,10 @@
import android.hardware.display.NightDisplayListener;
import android.os.Handler;
import android.os.UserHandle;
-import android.testing.AndroidTestingRunner;
import android.testing.TestableLooper;
import android.testing.TestableLooper.RunWithLooper;
+import androidx.test.ext.junit.runners.AndroidJUnit4;
import androidx.test.filters.SmallTest;
import com.android.systemui.SysuiTestCase;
@@ -89,7 +89,7 @@
import javax.inject.Named;
-@RunWith(AndroidTestingRunner.class)
+@RunWith(AndroidJUnit4.class)
@RunWithLooper
@SmallTest
public class AutoTileManagerTest extends SysuiTestCase {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/BiometricsUnlockControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/BiometricsUnlockControllerTest.java
index dc7525c..285949a 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/BiometricsUnlockControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/BiometricsUnlockControllerTest.java
@@ -37,11 +37,11 @@
import android.os.Handler;
import android.os.PowerManager;
import android.os.UserHandle;
-import android.testing.AndroidTestingRunner;
import android.testing.TestableLooper.RunWithLooper;
import android.testing.TestableResources;
import android.view.ViewRootImpl;
+import androidx.test.ext.junit.runners.AndroidJUnit4;
import androidx.test.filters.SmallTest;
import com.android.internal.logging.MetricsLogger;
@@ -77,7 +77,7 @@
import org.mockito.MockitoAnnotations;
@SmallTest
-@RunWith(AndroidTestingRunner.class)
+@RunWith(AndroidJUnit4.class)
@RunWithLooper
public class BiometricsUnlockControllerTest extends SysuiTestCase {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/CentralSurfacesCommandQueueCallbacksTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/CentralSurfacesCommandQueueCallbacksTest.java
index fe6a88d..5675915 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/CentralSurfacesCommandQueueCallbacksTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/CentralSurfacesCommandQueueCallbacksTest.java
@@ -30,9 +30,9 @@
import android.os.PowerManager;
import android.os.UserHandle;
import android.os.Vibrator;
-import android.testing.AndroidTestingRunner;
import android.view.HapticFeedbackConstants;
+import androidx.test.ext.junit.runners.AndroidJUnit4;
import androidx.test.filters.SmallTest;
import com.android.internal.logging.MetricsLogger;
@@ -71,7 +71,7 @@
import java.util.Optional;
@SmallTest
-@RunWith(AndroidTestingRunner.class)
+@RunWith(AndroidJUnit4.class)
public class CentralSurfacesCommandQueueCallbacksTest extends SysuiTestCase {
@Mock private CentralSurfaces mCentralSurfaces;
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/CentralSurfacesImplTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/CentralSurfacesImplTest.java
index b9312d3..cde241b 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/CentralSurfacesImplTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/CentralSurfacesImplTest.java
@@ -21,6 +21,7 @@
import static android.provider.Settings.Global.HEADS_UP_NOTIFICATIONS_ENABLED;
import static android.provider.Settings.Global.HEADS_UP_ON;
+import static com.android.systemui.Flags.FLAG_DEVICE_ENTRY_UDFPS_REFACTOR;
import static com.android.systemui.Flags.FLAG_KEYBOARD_SHORTCUT_HELPER_REWRITE;
import static com.android.systemui.Flags.FLAG_LIGHT_REVEAL_MIGRATION;
import static com.android.systemui.flags.Flags.SHORTCUT_LIST_SEARCH_LAYOUT;
@@ -56,6 +57,7 @@
import android.app.trust.TrustManager;
import android.content.BroadcastReceiver;
import android.content.IntentFilter;
+import android.content.pm.PackageManager;
import android.content.res.Configuration;
import android.graphics.Rect;
import android.hardware.devicestate.DeviceState;
@@ -70,9 +72,10 @@
import android.os.Looper;
import android.os.PowerManager;
import android.os.UserHandle;
+import android.platform.test.annotations.DisableFlags;
+import android.platform.test.annotations.EnableFlags;
import android.service.dreams.IDreamManager;
import android.support.test.metricshelper.MetricsAsserts;
-import android.testing.AndroidTestingRunner;
import android.testing.TestableLooper;
import android.testing.TestableLooper.RunWithLooper;
import android.util.DisplayMetrics;
@@ -82,6 +85,7 @@
import android.view.WindowManager;
import android.view.WindowMetrics;
+import androidx.test.ext.junit.runners.AndroidJUnit4;
import androidx.test.filters.SmallTest;
import com.android.compose.animation.scene.ObservableTransitionState;
@@ -105,8 +109,6 @@
import com.android.systemui.classifier.FalsingCollectorFake;
import com.android.systemui.classifier.FalsingManagerFake;
import com.android.systemui.colorextraction.SysuiColorExtractor;
-import com.android.systemui.communal.data.repository.CommunalRepository;
-import com.android.systemui.communal.domain.interactor.CommunalInteractor;
import com.android.systemui.communal.shared.model.CommunalScenes;
import com.android.systemui.demomode.DemoModeController;
import com.android.systemui.dump.DumpManager;
@@ -222,8 +224,9 @@
import javax.inject.Provider;
@SmallTest
-@RunWith(AndroidTestingRunner.class)
+@RunWith(AndroidJUnit4.class)
@RunWithLooper(setAsMainLooper = true)
+@EnableFlags(FLAG_LIGHT_REVEAL_MIGRATION)
public class CentralSurfacesImplTest extends SysuiTestCase {
private static final int FOLD_STATE_FOLDED = 0;
@@ -238,8 +241,6 @@
private final TestScope mTestScope = mKosmos.getTestScope();
- private final CommunalInteractor mCommunalInteractor = mKosmos.getCommunalInteractor();
- private final CommunalRepository mCommunalRepository = mKosmos.getCommunalRepository();
@Mock private NotificationsController mNotificationsController;
@Mock private LightBarController mLightBarController;
@Mock private StatusBarKeyguardViewManager mStatusBarKeyguardViewManager;
@@ -339,6 +340,7 @@
@Mock private WindowRootViewVisibilityInteractor mWindowRootViewVisibilityInteractor;
@Mock private KeyboardShortcuts mKeyboardShortcuts;
@Mock private KeyboardShortcutListSearch mKeyboardShortcutListSearch;
+ @Mock private PackageManager mPackageManager;
private ShadeController mShadeController;
private final FakeSystemClock mFakeSystemClock = new FakeSystemClock();
@@ -362,13 +364,9 @@
// Set default value to avoid IllegalStateException.
mFeatureFlags.set(SHORTCUT_LIST_SEARCH_LAYOUT, false);
- mSetFlagsRule.enableFlags(FLAG_LIGHT_REVEAL_MIGRATION);
// Turn AOD on and toggle feature flag for jank fixes
mFeatureFlags.set(Flags.ZJ_285570694_LOCKSCREEN_TRANSITION_FROM_AOD, true);
when(mDozeParameters.getAlwaysOn()).thenReturn(true);
- if (!SceneContainerFlag.isEnabled()) {
- mSetFlagsRule.disableFlags(com.android.systemui.Flags.FLAG_DEVICE_ENTRY_UDFPS_REFACTOR);
- }
IThermalService thermalService = mock(IThermalService.class);
mPowerManager = new PowerManager(mContext, mPowerManagerService, thermalService,
@@ -396,7 +394,8 @@
mock(UiEventLogger.class),
mUserTracker,
mAvalancheProvider,
- mSystemSettings);
+ mSystemSettings,
+ mPackageManager);
mVisualInterruptionDecisionProvider.start();
mContext.addMockSystemService(TrustManager.class, mock(TrustManager.class));
@@ -528,7 +527,7 @@
mScreenLifecycle,
mWakefulnessLifecycle,
mPowerInteractor,
- mCommunalInteractor,
+ mKosmos.getCommunalInteractor(),
mStatusBarStateController,
Optional.of(mBubbles),
() -> mNoteTaskController,
@@ -837,6 +836,7 @@
}
@Test
+ @DisableFlags(FLAG_DEVICE_ENTRY_UDFPS_REFACTOR)
public void testSetDozingNotUnlocking_transitionToAuthScrimmed_cancelKeyguardFadingAway() {
when(mAlternateBouncerInteractor.isVisibleState()).thenReturn(true);
when(mKeyguardStateController.isKeyguardFadingAway()).thenReturn(true);
@@ -848,7 +848,8 @@
}
@Test
- public void testOccludingQSNotExpanded_transitionToAuthScrimmed() {
+ @DisableFlags(FLAG_DEVICE_ENTRY_UDFPS_REFACTOR)
+ public void testOccludingQSNotExpanded_flagOff_transitionToAuthScrimmed() {
when(mAlternateBouncerInteractor.isVisibleState()).thenReturn(true);
// GIVEN device occluded and panel is NOT expanded
@@ -862,6 +863,39 @@
}
@Test
+ @EnableFlags(FLAG_DEVICE_ENTRY_UDFPS_REFACTOR)
+ public void testNotOccluding_QSNotExpanded_flagOn_doesNotTransitionScrimState() {
+ when(mAlternateBouncerInteractor.isVisibleState()).thenReturn(true);
+
+ // GIVEN device occluded and panel is NOT expanded
+ mCentralSurfaces.setBarStateForTest(SHADE);
+ when(mKeyguardStateController.isOccluded()).thenReturn(false);
+ when(mNotificationPanelViewController.isPanelExpanded()).thenReturn(false);
+
+ mCentralSurfaces.updateScrimController();
+
+ // Tests the safeguard to reset the scrimstate
+ verify(mScrimController, never()).transitionTo(any());
+ }
+
+ @Test
+ @EnableFlags(FLAG_DEVICE_ENTRY_UDFPS_REFACTOR)
+ public void testNotOccluding_QSExpanded_flagOn_doesTransitionScrimStateToKeyguard() {
+ when(mAlternateBouncerInteractor.isVisibleState()).thenReturn(true);
+
+ // GIVEN device occluded and panel is NOT expanded
+ mCentralSurfaces.setBarStateForTest(SHADE);
+ when(mKeyguardStateController.isOccluded()).thenReturn(false);
+ when(mNotificationPanelViewController.isPanelExpanded()).thenReturn(true);
+
+ mCentralSurfaces.updateScrimController();
+
+ // Tests the safeguard to reset the scrimstate
+ verify(mScrimController, never()).transitionTo(eq(ScrimState.KEYGUARD));
+ }
+
+ @Test
+ @DisableFlags(FLAG_DEVICE_ENTRY_UDFPS_REFACTOR)
public void testOccludingQSExpanded_transitionToAuthScrimmedShade() {
when(mAlternateBouncerInteractor.isVisibleState()).thenReturn(true);
@@ -878,16 +912,18 @@
@Test
public void testEnteringGlanceableHub_updatesScrim() {
// Transition to the glanceable hub.
- mCommunalRepository.setTransitionState(flowOf(new ObservableTransitionState.Idle(
- CommunalScenes.Communal)));
+ mKosmos.getCommunalRepository()
+ .setTransitionState(
+ flowOf(new ObservableTransitionState.Idle(CommunalScenes.Communal)));
mTestScope.getTestScheduler().runCurrent();
// ScrimState also transitions.
verify(mScrimController).transitionTo(ScrimState.GLANCEABLE_HUB);
// Transition away from the glanceable hub.
- mCommunalRepository.setTransitionState(flowOf(new ObservableTransitionState.Idle(
- CommunalScenes.Blank)));
+ mKosmos.getCommunalRepository()
+ .setTransitionState(
+ flowOf(new ObservableTransitionState.Idle(CommunalScenes.Blank)));
mTestScope.getTestScheduler().runCurrent();
// ScrimState goes back to UNLOCKED.
@@ -901,16 +937,18 @@
when(mKeyguardUpdateMonitor.isDreaming()).thenReturn(true);
// Transition to the glanceable hub.
- mCommunalRepository.setTransitionState(flowOf(new ObservableTransitionState.Idle(
- CommunalScenes.Communal)));
+ mKosmos.getCommunalRepository()
+ .setTransitionState(
+ flowOf(new ObservableTransitionState.Idle(CommunalScenes.Communal)));
mTestScope.getTestScheduler().runCurrent();
// ScrimState also transitions.
verify(mScrimController).transitionTo(ScrimState.GLANCEABLE_HUB_OVER_DREAM);
// Transition away from the glanceable hub.
- mCommunalRepository.setTransitionState(flowOf(new ObservableTransitionState.Idle(
- CommunalScenes.Blank)));
+ mKosmos.getCommunalRepository()
+ .setTransitionState(
+ flowOf(new ObservableTransitionState.Idle(CommunalScenes.Blank)));
mTestScope.getTestScheduler().runCurrent();
// ScrimState goes back to UNLOCKED.
@@ -1105,18 +1143,16 @@
}
@Test
+ @EnableFlags(com.android.systemui.Flags.FLAG_TRUNCATED_STATUS_BAR_ICONS_FIX)
public void updateResources_flagEnabled_doesNotUpdateStatusBarWindowHeight() {
- mSetFlagsRule.enableFlags(com.android.systemui.Flags.FLAG_TRUNCATED_STATUS_BAR_ICONS_FIX);
-
mCentralSurfaces.updateResources();
verify(mStatusBarWindowController, never()).refreshStatusBarHeight();
}
@Test
+ @DisableFlags(com.android.systemui.Flags.FLAG_TRUNCATED_STATUS_BAR_ICONS_FIX)
public void updateResources_flagDisabled_updatesStatusBarWindowHeight() {
- mSetFlagsRule.disableFlags(com.android.systemui.Flags.FLAG_TRUNCATED_STATUS_BAR_ICONS_FIX);
-
mCentralSurfaces.updateResources();
verify(mStatusBarWindowController).refreshStatusBarHeight();
@@ -1151,10 +1187,10 @@
}
@Test
+ @EnableFlags(FLAG_KEYBOARD_SHORTCUT_HELPER_REWRITE)
public void dismissKeyboardShortcuts_largeScreen_bothFlagsEnabled_doesNotDismissAny() {
switchToLargeScreen();
mFeatureFlags.set(SHORTCUT_LIST_SEARCH_LAYOUT, true);
- mSetFlagsRule.enableFlags(FLAG_KEYBOARD_SHORTCUT_HELPER_REWRITE);
createCentralSurfaces();
dismissKeyboardShortcuts();
@@ -1163,10 +1199,10 @@
}
@Test
+ @DisableFlags(FLAG_KEYBOARD_SHORTCUT_HELPER_REWRITE)
public void dismissKeyboardShortcuts_largeScreen_newFlagsDisabled_dismissesTabletVersion() {
switchToLargeScreen();
mFeatureFlags.set(SHORTCUT_LIST_SEARCH_LAYOUT, true);
- mSetFlagsRule.disableFlags(FLAG_KEYBOARD_SHORTCUT_HELPER_REWRITE);
createCentralSurfaces();
dismissKeyboardShortcuts();
@@ -1175,10 +1211,10 @@
}
@Test
+ @DisableFlags(FLAG_KEYBOARD_SHORTCUT_HELPER_REWRITE)
public void dismissKeyboardShortcuts_largeScreen_bothFlagsDisabled_dismissesPhoneVersion() {
switchToLargeScreen();
mFeatureFlags.set(SHORTCUT_LIST_SEARCH_LAYOUT, false);
- mSetFlagsRule.disableFlags(FLAG_KEYBOARD_SHORTCUT_HELPER_REWRITE);
createCentralSurfaces();
dismissKeyboardShortcuts();
@@ -1188,10 +1224,10 @@
}
@Test
+ @EnableFlags(FLAG_KEYBOARD_SHORTCUT_HELPER_REWRITE)
public void dismissKeyboardShortcuts_smallScreen_bothFlagsEnabled_doesNotDismissAny() {
switchToSmallScreen();
mFeatureFlags.set(SHORTCUT_LIST_SEARCH_LAYOUT, true);
- mSetFlagsRule.enableFlags(FLAG_KEYBOARD_SHORTCUT_HELPER_REWRITE);
createCentralSurfaces();
dismissKeyboardShortcuts();
@@ -1200,10 +1236,10 @@
}
@Test
+ @DisableFlags(FLAG_KEYBOARD_SHORTCUT_HELPER_REWRITE)
public void dismissKeyboardShortcuts_smallScreen_newFlagsDisabled_dismissesPhoneVersion() {
switchToSmallScreen();
mFeatureFlags.set(SHORTCUT_LIST_SEARCH_LAYOUT, true);
- mSetFlagsRule.disableFlags(FLAG_KEYBOARD_SHORTCUT_HELPER_REWRITE);
createCentralSurfaces();
dismissKeyboardShortcuts();
@@ -1213,10 +1249,10 @@
}
@Test
+ @DisableFlags(FLAG_KEYBOARD_SHORTCUT_HELPER_REWRITE)
public void dismissKeyboardShortcuts_smallScreen_bothFlagsDisabled_dismissesPhoneVersion() {
switchToSmallScreen();
mFeatureFlags.set(SHORTCUT_LIST_SEARCH_LAYOUT, false);
- mSetFlagsRule.disableFlags(FLAG_KEYBOARD_SHORTCUT_HELPER_REWRITE);
createCentralSurfaces();
dismissKeyboardShortcuts();
@@ -1226,10 +1262,10 @@
}
@Test
+ @EnableFlags(FLAG_KEYBOARD_SHORTCUT_HELPER_REWRITE)
public void toggleKeyboardShortcuts_largeScreen_bothFlagsEnabled_doesNotTogglesAny() {
switchToLargeScreen();
mFeatureFlags.set(SHORTCUT_LIST_SEARCH_LAYOUT, true);
- mSetFlagsRule.enableFlags(FLAG_KEYBOARD_SHORTCUT_HELPER_REWRITE);
createCentralSurfaces();
int deviceId = 321;
@@ -1239,10 +1275,10 @@
}
@Test
+ @DisableFlags(FLAG_KEYBOARD_SHORTCUT_HELPER_REWRITE)
public void toggleKeyboardShortcuts_largeScreen_newFlagsDisabled_togglesTabletVersion() {
switchToLargeScreen();
mFeatureFlags.set(SHORTCUT_LIST_SEARCH_LAYOUT, true);
- mSetFlagsRule.disableFlags(FLAG_KEYBOARD_SHORTCUT_HELPER_REWRITE);
createCentralSurfaces();
int deviceId = 654;
@@ -1253,10 +1289,10 @@
}
@Test
+ @DisableFlags(FLAG_KEYBOARD_SHORTCUT_HELPER_REWRITE)
public void toggleKeyboardShortcuts_largeScreen_bothFlagsDisabled_togglesPhoneVersion() {
switchToLargeScreen();
mFeatureFlags.set(SHORTCUT_LIST_SEARCH_LAYOUT, false);
- mSetFlagsRule.disableFlags(FLAG_KEYBOARD_SHORTCUT_HELPER_REWRITE);
createCentralSurfaces();
int deviceId = 987;
@@ -1267,10 +1303,10 @@
}
@Test
+ @EnableFlags(FLAG_KEYBOARD_SHORTCUT_HELPER_REWRITE)
public void toggleKeyboardShortcuts_smallScreen_bothFlagsEnabled_doesNotToggleAny() {
switchToSmallScreen();
mFeatureFlags.set(SHORTCUT_LIST_SEARCH_LAYOUT, true);
- mSetFlagsRule.enableFlags(FLAG_KEYBOARD_SHORTCUT_HELPER_REWRITE);
createCentralSurfaces();
int deviceId = 789;
@@ -1280,10 +1316,10 @@
}
@Test
+ @DisableFlags(FLAG_KEYBOARD_SHORTCUT_HELPER_REWRITE)
public void toggleKeyboardShortcuts_smallScreen_newFlagsDisabled_togglesPhoneVersion() {
switchToSmallScreen();
mFeatureFlags.set(SHORTCUT_LIST_SEARCH_LAYOUT, true);
- mSetFlagsRule.disableFlags(FLAG_KEYBOARD_SHORTCUT_HELPER_REWRITE);
createCentralSurfaces();
int deviceId = 456;
@@ -1294,10 +1330,10 @@
}
@Test
+ @DisableFlags(FLAG_KEYBOARD_SHORTCUT_HELPER_REWRITE)
public void toggleKeyboardShortcuts_smallScreen_bothFlagsDisabled_togglesPhoneVersion() {
switchToSmallScreen();
mFeatureFlags.set(SHORTCUT_LIST_SEARCH_LAYOUT, false);
- mSetFlagsRule.disableFlags(FLAG_KEYBOARD_SHORTCUT_HELPER_REWRITE);
createCentralSurfaces();
int deviceId = 123;
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ConfigurationControllerImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ConfigurationControllerImplTest.kt
index 56d2397..942ea65 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ConfigurationControllerImplTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ConfigurationControllerImplTest.kt
@@ -21,7 +21,7 @@
import android.content.res.Configuration.UI_MODE_NIGHT_YES
import android.content.res.Configuration.UI_MODE_TYPE_CAR
import android.os.LocaleList
-import android.testing.AndroidTestingRunner
+import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.statusbar.policy.ConfigurationController.ConfigurationListener
@@ -36,7 +36,7 @@
import org.mockito.Mockito.verify
import java.util.Locale
-@RunWith(AndroidTestingRunner::class)
+@RunWith(AndroidJUnit4::class)
@SmallTest
class ConfigurationControllerImplTest : SysuiTestCase() {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/DozeScrimControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/DozeScrimControllerTest.java
index 34c43ef..3b3ec26 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/DozeScrimControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/DozeScrimControllerTest.java
@@ -20,9 +20,9 @@
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.verify;
-import android.testing.AndroidTestingRunner;
import android.testing.TestableLooper;
+import androidx.test.ext.junit.runners.AndroidJUnit4;
import androidx.test.filters.SmallTest;
import com.android.systemui.SysuiTestCase;
@@ -36,7 +36,7 @@
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
-@RunWith(AndroidTestingRunner.class)
+@RunWith(AndroidJUnit4.class)
@TestableLooper.RunWithLooper
@SmallTest
public class DozeScrimControllerTest extends SysuiTestCase {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/FoldStateListenerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/FoldStateListenerTest.kt
index 5d42d51..a3e2d19 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/FoldStateListenerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/FoldStateListenerTest.kt
@@ -16,7 +16,7 @@
package com.android.systemui.statusbar.phone
import android.hardware.devicestate.DeviceState
-import android.testing.AndroidTestingRunner
+import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.internal.R
import com.android.systemui.SysuiTestCase
@@ -30,7 +30,7 @@
import org.mockito.Mockito.verify
import org.mockito.MockitoAnnotations.initMocks
-@RunWith(AndroidTestingRunner::class)
+@RunWith(AndroidJUnit4::class)
@SmallTest
class FoldStateListenerTest : SysuiTestCase() {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/HeadsUpAppearanceControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/HeadsUpAppearanceControllerTest.java
index 3e9006e..0d06b64 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/HeadsUpAppearanceControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/HeadsUpAppearanceControllerTest.java
@@ -26,12 +26,12 @@
import static org.mockito.Mockito.when;
import android.platform.test.annotations.DisableFlags;
-import android.testing.AndroidTestingRunner;
import android.testing.TestableLooper;
import android.testing.TestableLooper.RunWithLooper;
import android.view.View;
import android.widget.TextView;
+import androidx.test.ext.junit.runners.AndroidJUnit4;
import androidx.test.filters.SmallTest;
import com.android.systemui.SysuiTestCase;
@@ -62,7 +62,7 @@
import java.util.Optional;
@SmallTest
-@RunWith(AndroidTestingRunner.class)
+@RunWith(AndroidJUnit4.class)
@RunWithLooper
public class HeadsUpAppearanceControllerTest extends SysuiTestCase {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/KeyguardClockPositionAlgorithmTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/KeyguardClockPositionAlgorithmTest.java
index fd295b5..cf87afb 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/KeyguardClockPositionAlgorithmTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/KeyguardClockPositionAlgorithmTest.java
@@ -26,8 +26,10 @@
import static org.mockito.Mockito.when;
import android.content.res.Resources;
-import android.testing.AndroidTestingRunner;
+import android.platform.test.annotations.DisableFlags;
+import android.platform.test.annotations.EnableFlags;
+import androidx.test.ext.junit.runners.AndroidJUnit4;
import androidx.test.filters.SmallTest;
import com.android.systemui.Flags;
@@ -48,7 +50,7 @@
import org.mockito.quality.Strictness;
@SmallTest
-@RunWith(AndroidTestingRunner.class)
+@RunWith(AndroidJUnit4.class)
public class KeyguardClockPositionAlgorithmTest extends SysuiTestCase {
private static final int SCREEN_HEIGHT = 2000;
private static final int EMPTY_HEIGHT = 0;
@@ -297,8 +299,8 @@
}
@Test
+ @DisableFlags(Flags.FLAG_CENTRALIZED_STATUS_BAR_HEIGHT_FIX)
public void notifPaddingMakesUpToFullMarginInSplitShade_refactorFlagOff_usesResource() {
- mSetFlagsRule.disableFlags(Flags.FLAG_CENTRALIZED_STATUS_BAR_HEIGHT_FIX);
int keyguardSplitShadeTopMargin = 100;
int largeScreenHeaderHeightResource = 70;
when(mResources.getDimensionPixelSize(R.dimen.keyguard_split_shade_top_margin))
@@ -316,8 +318,8 @@
}
@Test
+ @EnableFlags(Flags.FLAG_CENTRALIZED_STATUS_BAR_HEIGHT_FIX)
public void notifPaddingMakesUpToFullMarginInSplitShade_refactorFlagOn_usesHelper() {
- mSetFlagsRule.enableFlags(Flags.FLAG_CENTRALIZED_STATUS_BAR_HEIGHT_FIX);
int keyguardSplitShadeTopMargin = 100;
int largeScreenHeaderHeightHelper = 50;
int largeScreenHeaderHeightResource = 70;
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/KeyguardDismissUtilTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/KeyguardDismissUtilTest.java
index b0aa2d3..d880bec 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/KeyguardDismissUtilTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/KeyguardDismissUtilTest.java
@@ -20,8 +20,8 @@
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
-import android.testing.AndroidTestingRunner;
+import androidx.test.ext.junit.runners.AndroidJUnit4;
import androidx.test.filters.SmallTest;
import com.android.systemui.SysuiTestCase;
@@ -35,7 +35,7 @@
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
-@RunWith(AndroidTestingRunner.class)
+@RunWith(AndroidJUnit4.class)
@SmallTest
public class KeyguardDismissUtilTest extends SysuiTestCase {
@Mock
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/KeyguardIndicationTextViewTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/KeyguardIndicationTextViewTest.java
index 5cea931..109cd94 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/KeyguardIndicationTextViewTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/KeyguardIndicationTextViewTest.java
@@ -21,10 +21,10 @@
import static com.google.common.truth.Truth.assertThat;
-import android.testing.AndroidTestingRunner;
import android.testing.TestableLooper;
import android.view.View;
+import androidx.test.ext.junit.runners.AndroidJUnit4;
import androidx.test.filters.SmallTest;
import com.android.systemui.SysuiTestCase;
@@ -35,7 +35,7 @@
import org.junit.runner.RunWith;
@SmallTest
-@RunWith(AndroidTestingRunner.class)
+@RunWith(AndroidJUnit4.class)
@TestableLooper.RunWithLooper
public class KeyguardIndicationTextViewTest extends SysuiTestCase {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/KeyguardStatusBarViewControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/KeyguardStatusBarViewControllerTest.java
index dfee737..71f09a5 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/KeyguardStatusBarViewControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/KeyguardStatusBarViewControllerTest.java
@@ -42,11 +42,11 @@
import android.platform.test.annotations.DisableFlags;
import android.platform.test.annotations.EnableFlags;
import android.provider.Settings;
-import android.testing.AndroidTestingRunner;
import android.testing.TestableLooper;
import android.view.LayoutInflater;
import android.view.View;
+import androidx.test.ext.junit.runners.AndroidJUnit4;
import androidx.test.filters.SmallTest;
import com.android.keyguard.CarrierTextController;
@@ -71,7 +71,6 @@
import com.android.systemui.shade.ShadeViewStateProvider;
import com.android.systemui.shade.data.repository.FakeShadeRepository;
import com.android.systemui.statusbar.CommandQueue;
-import com.android.systemui.statusbar.NotificationMediaManager;
import com.android.systemui.statusbar.SysuiStatusBarStateController;
import com.android.systemui.statusbar.data.repository.FakeKeyguardStatusBarRepository;
import com.android.systemui.statusbar.domain.interactor.KeyguardStatusBarInteractor;
@@ -100,7 +99,7 @@
import org.mockito.MockitoAnnotations;
@SmallTest
-@RunWith(AndroidTestingRunner.class)
+@RunWith(AndroidJUnit4.class)
@TestableLooper.RunWithLooper
public class KeyguardStatusBarViewControllerTest extends SysuiTestCase {
@Mock
@@ -144,8 +143,6 @@
@Mock private SecureSettings mSecureSettings;
@Mock private CommandQueue mCommandQueue;
@Mock private KeyguardLogger mLogger;
-
- @Mock private NotificationMediaManager mNotificationMediaManager;
@Mock private StatusOverlayHoverListenerFactory mStatusOverlayHoverListenerFactory;
private TestShadeViewStateProvider mShadeViewStateProvider;
@@ -225,7 +222,6 @@
mFakeExecutor,
mBackgroundExecutor,
mLogger,
- mNotificationMediaManager,
mStatusOverlayHoverListenerFactory
);
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/KeyguardStatusBarViewTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/KeyguardStatusBarViewTest.java
index c44f979..0932a0c 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/KeyguardStatusBarViewTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/KeyguardStatusBarViewTest.java
@@ -18,11 +18,11 @@
import static com.google.common.truth.Truth.assertThat;
-import android.testing.AndroidTestingRunner;
import android.testing.TestableLooper;
import android.view.LayoutInflater;
import android.view.View;
+import androidx.test.ext.junit.runners.AndroidJUnit4;
import androidx.test.filters.SmallTest;
import com.android.systemui.SysuiTestCase;
@@ -33,7 +33,7 @@
import org.junit.runner.RunWith;
@SmallTest
-@RunWith(AndroidTestingRunner.class)
+@RunWith(AndroidJUnit4.class)
@TestableLooper.RunWithLooper
public class KeyguardStatusBarViewTest extends SysuiTestCase {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/LegacyLightsOutNotifControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/LegacyLightsOutNotifControllerTest.java
index f91064b..782ca91 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/LegacyLightsOutNotifControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/LegacyLightsOutNotifControllerTest.java
@@ -26,7 +26,6 @@
import static org.mockito.Mockito.when;
import android.platform.test.annotations.DisableFlags;
-import android.testing.AndroidTestingRunner;
import android.testing.TestableLooper.RunWithLooper;
import android.view.Display;
import android.view.View;
@@ -35,6 +34,7 @@
import android.view.WindowManager;
import androidx.lifecycle.Observer;
+import androidx.test.ext.junit.runners.AndroidJUnit4;
import androidx.test.filters.SmallTest;
import com.android.systemui.SysuiTestCase;
@@ -54,7 +54,7 @@
import java.util.Objects;
@SmallTest
-@RunWith(AndroidTestingRunner.class)
+@RunWith(AndroidJUnit4.class)
@RunWithLooper
@DisableFlags(NotificationsLiveDataStoreRefactor.FLAG_NAME)
public class LegacyLightsOutNotifControllerTest extends SysuiTestCase {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/LegacyNotificationIconAreaControllerImplTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/LegacyNotificationIconAreaControllerImplTest.java
index 9d53b9c..fea0e72 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/LegacyNotificationIconAreaControllerImplTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/LegacyNotificationIconAreaControllerImplTest.java
@@ -21,9 +21,9 @@
import static org.mockito.Mockito.when;
import android.platform.test.annotations.DisableFlags;
-import android.testing.AndroidTestingRunner;
import android.testing.TestableLooper;
+import androidx.test.ext.junit.runners.AndroidJUnit4;
import androidx.test.filters.SmallTest;
import com.android.systemui.Flags;
@@ -49,7 +49,7 @@
import java.util.Optional;
@SmallTest
-@RunWith(AndroidTestingRunner.class)
+@RunWith(AndroidJUnit4.class)
@TestableLooper.RunWithLooper
@DisableFlags(NotificationIconContainerRefactor.FLAG_NAME)
public class LegacyNotificationIconAreaControllerImplTest extends SysuiTestCase {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/LetterboxAppearanceCalculatorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/LetterboxAppearanceCalculatorTest.kt
index e7b287c..518b327 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/LetterboxAppearanceCalculatorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/LetterboxAppearanceCalculatorTest.kt
@@ -18,9 +18,9 @@
import android.graphics.Color
import android.graphics.Rect
-import android.testing.AndroidTestingRunner
import android.view.WindowInsetsController
import android.view.WindowInsetsController.APPEARANCE_SEMI_TRANSPARENT_STATUS_BARS
+import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.internal.statusbar.LetterboxDetails
import com.android.internal.view.AppearanceRegion
@@ -36,7 +36,7 @@
import org.mockito.Mockito.`when` as whenever
import org.mockito.MockitoAnnotations
-@RunWith(AndroidTestingRunner::class)
+@RunWith(AndroidJUnit4::class)
@SmallTest
class LetterboxAppearanceCalculatorTest : SysuiTestCase() {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/LetterboxBackgroundProviderTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/LetterboxBackgroundProviderTest.kt
index 1cc0bd3..788c2cb2 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/LetterboxBackgroundProviderTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/LetterboxBackgroundProviderTest.kt
@@ -21,8 +21,8 @@
import android.graphics.Color
import android.os.Handler
import android.os.Looper
-import android.testing.AndroidTestingRunner
import android.view.IWindowManager
+import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.util.concurrency.FakeExecutor
@@ -40,7 +40,7 @@
import org.mockito.Mockito.`when` as whenever
import org.mockito.MockitoAnnotations
-@RunWith(AndroidTestingRunner::class)
+@RunWith(AndroidJUnit4::class)
@SmallTest
class LetterboxBackgroundProviderTest : SysuiTestCase() {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/LightBarControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/LightBarControllerTest.java
index 7271a5e..a27073c 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/LightBarControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/LightBarControllerTest.java
@@ -35,10 +35,10 @@
import android.graphics.Color;
import android.graphics.Rect;
-import android.testing.AndroidTestingRunner;
import android.testing.TestableLooper;
import androidx.annotation.ColorInt;
+import androidx.test.ext.junit.runners.AndroidJUnit4;
import androidx.test.filters.SmallTest;
import com.android.internal.colorextraction.ColorExtractor.GradientColors;
@@ -67,7 +67,7 @@
import java.util.List;
@SmallTest
-@RunWith(AndroidTestingRunner.class)
+@RunWith(AndroidJUnit4.class)
@TestableLooper.RunWithLooper
public class LightBarControllerTest extends SysuiTestCase {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/LightBarTransitionsControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/LightBarTransitionsControllerTest.java
index f71114d..43c19b8 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/LightBarTransitionsControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/LightBarTransitionsControllerTest.java
@@ -28,9 +28,9 @@
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
-import android.testing.AndroidTestingRunner;
import android.testing.TestableLooper;
+import androidx.test.ext.junit.runners.AndroidJUnit4;
import androidx.test.filters.SmallTest;
import com.android.internal.policy.GestureNavigationSettingsObserver;
@@ -48,7 +48,7 @@
import org.mockito.MockitoAnnotations;
@SmallTest
-@RunWith(AndroidTestingRunner.class)
+@RunWith(AndroidJUnit4.class)
@TestableLooper.RunWithLooper
public class LightBarTransitionsControllerTest extends SysuiTestCase {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationIconContainerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationIconContainerTest.kt
index 9f4e1dd..9d97e5a 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationIconContainerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationIconContainerTest.kt
@@ -16,8 +16,8 @@
package com.android.systemui.statusbar.phone
import android.service.notification.StatusBarNotification
-import android.testing.AndroidTestingRunner
import android.testing.TestableLooper.RunWithLooper
+import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.statusbar.StatusBarIconView
@@ -34,7 +34,7 @@
/** Tests for {@link NotificationIconContainer}. */
@SmallTest
-@RunWith(AndroidTestingRunner::class)
+@RunWith(AndroidJUnit4::class)
@RunWithLooper
class NotificationIconContainerTest : SysuiTestCase() {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationTapHelperTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationTapHelperTest.java
index ccd1a8c..9522e1f 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationTapHelperTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationTapHelperTest.java
@@ -22,11 +22,11 @@
import static org.mockito.Mockito.when;
import android.content.res.Resources;
-import android.testing.AndroidTestingRunner;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewConfiguration;
+import androidx.test.ext.junit.runners.AndroidJUnit4;
import androidx.test.filters.SmallTest;
import com.android.systemui.SysuiTestCase;
@@ -42,7 +42,7 @@
import org.mockito.MockitoAnnotations;
@SmallTest
-@RunWith(AndroidTestingRunner.class)
+@RunWith(AndroidJUnit4.class)
public class NotificationTapHelperTest extends SysuiTestCase {
private NotificationTapHelper mNotificationTapHelper;
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/PhoneStatusBarPolicyTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/PhoneStatusBarPolicyTest.kt
index 8d2c158..f2f336c 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/PhoneStatusBarPolicyTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/PhoneStatusBarPolicyTest.kt
@@ -22,9 +22,9 @@
import android.content.SharedPreferences
import android.os.UserManager
import android.telecom.TelecomManager
-import android.testing.AndroidTestingRunner
import android.testing.TestableLooper
import android.testing.TestableLooper.RunWithLooper
+import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.broadcast.BroadcastDispatcher
@@ -79,7 +79,7 @@
import org.mockito.Mockito.`when` as whenever
import org.mockito.MockitoAnnotations
-@RunWith(AndroidTestingRunner::class)
+@RunWith(AndroidJUnit4::class)
@RunWithLooper
@OptIn(ExperimentalCoroutinesApi::class)
@SmallTest
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ScrimControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ScrimControllerTest.java
index 1000329..416a869 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ScrimControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ScrimControllerTest.java
@@ -49,12 +49,12 @@
import android.content.res.ColorStateList;
import android.content.res.TypedArray;
import android.graphics.Color;
-import android.testing.AndroidTestingRunner;
import android.testing.TestableLooper;
import android.testing.ViewUtils;
import android.util.MathUtils;
import android.view.View;
+import androidx.test.ext.junit.runners.AndroidJUnit4;
import androidx.test.filters.SmallTest;
import com.android.internal.colorextraction.ColorExtractor.GradientColors;
@@ -107,7 +107,7 @@
import java.util.HashSet;
import java.util.Map;
-@RunWith(AndroidTestingRunner.class)
+@RunWith(AndroidJUnit4.class)
@TestableLooper.RunWithLooper(setAsMainLooper = true)
@SmallTest
public class ScrimControllerTest extends SysuiTestCase {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarBoundsProviderTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarBoundsProviderTest.kt
index 61da701..b9cfe21 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarBoundsProviderTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarBoundsProviderTest.kt
@@ -17,10 +17,10 @@
package com.android.systemui.statusbar.phone
import android.graphics.Rect
-import android.testing.AndroidTestingRunner
import android.testing.TestableLooper.RunWithLooper
import android.view.View
import android.widget.FrameLayout
+import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.statusbar.phone.StatusBarBoundsProvider.BoundsChangeListener
@@ -37,7 +37,7 @@
import org.mockito.Mockito.verify
import org.mockito.MockitoAnnotations
-@RunWith(AndroidTestingRunner::class)
+@RunWith(AndroidJUnit4::class)
@RunWithLooper(setAsMainLooper = true)
@SmallTest
class StatusBarBoundsProviderTest : SysuiTestCase() {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManagerTest.java
index 6b3c005..3ca4c59 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManagerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManagerTest.java
@@ -41,7 +41,6 @@
import android.platform.test.flag.junit.CheckFlagsRule;
import android.platform.test.flag.junit.DeviceFlagsValueProvider;
import android.service.trust.TrustAgentService;
-import android.testing.AndroidTestingRunner;
import android.testing.TestableLooper;
import android.view.MotionEvent;
import android.view.View;
@@ -55,6 +54,7 @@
import android.window.OnBackInvokedDispatcher;
import android.window.WindowOnBackInvokedDispatcher;
+import androidx.test.ext.junit.runners.AndroidJUnit4;
import androidx.test.filters.SmallTest;
import com.android.internal.util.LatencyTracker;
@@ -119,7 +119,7 @@
import java.util.Optional;
@SmallTest
-@RunWith(AndroidTestingRunner.class)
+@RunWith(AndroidJUnit4.class)
@TestableLooper.RunWithLooper
public class StatusBarKeyguardViewManagerTest extends SysuiTestCase {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarterTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarterTest.java
index 269510e..9fa392f 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarterTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarterTest.java
@@ -51,9 +51,9 @@
import android.os.UserHandle;
import android.service.dreams.IDreamManager;
import android.service.notification.StatusBarNotification;
-import android.testing.AndroidTestingRunner;
import android.testing.TestableLooper;
+import androidx.test.ext.junit.runners.AndroidJUnit4;
import androidx.test.filters.SmallTest;
import com.android.internal.jank.InteractionJankMonitor;
@@ -119,7 +119,7 @@
import java.util.Optional;
@SmallTest
-@RunWith(AndroidTestingRunner.class)
+@RunWith(AndroidJUnit4.class)
@TestableLooper.RunWithLooper(setAsMainLooper = true)
public class StatusBarNotificationActivityStarterTest extends SysuiTestCase {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarNotificationPresenterTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarNotificationPresenterTest.java
index a8c5fc3..95472cad 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarNotificationPresenterTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarNotificationPresenterTest.java
@@ -34,10 +34,10 @@
import android.app.StatusBarManager;
import android.platform.test.annotations.DisableFlags;
import android.platform.test.annotations.EnableFlags;
-import android.testing.AndroidTestingRunner;
import android.testing.TestableLooper;
import android.testing.TestableLooper.RunWithLooper;
+import androidx.test.ext.junit.runners.AndroidJUnit4;
import androidx.test.filters.SmallTest;
import com.android.systemui.InitController;
@@ -85,7 +85,7 @@
import java.util.Set;
@SmallTest
-@RunWith(AndroidTestingRunner.class)
+@RunWith(AndroidJUnit4.class)
@RunWithLooper()
public class StatusBarNotificationPresenterTest extends SysuiTestCase {
private StatusBarNotificationPresenter mStatusBarNotificationPresenter;
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarRemoteInputCallbackTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarRemoteInputCallbackTest.java
index 929099a..35888a5 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarRemoteInputCallbackTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarRemoteInputCallbackTest.java
@@ -24,10 +24,10 @@
import static org.mockito.internal.verification.VerificationModeFactory.times;
import android.content.Intent;
-import android.testing.AndroidTestingRunner;
import android.testing.TestableLooper;
import android.view.View;
+import androidx.test.ext.junit.runners.AndroidJUnit4;
import androidx.test.filters.SmallTest;
import com.android.systemui.SysuiTestCase;
@@ -52,7 +52,7 @@
import org.mockito.MockitoAnnotations;
@SmallTest
-@RunWith(AndroidTestingRunner.class)
+@RunWith(AndroidJUnit4.class)
@TestableLooper.RunWithLooper
public class StatusBarRemoteInputCallbackTest extends SysuiTestCase {
@Mock private DeviceProvisionedController mDeviceProvisionedController;
@@ -103,4 +103,4 @@
verify(mStatusBarKeyguardViewManager).showBouncer(true);
}
-}
\ No newline at end of file
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusOverlayHoverListenerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusOverlayHoverListenerTest.kt
index 1455693..11dd587 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusOverlayHoverListenerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusOverlayHoverListenerTest.kt
@@ -21,7 +21,6 @@
import android.graphics.drawable.Drawable
import android.graphics.drawable.PaintDrawable
import android.os.SystemClock
-import android.testing.AndroidTestingRunner
import android.testing.TestableLooper
import android.testing.TestableLooper.RunWithLooper
import android.testing.ViewUtils
@@ -30,6 +29,7 @@
import android.view.ViewGroupOverlay
import android.widget.LinearLayout
import androidx.annotation.ColorInt
+import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.systemui.res.R
import com.android.systemui.SysuiTestCase
@@ -45,7 +45,7 @@
import org.junit.runner.RunWith
import org.mockito.Mockito.verify
-@RunWith(AndroidTestingRunner::class)
+@RunWith(AndroidJUnit4::class)
@RunWithLooper(setAsMainLooper = true)
@SmallTest
class StatusOverlayHoverListenerTest : SysuiTestCase() {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/SystemUIBottomSheetDialogTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/SystemUIBottomSheetDialogTest.kt
index dedd0af..b560c59 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/SystemUIBottomSheetDialogTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/SystemUIBottomSheetDialogTest.kt
@@ -15,9 +15,9 @@
import android.app.Dialog
import android.content.res.Configuration
-import android.testing.AndroidTestingRunner
import android.testing.TestableLooper.RunWithLooper
import android.view.WindowManager
+import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.kosmos.testScope
@@ -39,7 +39,7 @@
@OptIn(ExperimentalCoroutinesApi::class)
@SmallTest
-@RunWith(AndroidTestingRunner::class)
+@RunWith(AndroidJUnit4::class)
@RunWithLooper(setAsMainLooper = true)
class SystemUIBottomSheetDialogTest : SysuiTestCase() {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/UnlockedScreenOffAnimationControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/UnlockedScreenOffAnimationControllerTest.kt
index c8ff20b..624c070 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/UnlockedScreenOffAnimationControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/UnlockedScreenOffAnimationControllerTest.kt
@@ -18,9 +18,9 @@
import android.os.Handler
import android.os.PowerManager
-import android.testing.AndroidTestingRunner
import android.testing.TestableLooper.RunWithLooper
import android.view.Display
+import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.internal.jank.InteractionJankMonitor
import com.android.systemui.SysuiTestCase
@@ -51,7 +51,7 @@
import org.mockito.MockitoAnnotations
@SmallTest
-@RunWith(AndroidTestingRunner::class)
+@RunWith(AndroidJUnit4::class)
@RunWithLooper(setAsMainLooper = true)
class UnlockedScreenOffAnimationControllerTest : SysuiTestCase() {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/fragment/CollapsedStatusBarFragmentTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/fragment/CollapsedStatusBarFragmentTest.java
index 66211c9..fdf77ae 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/fragment/CollapsedStatusBarFragmentTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/fragment/CollapsedStatusBarFragmentTest.java
@@ -426,7 +426,7 @@
fragment.disable(DEFAULT_DISPLAY, 0, 0, false);
assertEquals(View.GONE,
- mFragment.getView().findViewById(R.id.ongoing_call_chip).getVisibility());
+ mFragment.getView().findViewById(R.id.ongoing_activity_chip).getVisibility());
}
@Test
@@ -438,7 +438,7 @@
fragment.disable(DEFAULT_DISPLAY, 0, 0, false);
assertEquals(View.VISIBLE,
- mFragment.getView().findViewById(R.id.ongoing_call_chip).getVisibility());
+ mFragment.getView().findViewById(R.id.ongoing_activity_chip).getVisibility());
assertEquals(View.INVISIBLE, getNotificationAreaView().getVisibility());
}
@@ -452,7 +452,7 @@
StatusBarManager.DISABLE_NOTIFICATION_ICONS, 0, false);
assertEquals(View.GONE,
- mFragment.getView().findViewById(R.id.ongoing_call_chip).getVisibility());
+ mFragment.getView().findViewById(R.id.ongoing_activity_chip).getVisibility());
}
@Test
@@ -465,7 +465,7 @@
fragment.disable(DEFAULT_DISPLAY, 0, 0, false);
assertEquals(View.GONE,
- mFragment.getView().findViewById(R.id.ongoing_call_chip).getVisibility());
+ mFragment.getView().findViewById(R.id.ongoing_activity_chip).getVisibility());
}
@Test
@@ -477,21 +477,21 @@
fragment.disable(DEFAULT_DISPLAY, 0, 0, false);
assertEquals(View.VISIBLE,
- mFragment.getView().findViewById(R.id.ongoing_call_chip).getVisibility());
+ mFragment.getView().findViewById(R.id.ongoing_activity_chip).getVisibility());
// Ongoing call ended
when(mOngoingCallController.hasOngoingCall()).thenReturn(false);
fragment.disable(DEFAULT_DISPLAY, 0, 0, false);
assertEquals(View.GONE,
- mFragment.getView().findViewById(R.id.ongoing_call_chip).getVisibility());
+ mFragment.getView().findViewById(R.id.ongoing_activity_chip).getVisibility());
// Ongoing call started
when(mOngoingCallController.hasOngoingCall()).thenReturn(true);
fragment.disable(DEFAULT_DISPLAY, 0, 0, false);
assertEquals(View.VISIBLE,
- mFragment.getView().findViewById(R.id.ongoing_call_chip).getVisibility());
+ mFragment.getView().findViewById(R.id.ongoing_activity_chip).getVisibility());
}
@Test
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ongoingcall/OngoingCallControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ongoingcall/OngoingCallControllerTest.kt
index 05464f3..4d6798b 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ongoingcall/OngoingCallControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ongoingcall/OngoingCallControllerTest.kt
@@ -106,7 +106,7 @@
fun setUp() {
allowTestableLooperAsMainThread()
TestableLooper.get(this).runWithLooper {
- chipView = LayoutInflater.from(mContext).inflate(R.layout.ongoing_call_chip, null)
+ chipView = LayoutInflater.from(mContext).inflate(R.layout.ongoing_activity_chip, null)
}
MockitoAnnotations.initMocks(this)
@@ -206,7 +206,7 @@
View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED)
)
- assertThat(chipView.findViewById<View>(R.id.ongoing_call_chip_time)?.measuredWidth)
+ assertThat(chipView.findViewById<View>(R.id.ongoing_activity_chip_time)?.measuredWidth)
.isEqualTo(0)
}
@@ -222,7 +222,7 @@
View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED)
)
- assertThat(chipView.findViewById<View>(R.id.ongoing_call_chip_time)?.measuredWidth)
+ assertThat(chipView.findViewById<View>(R.id.ongoing_activity_chip_time)?.measuredWidth)
.isGreaterThan(0)
}
@@ -237,7 +237,7 @@
View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED)
)
- assertThat(chipView.findViewById<View>(R.id.ongoing_call_chip_time)?.measuredWidth)
+ assertThat(chipView.findViewById<View>(R.id.ongoing_activity_chip_time)?.measuredWidth)
.isGreaterThan(0)
}
@@ -472,7 +472,10 @@
lateinit var newChipView: View
TestableLooper.get(this).runWithLooper {
- newChipView = LayoutInflater.from(mContext).inflate(R.layout.ongoing_call_chip, null)
+ newChipView = LayoutInflater.from(mContext).inflate(
+ R.layout.ongoing_activity_chip,
+ null
+ )
}
// Change the chip view associated with the controller.
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/MobileRepositorySwitcherTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/MobileRepositorySwitcherTest.kt
index 598b12c..eb2538e 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/MobileRepositorySwitcherTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/MobileRepositorySwitcherTest.kt
@@ -20,6 +20,7 @@
import android.telephony.SubscriptionManager
import android.telephony.SubscriptionManager.PROFILE_CLASS_UNSET
import android.telephony.TelephonyManager
+import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.demomode.DemoMode
@@ -60,7 +61,6 @@
import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
-import org.junit.runners.JUnit4
import org.mockito.Mock
import org.mockito.Mockito.verify
import org.mockito.MockitoAnnotations
@@ -73,7 +73,7 @@
@Suppress("EXPERIMENTAL_IS_NOT_ENABLED")
@OptIn(ExperimentalCoroutinesApi::class)
@SmallTest
-@RunWith(JUnit4::class)
+@RunWith(AndroidJUnit4::class)
class MobileRepositorySwitcherTest : SysuiTestCase() {
private lateinit var underTest: MobileRepositorySwitcher
private lateinit var realRepo: MobileConnectionsRepositoryImpl
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/CarrierMergedConnectionRepositoryTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/CarrierMergedConnectionRepositoryTest.kt
index 2654401..237aabc 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/CarrierMergedConnectionRepositoryTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/CarrierMergedConnectionRepositoryTest.kt
@@ -17,7 +17,7 @@
package com.android.systemui.statusbar.pipeline.mobile.data.repository.prod
import android.telephony.TelephonyManager
-import android.testing.AndroidTestingRunner
+import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.coroutines.collectLastValue
@@ -44,7 +44,7 @@
@SmallTest
@OptIn(ExperimentalCoroutinesApi::class)
-@RunWith(AndroidTestingRunner::class)
+@RunWith(AndroidJUnit4::class)
class CarrierMergedConnectionRepositoryTest : SysuiTestCase() {
private lateinit var underTest: CarrierMergedConnectionRepository
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/MobileConnectionsRepositoryTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/MobileConnectionsRepositoryTest.kt
index 36df61d..96e599f 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/MobileConnectionsRepositoryTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/MobileConnectionsRepositoryTest.kt
@@ -16,6 +16,7 @@
package com.android.systemui.statusbar.pipeline.mobile.data.repository.prod
+import android.annotation.SuppressLint
import android.content.Intent
import android.net.ConnectivityManager
import android.net.Network
@@ -27,8 +28,10 @@
import android.net.vcn.VcnTransportInfo
import android.net.wifi.WifiInfo
import android.net.wifi.WifiManager
+import android.os.Bundle
import android.os.ParcelUuid
import android.telephony.CarrierConfigManager
+import android.telephony.ServiceState
import android.telephony.SubscriptionInfo
import android.telephony.SubscriptionManager
import android.telephony.SubscriptionManager.INVALID_SUBSCRIPTION_ID
@@ -53,6 +56,7 @@
import com.android.systemui.statusbar.connectivity.WifiPickerTrackerFactory
import com.android.systemui.statusbar.pipeline.airplane.data.repository.FakeAirplaneModeRepository
import com.android.systemui.statusbar.pipeline.mobile.data.MobileInputLogger
+import com.android.systemui.statusbar.pipeline.mobile.data.model.ServiceStateModel
import com.android.systemui.statusbar.pipeline.mobile.data.model.SubscriptionModel
import com.android.systemui.statusbar.pipeline.mobile.data.repository.CarrierConfigRepository
import com.android.systemui.statusbar.pipeline.mobile.data.repository.MobileConnectionRepository
@@ -595,6 +599,51 @@
assertThat(mobileRepo.getIsCarrierMerged()).isFalse()
}
+ @SuppressLint("UnspecifiedRegisterReceiverFlag")
+ @Test
+ fun testDeviceServiceStateFromBroadcast_eagerlyWatchesBroadcast() =
+ testScope.runTest {
+ // Value starts out empty (null)
+ assertThat(underTest.deviceServiceState.value).isNull()
+
+ // WHEN an appropriate intent gets sent out
+ val intent = serviceStateIntent(subId = -1, emergencyOnly = false)
+ fakeBroadcastDispatcher.sendIntentToMatchingReceiversOnly(
+ context,
+ intent,
+ )
+ runCurrent()
+
+ // THEN the repo's state is updated
+ val expected = ServiceStateModel(isEmergencyOnly = false)
+ assertThat(underTest.deviceServiceState.value).isEqualTo(expected)
+ }
+
+ @Test
+ fun testDeviceServiceStateFromBroadcast_followsSubIdNegativeOne() =
+ testScope.runTest {
+ // device based state tracks -1
+ val intent = serviceStateIntent(subId = -1, emergencyOnly = false)
+ fakeBroadcastDispatcher.sendIntentToMatchingReceiversOnly(
+ context,
+ intent,
+ )
+ runCurrent()
+
+ val deviceBasedState = ServiceStateModel(isEmergencyOnly = false)
+ assertThat(underTest.deviceServiceState.value).isEqualTo(deviceBasedState)
+
+ // ... and ignores any other subId
+ val intent2 = serviceStateIntent(subId = 1, emergencyOnly = true)
+ fakeBroadcastDispatcher.sendIntentToMatchingReceiversOnly(
+ context,
+ intent2,
+ )
+ runCurrent()
+
+ assertThat(underTest.deviceServiceState.value).isEqualTo(deviceBasedState)
+ }
+
@Test
@Ignore("b/333912012")
fun testConnectionCache_clearsInvalidSubscriptions() =
@@ -1491,5 +1540,24 @@
whenever(it.transportInfo).thenReturn(WIFI_INFO_ACTIVE)
whenever(it.hasCapability(NET_CAPABILITY_VALIDATED)).thenReturn(true)
}
+
+ /**
+ * To properly mimic telephony manager, create a service state, and then turn it into an
+ * intent
+ */
+ private fun serviceStateIntent(
+ subId: Int,
+ emergencyOnly: Boolean = false,
+ ): Intent {
+ val serviceState = ServiceState().apply { isEmergencyOnly = emergencyOnly }
+
+ val bundle = Bundle()
+ serviceState.fillInNotifierBundle(bundle)
+
+ return Intent(Intent.ACTION_SERVICE_STATE).apply {
+ putExtras(bundle)
+ putExtra(SubscriptionManager.EXTRA_SUBSCRIPTION_INDEX, subId)
+ }
+ }
}
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/domain/interactor/MobileIconsInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/domain/interactor/MobileIconsInteractorTest.kt
index 0f9cbfa..58d9ee3 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/domain/interactor/MobileIconsInteractorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/domain/interactor/MobileIconsInteractorTest.kt
@@ -28,6 +28,7 @@
import com.android.systemui.flags.FakeFeatureFlagsClassic
import com.android.systemui.flags.Flags
import com.android.systemui.log.table.TableLogBuffer
+import com.android.systemui.statusbar.pipeline.mobile.data.model.ServiceStateModel
import com.android.systemui.statusbar.pipeline.mobile.data.model.SubscriptionModel
import com.android.systemui.statusbar.pipeline.mobile.data.repository.FakeMobileConnectionRepository
import com.android.systemui.statusbar.pipeline.mobile.data.repository.FakeMobileConnectionsRepository
@@ -888,6 +889,22 @@
assertThat(interactor1).isSameInstanceAs(interactor2)
}
+ @Test
+ fun deviceBasedEmergencyMode_emergencyCallsOnly_followsDeviceServiceStateFromRepo() =
+ testScope.runTest {
+ val latest by collectLastValue(underTest.isDeviceInEmergencyCallsOnlyMode)
+
+ connectionsRepository.deviceServiceState.value =
+ ServiceStateModel(isEmergencyOnly = true)
+
+ assertThat(latest).isTrue()
+
+ connectionsRepository.deviceServiceState.value =
+ ServiceStateModel(isEmergencyOnly = false)
+
+ assertThat(latest).isFalse()
+ }
+
/**
* Convenience method for creating a pair of subscriptions to test the filteredSubscriptions
* flow.
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/satellite/domain/interactor/DeviceBasedSatelliteInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/satellite/domain/interactor/DeviceBasedSatelliteInteractorTest.kt
index 405e3ed..d303976 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/satellite/domain/interactor/DeviceBasedSatelliteInteractorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/satellite/domain/interactor/DeviceBasedSatelliteInteractorTest.kt
@@ -22,6 +22,7 @@
import com.android.internal.telephony.flags.Flags.FLAG_OEM_ENABLED_SATELLITE_FLAG
import com.android.systemui.SysuiTestCase
import com.android.systemui.coroutines.collectLastValue
+import com.android.systemui.log.core.FakeLogBuffer
import com.android.systemui.statusbar.pipeline.mobile.domain.interactor.FakeMobileIconsInteractor
import com.android.systemui.statusbar.pipeline.mobile.util.FakeMobileMappingsProxy
import com.android.systemui.statusbar.pipeline.satellite.data.prod.FakeDeviceBasedSatelliteRepository
@@ -71,6 +72,7 @@
deviceProvisioningInteractor,
wifiInteractor,
testScope.backgroundScope,
+ FakeLogBuffer.Factory.create(),
)
}
@@ -114,6 +116,7 @@
deviceProvisioningInteractor,
wifiInteractor,
testScope.backgroundScope,
+ FakeLogBuffer.Factory.create(),
)
val latest by collectLastValue(underTest.isSatelliteAllowed)
@@ -162,6 +165,7 @@
deviceProvisioningInteractor,
wifiInteractor,
testScope.backgroundScope,
+ FakeLogBuffer.Factory.create(),
)
val latest by collectLastValue(underTest.connectionState)
@@ -218,6 +222,7 @@
deviceProvisioningInteractor,
wifiInteractor,
testScope.backgroundScope,
+ FakeLogBuffer.Factory.create(),
)
val latest by collectLastValue(underTest.signalStrength)
@@ -238,25 +243,97 @@
@Test
@EnableFlags(FLAG_OEM_ENABLED_SATELLITE_FLAG)
- fun areAllConnectionsOutOfService_noConnections_yes() =
+ fun areAllConnectionsOutOfService_noConnections_noDeviceEmergencyCalls_yes() =
testScope.runTest {
val latest by collectLastValue(underTest.areAllConnectionsOutOfService)
// GIVEN, 0 connections
+ // GIVEN, device is not in emergency calls only mode
+ iconsInteractor.isDeviceInEmergencyCallsOnlyMode.value = false
+
// THEN the value is propagated to this interactor
assertThat(latest).isTrue()
}
@Test
@EnableFlags(FLAG_OEM_ENABLED_SATELLITE_FLAG)
- fun areAllConnectionsOutOfService_twoConnectionsOos_nonNtn_yes() =
+ fun areAllConnectionsOutOfService_noConnections_deviceEmergencyCalls_yes() =
+ testScope.runTest {
+ val latest by collectLastValue(underTest.areAllConnectionsOutOfService)
+
+ // GIVEN, 0 connections
+
+ // GIVEN, device is in emergency calls only mode
+ iconsInteractor.isDeviceInEmergencyCallsOnlyMode.value = true
+
+ // THEN the value is propagated to this interactor
+ assertThat(latest).isFalse()
+ }
+
+ @Test
+ @EnableFlags(FLAG_OEM_ENABLED_SATELLITE_FLAG)
+ fun areAllConnectionsOutOfService_oneConnectionInService_thenLost_noDeviceEmergencyCalls_yes() =
+ testScope.runTest {
+ val latest by collectLastValue(underTest.areAllConnectionsOutOfService)
+
+ // GIVEN, 1 connections
+ val i1 = iconsInteractor.getMobileConnectionInteractorForSubId(1)
+ // GIVEN, no device-based emergency calls
+ iconsInteractor.isDeviceInEmergencyCallsOnlyMode.value = false
+
+ // WHEN connection is in service
+ i1.isInService.value = true
+ i1.isEmergencyOnly.value = false
+ i1.isNonTerrestrial.value = false
+
+ // THEN we are considered NOT to be OOS
+ assertThat(latest).isFalse()
+
+ // WHEN the connection disappears
+ iconsInteractor.icons.value = listOf()
+
+ // THEN we are back to OOS
+ assertThat(latest).isTrue()
+ }
+
+ @Test
+ @EnableFlags(FLAG_OEM_ENABLED_SATELLITE_FLAG)
+ fun areAllConnectionsOutOfService_oneConnectionInService_thenLost_deviceEmergencyCalls_no() =
+ testScope.runTest {
+ val latest by collectLastValue(underTest.areAllConnectionsOutOfService)
+
+ // GIVEN, 1 connections
+ val i1 = iconsInteractor.getMobileConnectionInteractorForSubId(1)
+ // GIVEN, device-based emergency calls
+ iconsInteractor.isDeviceInEmergencyCallsOnlyMode.value = true
+
+ // WHEN one connection is in service
+ i1.isInService.value = true
+ i1.isEmergencyOnly.value = false
+ i1.isNonTerrestrial.value = false
+
+ // THEN we are considered NOT to be OOS
+ assertThat(latest).isFalse()
+
+ // WHEN the connection disappears
+ iconsInteractor.icons.value = listOf()
+
+ // THEN we are still NOT in OOS, due to device-based emergency calls
+ assertThat(latest).isFalse()
+ }
+
+ @Test
+ @EnableFlags(FLAG_OEM_ENABLED_SATELLITE_FLAG)
+ fun areAllConnectionsOutOfService_twoConnectionsOos_nonNtn_noDeviceEmergencyCalls_yes() =
testScope.runTest {
val latest by collectLastValue(underTest.areAllConnectionsOutOfService)
// GIVEN, 2 connections
val i1 = iconsInteractor.getMobileConnectionInteractorForSubId(1)
val i2 = iconsInteractor.getMobileConnectionInteractorForSubId(2)
+ // GIVEN, no device-based emergency calls
+ iconsInteractor.isDeviceInEmergencyCallsOnlyMode.value = false
// WHEN all of the connections are OOS and none are NTN
i1.isInService.value = false
@@ -272,13 +349,39 @@
@Test
@EnableFlags(FLAG_OEM_ENABLED_SATELLITE_FLAG)
- fun areAllConnectionsOutOfService_twoConnectionsOos_oneNtn_no() =
+ fun areAllConnectionsOutOfService_twoConnectionsOos_nonNtn_deviceEmergencyCalls_no() =
testScope.runTest {
val latest by collectLastValue(underTest.areAllConnectionsOutOfService)
// GIVEN, 2 connections
val i1 = iconsInteractor.getMobileConnectionInteractorForSubId(1)
val i2 = iconsInteractor.getMobileConnectionInteractorForSubId(2)
+ // GIVEN, device-based emergency calls
+ iconsInteractor.isDeviceInEmergencyCallsOnlyMode.value = true
+
+ // WHEN all of the connections are OOS and none are NTN
+ i1.isInService.value = false
+ i1.isEmergencyOnly.value = false
+ i1.isNonTerrestrial.value = false
+ i2.isInService.value = false
+ i2.isEmergencyOnly.value = false
+ i2.isNonTerrestrial.value = false
+
+ // THEN we are not considered OOS due to device based emergency calling
+ assertThat(latest).isFalse()
+ }
+
+ @Test
+ @EnableFlags(FLAG_OEM_ENABLED_SATELLITE_FLAG)
+ fun areAllConnectionsOutOfService_twoConnectionsOos_noDeviceEmergencyCalls_oneNtn_no() =
+ testScope.runTest {
+ val latest by collectLastValue(underTest.areAllConnectionsOutOfService)
+
+ // GIVEN, 2 connections
+ val i1 = iconsInteractor.getMobileConnectionInteractorForSubId(1)
+ val i2 = iconsInteractor.getMobileConnectionInteractorForSubId(2)
+ // GIVEN, no device-based emergency calls
+ iconsInteractor.isDeviceInEmergencyCallsOnlyMode.value = false
// WHEN all of the connections are OOS and one is NTN
i1.isInService.value = false
@@ -296,12 +399,14 @@
@Test
@EnableFlags(FLAG_OEM_ENABLED_SATELLITE_FLAG)
- fun areAllConnectionsOutOfService_oneConnectionOos_nonNtn_yes() =
+ fun areAllConnectionsOutOfService_oneConnectionOos_noDeviceEmergencyCalls_nonNtn_yes() =
testScope.runTest {
val latest by collectLastValue(underTest.areAllConnectionsOutOfService)
// GIVEN, 1 connection
val i1 = iconsInteractor.getMobileConnectionInteractorForSubId(1)
+ // GIVEN, no device-based emergency calls
+ iconsInteractor.isDeviceInEmergencyCallsOnlyMode.value = false
// WHEN all of the connections are OOS
i1.isInService.value = false
@@ -314,7 +419,27 @@
@Test
@EnableFlags(FLAG_OEM_ENABLED_SATELLITE_FLAG)
- fun areAllConnectionsOutOfService_oneConnectionOos_ntn_yes() =
+ fun areAllConnectionsOutOfService_oneConnectionOos_nonNtn_no() =
+ testScope.runTest {
+ val latest by collectLastValue(underTest.areAllConnectionsOutOfService)
+
+ // GIVEN, 1 connection
+ val i1 = iconsInteractor.getMobileConnectionInteractorForSubId(1)
+ // GIVEN, device-based emergency calls
+ iconsInteractor.isDeviceInEmergencyCallsOnlyMode.value = true
+
+ // WHEN all of the connections are OOS
+ i1.isInService.value = false
+ i1.isEmergencyOnly.value = false
+ i1.isNonTerrestrial.value = false
+
+ // THEN the value is propagated to this interactor
+ assertThat(latest).isFalse()
+ }
+
+ @Test
+ @EnableFlags(FLAG_OEM_ENABLED_SATELLITE_FLAG)
+ fun areAllConnectionsOutOfService_oneConnectionOos_ntn_no() =
testScope.runTest {
val latest by collectLastValue(underTest.areAllConnectionsOutOfService)
@@ -416,6 +541,7 @@
deviceProvisioningInteractor,
wifiInteractor,
testScope.backgroundScope,
+ FakeLogBuffer.Factory.create(),
)
val latest by collectLastValue(underTest.areAllConnectionsOutOfService)
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/satellite/ui/viewmodel/DeviceBasedSatelliteViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/satellite/ui/viewmodel/DeviceBasedSatelliteViewModelTest.kt
index ceaae9e..43b9568 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/satellite/ui/viewmodel/DeviceBasedSatelliteViewModelTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/satellite/ui/viewmodel/DeviceBasedSatelliteViewModelTest.kt
@@ -75,6 +75,7 @@
deviceProvisioningInteractor,
wifiInteractor,
testScope.backgroundScope,
+ FakeLogBuffer.Factory.create(),
)
underTest =
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/BaseUserSwitcherAdapterTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/BaseUserSwitcherAdapterTest.kt
index d1c38f6..0a5e630 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/BaseUserSwitcherAdapterTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/BaseUserSwitcherAdapterTest.kt
@@ -22,6 +22,7 @@
import android.os.UserHandle
import android.view.View
import android.view.ViewGroup
+import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.qs.user.UserSwitchDialogController
@@ -33,14 +34,13 @@
import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
-import org.junit.runners.JUnit4
import org.mockito.Mock
import org.mockito.Mockito.verify
import org.mockito.Mockito.`when` as whenever
import org.mockito.MockitoAnnotations
@SmallTest
-@RunWith(JUnit4::class)
+@RunWith(AndroidJUnit4::class)
class BaseUserSwitcherAdapterTest : SysuiTestCase() {
@Mock private lateinit var controller: UserSwitcherController
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/DeviceControlsControllerImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/DeviceControlsControllerImplTest.kt
index fb4ccb5..c22c628 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/DeviceControlsControllerImplTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/DeviceControlsControllerImplTest.kt
@@ -20,8 +20,8 @@
import android.content.Context
import android.content.pm.ServiceInfo
import android.provider.Settings
-import android.testing.AndroidTestingRunner
+import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.systemui.res.R
@@ -60,7 +60,7 @@
import org.mockito.ArgumentMatchers.anyObject
@SmallTest
-@RunWith(AndroidTestingRunner::class)
+@RunWith(AndroidJUnit4::class)
class DeviceControlsControllerImplTest : SysuiTestCase() {
@Mock
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/DeviceStateRotationLockSettingControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/DeviceStateRotationLockSettingControllerTest.java
index 2955162..f6e07d3 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/DeviceStateRotationLockSettingControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/DeviceStateRotationLockSettingControllerTest.java
@@ -29,10 +29,10 @@
import android.hardware.devicestate.DeviceStateManager;
import android.os.UserHandle;
import android.provider.Settings;
-import android.testing.AndroidTestingRunner;
import android.testing.TestableContentResolver;
import android.testing.TestableResources;
+import androidx.test.ext.junit.runners.AndroidJUnit4;
import androidx.test.filters.SmallTest;
import com.android.internal.R;
@@ -51,7 +51,7 @@
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
-@RunWith(AndroidTestingRunner.class)
+@RunWith(AndroidJUnit4.class)
@SmallTest
public class DeviceStateRotationLockSettingControllerTest extends SysuiTestCase {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/FlashlightControllerImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/FlashlightControllerImplTest.kt
index 1c54263..80cc6ec 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/FlashlightControllerImplTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/FlashlightControllerImplTest.kt
@@ -20,8 +20,8 @@
import android.hardware.camera2.CameraCharacteristics
import android.hardware.camera2.CameraManager
import android.hardware.camera2.impl.CameraMetadataNative
+import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
-import android.testing.AndroidTestingRunner
import com.android.systemui.SysuiTestCase
import com.android.systemui.broadcast.BroadcastSender
import com.android.systemui.dump.DumpManager
@@ -46,7 +46,7 @@
import org.mockito.MockitoAnnotations
@SmallTest
-@RunWith(AndroidTestingRunner::class)
+@RunWith(AndroidJUnit4::class)
class FlashlightControllerImplTest : SysuiTestCase() {
@Mock
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/KeyguardUserSwitcherAdapterTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/KeyguardUserSwitcherAdapterTest.kt
index 0bd6a68..9f74915 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/KeyguardUserSwitcherAdapterTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/KeyguardUserSwitcherAdapterTest.kt
@@ -19,10 +19,10 @@
import android.content.Context
import android.content.pm.UserInfo
import android.graphics.Bitmap
-import android.testing.AndroidTestingRunner
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
+import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.internal.util.UserIcons
import com.android.systemui.res.R
@@ -44,7 +44,7 @@
import org.mockito.Mockito.`when`
import org.mockito.MockitoAnnotations
-@RunWith(AndroidTestingRunner::class)
+@RunWith(AndroidJUnit4::class)
@SmallTest
class KeyguardUserSwitcherAdapterTest : SysuiTestCase() {
@Mock
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/SafetyControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/SafetyControllerTest.kt
index b03edaf..4b14e64 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/SafetyControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/SafetyControllerTest.kt
@@ -23,7 +23,7 @@
import android.net.Uri
import android.os.Handler
import android.safetycenter.SafetyCenterManager
-import android.testing.AndroidTestingRunner
+import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.util.mockito.any
@@ -44,7 +44,7 @@
import org.mockito.Mockito.`when`
@SmallTest
-@RunWith(AndroidTestingRunner::class)
+@RunWith(AndroidJUnit4::class)
class SafetyControllerTest : SysuiTestCase() {
private val TEST_PC_PKG = "testPermissionControllerPackageName"
@@ -188,4 +188,4 @@
assertThat(called).isTrue()
}
-}
\ No newline at end of file
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/SensitiveNotificationProtectionControllerFlagDisabledTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/SensitiveNotificationProtectionControllerFlagDisabledTest.kt
index 3e20f68..81f0950 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/SensitiveNotificationProtectionControllerFlagDisabledTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/SensitiveNotificationProtectionControllerFlagDisabledTest.kt
@@ -22,7 +22,7 @@
import android.os.Handler
import android.platform.test.annotations.DisableFlags
import android.telephony.TelephonyManager
-import android.testing.AndroidTestingRunner
+import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.server.notification.Flags
import com.android.systemui.SysuiTestCase
@@ -38,7 +38,7 @@
import org.mockito.MockitoAnnotations
@SmallTest
-@RunWith(AndroidTestingRunner::class)
+@RunWith(AndroidJUnit4::class)
@DisableFlags(Flags.FLAG_SCREENSHARE_NOTIFICATION_HIDING)
class SensitiveNotificationProtectionControllerFlagDisabledTest : SysuiTestCase() {
private val logger = SensitiveNotificationProtectionControllerLogger(logcatLogBuffer())
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/WalletControllerImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/WalletControllerImplTest.kt
index dbc2e347..0249ab8 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/WalletControllerImplTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/WalletControllerImplTest.kt
@@ -17,8 +17,8 @@
package com.android.systemui.statusbar.policy
import android.service.quickaccesswallet.QuickAccessWalletClient
-import android.testing.AndroidTestingRunner
+import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
@@ -35,7 +35,7 @@
import org.mockito.Mockito.`when`
@SmallTest
-@RunWith(AndroidTestingRunner::class)
+@RunWith(AndroidJUnit4::class)
class WalletControllerImplTest : SysuiTestCase() {
@Mock
diff --git a/packages/SystemUI/tests/src/com/android/systemui/theme/ThemeOverlayControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/theme/ThemeOverlayControllerTest.java
index ed7c956..a5e7a67 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/theme/ThemeOverlayControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/theme/ThemeOverlayControllerTest.java
@@ -995,9 +995,11 @@
.setResourceValue(any(String.class), eq(TYPE_INT_COLOR_ARGB8), anyInt(), eq(null));
// All dynamic colors were added twice: light and dark them
// All fixed colors were added once
+ // All custom dynamic tokens added twice
verify(dynamic, times(
DynamicColors.allDynamicColorsMapped(false).size() * 2
- + DynamicColors.getFixedColorsMapped(false).size())
+ + DynamicColors.getFixedColorsMapped(false).size()
+ + DynamicColors.getCustomColorsMapped(false).size() * 2)
).setResourceValue(any(String.class), eq(TYPE_INT_COLOR_ARGB8), anyInt(), eq(null));
}
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/unfold/updates/RotationChangeProviderTest.kt b/packages/SystemUI/tests/src/com/android/systemui/unfold/updates/RotationChangeProviderTest.kt
index fd368eb..eaef007 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/unfold/updates/RotationChangeProviderTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/unfold/updates/RotationChangeProviderTest.kt
@@ -18,8 +18,11 @@
import android.content.Context
import android.hardware.display.DisplayManager
+import android.os.HandlerThread
import android.os.Looper
+import android.os.Process
import android.testing.AndroidTestingRunner
+import android.testing.TestableLooper.RunWithLooper
import android.view.Display
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
@@ -40,6 +43,7 @@
@RunWith(AndroidTestingRunner::class)
@SmallTest
+@RunWithLooper
class RotationChangeProviderTest : SysuiTestCase() {
private lateinit var rotationChangeProvider: RotationChangeProvider
@@ -48,7 +52,10 @@
@Mock lateinit var listener: RotationListener
@Mock lateinit var display: Display
@Captor lateinit var displayListener: ArgumentCaptor<DisplayManager.DisplayListener>
- private val fakeHandler = FakeHandler(Looper.getMainLooper())
+ private val bgThread =
+ HandlerThread("UnfoldBgTest", Process.THREAD_PRIORITY_FOREGROUND).apply { start() }
+ private val bgHandler = FakeHandler(bgThread.looper)
+ private val callbackHandler = FakeHandler(Looper.getMainLooper())
private lateinit var spyContext: Context
@@ -57,9 +64,10 @@
MockitoAnnotations.initMocks(this)
spyContext = spy(context)
whenever(spyContext.display).thenReturn(display)
- rotationChangeProvider = RotationChangeProvider(displayManager, spyContext, fakeHandler)
+ rotationChangeProvider =
+ RotationChangeProvider(displayManager, spyContext, bgHandler, callbackHandler)
rotationChangeProvider.addCallback(listener)
- fakeHandler.dispatchQueuedMessages()
+ bgHandler.dispatchQueuedMessages()
verify(displayManager).registerDisplayListener(displayListener.capture(), any())
}
@@ -76,7 +84,7 @@
verify(listener).onRotationChanged(42)
rotationChangeProvider.removeCallback(listener)
- fakeHandler.dispatchQueuedMessages()
+ bgHandler.dispatchQueuedMessages()
sendRotationUpdate(43)
verify(displayManager).unregisterDisplayListener(any())
@@ -86,6 +94,6 @@
private fun sendRotationUpdate(newRotation: Int) {
whenever(display.rotation).thenReturn(newRotation)
displayListener.allValues.forEach { it.onDisplayChanged(display.displayId) }
- fakeHandler.dispatchQueuedMessages()
+ callbackHandler.dispatchQueuedMessages()
}
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/user/domain/interactor/GuestUserInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/user/domain/interactor/GuestUserInteractorTest.kt
index 948670f..01795e9 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/user/domain/interactor/GuestUserInteractorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/user/domain/interactor/GuestUserInteractorTest.kt
@@ -18,6 +18,7 @@
package com.android.systemui.user.domain.interactor
import android.app.admin.DevicePolicyManager
+import android.content.Context
import android.content.pm.UserInfo
import android.os.UserHandle
import android.os.UserManager
@@ -59,6 +60,7 @@
@Mock private lateinit var switchUser: (Int) -> Unit
@Mock private lateinit var resumeSessionReceiver: GuestResumeSessionReceiver
@Mock private lateinit var resetOrExitSessionReceiver: GuestResetOrExitSessionReceiver
+ @Mock private lateinit var otherContext: Context
private lateinit var underTest: GuestUserInteractor
@@ -74,28 +76,30 @@
repository = FakeUserRepository()
repository.setUserInfos(ALL_USERS)
- underTest =
- GuestUserInteractor(
- applicationContext = context,
- applicationScope = scope,
- mainDispatcher = IMMEDIATE,
- backgroundDispatcher = IMMEDIATE,
- manager = manager,
- repository = repository,
- deviceProvisionedController = deviceProvisionedController,
- devicePolicyManager = devicePolicyManager,
- refreshUsersScheduler =
- RefreshUsersScheduler(
- applicationScope = scope,
- mainDispatcher = IMMEDIATE,
- repository = repository,
- ),
- uiEventLogger = uiEventLogger,
- resumeSessionReceiver = resumeSessionReceiver,
- resetOrExitSessionReceiver = resetOrExitSessionReceiver,
- )
+ underTest = initGuestUserInteractor(context)
}
+ private fun initGuestUserInteractor(context: Context) =
+ GuestUserInteractor(
+ applicationContext = context,
+ applicationScope = scope,
+ mainDispatcher = IMMEDIATE,
+ backgroundDispatcher = IMMEDIATE,
+ manager = manager,
+ repository = repository,
+ deviceProvisionedController = deviceProvisionedController,
+ devicePolicyManager = devicePolicyManager,
+ refreshUsersScheduler =
+ RefreshUsersScheduler(
+ applicationScope = scope,
+ mainDispatcher = IMMEDIATE,
+ repository = repository,
+ ),
+ uiEventLogger = uiEventLogger,
+ resumeSessionReceiver = resumeSessionReceiver,
+ resetOrExitSessionReceiver = resetOrExitSessionReceiver,
+ )
+
@Test
fun registersBroadcastReceivers() {
verify(resumeSessionReceiver).register()
@@ -103,6 +107,16 @@
}
@Test
+ fun registersBroadcastReceiversOnlyForSystemUser() {
+ for (i in 1..5) {
+ whenever(otherContext.userId).thenReturn(UserHandle.MIN_SECONDARY_USER_ID + i)
+ initGuestUserInteractor(otherContext)
+ }
+ verify(resumeSessionReceiver).register()
+ verify(resetOrExitSessionReceiver).register()
+ }
+
+ @Test
fun onDeviceBootCompleted_allowedToAdd_createGuest() =
runBlocking(IMMEDIATE) {
setAllowedToAdd()
diff --git a/packages/SystemUI/tests/src/com/android/systemui/user/domain/interactor/UserSwitcherInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/user/domain/interactor/UserSwitcherInteractorTest.kt
index 3dee093..96c6eb8 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/user/domain/interactor/UserSwitcherInteractorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/user/domain/interactor/UserSwitcherInteractorTest.kt
@@ -887,6 +887,46 @@
}
@Test
+ fun removeGuestUser_shouldNotShowExitGuestDialog() {
+ createUserInteractor()
+ testScope.runTest {
+ val (userInfo, guestUserInfo) = createUserInfos(count = 2, includeGuest = true)
+ userRepository.setUserInfos(listOf(userInfo, guestUserInfo))
+ userRepository.setSelectedUserInfo(guestUserInfo)
+
+ whenever(manager.markGuestForDeletion(guestUserInfo.id)).thenReturn(true)
+ underTest.removeGuestUser(guestUserInfo.id, userInfo.id)
+ runCurrent()
+
+ verify(manager).markGuestForDeletion(guestUserInfo.id)
+ verify(activityManager).switchUser(userInfo.id)
+ assertThat(collectLastValue(underTest.dialogShowRequests)()).isNull()
+ }
+ }
+
+ @Test
+ fun resetGuestUser_shouldNotShowExitGuestDialog() {
+ createUserInteractor()
+ testScope.runTest {
+ val (userInfo, guestUserInfo) = createUserInfos(count = 2, includeGuest = true)
+ val otherGuestUserInfo = createUserInfos(count = 1, includeGuest = true)[0]
+ userRepository.setUserInfos(listOf(userInfo, guestUserInfo))
+ userRepository.setSelectedUserInfo(guestUserInfo)
+
+ whenever(manager.markGuestForDeletion(guestUserInfo.id)).thenReturn(true)
+ whenever(manager.createGuest(any())).thenReturn(otherGuestUserInfo)
+ underTest.removeGuestUser(guestUserInfo.id, UserHandle.USER_NULL)
+ runCurrent()
+
+ verify(manager).markGuestForDeletion(guestUserInfo.id)
+ verify(manager).createGuest(any())
+ verify(activityManager).switchUser(otherGuestUserInfo.id)
+ assertThat(collectLastValue(underTest.dialogShowRequests)())
+ .isEqualTo(ShowDialogRequestModel.ShowUserCreationDialog(isGuest = true))
+ }
+ }
+
+ @Test
fun showUserSwitcher_fullScreenDisabled_showsDialogSwitcher() {
createUserInteractor()
testScope.runTest {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/util/FloatingContentCoordinatorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/util/FloatingContentCoordinatorTest.kt
index 31848a6..e1dcb14 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/util/FloatingContentCoordinatorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/util/FloatingContentCoordinatorTest.kt
@@ -1,8 +1,8 @@
package com.android.systemui.util
import android.graphics.Rect
-import android.testing.AndroidTestingRunner
import android.testing.TestableLooper
+import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
import com.android.wm.shell.common.FloatingContentCoordinator
@@ -14,7 +14,7 @@
import org.junit.runner.RunWith
@TestableLooper.RunWithLooper
-@RunWith(AndroidTestingRunner::class)
+@RunWith(AndroidJUnit4::class)
@SmallTest
class FloatingContentCoordinatorTest : SysuiTestCase() {
@@ -198,12 +198,11 @@
}
/**
- * Helper class that uses [floatingCoordinator.findAreaForContentVertically] to move a
- * Rect when needed.
+ * Helper class that uses [floatingCoordinator.findAreaForContentVertically] to move a Rect when
+ * needed.
*/
- inner class FloatingRect(
- private val underlyingRect: Rect
- ) : FloatingContentCoordinator.FloatingContent {
+ inner class FloatingRect(private val underlyingRect: Rect) :
+ FloatingContentCoordinator.FloatingContent {
override fun moveToBounds(bounds: Rect) {
underlyingRect.set(bounds)
}
@@ -216,4 +215,4 @@
return underlyingRect
}
}
-}
\ No newline at end of file
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/util/RingerModeLiveDataTest.kt b/packages/SystemUI/tests/src/com/android/systemui/util/RingerModeLiveDataTest.kt
index 436f5b8..457f2bb 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/util/RingerModeLiveDataTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/util/RingerModeLiveDataTest.kt
@@ -19,12 +19,13 @@
import android.content.BroadcastReceiver
import android.content.IntentFilter
import android.os.UserHandle
-import android.testing.AndroidTestingRunner
import android.testing.TestableLooper
import androidx.lifecycle.Observer
+import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.broadcast.BroadcastDispatcher
+import java.util.concurrent.Executor
import org.junit.After
import org.junit.Assert.assertTrue
import org.junit.Before
@@ -38,10 +39,9 @@
import org.mockito.Mockito.verify
import org.mockito.Mockito.verifyNoMoreInteractions
import org.mockito.MockitoAnnotations
-import java.util.concurrent.Executor
@SmallTest
-@RunWith(AndroidTestingRunner::class)
+@RunWith(AndroidJUnit4::class)
@TestableLooper.RunWithLooper(setAsMainLooper = true)
class RingerModeLiveDataTest : SysuiTestCase() {
@@ -52,16 +52,11 @@
private val INTENT = "INTENT"
}
- @Mock
- private lateinit var broadcastDispatcher: BroadcastDispatcher
- @Mock
- private lateinit var valueSupplier: () -> Int
- @Mock
- private lateinit var observer: Observer<Int>
- @Captor
- private lateinit var broadcastReceiverCaptor: ArgumentCaptor<BroadcastReceiver>
- @Captor
- private lateinit var intentFilterCaptor: ArgumentCaptor<IntentFilter>
+ @Mock private lateinit var broadcastDispatcher: BroadcastDispatcher
+ @Mock private lateinit var valueSupplier: () -> Int
+ @Mock private lateinit var observer: Observer<Int>
+ @Captor private lateinit var broadcastReceiverCaptor: ArgumentCaptor<BroadcastReceiver>
+ @Captor private lateinit var intentFilterCaptor: ArgumentCaptor<IntentFilter>
// Run everything immediately
private val executor = Executor { it.run() }
@@ -88,14 +83,14 @@
fun testOnActive_broadcastRegistered() {
liveData.observeForever(observer)
verify(broadcastDispatcher)
- .registerReceiver(any(), any(), eq(executor), eq(UserHandle.ALL), anyInt(), any())
+ .registerReceiver(any(), any(), eq(executor), eq(UserHandle.ALL), anyInt(), any())
}
@Test
fun testOnActive_intentFilterHasIntent() {
liveData.observeForever(observer)
- verify(broadcastDispatcher).registerReceiver(any(), capture(intentFilterCaptor), any(),
- any(), anyInt(), any())
+ verify(broadcastDispatcher)
+ .registerReceiver(any(), capture(intentFilterCaptor), any(), any(), anyInt(), any())
assertTrue(intentFilterCaptor.value.hasAction(INTENT))
}
@@ -111,4 +106,4 @@
liveData.removeObserver(observer)
verify(broadcastDispatcher).unregisterReceiver(any())
}
-}
\ No newline at end of file
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/util/WallpaperControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/util/WallpaperControllerTest.kt
index b13cb72..6271904 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/util/WallpaperControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/util/WallpaperControllerTest.kt
@@ -19,10 +19,10 @@
import android.app.WallpaperInfo
import android.app.WallpaperManager
import android.os.IBinder
-import android.testing.AndroidTestingRunner
import android.testing.TestableLooper.RunWithLooper
import android.view.View
import android.view.ViewRootImpl
+import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.util.mockito.eq
@@ -32,36 +32,30 @@
import org.junit.Test
import org.junit.runner.RunWith
import org.mockito.Mock
-import org.mockito.Mockito.`when`
import org.mockito.Mockito.any
import org.mockito.Mockito.anyFloat
import org.mockito.Mockito.clearInvocations
import org.mockito.Mockito.doThrow
-import org.mockito.Mockito.times
-import org.mockito.Mockito.verify
import org.mockito.Mockito.mock
import org.mockito.Mockito.never
+import org.mockito.Mockito.times
+import org.mockito.Mockito.verify
import org.mockito.Mockito.`when` as whenever
+import org.mockito.Mockito.`when`
import org.mockito.junit.MockitoJUnit
-@RunWith(AndroidTestingRunner::class)
+@RunWith(AndroidJUnit4::class)
@RunWithLooper
@SmallTest
class WallpaperControllerTest : SysuiTestCase() {
- @Mock
- private lateinit var wallpaperManager: WallpaperManager
- @Mock
- private lateinit var root: View
- @Mock
- private lateinit var viewRootImpl: ViewRootImpl
- @Mock
- private lateinit var windowToken: IBinder
+ @Mock private lateinit var wallpaperManager: WallpaperManager
+ @Mock private lateinit var root: View
+ @Mock private lateinit var viewRootImpl: ViewRootImpl
+ @Mock private lateinit var windowToken: IBinder
private val wallpaperRepository = FakeWallpaperRepository()
- @JvmField
- @Rule
- val mockitoRule = MockitoJUnit.rule()
+ @JvmField @Rule val mockitoRule = MockitoJUnit.rule()
private lateinit var wallaperController: WallpaperController
@@ -92,9 +86,7 @@
@Test
fun setUnfoldTransitionZoom_defaultUnfoldTransitionIsDisabled_doesNotUpdateWallpaperZoom() {
- wallpaperRepository.wallpaperInfo.value = createWallpaperInfo(
- useDefaultTransition = false
- )
+ wallpaperRepository.wallpaperInfo.value = createWallpaperInfo(useDefaultTransition = false)
wallaperController.setUnfoldTransitionZoom(0.5f)
@@ -130,7 +122,8 @@
@Test
fun setNotificationZoom_exceptionWhenUpdatingZoom_doesNotFail() {
- doThrow(IllegalArgumentException("test exception")).`when`(wallpaperManager)
+ doThrow(IllegalArgumentException("test exception"))
+ .`when`(wallpaperManager)
.setWallpaperZoomOut(any(), anyFloat())
wallaperController.setNotificationShadeZoom(0.5f)
@@ -140,8 +133,7 @@
private fun createWallpaperInfo(useDefaultTransition: Boolean = true): WallpaperInfo {
val info = mock(WallpaperInfo::class.java)
- whenever(info.shouldUseDefaultUnfoldTransition())
- .thenReturn(useDefaultTransition)
+ whenever(info.shouldUseDefaultUnfoldTransition()).thenReturn(useDefaultTransition)
return info
}
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/util/animation/AnimationUtilTest.kt b/packages/SystemUI/tests/src/com/android/systemui/util/animation/AnimationUtilTest.kt
index 92afb03..b26598c 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/util/animation/AnimationUtilTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/util/animation/AnimationUtilTest.kt
@@ -16,13 +16,16 @@
package com.android.systemui.util.animation
+import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
import com.google.common.truth.Truth.assertThat
-import org.junit.Test
import java.lang.IllegalArgumentException
+import org.junit.runner.RunWith
+import org.junit.Test
@SmallTest
+@RunWith(AndroidJUnit4::class)
class AnimationUtilTest : SysuiTestCase() {
@Test
fun getMsForFrames_5frames_returns83() {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/util/concurrency/FakeExecutorTest.java b/packages/SystemUI/tests/src/com/android/systemui/util/concurrency/FakeExecutorTest.java
index 9dfa14d..7dfac0a 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/util/concurrency/FakeExecutorTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/util/concurrency/FakeExecutorTest.java
@@ -22,8 +22,7 @@
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
-import android.testing.AndroidTestingRunner;
-
+import androidx.test.ext.junit.runners.AndroidJUnit4;
import androidx.test.filters.SmallTest;
import com.android.systemui.SysuiTestCase;
@@ -39,7 +38,7 @@
import java.util.List;
@SmallTest
-@RunWith(AndroidTestingRunner.class)
+@RunWith(AndroidJUnit4.class)
public class FakeExecutorTest extends SysuiTestCase {
@Before
public void setUp() throws Exception {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/util/concurrency/MessageRouterImplTest.java b/packages/SystemUI/tests/src/com/android/systemui/util/concurrency/MessageRouterImplTest.java
index 78fc680..48fb745 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/util/concurrency/MessageRouterImplTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/util/concurrency/MessageRouterImplTest.java
@@ -24,8 +24,7 @@
import static org.mockito.Mockito.reset;
import static org.mockito.Mockito.verify;
-import android.testing.AndroidTestingRunner;
-
+import androidx.test.ext.junit.runners.AndroidJUnit4;
import androidx.test.filters.SmallTest;
import com.android.systemui.SysuiTestCase;
@@ -39,7 +38,7 @@
import org.mockito.MockitoAnnotations;
@SmallTest
-@RunWith(AndroidTestingRunner.class)
+@RunWith(AndroidJUnit4.class)
public class MessageRouterImplTest extends SysuiTestCase {
private static final int MESSAGE_A = 0;
private static final int MESSAGE_B = 1;
diff --git a/packages/SystemUI/tests/src/com/android/systemui/util/concurrency/MockExecutorHandlerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/util/concurrency/MockExecutorHandlerTest.kt
index 15032dc..7ec420f 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/util/concurrency/MockExecutorHandlerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/util/concurrency/MockExecutorHandlerTest.kt
@@ -15,7 +15,7 @@
*/
package com.android.systemui.util.concurrency
-import android.testing.AndroidTestingRunner
+import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.util.time.FakeSystemClock
@@ -26,7 +26,7 @@
import org.junit.runner.RunWith
@SmallTest
-@RunWith(AndroidTestingRunner::class)
+@RunWith(AndroidJUnit4::class)
class MockExecutorHandlerTest : SysuiTestCase() {
/** Test FakeExecutor that receives non-delayed items to execute. */
@Test
diff --git a/packages/SystemUI/tests/src/com/android/systemui/util/concurrency/RepeatableExecutorTest.java b/packages/SystemUI/tests/src/com/android/systemui/util/concurrency/RepeatableExecutorTest.java
index 00f37ae..13fff29d 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/util/concurrency/RepeatableExecutorTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/util/concurrency/RepeatableExecutorTest.java
@@ -18,8 +18,7 @@
import static com.google.common.truth.Truth.assertThat;
-import android.testing.AndroidTestingRunner;
-
+import androidx.test.ext.junit.runners.AndroidJUnit4;
import androidx.test.filters.SmallTest;
import com.android.systemui.SysuiTestCase;
@@ -30,7 +29,7 @@
import org.junit.runner.RunWith;
@SmallTest
-@RunWith(AndroidTestingRunner.class)
+@RunWith(AndroidJUnit4.class)
public class RepeatableExecutorTest extends SysuiTestCase {
private static final int DELAY = 100;
diff --git a/packages/SystemUI/tests/src/com/android/systemui/util/condition/ConditionalCoreStartableTest.java b/packages/SystemUI/tests/src/com/android/systemui/util/condition/ConditionalCoreStartableTest.java
index b367a60..37015e3 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/util/condition/ConditionalCoreStartableTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/util/condition/ConditionalCoreStartableTest.java
@@ -23,8 +23,7 @@
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
-import android.testing.AndroidTestingRunner;
-
+import androidx.test.ext.junit.runners.AndroidJUnit4;
import androidx.test.filters.SmallTest;
import com.android.systemui.CoreStartable;
@@ -44,7 +43,7 @@
import java.util.Set;
@SmallTest
-@RunWith(AndroidTestingRunner.class)
+@RunWith(AndroidJUnit4.class)
public class ConditionalCoreStartableTest extends SysuiTestCase {
public static class FakeConditionalCoreStartable extends ConditionalCoreStartable {
interface Callback {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/util/drawable/DrawableSizeTest.kt b/packages/SystemUI/tests/src/com/android/systemui/util/drawable/DrawableSizeTest.kt
index ac357ea..b8f5815 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/util/drawable/DrawableSizeTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/util/drawable/DrawableSizeTest.kt
@@ -4,7 +4,7 @@
import android.graphics.Bitmap
import android.graphics.drawable.BitmapDrawable
import android.graphics.drawable.ShapeDrawable
-import android.testing.AndroidTestingRunner
+import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
import com.google.common.truth.Truth.assertThat
@@ -12,7 +12,7 @@
import org.junit.Test
import org.junit.runner.RunWith
-@RunWith(AndroidTestingRunner::class)
+@RunWith(AndroidJUnit4::class)
@SmallTest
class DrawableSizeTest : SysuiTestCase() {
@@ -32,14 +32,11 @@
@Test
fun testDownscaleToSize_drawableSmallerThanRequirement_unchanged() {
- val drawable = BitmapDrawable(resources,
- Bitmap.createBitmap(
- resources.displayMetrics,
- 150,
- 150,
- Bitmap.Config.ARGB_8888
- )
- )
+ val drawable =
+ BitmapDrawable(
+ resources,
+ Bitmap.createBitmap(resources.displayMetrics, 150, 150, Bitmap.Config.ARGB_8888)
+ )
val result = DrawableSize.downscaleToSize(resources, drawable, 300, 300)
assertThat(result).isSameInstanceAs(drawable)
}
@@ -48,14 +45,11 @@
fun testDownscaleToSize_drawableLargerThanRequirementWithDensity_resized() {
// This bitmap would actually fail to resize if the method doesn't check for
// bitmap dimensions inside drawable.
- val drawable = BitmapDrawable(resources,
- Bitmap.createBitmap(
- resources.displayMetrics,
- 150,
- 75,
- Bitmap.Config.ARGB_8888
- )
- )
+ val drawable =
+ BitmapDrawable(
+ resources,
+ Bitmap.createBitmap(resources.displayMetrics, 150, 75, Bitmap.Config.ARGB_8888)
+ )
val result = DrawableSize.downscaleToSize(resources, drawable, 75, 75)
assertThat(result).isNotSameInstanceAs(drawable)
@@ -65,9 +59,9 @@
@Test
fun testDownscaleToSize_drawableAnimated_unchanged() {
- val drawable = resources.getDrawable(android.R.drawable.stat_sys_download,
- resources.newTheme())
+ val drawable =
+ resources.getDrawable(android.R.drawable.stat_sys_download, resources.newTheme())
val result = DrawableSize.downscaleToSize(resources, drawable, 1, 1)
assertThat(result).isSameInstanceAs(drawable)
}
-}
\ No newline at end of file
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/util/kotlin/FlowUtilTests.kt b/packages/SystemUI/tests/src/com/android/systemui/util/kotlin/FlowUtilTests.kt
index 7d0d57b..e2ce50c 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/util/kotlin/FlowUtilTests.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/util/kotlin/FlowUtilTests.kt
@@ -16,7 +16,7 @@
package com.android.systemui.util.kotlin
-import android.testing.AndroidTestingRunner
+import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.util.time.FakeSystemClock
@@ -47,7 +47,7 @@
import org.junit.runner.RunWith
@SmallTest
-@RunWith(AndroidTestingRunner::class)
+@RunWith(AndroidJUnit4::class)
class PairwiseFlowTest : SysuiTestCase() {
@Test
fun simple() = runBlocking {
@@ -89,7 +89,9 @@
initRun = true
"initial"
}
- ) { prev: String, next: String -> "$prev|$next" }
+ ) { prev: String, next: String ->
+ "$prev|$next"
+ }
)
.emitsExactly("initial|val1", "val1|val2")
assertThat(initRun).isTrue()
@@ -104,7 +106,9 @@
initRun = true
"initial"
}
- ) { prev: String, next: String -> "$prev|$next" }
+ ) { prev: String, next: String ->
+ "$prev|$next"
+ }
)
.emitsNothing()
// Even though the flow will not emit anything, the initial value function should still get
@@ -120,7 +124,9 @@
initRun = true
"initial"
}
- ) { prev: String, next: String -> "$prev|$next" }
+ ) { prev: String, next: String ->
+ "$prev|$next"
+ }
// Since the flow isn't collected, ensure [initialValueFun] isn't run.
assertThat(initRun).isFalse()
@@ -146,7 +152,7 @@
}
@SmallTest
-@RunWith(AndroidTestingRunner::class)
+@RunWith(AndroidJUnit4::class)
class SetChangesFlowTest : SysuiTestCase() {
@Test
fun simple() = runBlocking {
@@ -198,7 +204,7 @@
}
@SmallTest
-@RunWith(AndroidTestingRunner::class)
+@RunWith(AndroidJUnit4::class)
class SampleFlowTest : SysuiTestCase() {
@Test
fun simple() = runBlocking {
@@ -240,7 +246,7 @@
@OptIn(ExperimentalCoroutinesApi::class)
@SmallTest
-@RunWith(AndroidTestingRunner::class)
+@RunWith(AndroidJUnit4::class)
class ThrottleFlowTest : SysuiTestCase() {
@Test
@@ -248,13 +254,16 @@
// Arrange
val choreographer = createChoreographer(this)
val output = mutableListOf<Int>()
- val collectJob = backgroundScope.launch {
- flow {
- emit(1)
- delay(1000)
- emit(2)
- }.throttle(1000, choreographer.fakeClock).toList(output)
- }
+ val collectJob =
+ backgroundScope.launch {
+ flow {
+ emit(1)
+ delay(1000)
+ emit(2)
+ }
+ .throttle(1000, choreographer.fakeClock)
+ .toList(output)
+ }
// Act
choreographer.advanceAndRun(0)
@@ -283,13 +292,16 @@
// Arrange
val choreographer = createChoreographer(this)
val output = mutableListOf<Int>()
- val collectJob = backgroundScope.launch {
- flow {
- emit(1)
- delay(500)
- emit(2)
- }.throttle(1000, choreographer.fakeClock).toList(output)
- }
+ val collectJob =
+ backgroundScope.launch {
+ flow {
+ emit(1)
+ delay(500)
+ emit(2)
+ }
+ .throttle(1000, choreographer.fakeClock)
+ .toList(output)
+ }
// Act
choreographer.advanceAndRun(0)
@@ -319,15 +331,18 @@
// Arrange
val choreographer = createChoreographer(this)
val output = mutableListOf<Int>()
- val collectJob = backgroundScope.launch {
- flow {
- emit(1)
- delay(500)
- emit(2)
- delay(500)
- emit(3)
- }.throttle(1000, choreographer.fakeClock).toList(output)
- }
+ val collectJob =
+ backgroundScope.launch {
+ flow {
+ emit(1)
+ delay(500)
+ emit(2)
+ delay(500)
+ emit(3)
+ }
+ .throttle(1000, choreographer.fakeClock)
+ .toList(output)
+ }
// Act
choreographer.advanceAndRun(0)
@@ -357,15 +372,18 @@
// Arrange
val choreographer = createChoreographer(this)
val output = mutableListOf<Int>()
- val collectJob = backgroundScope.launch {
- flow {
- emit(1)
- delay(500)
- emit(2)
- delay(250)
- emit(3)
- }.throttle(1000, choreographer.fakeClock).toList(output)
- }
+ val collectJob =
+ backgroundScope.launch {
+ flow {
+ emit(1)
+ delay(500)
+ emit(2)
+ delay(250)
+ emit(3)
+ }
+ .throttle(1000, choreographer.fakeClock)
+ .toList(output)
+ }
// Act
choreographer.advanceAndRun(0)
@@ -391,15 +409,16 @@
collectJob.cancel()
}
- private fun createChoreographer(testScope: TestScope) = object {
- val fakeClock = FakeSystemClock()
+ private fun createChoreographer(testScope: TestScope) =
+ object {
+ val fakeClock = FakeSystemClock()
- fun advanceAndRun(millis: Long) {
- fakeClock.advanceTime(millis)
- testScope.advanceTimeBy(millis)
- testScope.runCurrent()
+ fun advanceAndRun(millis: Long) {
+ fakeClock.advanceTime(millis)
+ testScope.advanceTimeBy(millis)
+ testScope.runCurrent()
+ }
}
- }
}
private fun <T> assertThatFlow(flow: Flow<T>) =
diff --git a/packages/SystemUI/tests/src/com/android/systemui/util/kotlin/IpcSerializerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/util/kotlin/IpcSerializerTest.kt
index 4ca1fd3..c31b287 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/util/kotlin/IpcSerializerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/util/kotlin/IpcSerializerTest.kt
@@ -16,7 +16,7 @@
package com.android.systemui.util.kotlin
-import android.testing.AndroidTestingRunner
+import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
import java.util.concurrent.atomic.AtomicLong
@@ -31,43 +31,42 @@
import org.junit.runner.RunWith
@SmallTest
-@RunWith(AndroidTestingRunner::class)
+@RunWith(AndroidJUnit4::class)
class IpcSerializerTest : SysuiTestCase() {
private val serializer = IpcSerializer()
@Ignore("b/253046405")
@Test
- fun serializeManyIncomingIpcs(): Unit = runBlocking(Dispatchers.Main.immediate) {
- val processor = launch(start = CoroutineStart.LAZY) { serializer.process() }
- withContext(Dispatchers.IO) {
- val lastEvaluatedTime = AtomicLong(System.currentTimeMillis())
- // First, launch many serialization requests in parallel
- repeat(100_000) {
- launch(Dispatchers.Unconfined) {
- val enqueuedTime = System.currentTimeMillis()
- serializer.runSerialized {
- val last = lastEvaluatedTime.getAndSet(enqueuedTime)
- assertTrue(
- "expected $last less than or equal to $enqueuedTime ",
- last <= enqueuedTime,
- )
+ fun serializeManyIncomingIpcs(): Unit =
+ runBlocking(Dispatchers.Main.immediate) {
+ val processor = launch(start = CoroutineStart.LAZY) { serializer.process() }
+ withContext(Dispatchers.IO) {
+ val lastEvaluatedTime = AtomicLong(System.currentTimeMillis())
+ // First, launch many serialization requests in parallel
+ repeat(100_000) {
+ launch(Dispatchers.Unconfined) {
+ val enqueuedTime = System.currentTimeMillis()
+ serializer.runSerialized {
+ val last = lastEvaluatedTime.getAndSet(enqueuedTime)
+ assertTrue(
+ "expected $last less than or equal to $enqueuedTime ",
+ last <= enqueuedTime,
+ )
+ }
}
}
+ // Then, process them all in the order they came in.
+ processor.start()
}
- // Then, process them all in the order they came in.
- processor.start()
+ // All done, stop processing
+ processor.cancel()
}
- // All done, stop processing
- processor.cancel()
- }
@Test(timeout = 5000)
fun serializeOnOneThread_doesNotDeadlock() = runBlocking {
val job = launch { serializer.process() }
- repeat(100) {
- serializer.runSerializedBlocking { }
- }
+ repeat(100) { serializer.runSerializedBlocking {} }
job.cancel()
}
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/util/kotlin/PackageManagerExtComponentEnabledTest.kt b/packages/SystemUI/tests/src/com/android/systemui/util/kotlin/PackageManagerExtComponentEnabledTest.kt
index 2013bb0..8bfff9c2 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/util/kotlin/PackageManagerExtComponentEnabledTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/util/kotlin/PackageManagerExtComponentEnabledTest.kt
@@ -27,13 +27,13 @@
import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
-import org.junit.runners.Parameterized
-import org.junit.runners.Parameterized.Parameters
import org.mockito.Mock
import org.mockito.MockitoAnnotations
+import platform.test.runner.parameterized.ParameterizedAndroidJunit4
+import platform.test.runner.parameterized.Parameters
@SmallTest
-@RunWith(Parameterized::class)
+@RunWith(ParameterizedAndroidJunit4::class)
internal class PackageManagerExtComponentEnabledTest(private val testCase: TestCase) :
SysuiTestCase() {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/util/kotlin/SuspendUtilTests.kt b/packages/SystemUI/tests/src/com/android/systemui/util/kotlin/SuspendUtilTests.kt
index 6848b83..b2f7c1a 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/util/kotlin/SuspendUtilTests.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/util/kotlin/SuspendUtilTests.kt
@@ -16,7 +16,7 @@
package com.android.systemui.util.kotlin
-import android.testing.AndroidTestingRunner
+import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
import com.google.common.truth.Truth.assertThat
@@ -28,7 +28,7 @@
import org.junit.runner.RunWith
@SmallTest
-@RunWith(AndroidTestingRunner::class)
+@RunWith(AndroidJUnit4::class)
class RaceSuspendTest : SysuiTestCase() {
@Test
fun raceSimple() = runBlocking {
@@ -46,10 +46,11 @@
@Test
fun raceImmediate() = runBlocking {
assertThat(
- race<Int>(
- { 1 },
- { 2 },
+ race<Int>(
+ { 1 },
+ { 2 },
+ )
)
- ).isEqualTo(1)
+ .isEqualTo(1)
}
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/util/sensors/PostureDependentProximitySensorTest.java b/packages/SystemUI/tests/src/com/android/systemui/util/sensors/PostureDependentProximitySensorTest.java
index 84129be..300c298 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/util/sensors/PostureDependentProximitySensorTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/util/sensors/PostureDependentProximitySensorTest.java
@@ -26,9 +26,9 @@
import android.content.res.Resources;
import android.hardware.Sensor;
-import android.testing.AndroidTestingRunner;
import android.testing.TestableLooper;
+import androidx.test.ext.junit.runners.AndroidJUnit4;
import androidx.test.filters.SmallTest;
import com.android.systemui.SysuiTestCase;
@@ -46,7 +46,7 @@
import org.mockito.MockitoAnnotations;
@SmallTest
-@RunWith(AndroidTestingRunner.class)
+@RunWith(AndroidJUnit4.class)
@TestableLooper.RunWithLooper
public class PostureDependentProximitySensorTest extends SysuiTestCase {
@Mock private Resources mResources;
diff --git a/packages/SystemUI/tests/src/com/android/systemui/util/sensors/ProximityCheckTest.java b/packages/SystemUI/tests/src/com/android/systemui/util/sensors/ProximityCheckTest.java
index 19dbf9a..5dd008a 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/util/sensors/ProximityCheckTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/util/sensors/ProximityCheckTest.java
@@ -23,9 +23,9 @@
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;
-import android.testing.AndroidTestingRunner;
import android.testing.TestableLooper;
+import androidx.test.ext.junit.runners.AndroidJUnit4;
import androidx.test.filters.SmallTest;
import com.android.systemui.SysuiTestCase;
@@ -40,7 +40,7 @@
import java.util.function.Consumer;
@SmallTest
-@RunWith(AndroidTestingRunner.class)
+@RunWith(AndroidJUnit4.class)
@TestableLooper.RunWithLooper
public class ProximityCheckTest extends SysuiTestCase {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/util/sensors/ProximitySensorImplDualTest.java b/packages/SystemUI/tests/src/com/android/systemui/util/sensors/ProximitySensorImplDualTest.java
index 5e75578..0eab74e 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/util/sensors/ProximitySensorImplDualTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/util/sensors/ProximitySensorImplDualTest.java
@@ -24,9 +24,9 @@
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;
-import android.testing.AndroidTestingRunner;
import android.testing.TestableLooper;
+import androidx.test.ext.junit.runners.AndroidJUnit4;
import androidx.test.filters.SmallTest;
import com.android.systemui.SysuiTestCase;
@@ -40,7 +40,7 @@
import org.mockito.MockitoAnnotations;
@SmallTest
-@RunWith(AndroidTestingRunner.class)
+@RunWith(AndroidJUnit4.class)
@TestableLooper.RunWithLooper
public class ProximitySensorImplDualTest extends SysuiTestCase {
private ProximitySensor mProximitySensor;
diff --git a/packages/SystemUI/tests/src/com/android/systemui/util/sensors/ProximitySensorImplSingleTest.java b/packages/SystemUI/tests/src/com/android/systemui/util/sensors/ProximitySensorImplSingleTest.java
index 752cd32..f44c842 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/util/sensors/ProximitySensorImplSingleTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/util/sensors/ProximitySensorImplSingleTest.java
@@ -21,9 +21,9 @@
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;
-import android.testing.AndroidTestingRunner;
import android.testing.TestableLooper;
+import androidx.test.ext.junit.runners.AndroidJUnit4;
import androidx.test.filters.SmallTest;
import com.android.systemui.SysuiTestCase;
@@ -40,7 +40,7 @@
* Tests for ProximitySensor that rely on a single hardware sensor.
*/
@SmallTest
-@RunWith(AndroidTestingRunner.class)
+@RunWith(AndroidJUnit4.class)
@TestableLooper.RunWithLooper
public class ProximitySensorImplSingleTest extends SysuiTestCase {
private ProximitySensor mProximitySensor;
diff --git a/packages/SystemUI/tests/src/com/android/systemui/util/service/ObservableServiceConnectionTest.java b/packages/SystemUI/tests/src/com/android/systemui/util/service/ObservableServiceConnectionTest.java
index 8d26c87..a54afad 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/util/service/ObservableServiceConnectionTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/util/service/ObservableServiceConnectionTest.java
@@ -30,8 +30,8 @@
import android.content.pm.UserInfo;
import android.os.IBinder;
import android.os.UserHandle;
-import android.testing.AndroidTestingRunner;
+import androidx.test.ext.junit.runners.AndroidJUnit4;
import androidx.test.filters.SmallTest;
import com.android.systemui.SysuiTestCase;
@@ -49,7 +49,7 @@
import java.util.Objects;
@SmallTest
-@RunWith(AndroidTestingRunner.class)
+@RunWith(AndroidJUnit4.class)
public class ObservableServiceConnectionTest extends SysuiTestCase {
static class Foo {
int mValue;
diff --git a/packages/SystemUI/tests/src/com/android/systemui/util/service/PackageObserverTest.java b/packages/SystemUI/tests/src/com/android/systemui/util/service/PackageObserverTest.java
index a2fd288..a70b00c 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/util/service/PackageObserverTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/util/service/PackageObserverTest.java
@@ -24,8 +24,8 @@
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
-import android.testing.AndroidTestingRunner;
+import androidx.test.ext.junit.runners.AndroidJUnit4;
import androidx.test.filters.SmallTest;
import com.android.systemui.SysuiTestCase;
@@ -38,7 +38,7 @@
import org.mockito.MockitoAnnotations;
@SmallTest
-@RunWith(AndroidTestingRunner.class)
+@RunWith(AndroidJUnit4.class)
public class PackageObserverTest extends SysuiTestCase {
@Mock
Context mContext;
diff --git a/packages/SystemUI/tests/src/com/android/systemui/util/service/PersistentConnectionManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/util/service/PersistentConnectionManagerTest.java
index 55c49ee..ef10fdf 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/util/service/PersistentConnectionManagerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/util/service/PersistentConnectionManagerTest.java
@@ -19,8 +19,7 @@
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.verify;
-import android.testing.AndroidTestingRunner;
-
+import androidx.test.ext.junit.runners.AndroidJUnit4;
import androidx.test.filters.SmallTest;
import com.android.systemui.SysuiTestCase;
@@ -37,7 +36,7 @@
import org.mockito.MockitoAnnotations;
@SmallTest
-@RunWith(AndroidTestingRunner.class)
+@RunWith(AndroidJUnit4.class)
public class PersistentConnectionManagerTest extends SysuiTestCase {
private static final int MAX_RETRIES = 5;
private static final int RETRY_DELAY_MS = 1000;
diff --git a/packages/SystemUI/tests/src/com/android/systemui/util/settings/FakeSettingsTest.java b/packages/SystemUI/tests/src/com/android/systemui/util/settings/FakeSettingsTest.java
index f65caee2..99f6303 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/util/settings/FakeSettingsTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/util/settings/FakeSettingsTest.java
@@ -28,8 +28,8 @@
import android.database.ContentObserver;
import android.os.UserHandle;
import android.provider.Settings;
-import android.testing.AndroidTestingRunner;
+import androidx.test.ext.junit.runners.AndroidJUnit4;
import androidx.test.filters.SmallTest;
import com.android.systemui.SysuiTestCase;
@@ -44,7 +44,7 @@
import java.util.Map;
@SmallTest
-@RunWith(AndroidTestingRunner.class)
+@RunWith(AndroidJUnit4.class)
public class FakeSettingsTest extends SysuiTestCase {
@Mock
ContentObserver mContentObserver;
diff --git a/packages/SystemUI/tests/src/com/android/systemui/util/settings/repository/UserAwareSecureSettingsRepositoryTest.kt b/packages/SystemUI/tests/src/com/android/systemui/util/settings/repository/UserAwareSecureSettingsRepositoryTest.kt
index 913759f..88b2630 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/util/settings/repository/UserAwareSecureSettingsRepositoryTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/util/settings/repository/UserAwareSecureSettingsRepositoryTest.kt
@@ -17,6 +17,7 @@
package com.android.systemui.util.settings.repository
import android.content.pm.UserInfo
+import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.coroutines.collectLastValue
@@ -33,11 +34,10 @@
import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
-import org.junit.runners.JUnit4
@OptIn(ExperimentalCoroutinesApi::class)
@SmallTest
-@RunWith(JUnit4::class)
+@RunWith(AndroidJUnit4::class)
class UserAwareSecureSettingsRepositoryTest : SysuiTestCase() {
private val dispatcher = StandardTestDispatcher()
@@ -48,11 +48,12 @@
@Before
fun setup() {
- repository = UserAwareSecureSettingsRepositoryImpl(
- secureSettings,
- userRepository,
- dispatcher,
- )
+ repository =
+ UserAwareSecureSettingsRepositoryImpl(
+ secureSettings,
+ userRepository,
+ dispatcher,
+ )
userRepository.setUserInfos(USER_INFOS)
setSettingValueForUser(enabled = true, userInfo = SETTING_ENABLED_USER)
setSettingValueForUser(enabled = false, userInfo = SETTING_DISABLED_USER)
@@ -105,4 +106,4 @@
val SETTING_DISABLED_USER = UserInfo(/* id= */ 1, "user2", /* flags= */ 0)
val USER_INFOS = listOf(SETTING_ENABLED_USER, SETTING_DISABLED_USER)
}
-}
\ No newline at end of file
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/util/ui/AnimatedValueTest.kt b/packages/SystemUI/tests/src/com/android/systemui/util/ui/AnimatedValueTest.kt
index 94100fe..6637d5f 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/util/ui/AnimatedValueTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/util/ui/AnimatedValueTest.kt
@@ -17,7 +17,7 @@
package com.android.systemui.util.ui
-import android.testing.AndroidTestingRunner
+import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.coroutines.collectLastValue
@@ -30,7 +30,7 @@
import org.junit.runner.RunWith
@SmallTest
-@RunWith(AndroidTestingRunner::class)
+@RunWith(AndroidJUnit4::class)
class AnimatedValueTest : SysuiTestCase() {
@Test
diff --git a/packages/SystemUI/tests/src/com/android/systemui/util/view/ViewUtilTest.kt b/packages/SystemUI/tests/src/com/android/systemui/util/view/ViewUtilTest.kt
index e3cd9b2..3dcb828 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/util/view/ViewUtilTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/util/view/ViewUtilTest.kt
@@ -19,17 +19,20 @@
import android.graphics.Rect
import android.view.View
import android.widget.TextView
+import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.util.mockito.any
import com.google.common.truth.Truth.assertThat
import org.junit.Before
import org.junit.Test
+import org.junit.runner.RunWith
import org.mockito.Mockito.doAnswer
import org.mockito.Mockito.spy
import org.mockito.Mockito.`when`
@SmallTest
+@RunWith(AndroidJUnit4::class)
class ViewUtilTest : SysuiTestCase() {
private val viewUtil = ViewUtil()
private lateinit var view: View
@@ -45,11 +48,13 @@
location[1] = VIEW_TOP
`when`(view.locationOnScreen).thenReturn(location)
doAnswer { invocation ->
- val pos = invocation.arguments[0] as IntArray
- pos[0] = VIEW_LEFT
- pos[1] = VIEW_TOP
- null
- }.`when`(view).getLocationInWindow(any())
+ val pos = invocation.arguments[0] as IntArray
+ pos[0] = VIEW_LEFT
+ pos[1] = VIEW_TOP
+ null
+ }
+ .`when`(view)
+ .getLocationInWindow(any())
}
@Test
@@ -59,9 +64,8 @@
@Test
fun touchIsWithinView_onTopLeftCorner_returnsTrue() {
- assertThat(viewUtil.touchIsWithinView(
- view, VIEW_LEFT.toFloat(), VIEW_TOP.toFloat())
- ).isTrue()
+ assertThat(viewUtil.touchIsWithinView(view, VIEW_LEFT.toFloat(), VIEW_TOP.toFloat()))
+ .isTrue()
}
@Test
diff --git a/packages/SystemUI/tests/src/com/android/systemui/util/wakelock/WakeLockTest.java b/packages/SystemUI/tests/src/com/android/systemui/util/wakelock/WakeLockTest.java
index ed07ad2..207c35d 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/util/wakelock/WakeLockTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/util/wakelock/WakeLockTest.java
@@ -35,15 +35,18 @@
import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
-import org.junit.runners.Parameterized;
import java.util.List;
+import platform.test.runner.parameterized.ParameterizedAndroidJunit4;
+import platform.test.runner.parameterized.Parameters;
+
+
@SmallTest
-@RunWith(Parameterized.class)
+@RunWith(ParameterizedAndroidJunit4.class)
public class WakeLockTest extends SysuiTestCase {
- @Parameterized.Parameters(name = "{0}")
+ @Parameters(name = "{0}")
public static List<FlagsParameterization> getFlags() {
return FlagsParameterization.allCombinationsOf(
Flags.FLAG_DELAYED_WAKELOCK_RELEASE_ON_BACKGROUND_THREAD);
@@ -114,4 +117,4 @@
// shouldn't throw an exception on production builds
mWakeLock.release(WHY);
}
-}
\ No newline at end of file
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/wmshell/BubblesTest.java b/packages/SystemUI/tests/src/com/android/systemui/wmshell/BubblesTest.java
index aac3640..7b0a556 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/wmshell/BubblesTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/wmshell/BubblesTest.java
@@ -27,6 +27,7 @@
import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn;
import static com.android.server.notification.Flags.FLAG_SCREENSHARE_NOTIFICATION_HIDING;
+import static com.android.wm.shell.Flags.FLAG_ENABLE_BUBBLE_BAR;
import static com.google.common.truth.Truth.assertThat;
@@ -466,7 +467,8 @@
mock(UiEventLogger.class),
mock(UserTracker.class),
mock(AvalancheProvider.class),
- mock(SystemSettings.class)
+ mock(SystemSettings.class),
+ mock(PackageManager.class)
);
interruptionDecisionProvider.start();
@@ -2131,8 +2133,7 @@
FakeBubbleStateListener bubbleStateListener = new FakeBubbleStateListener();
mBubbleController.registerBubbleStateListener(bubbleStateListener);
- mBubbleController.expandStackAndSelectBubbleFromLauncher(mBubbleEntry.getKey(),
- new Rect(500, 1000, 600, 1100));
+ mBubbleController.expandStackAndSelectBubbleFromLauncher(mBubbleEntry.getKey(), 1000);
assertThat(mBubbleController.getLayerView().isExpanded()).isTrue();
@@ -2141,6 +2142,112 @@
assertThat(mBubbleController.getLayerView().isExpanded()).isFalse();
}
+ @EnableFlags(FLAG_ENABLE_BUBBLE_BAR)
+ @Test
+ public void dragBubbleBarBubble_selectedBubble_expandedViewCollapsesDuringDrag() {
+ mBubbleProperties.mIsBubbleBarEnabled = true;
+ mPositioner.setIsLargeScreen(true);
+ FakeBubbleStateListener bubbleStateListener = new FakeBubbleStateListener();
+ mBubbleController.registerBubbleStateListener(bubbleStateListener);
+
+ // Add 2 bubbles
+ mEntryListener.onEntryAdded(mRow);
+ mEntryListener.onEntryAdded(mRow2);
+ mBubbleController.updateBubble(mBubbleEntry);
+ mBubbleController.updateBubble(mBubbleEntry2);
+
+ // Select first bubble
+ mBubbleController.expandStackAndSelectBubbleFromLauncher(mBubbleEntry.getKey(), 0);
+ assertThat(mBubbleData.getSelectedBubbleKey()).isEqualTo(mBubbleEntry.getKey());
+ assertThat(mBubbleController.getLayerView().isExpanded()).isTrue();
+
+ // Drag first bubble, bubble should collapse
+ mBubbleController.startBubbleDrag(mBubbleEntry.getKey());
+ assertThat(mBubbleController.getLayerView().isExpanded()).isFalse();
+
+ // Stop dragging, first bubble should be expanded
+ mBubbleController.stopBubbleDrag(BubbleBarLocation.LEFT, 0);
+ assertThat(mBubbleData.getSelectedBubbleKey()).isEqualTo(mBubbleEntry.getKey());
+ assertThat(mBubbleController.getLayerView().isExpanded()).isTrue();
+ }
+
+ @EnableFlags(FLAG_ENABLE_BUBBLE_BAR)
+ @Test
+ public void dragBubbleBarBubble_unselectedBubble_expandedViewCollapsesDuringDrag() {
+ mBubbleProperties.mIsBubbleBarEnabled = true;
+ mPositioner.setIsLargeScreen(true);
+ FakeBubbleStateListener bubbleStateListener = new FakeBubbleStateListener();
+ mBubbleController.registerBubbleStateListener(bubbleStateListener);
+
+ // Add 2 bubbles
+ mEntryListener.onEntryAdded(mRow);
+ mEntryListener.onEntryAdded(mRow2);
+ mBubbleController.updateBubble(mBubbleEntry);
+ mBubbleController.updateBubble(mBubbleEntry2);
+
+ // Select first bubble
+ mBubbleController.expandStackAndSelectBubbleFromLauncher(mBubbleEntry.getKey(), 0);
+ assertThat(mBubbleData.getSelectedBubbleKey()).isEqualTo(mBubbleEntry.getKey());
+ assertThat(mBubbleController.getLayerView().isExpanded()).isTrue();
+
+ // Drag second bubble, bubble should collapse
+ mBubbleController.startBubbleDrag(mBubbleEntry2.getKey());
+ assertThat(mBubbleController.getLayerView().isExpanded()).isFalse();
+
+ // Stop dragging, first bubble should be expanded
+ mBubbleController.stopBubbleDrag(BubbleBarLocation.LEFT, 0);
+ assertThat(mBubbleData.getSelectedBubbleKey()).isEqualTo(mBubbleEntry.getKey());
+ assertThat(mBubbleController.getLayerView().isExpanded()).isTrue();
+ }
+
+ @EnableFlags(FLAG_ENABLE_BUBBLE_BAR)
+ @Test
+ public void dismissBubbleBarBubble_selected_selectsAndExpandsNext() {
+ mBubbleProperties.mIsBubbleBarEnabled = true;
+ mPositioner.setIsLargeScreen(true);
+ FakeBubbleStateListener bubbleStateListener = new FakeBubbleStateListener();
+ mBubbleController.registerBubbleStateListener(bubbleStateListener);
+
+ // Add 2 bubbles
+ mEntryListener.onEntryAdded(mRow);
+ mEntryListener.onEntryAdded(mRow2);
+ mBubbleController.updateBubble(mBubbleEntry);
+ mBubbleController.updateBubble(mBubbleEntry2);
+
+ // Select first bubble
+ mBubbleController.expandStackAndSelectBubbleFromLauncher(mBubbleEntry.getKey(), 0);
+ // Drag first bubble to dismiss
+ mBubbleController.startBubbleDrag(mBubbleEntry.getKey());
+ mBubbleController.dragBubbleToDismiss(mBubbleEntry.getKey());
+ // Second bubble is selected and expanded
+ assertThat(mBubbleData.getSelectedBubbleKey()).isEqualTo(mBubbleEntry2.getKey());
+ assertThat(mBubbleController.getLayerView().isExpanded()).isTrue();
+ }
+
+ @EnableFlags(FLAG_ENABLE_BUBBLE_BAR)
+ @Test
+ public void dismissBubbleBarBubble_unselected_selectionDoesNotChange() {
+ mBubbleProperties.mIsBubbleBarEnabled = true;
+ mPositioner.setIsLargeScreen(true);
+ FakeBubbleStateListener bubbleStateListener = new FakeBubbleStateListener();
+ mBubbleController.registerBubbleStateListener(bubbleStateListener);
+
+ // Add 2 bubbles
+ mEntryListener.onEntryAdded(mRow);
+ mEntryListener.onEntryAdded(mRow2);
+ mBubbleController.updateBubble(mBubbleEntry);
+ mBubbleController.updateBubble(mBubbleEntry2);
+
+ // Select first bubble
+ mBubbleController.expandStackAndSelectBubbleFromLauncher(mBubbleEntry.getKey(), 0);
+ // Drag second bubble to dismiss
+ mBubbleController.startBubbleDrag(mBubbleEntry2.getKey());
+ mBubbleController.dragBubbleToDismiss(mBubbleEntry2.getKey());
+ // First bubble remains selected and expanded
+ assertThat(mBubbleData.getSelectedBubbleKey()).isEqualTo(mBubbleEntry.getKey());
+ assertThat(mBubbleController.getLayerView().isExpanded()).isTrue();
+ }
+
@DisableFlags(FLAG_SCREENSHARE_NOTIFICATION_HIDING)
@Test
public void doesNotRegisterSensitiveStateListener() {
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/SysuiTestCase.java b/packages/SystemUI/tests/utils/src/com/android/systemui/SysuiTestCase.java
index 9dcd946..8eef930 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/SysuiTestCase.java
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/SysuiTestCase.java
@@ -15,8 +15,6 @@
*/
package com.android.systemui;
-import static android.platform.test.flag.junit.SetFlagsRule.DefaultInitValueType.DEVICE_DEFAULT;
-
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.when;
@@ -99,8 +97,22 @@
.setProvideMainThread(true)
.build();
- @Rule
- public final SetFlagsRule mSetFlagsRule = new SetFlagsRule(DEVICE_DEFAULT);
+ @ClassRule
+ public static final SetFlagsRule.ClassRule mSetFlagsClassRule =
+ new SetFlagsRule.ClassRule(
+ android.app.Flags.class,
+ android.hardware.biometrics.Flags.class,
+ android.multiuser.Flags.class,
+ android.net.platform.flags.Flags.class,
+ android.os.Flags.class,
+ android.service.controls.flags.Flags.class,
+ com.android.internal.telephony.flags.Flags.class,
+ com.android.server.notification.Flags.class,
+ com.android.systemui.Flags.class);
+
+ // TODO(b/339471826): Fix Robolectric to execute the @ClassRule correctly
+ @Rule public final SetFlagsRule mSetFlagsRule =
+ isRobolectricTest() ? new SetFlagsRule() : mSetFlagsClassRule.createSetFlagsRule();
@Rule(order = 10)
public final SceneContainerRule mSceneContainerRule = new SceneContainerRule();
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/biometrics/data/repository/FakePromptRepository.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/biometrics/data/repository/FakePromptRepository.kt
index e37bdc1..2809967 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/biometrics/data/repository/FakePromptRepository.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/biometrics/data/repository/FakePromptRepository.kt
@@ -17,6 +17,9 @@
private val _userId = MutableStateFlow<Int?>(null)
override val userId = _userId.asStateFlow()
+ private val _requestId = MutableStateFlow<Long?>(null)
+ override val requestId = _requestId.asStateFlow()
+
private var _challenge = MutableStateFlow<Long?>(null)
override val challenge = _challenge.asStateFlow()
@@ -32,6 +35,7 @@
override fun setPrompt(
promptInfo: PromptInfo,
userId: Int,
+ requestId: Long,
gatekeeperChallenge: Long?,
kind: PromptKind,
opPackageName: String,
@@ -39,6 +43,7 @@
setPrompt(
promptInfo,
userId,
+ requestId,
gatekeeperChallenge,
kind,
forceConfirmation = false,
@@ -48,6 +53,7 @@
fun setPrompt(
promptInfo: PromptInfo,
userId: Int,
+ requestId: Long,
gatekeeperChallenge: Long?,
kind: PromptKind,
forceConfirmation: Boolean = false,
@@ -55,15 +61,17 @@
) {
_promptInfo.value = promptInfo
_userId.value = userId
+ _requestId.value = requestId
_challenge.value = gatekeeperChallenge
_promptKind.value = kind
_isConfirmationRequired.value = promptInfo.isConfirmationRequested || forceConfirmation
_opPackageName.value = opPackageName
}
- override fun unsetPrompt() {
+ override fun unsetPrompt(requestId: Long) {
_promptInfo.value = null
_userId.value = null
+ _requestId.value = null
_challenge.value = null
_promptKind.value = PromptKind.None
_opPackageName.value = null
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/brightness/data/repository/FakeBrightnessPolicyRepository.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/brightness/data/repository/FakeBrightnessPolicyRepository.kt
index d3ceb15..f5d02f3 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/brightness/data/repository/FakeBrightnessPolicyRepository.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/brightness/data/repository/FakeBrightnessPolicyRepository.kt
@@ -38,4 +38,8 @@
)
)
}
+
+ fun setBaseUserRestriction() {
+ _restrictionPolicy.value = PolicyRestriction.Restricted(RestrictedLockUtils.EnforcedAdmin())
+ }
}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/keyboard/shortcut/KeyboardShortcutHelperKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/keyboard/shortcut/KeyboardShortcutHelperKosmos.kt
index 38f2a56..3401cc4 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/keyboard/shortcut/KeyboardShortcutHelperKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/keyboard/shortcut/KeyboardShortcutHelperKosmos.kt
@@ -27,6 +27,9 @@
import com.android.systemui.kosmos.Kosmos
import com.android.systemui.kosmos.applicationCoroutineScope
import com.android.systemui.kosmos.testDispatcher
+import com.android.systemui.kosmos.testScope
+import com.android.systemui.model.sysUiState
+import com.android.systemui.settings.displayTracker
val Kosmos.shortcutHelperRepository by
Kosmos.Fixture { ShortcutHelperRepository(fakeCommandQueue, broadcastDispatcher) }
@@ -42,7 +45,9 @@
}
val Kosmos.shortcutHelperInteractor by
- Kosmos.Fixture { ShortcutHelperInteractor(shortcutHelperRepository) }
+ Kosmos.Fixture {
+ ShortcutHelperInteractor(displayTracker, testScope, sysUiState, shortcutHelperRepository)
+ }
val Kosmos.shortcutHelperViewModel by
Kosmos.Fixture { ShortcutHelperViewModel(testDispatcher, shortcutHelperInteractor) }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/KeyguardTransitionRepositoryKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/KeyguardTransitionRepositoryKosmos.kt
index 408157b..3e69e87 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/KeyguardTransitionRepositoryKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/KeyguardTransitionRepositoryKosmos.kt
@@ -17,7 +17,10 @@
package com.android.systemui.keyguard.data.repository
import com.android.systemui.kosmos.Kosmos
+import com.android.systemui.kosmos.testDispatcher
var Kosmos.keyguardTransitionRepository: KeyguardTransitionRepository by
Kosmos.Fixture { fakeKeyguardTransitionRepository }
var Kosmos.fakeKeyguardTransitionRepository by Kosmos.Fixture { FakeKeyguardTransitionRepository() }
+var Kosmos.realKeyguardTransitionRepository: KeyguardTransitionRepository by
+ Kosmos.Fixture { KeyguardTransitionRepositoryImpl(testDispatcher) }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/LockscreenSceneTransitionRepository.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/LockscreenSceneTransitionRepository.kt
new file mode 100644
index 0000000..7d0e8b1
--- /dev/null
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/LockscreenSceneTransitionRepository.kt
@@ -0,0 +1,22 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.keyguard.data.repository
+
+import com.android.systemui.kosmos.Kosmos
+
+val Kosmos.lockscreenSceneTransitionRepository by
+ Kosmos.Fixture { LockscreenSceneTransitionRepository() }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/KeyguardDismissActionInteractorKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/KeyguardDismissActionInteractorKosmos.kt
index 2c6d44f..03e5a90 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/KeyguardDismissActionInteractorKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/KeyguardDismissActionInteractorKosmos.kt
@@ -19,6 +19,7 @@
import com.android.systemui.keyguard.data.repository.keyguardRepository
import com.android.systemui.kosmos.Kosmos
import com.android.systemui.kosmos.testScope
+import com.android.systemui.scene.domain.interactor.sceneInteractor
import kotlinx.coroutines.ExperimentalCoroutinesApi
@ExperimentalCoroutinesApi
@@ -29,5 +30,6 @@
transitionInteractor = keyguardTransitionInteractor,
dismissInteractor = keyguardDismissInteractor,
applicationScope = testScope.backgroundScope,
+ sceneInteractor = sceneInteractor,
)
}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionInteractorKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionInteractorKosmos.kt
index 6cc1e8e..c90642d 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionInteractorKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionInteractorKosmos.kt
@@ -20,6 +20,7 @@
import com.android.systemui.keyguard.data.repository.keyguardTransitionRepository
import com.android.systemui.kosmos.Kosmos
import com.android.systemui.kosmos.applicationCoroutineScope
+import com.android.systemui.scene.domain.interactor.sceneInteractor
val Kosmos.keyguardTransitionInteractor: KeyguardTransitionInteractor by
Kosmos.Fixture {
@@ -32,5 +33,6 @@
fromAodTransitionInteractor = { fromAodTransitionInteractor },
fromAlternateBouncerTransitionInteractor = { fromAlternateBouncerTransitionInteractor },
fromDozingTransitionInteractor = { fromDozingTransitionInteractor },
+ sceneInteractor = { sceneInteractor }
)
}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/WindowManagerLockscreenVisibilityInteractorKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/WindowManagerLockscreenVisibilityInteractorKosmos.kt
index b38acc8..bd9c0be 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/WindowManagerLockscreenVisibilityInteractorKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/WindowManagerLockscreenVisibilityInteractorKosmos.kt
@@ -16,6 +16,7 @@
package com.android.systemui.keyguard.domain.interactor
+import com.android.systemui.deviceentry.domain.interactor.deviceEntryInteractor
import com.android.systemui.kosmos.Kosmos
import com.android.systemui.scene.domain.interactor.sceneInteractor
import com.android.systemui.statusbar.notification.domain.interactor.notificationLaunchAnimationInteractor
@@ -30,6 +31,7 @@
fromBouncerInteractor = fromPrimaryBouncerTransitionInteractor,
fromAlternateBouncerInteractor = fromAlternateBouncerTransitionInteractor,
notificationLaunchAnimationInteractor = notificationLaunchAnimationInteractor,
- sceneInteractor = sceneInteractor,
+ sceneInteractor = { sceneInteractor },
+ deviceEntryInteractor = { deviceEntryInteractor },
)
}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/scenetransition/LockscreenSceneTransitionInteractor.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/scenetransition/LockscreenSceneTransitionInteractor.kt
new file mode 100644
index 0000000..3c1f7b1
--- /dev/null
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/scenetransition/LockscreenSceneTransitionInteractor.kt
@@ -0,0 +1,33 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.keyguard.domain.interactor.scenetransition
+
+import com.android.systemui.keyguard.data.repository.lockscreenSceneTransitionRepository
+import com.android.systemui.keyguard.domain.interactor.keyguardTransitionInteractor
+import com.android.systemui.kosmos.Kosmos
+import com.android.systemui.kosmos.applicationCoroutineScope
+import com.android.systemui.scene.domain.interactor.sceneInteractor
+
+var Kosmos.lockscreenSceneTransitionInteractor by
+ Kosmos.Fixture {
+ LockscreenSceneTransitionInteractor(
+ transitionInteractor = keyguardTransitionInteractor,
+ applicationScope = applicationCoroutineScope,
+ sceneInteractor = sceneInteractor,
+ repository = lockscreenSceneTransitionRepository,
+ )
+ }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/AodToLockscreenTransitionViewModelKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/AodToLockscreenTransitionViewModelKosmos.kt
index 460913f..b8fcec6 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/AodToLockscreenTransitionViewModelKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/AodToLockscreenTransitionViewModelKosmos.kt
@@ -18,7 +18,6 @@
package com.android.systemui.keyguard.ui.viewmodel
-import com.android.systemui.deviceentry.domain.interactor.deviceEntryUdfpsInteractor
import com.android.systemui.keyguard.ui.keyguardTransitionAnimationFlow
import com.android.systemui.kosmos.Kosmos
import com.android.systemui.kosmos.Kosmos.Fixture
@@ -27,7 +26,6 @@
val Kosmos.aodToLockscreenTransitionViewModel by Fixture {
AodToLockscreenTransitionViewModel(
- deviceEntryUdfpsInteractor = deviceEntryUdfpsInteractor,
shadeInteractor = shadeInteractor,
animationFlow = keyguardTransitionAnimationFlow,
)
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/qrcodescanner/QRCodeScannerControllerKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/qrcodescanner/QRCodeScannerControllerKosmos.kt
new file mode 100644
index 0000000..8ad6087
--- /dev/null
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/qrcodescanner/QRCodeScannerControllerKosmos.kt
@@ -0,0 +1,23 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.qrcodescanner
+
+import com.android.systemui.kosmos.Kosmos
+import com.android.systemui.qrcodescanner.controller.QRCodeScannerController
+import com.android.systemui.util.mockito.mock
+
+val Kosmos.qrCodeScannerController by Kosmos.Fixture { mock<QRCodeScannerController>() }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/tiles/base/actions/FakeQSTileIntentUserInputHandler.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/tiles/base/actions/FakeQSTileIntentUserInputHandler.kt
index c4bf8ff..f50443e 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/tiles/base/actions/FakeQSTileIntentUserInputHandler.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/tiles/base/actions/FakeQSTileIntentUserInputHandler.kt
@@ -31,7 +31,11 @@
private val mutableInputs = mutableListOf<Input>()
- override fun handle(expandable: Expandable?, intent: Intent) {
+ override fun handle(
+ expandable: Expandable?,
+ intent: Intent,
+ handleDismissShadeShowOverLockScreenWhenLocked: Boolean
+ ) {
mutableInputs.add(Input.Intent(expandable, intent))
}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/tiles/base/actions/QSTileIntentUserInputHandlerKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/tiles/base/actions/QSTileIntentUserInputHandlerKosmos.kt
new file mode 100644
index 0000000..ccfb609
--- /dev/null
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/tiles/base/actions/QSTileIntentUserInputHandlerKosmos.kt
@@ -0,0 +1,21 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.qs.tiles.base.actions
+
+import com.android.systemui.kosmos.Kosmos
+
+val Kosmos.qsTileIntentUserInputHandler by Kosmos.Fixture { FakeQSTileIntentUserInputHandler() }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/tiles/base/analytics/QSTileAnalyticsKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/tiles/base/analytics/QSTileAnalyticsKosmos.kt
new file mode 100644
index 0000000..146c1ad
--- /dev/null
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/tiles/base/analytics/QSTileAnalyticsKosmos.kt
@@ -0,0 +1,22 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.qs.tiles.base.analytics
+
+import com.android.systemui.kosmos.Kosmos
+import com.android.systemui.util.mockito.mock
+
+val Kosmos.qsTileAnalytics by Kosmos.Fixture { mock<QSTileAnalytics>() }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/tiles/base/interactor/DisabledByPolicyInteractorKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/tiles/base/interactor/DisabledByPolicyInteractorKosmos.kt
new file mode 100644
index 0000000..9ad49f0
--- /dev/null
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/tiles/base/interactor/DisabledByPolicyInteractorKosmos.kt
@@ -0,0 +1,21 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.qs.tiles.base.interactor
+
+import com.android.systemui.kosmos.Kosmos
+
+val Kosmos.fakeDisabledByPolicyInteractor by Kosmos.Fixture { FakeDisabledByPolicyInteractor() }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/tiles/impl/qr/QRCodeScannerTileKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/tiles/impl/qr/QRCodeScannerTileKosmos.kt
new file mode 100644
index 0000000..dcfcce7
--- /dev/null
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/tiles/impl/qr/QRCodeScannerTileKosmos.kt
@@ -0,0 +1,74 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.qs.tiles.impl.qr
+
+import android.content.res.mainResources
+import com.android.systemui.classifier.fakeFalsingManager
+import com.android.systemui.kosmos.Kosmos
+import com.android.systemui.kosmos.applicationCoroutineScope
+import com.android.systemui.kosmos.backgroundCoroutineContext
+import com.android.systemui.kosmos.testDispatcher
+import com.android.systemui.kosmos.testScope
+import com.android.systemui.qrcodescanner.dagger.QRCodeScannerModule
+import com.android.systemui.qrcodescanner.qrCodeScannerController
+import com.android.systemui.qs.qsEventLogger
+import com.android.systemui.qs.tiles.base.actions.qsTileIntentUserInputHandler
+import com.android.systemui.qs.tiles.base.analytics.qsTileAnalytics
+import com.android.systemui.qs.tiles.base.interactor.fakeDisabledByPolicyInteractor
+import com.android.systemui.qs.tiles.base.viewmodel.QSTileViewModelImpl
+import com.android.systemui.qs.tiles.impl.custom.qsTileLogger
+import com.android.systemui.qs.tiles.impl.qr.domain.interactor.QRCodeScannerTileDataInteractor
+import com.android.systemui.qs.tiles.impl.qr.domain.interactor.QRCodeScannerTileUserActionInteractor
+import com.android.systemui.qs.tiles.impl.qr.ui.QRCodeScannerTileMapper
+import com.android.systemui.user.data.repository.fakeUserRepository
+import com.android.systemui.util.time.systemClock
+
+val Kosmos.qsQRCodeScannerTileConfig by
+ Kosmos.Fixture { QRCodeScannerModule.provideQRCodeScannerTileConfig(qsEventLogger) }
+
+val Kosmos.qrCodeScannerTileDataInteractor by
+ Kosmos.Fixture {
+ QRCodeScannerTileDataInteractor(
+ backgroundCoroutineContext,
+ applicationCoroutineScope,
+ qrCodeScannerController
+ )
+ }
+
+val Kosmos.qrCodeScannerTileUserActionInteractor by
+ Kosmos.Fixture { QRCodeScannerTileUserActionInteractor(qsTileIntentUserInputHandler) }
+
+val Kosmos.qrCodeScannerTileMapper by
+ Kosmos.Fixture { QRCodeScannerTileMapper(mainResources, mainResources.newTheme()) }
+
+val Kosmos.qsQRCodeScannerViewModel by
+ Kosmos.Fixture {
+ QSTileViewModelImpl(
+ qsQRCodeScannerTileConfig,
+ { qrCodeScannerTileUserActionInteractor },
+ { qrCodeScannerTileDataInteractor },
+ { qrCodeScannerTileMapper },
+ fakeDisabledByPolicyInteractor,
+ fakeUserRepository,
+ fakeFalsingManager,
+ qsTileAnalytics,
+ qsTileLogger,
+ systemClock,
+ testDispatcher,
+ testScope.backgroundScope,
+ )
+ }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/scene/data/repository/SceneContainerRepositoryUtil.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/scene/data/repository/SceneContainerRepositoryUtil.kt
new file mode 100644
index 0000000..641a757
--- /dev/null
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/scene/data/repository/SceneContainerRepositoryUtil.kt
@@ -0,0 +1,63 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.scene.data.repository
+
+import com.android.compose.animation.scene.ObservableTransitionState
+import com.android.compose.animation.scene.SceneKey
+import com.android.systemui.kosmos.Kosmos
+import com.android.systemui.kosmos.testScope
+import com.android.systemui.scene.shared.model.Scenes
+import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.MutableStateFlow
+import kotlinx.coroutines.flow.flowOf
+import kotlinx.coroutines.test.TestScope
+import kotlinx.coroutines.test.runCurrent
+
+private val mutableTransitionState =
+ MutableStateFlow<ObservableTransitionState>(ObservableTransitionState.Idle(Scenes.Lockscreen))
+
+fun Kosmos.setSceneTransition(
+ transition: ObservableTransitionState,
+ scope: TestScope = testScope,
+ repository: SceneContainerRepository = sceneContainerRepository
+) {
+ repository.setTransitionState(mutableTransitionState)
+ mutableTransitionState.value = transition
+ scope.runCurrent()
+}
+
+fun Transition(
+ from: SceneKey,
+ to: SceneKey,
+ currentScene: Flow<SceneKey> = flowOf(to),
+ progress: Flow<Float> = flowOf(0f),
+ isInitiatedByUserInput: Boolean = false,
+ isUserInputOngoing: Flow<Boolean> = flowOf(false),
+): ObservableTransitionState.Transition {
+ return ObservableTransitionState.Transition(
+ fromScene = from,
+ toScene = to,
+ currentScene = currentScene,
+ progress = progress,
+ isInitiatedByUserInput = isInitiatedByUserInput,
+ isUserInputOngoing = isUserInputOngoing
+ )
+}
+
+fun Idle(currentScene: SceneKey): ObservableTransitionState.Idle {
+ return ObservableTransitionState.Idle(currentScene)
+}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/collection/NotificationEntryBuilder.java b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/collection/NotificationEntryBuilder.java
index 2bd584e..562ac0c 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/collection/NotificationEntryBuilder.java
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/collection/NotificationEntryBuilder.java
@@ -27,6 +27,8 @@
import android.service.notification.SnoozeCriterion;
import android.service.notification.StatusBarNotification;
+import androidx.annotation.NonNull;
+
import com.android.internal.logging.InstanceId;
import com.android.systemui.statusbar.RankingBuilder;
import com.android.systemui.statusbar.SbnBuilder;
@@ -36,6 +38,7 @@
import kotlin.Unit;
import java.util.ArrayList;
+import java.util.function.Consumer;
/**
* Combined builder for constructing a NotificationEntry and its associated StatusBarNotification
@@ -73,6 +76,20 @@
mCreationTime = source.getCreationTime();
}
+ /** Allows the caller to sub-build the ranking */
+ @NonNull
+ public NotificationEntryBuilder updateRanking(@NonNull Consumer<RankingBuilder> rankingUpdater) {
+ rankingUpdater.accept(mRankingBuilder);
+ return this;
+ }
+
+ /** Allows the caller to sub-build the SBN */
+ @NonNull
+ public NotificationEntryBuilder updateSbn(@NonNull Consumer<SbnBuilder> sbnUpdater) {
+ sbnUpdater.accept(mSbnBuilder);
+ return this;
+ }
+
/** Update an the parent on an existing entry */
public static void setNewParent(NotificationEntry entry, GroupEntry parent) {
entry.setParent(parent);
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/FakeMobileConnectionsRepository.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/FakeMobileConnectionsRepository.kt
index cce038f..8229575 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/FakeMobileConnectionsRepository.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/FakeMobileConnectionsRepository.kt
@@ -23,6 +23,7 @@
import com.android.settingslib.mobile.MobileMappings
import com.android.settingslib.mobile.TelephonyIcons
import com.android.systemui.log.table.TableLogBuffer
+import com.android.systemui.statusbar.pipeline.mobile.data.model.ServiceStateModel
import com.android.systemui.statusbar.pipeline.mobile.data.model.SubscriptionModel
import com.android.systemui.statusbar.pipeline.mobile.util.FakeMobileMappingsProxy
import com.android.systemui.statusbar.pipeline.mobile.util.MobileMappingsProxy
@@ -93,6 +94,8 @@
private val _defaultMobileIconGroup = MutableStateFlow(DEFAULT_ICON)
override val defaultMobileIconGroup = _defaultMobileIconGroup
+ override val deviceServiceState = MutableStateFlow<ServiceStateModel?>(null)
+
override val isAnySimSecure = MutableStateFlow(false)
override fun getIsAnySimSecure(): Boolean = isAnySimSecure.value
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/pipeline/mobile/domain/interactor/FakeMobileIconsInteractor.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/pipeline/mobile/domain/interactor/FakeMobileIconsInteractor.kt
index de6c87c2..3a4bf8e 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/pipeline/mobile/domain/interactor/FakeMobileIconsInteractor.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/pipeline/mobile/domain/interactor/FakeMobileIconsInteractor.kt
@@ -81,6 +81,8 @@
override val isForceHidden = MutableStateFlow(false)
+ override val isDeviceInEmergencyCallsOnlyMode = MutableStateFlow(false)
+
/** Always returns a new fake interactor */
override fun getMobileConnectionInteractorForSubId(subId: Int): FakeMobileIconInteractor {
return FakeMobileIconInteractor(tableLogBuffer).also {
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/volume/MediaOutputKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/volume/MediaOutputKosmos.kt
index 59bbff1..1b58582 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/volume/MediaOutputKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/volume/MediaOutputKosmos.kt
@@ -18,8 +18,6 @@
import android.content.packageManager
import android.content.pm.ApplicationInfo
-import android.os.Handler
-import android.os.looper
import com.android.systemui.kosmos.Kosmos
import com.android.systemui.kosmos.testScope
import com.android.systemui.media.mediaOutputDialogManager
@@ -32,6 +30,7 @@
import com.android.systemui.volume.panel.component.mediaoutput.domain.interactor.MediaDeviceSessionInteractor
import com.android.systemui.volume.panel.component.mediaoutput.domain.interactor.MediaOutputActionsInteractor
import com.android.systemui.volume.panel.component.mediaoutput.domain.interactor.MediaOutputInteractor
+import com.android.systemui.volume.panel.component.mediaoutput.domain.interactor.mediaControllerInteractor
val Kosmos.localMediaRepository by Kosmos.Fixture { FakeLocalMediaRepository() }
val Kosmos.localMediaRepositoryFactory by
@@ -53,7 +52,7 @@
testScope.backgroundScope,
testScope.testScheduler,
mediaControllerRepository,
- Handler(looper),
+ mediaControllerInteractor,
)
}
@@ -61,7 +60,7 @@
Kosmos.Fixture {
MediaDeviceSessionInteractor(
testScope.testScheduler,
- Handler(looper),
+ mediaControllerInteractor,
mediaControllerRepository,
)
}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/volume/data/repository/FakeAudioRepository.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/volume/data/repository/FakeAudioRepository.kt
index 617fc52..6b27079 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/volume/data/repository/FakeAudioRepository.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/volume/data/repository/FakeAudioRepository.kt
@@ -17,6 +17,7 @@
package com.android.systemui.volume.data.repository
import android.media.AudioDeviceInfo
+import android.media.AudioManager
import com.android.settingslib.volume.data.repository.AudioRepository
import com.android.settingslib.volume.shared.model.AudioStream
import com.android.settingslib.volume.shared.model.AudioStreamModel
@@ -29,10 +30,10 @@
class FakeAudioRepository : AudioRepository {
- private val mutableMode = MutableStateFlow(0)
+ private val mutableMode = MutableStateFlow(AudioManager.MODE_NORMAL)
override val mode: StateFlow<Int> = mutableMode.asStateFlow()
- private val mutableRingerMode = MutableStateFlow(RingerMode(0))
+ private val mutableRingerMode = MutableStateFlow(RingerMode(AudioManager.RINGER_MODE_NORMAL))
override val ringerMode: StateFlow<RingerMode> = mutableRingerMode.asStateFlow()
private val mutableCommunicationDevice = MutableStateFlow<AudioDeviceInfo?>(null)
@@ -53,7 +54,7 @@
audioStream = audioStream,
volume = 0,
minVolume = 0,
- maxVolume = 0,
+ maxVolume = 10,
isAffectedByRingerMode = false,
isMuted = false,
)
@@ -67,8 +68,14 @@
getAudioStreamModelState(audioStream).update { it.copy(volume = volume) }
}
- override suspend fun setMuted(audioStream: AudioStream, isMuted: Boolean) {
- getAudioStreamModelState(audioStream).update { it.copy(isMuted = isMuted) }
+ override suspend fun setMuted(audioStream: AudioStream, isMuted: Boolean): Boolean {
+ val modelState = getAudioStreamModelState(audioStream)
+ return if (modelState.value.isMuted == isMuted) {
+ false
+ } else {
+ modelState.update { it.copy(isMuted = isMuted) }
+ true
+ }
}
override suspend fun getLastAudibleVolume(audioStream: AudioStream): Int =
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/volume/panel/component/anc/VolumePanelAncKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/volume/panel/component/anc/VolumePanelAncKosmos.kt
index f9b7e69..2b5d1b9 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/volume/panel/component/anc/VolumePanelAncKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/volume/panel/component/anc/VolumePanelAncKosmos.kt
@@ -20,10 +20,13 @@
import com.android.systemui.kosmos.Kosmos
import com.android.systemui.kosmos.testScope
import com.android.systemui.util.mockito.mock
+import com.android.systemui.volume.domain.interactor.audioOutputInteractor
import com.android.systemui.volume.panel.component.anc.data.repository.FakeAncSliceRepository
import com.android.systemui.volume.panel.component.anc.domain.interactor.AncSliceInteractor
var Kosmos.sliceViewManager: SliceViewManager by Kosmos.Fixture { mock {} }
val Kosmos.ancSliceRepository by Kosmos.Fixture { FakeAncSliceRepository() }
val Kosmos.ancSliceInteractor by
- Kosmos.Fixture { AncSliceInteractor(ancSliceRepository, testScope.backgroundScope) }
+ Kosmos.Fixture {
+ AncSliceInteractor(audioOutputInteractor, ancSliceRepository, testScope.backgroundScope)
+ }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/volume/panel/component/anc/data/repository/FakeAncSliceRepository.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/volume/panel/component/anc/data/repository/FakeAncSliceRepository.kt
index d4a72b4..ebe6850 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/volume/panel/component/anc/data/repository/FakeAncSliceRepository.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/volume/panel/component/anc/data/repository/FakeAncSliceRepository.kt
@@ -16,6 +16,7 @@
package com.android.systemui.volume.panel.component.anc.data.repository
+import android.bluetooth.BluetoothDevice
import androidx.slice.Slice
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.MutableStateFlow
@@ -24,7 +25,12 @@
private val sliceByWidth = mutableMapOf<Int, MutableStateFlow<Slice?>>()
- override fun ancSlice(width: Int, isCollapsed: Boolean, hideLabel: Boolean): Flow<Slice?> {
+ override fun ancSlice(
+ device: BluetoothDevice,
+ width: Int,
+ isCollapsed: Boolean,
+ hideLabel: Boolean
+ ): Flow<Slice?> {
return sliceByWidth.getOrPut(width) { MutableStateFlow(null) }
}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/volume/panel/component/mediaoutput/domain/interactor/FakeMediaControllerInteractor.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/volume/panel/component/mediaoutput/domain/interactor/FakeMediaControllerInteractor.kt
new file mode 100644
index 0000000..f03ec01
--- /dev/null
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/volume/panel/component/mediaoutput/domain/interactor/FakeMediaControllerInteractor.kt
@@ -0,0 +1,34 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.volume.panel.component.mediaoutput.domain.interactor
+
+import android.media.session.MediaController
+import com.android.systemui.volume.panel.component.mediaoutput.domain.model.MediaControllerChangeModel
+import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.MutableSharedFlow
+
+class FakeMediaControllerInteractor : MediaControllerInteractor {
+
+ private val stateChanges = MutableSharedFlow<MediaControllerChangeModel>(replay = 1)
+
+ override fun stateChanges(mediaController: MediaController): Flow<MediaControllerChangeModel> =
+ stateChanges
+
+ fun updateState(change: MediaControllerChangeModel) {
+ stateChanges.tryEmit(change)
+ }
+}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/volume/panel/component/mediaoutput/domain/interactor/MediaControllerInteractorKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/volume/panel/component/mediaoutput/domain/interactor/MediaControllerInteractorKosmos.kt
new file mode 100644
index 0000000..652b3ea
--- /dev/null
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/volume/panel/component/mediaoutput/domain/interactor/MediaControllerInteractorKosmos.kt
@@ -0,0 +1,24 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.volume.panel.component.mediaoutput.domain.interactor
+
+import android.os.Handler
+import android.os.looper
+import com.android.systemui.kosmos.Kosmos
+
+var Kosmos.mediaControllerInteractor: MediaControllerInteractor by
+ Kosmos.Fixture { MediaControllerInteractorImpl(Handler(looper)) }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/volume/panel/component/mediaoutput/domain/interactor/TestMediaDevicesFactory.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/volume/panel/component/mediaoutput/domain/interactor/TestMediaDevicesFactory.kt
new file mode 100644
index 0000000..4029609
--- /dev/null
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/volume/panel/component/mediaoutput/domain/interactor/TestMediaDevicesFactory.kt
@@ -0,0 +1,58 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.volume.panel.component.mediaoutput.domain.interactor
+
+import android.annotation.SuppressLint
+import android.bluetooth.BluetoothDevice
+import android.graphics.drawable.TestStubDrawable
+import com.android.settingslib.bluetooth.CachedBluetoothDevice
+import com.android.settingslib.media.BluetoothMediaDevice
+import com.android.settingslib.media.MediaDevice
+import com.android.settingslib.media.PhoneMediaDevice
+import com.android.systemui.util.mockito.mock
+import com.android.systemui.util.mockito.whenever
+
+@SuppressLint("StaticFieldLeak") // These are mocks
+object TestMediaDevicesFactory {
+
+ fun builtInMediaDevice(): MediaDevice = mock {
+ whenever(name).thenReturn("built_in_media")
+ whenever(icon).thenReturn(TestStubDrawable())
+ }
+
+ fun wiredMediaDevice(): MediaDevice =
+ mock<PhoneMediaDevice> {
+ whenever(deviceType)
+ .thenReturn(MediaDevice.MediaDeviceType.TYPE_3POINT5_MM_AUDIO_DEVICE)
+ whenever(name).thenReturn("wired_media")
+ whenever(icon).thenReturn(TestStubDrawable())
+ }
+
+ fun bluetoothMediaDevice(): MediaDevice {
+ val bluetoothDevice = mock<BluetoothDevice>()
+ val cachedBluetoothDevice: CachedBluetoothDevice = mock {
+ whenever(isHearingAidDevice).thenReturn(true)
+ whenever(address).thenReturn("bt_media_device")
+ whenever(device).thenReturn(bluetoothDevice)
+ }
+ return mock<BluetoothMediaDevice> {
+ whenever(name).thenReturn("bt_media")
+ whenever(icon).thenReturn(TestStubDrawable())
+ whenever(cachedDevice).thenReturn(cachedBluetoothDevice)
+ }
+ }
+}
diff --git a/packages/SystemUI/unfold/src/com/android/systemui/unfold/UnfoldRemoteModule.kt b/packages/SystemUI/unfold/src/com/android/systemui/unfold/UnfoldRemoteModule.kt
index 2bc2db3..fe10244 100644
--- a/packages/SystemUI/unfold/src/com/android/systemui/unfold/UnfoldRemoteModule.kt
+++ b/packages/SystemUI/unfold/src/com/android/systemui/unfold/UnfoldRemoteModule.kt
@@ -53,8 +53,8 @@
@UnfoldMain
fun provideMainRotationChangeProvider(
rotationChangeProviderFactory: RotationChangeProvider.Factory,
- @UnfoldMain mainHandler: Handler,
+ @UnfoldMain callbackHandler: Handler,
): RotationChangeProvider {
- return rotationChangeProviderFactory.create(mainHandler)
+ return rotationChangeProviderFactory.create(callbackHandler)
}
}
diff --git a/packages/SystemUI/unfold/src/com/android/systemui/unfold/UnfoldSharedComponent.kt b/packages/SystemUI/unfold/src/com/android/systemui/unfold/UnfoldSharedComponent.kt
index 31b7ccc..f382070 100644
--- a/packages/SystemUI/unfold/src/com/android/systemui/unfold/UnfoldSharedComponent.kt
+++ b/packages/SystemUI/unfold/src/com/android/systemui/unfold/UnfoldSharedComponent.kt
@@ -87,6 +87,7 @@
@BindsInstance @UnfoldMain executor: Executor,
@BindsInstance @UnfoldMain handler: Handler,
@BindsInstance @UnfoldSingleThreadBg singleThreadBgExecutor: Executor,
+ @BindsInstance @UnfoldBg bgHandler: Handler,
@BindsInstance displayManager: DisplayManager,
@BindsInstance @UnfoldTransitionATracePrefix tracingTagPrefix: String,
): RemoteUnfoldSharedComponent
diff --git a/packages/SystemUI/unfold/src/com/android/systemui/unfold/UnfoldSharedModule.kt b/packages/SystemUI/unfold/src/com/android/systemui/unfold/UnfoldSharedModule.kt
index 1b7e71a..f83ea84 100644
--- a/packages/SystemUI/unfold/src/com/android/systemui/unfold/UnfoldSharedModule.kt
+++ b/packages/SystemUI/unfold/src/com/android/systemui/unfold/UnfoldSharedModule.kt
@@ -270,9 +270,9 @@
@UnfoldMain
fun provideRotationChangeProvider(
rotationChangeProviderFactory: RotationChangeProvider.Factory,
- @UnfoldMain mainHandler: Handler,
+ @UnfoldMain callbackHandler: Handler,
): RotationChangeProvider {
- return rotationChangeProviderFactory.create(mainHandler)
+ return rotationChangeProviderFactory.create(callbackHandler)
}
@Provides
@@ -280,8 +280,9 @@
@UnfoldBg
fun provideBgRotationChangeProvider(
rotationChangeProviderFactory: RotationChangeProvider.Factory,
- @UnfoldBg bgHandler: Handler,
+ @UnfoldBg callbackHandler: Handler,
): RotationChangeProvider {
- return rotationChangeProviderFactory.create(bgHandler)
+ // For UnfoldBg RotationChangeProvider we use bgHandler as callbackHandler
+ return rotationChangeProviderFactory.create(callbackHandler)
}
}
diff --git a/packages/SystemUI/unfold/src/com/android/systemui/unfold/UnfoldTransitionFactory.kt b/packages/SystemUI/unfold/src/com/android/systemui/unfold/UnfoldTransitionFactory.kt
index 1cbaf31..8a4f985 100644
--- a/packages/SystemUI/unfold/src/com/android/systemui/unfold/UnfoldTransitionFactory.kt
+++ b/packages/SystemUI/unfold/src/com/android/systemui/unfold/UnfoldTransitionFactory.kt
@@ -77,6 +77,7 @@
mainExecutor: Executor,
mainHandler: Handler,
singleThreadBgExecutor: Executor,
+ bgHandler: Handler,
tracingTagPrefix: String,
displayManager: DisplayManager,
): RemoteUnfoldSharedComponent =
@@ -87,6 +88,7 @@
mainExecutor,
mainHandler,
singleThreadBgExecutor,
+ bgHandler,
displayManager,
tracingTagPrefix,
)
diff --git a/packages/SystemUI/unfold/src/com/android/systemui/unfold/updates/DeviceFoldStateProvider.kt b/packages/SystemUI/unfold/src/com/android/systemui/unfold/updates/DeviceFoldStateProvider.kt
index 77f637b..a100974 100644
--- a/packages/SystemUI/unfold/src/com/android/systemui/unfold/updates/DeviceFoldStateProvider.kt
+++ b/packages/SystemUI/unfold/src/com/android/systemui/unfold/updates/DeviceFoldStateProvider.kt
@@ -20,6 +20,7 @@
import android.util.Log
import androidx.annotation.FloatRange
import androidx.annotation.VisibleForTesting
+import androidx.annotation.WorkerThread
import androidx.core.util.Consumer
import com.android.systemui.unfold.compat.INNER_SCREEN_SMALLEST_SCREEN_WIDTH_THRESHOLD_DP
import com.android.systemui.unfold.config.UnfoldTransitionConfig
@@ -215,6 +216,7 @@
}
private inner class FoldRotationListener : RotationChangeProvider.RotationListener {
+ @WorkerThread
override fun onRotationChanged(newRotation: Int) {
assertInProgressThread()
if (isTransitionInProgress) cancelAnimation()
diff --git a/packages/SystemUI/unfold/src/com/android/systemui/unfold/updates/RotationChangeProvider.kt b/packages/SystemUI/unfold/src/com/android/systemui/unfold/updates/RotationChangeProvider.kt
index bb91f9b..4f3aee9 100644
--- a/packages/SystemUI/unfold/src/com/android/systemui/unfold/updates/RotationChangeProvider.kt
+++ b/packages/SystemUI/unfold/src/com/android/systemui/unfold/updates/RotationChangeProvider.kt
@@ -21,6 +21,8 @@
import android.os.Handler
import android.os.RemoteException
import android.os.Trace
+import androidx.annotation.AnyThread
+import com.android.systemui.unfold.dagger.UnfoldBg
import com.android.systemui.unfold.util.CallbackController
import dagger.assisted.Assisted
import dagger.assisted.AssistedFactory
@@ -35,7 +37,8 @@
constructor(
private val displayManager: DisplayManager,
private val context: Context,
- @Assisted private val handler: Handler,
+ @UnfoldBg private val bgHandler: Handler,
+ @Assisted private val callbackHandler: Handler,
) : CallbackController<RotationChangeProvider.RotationListener> {
private val listeners = mutableListOf<RotationListener>()
@@ -44,7 +47,7 @@
private var lastRotation: Int? = null
override fun addCallback(listener: RotationListener) {
- handler.post {
+ bgHandler.post {
if (listeners.isEmpty()) {
subscribeToRotation()
}
@@ -53,7 +56,7 @@
}
override fun removeCallback(listener: RotationListener) {
- handler.post {
+ bgHandler.post {
listeners -= listener
if (listeners.isEmpty()) {
unsubscribeToRotation()
@@ -64,7 +67,7 @@
private fun subscribeToRotation() {
try {
- displayManager.registerDisplayListener(displayListener, handler)
+ displayManager.registerDisplayListener(displayListener, callbackHandler)
} catch (e: RemoteException) {
throw e.rethrowFromSystemServer()
}
@@ -80,8 +83,11 @@
/** Gets notified of rotation changes. */
fun interface RotationListener {
- /** Called once rotation changes. */
- fun onRotationChanged(newRotation: Int)
+ /**
+ * Called once rotation changes. This callback is called on the handler provided to
+ * [RotationChangeProvider.Factory.create].
+ */
+ @AnyThread fun onRotationChanged(newRotation: Int)
}
private inner class RotationDisplayListener : DisplayManager.DisplayListener {
@@ -110,7 +116,7 @@
@AssistedFactory
interface Factory {
- /** Creates a new [RotationChangeProvider] that provides updated using [handler]. */
- fun create(handler: Handler): RotationChangeProvider
+ /** Creates a new [RotationChangeProvider] that provides updated using [callbackHandler]. */
+ fun create(callbackHandler: Handler): RotationChangeProvider
}
}
diff --git a/proto/src/am_capabilities.proto b/proto/src/am_capabilities.proto
index fc9f7a45..c2b3ac2 100644
--- a/proto/src/am_capabilities.proto
+++ b/proto/src/am_capabilities.proto
@@ -15,8 +15,16 @@
string name = 1;
}
+message VMInfo {
+ // The value of the "java.vm.name" system property
+ string name = 1;
+ // The value of the "java.vm.version" system property
+ string version = 2;
+}
+
message Capabilities {
repeated Capability values = 1;
repeated VMCapability vm_capabilities = 2;
repeated FrameworkCapability framework_capabilities = 3;
+ VMInfo vm_info = 4;
}
diff --git a/ravenwood/Android.bp b/ravenwood/Android.bp
index bc608c5..95cbb6b 100644
--- a/ravenwood/Android.bp
+++ b/ravenwood/Android.bp
@@ -221,7 +221,9 @@
data: [
":framework-minus-apex.ravenwood.stats",
":framework-minus-apex.ravenwood.apis",
+ ":framework-minus-apex.ravenwood.keep_all",
":services.core.ravenwood.stats",
":services.core.ravenwood.apis",
+ ":services.core.ravenwood.keep_all",
],
}
diff --git a/ravenwood/junit-impl-src/android/platform/test/ravenwood/RavenwoodBaseContext.java b/ravenwood/junit-impl-src/android/platform/test/ravenwood/RavenwoodBaseContext.java
new file mode 100644
index 0000000..4992c4b
--- /dev/null
+++ b/ravenwood/junit-impl-src/android/platform/test/ravenwood/RavenwoodBaseContext.java
@@ -0,0 +1,753 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package android.platform.test.ravenwood;
+
+import android.content.BroadcastReceiver;
+import android.content.ComponentName;
+import android.content.ContentResolver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.content.IntentSender;
+import android.content.IntentSender.SendIntentException;
+import android.content.ServiceConnection;
+import android.content.SharedPreferences;
+import android.content.pm.ApplicationInfo;
+import android.content.pm.PackageManager;
+import android.content.pm.PackageManager.NameNotFoundException;
+import android.content.res.AssetManager;
+import android.content.res.Configuration;
+import android.content.res.Resources;
+import android.content.res.Resources.Theme;
+import android.database.DatabaseErrorHandler;
+import android.database.sqlite.SQLiteDatabase;
+import android.database.sqlite.SQLiteDatabase.CursorFactory;
+import android.graphics.Bitmap;
+import android.graphics.drawable.Drawable;
+import android.net.Uri;
+import android.os.Bundle;
+import android.os.Handler;
+import android.os.IBinder;
+import android.os.Looper;
+import android.os.UserHandle;
+import android.view.Display;
+import android.view.DisplayAdjustments;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileNotFoundException;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+
+/**
+ * A subclass of Context with all the abstract methods replaced with concrete methods.
+ *
+ * <p>In order to make sure it implements all the abstract methods, we intentionally keep it
+ * non-abstract.
+ */
+public class RavenwoodBaseContext extends Context {
+ RavenwoodBaseContext() {
+ // Only usable by ravenwood.
+ }
+
+ private static RuntimeException notSupported() {
+ return new RuntimeException("This Context API is not yet supported under"
+ + " the Ravenwood deviceless testing environment. Contact g/ravenwood");
+ }
+
+ @Override
+ public AssetManager getAssets() {
+ throw notSupported();
+ }
+
+ @Override
+ public Resources getResources() {
+ throw notSupported();
+ }
+
+ @Override
+ public PackageManager getPackageManager() {
+ throw notSupported();
+ }
+
+ @Override
+ public ContentResolver getContentResolver() {
+ throw notSupported();
+ }
+
+ @Override
+ public Looper getMainLooper() {
+ throw notSupported();
+ }
+
+ @Override
+ public Context getApplicationContext() {
+ throw notSupported();
+ }
+
+ @Override
+ public void setTheme(int resid) {
+ throw notSupported();
+ }
+
+ @Override
+ public Theme getTheme() {
+ throw notSupported();
+ }
+
+ @Override
+ public ClassLoader getClassLoader() {
+ throw notSupported();
+ }
+
+ @Override
+ public String getPackageName() {
+ throw notSupported();
+ }
+
+ @Override
+ public String getBasePackageName() {
+ throw notSupported();
+ }
+
+ @Override
+ public ApplicationInfo getApplicationInfo() {
+ throw notSupported();
+ }
+
+ @Override
+ public String getPackageResourcePath() {
+ throw notSupported();
+ }
+
+ @Override
+ public String getPackageCodePath() {
+ throw notSupported();
+ }
+
+ @Override
+ public SharedPreferences getSharedPreferences(String name, int mode) {
+ throw notSupported();
+ }
+
+ @Override
+ public SharedPreferences getSharedPreferences(File file, int mode) {
+ throw notSupported();
+ }
+
+ @Override
+ public boolean moveSharedPreferencesFrom(Context sourceContext, String name) {
+ throw notSupported();
+ }
+
+ @Override
+ public boolean deleteSharedPreferences(String name) {
+ throw notSupported();
+ }
+
+ @Override
+ public void reloadSharedPreferences() {
+ throw notSupported();
+ }
+
+ @Override
+ public FileInputStream openFileInput(String name) throws FileNotFoundException {
+ throw notSupported();
+ }
+
+ @Override
+ public FileOutputStream openFileOutput(String name, int mode) throws FileNotFoundException {
+ throw notSupported();
+ }
+
+ @Override
+ public boolean deleteFile(String name) {
+ throw notSupported();
+ }
+
+ @Override
+ public File getFileStreamPath(String name) {
+ throw notSupported();
+ }
+
+ @Override
+ public File getSharedPreferencesPath(String name) {
+ throw notSupported();
+ }
+
+ @Override
+ public File getDataDir() {
+ throw notSupported();
+ }
+
+ @Override
+ public File getFilesDir() {
+ throw notSupported();
+ }
+
+ @Override
+ public File getNoBackupFilesDir() {
+ throw notSupported();
+ }
+
+ @Override
+ public File getExternalFilesDir(String type) {
+ throw notSupported();
+ }
+
+ @Override
+ public File[] getExternalFilesDirs(String type) {
+ throw notSupported();
+ }
+
+ @Override
+ public File getObbDir() {
+ throw notSupported();
+ }
+
+ @Override
+ public File[] getObbDirs() {
+ throw notSupported();
+ }
+
+ @Override
+ public File getCacheDir() {
+ throw notSupported();
+ }
+
+ @Override
+ public File getCodeCacheDir() {
+ throw notSupported();
+ }
+
+ @Override
+ public File getExternalCacheDir() {
+ throw notSupported();
+ }
+
+ @Override
+ public File getPreloadsFileCache() {
+ throw notSupported();
+ }
+
+ @Override
+ public File[] getExternalCacheDirs() {
+ throw notSupported();
+ }
+
+ @Override
+ public File[] getExternalMediaDirs() {
+ throw notSupported();
+ }
+
+ @Override
+ public String[] fileList() {
+ throw notSupported();
+ }
+
+ @Override
+ public File getDir(String name, int mode) {
+ throw notSupported();
+ }
+
+ @Override
+ public SQLiteDatabase openOrCreateDatabase(String name, int mode, CursorFactory factory) {
+ throw notSupported();
+ }
+
+ @Override
+ public SQLiteDatabase openOrCreateDatabase(String name, int mode, CursorFactory factory,
+ DatabaseErrorHandler errorHandler) {
+ throw notSupported();
+ }
+
+ @Override
+ public boolean moveDatabaseFrom(Context sourceContext, String name) {
+ throw notSupported();
+ }
+
+ @Override
+ public boolean deleteDatabase(String name) {
+ throw notSupported();
+ }
+
+ @Override
+ public File getDatabasePath(String name) {
+ throw notSupported();
+ }
+
+ @Override
+ public String[] databaseList() {
+ throw notSupported();
+ }
+
+ @Override
+ public Drawable getWallpaper() {
+ throw notSupported();
+ }
+
+ @Override
+ public Drawable peekWallpaper() {
+ throw notSupported();
+ }
+
+ @Override
+ public int getWallpaperDesiredMinimumWidth() {
+ throw notSupported();
+ }
+
+ @Override
+ public int getWallpaperDesiredMinimumHeight() {
+ throw notSupported();
+ }
+
+ @Override
+ public void setWallpaper(Bitmap bitmap) throws IOException {
+ throw notSupported();
+ }
+
+ @Override
+ public void setWallpaper(InputStream data) throws IOException {
+ throw notSupported();
+ }
+
+ @Override
+ public void clearWallpaper() throws IOException {
+ throw notSupported();
+ }
+
+ @Override
+ public void startActivity(Intent intent) {
+ throw notSupported();
+ }
+
+ @Override
+ public void startActivity(Intent intent, Bundle options) {
+ throw notSupported();
+ }
+
+ @Override
+ public void startActivities(Intent[] intents) {
+ throw notSupported();
+ }
+
+ @Override
+ public void startActivities(Intent[] intents, Bundle options) {
+ throw notSupported();
+ }
+
+ @Override
+ public void startIntentSender(IntentSender intent, Intent fillInIntent, int flagsMask,
+ int flagsValues, int extraFlags) throws SendIntentException {
+ throw notSupported();
+ }
+
+ @Override
+ public void startIntentSender(IntentSender intent, Intent fillInIntent, int flagsMask,
+ int flagsValues, int extraFlags, Bundle options) throws SendIntentException {
+ throw notSupported();
+ }
+
+ @Override
+ public void sendBroadcast(Intent intent) {
+ throw notSupported();
+ }
+
+ @Override
+ public void sendBroadcast(Intent intent, String receiverPermission) {
+ throw notSupported();
+ }
+
+ @Override
+ public void sendBroadcastAsUserMultiplePermissions(Intent intent, UserHandle user,
+ String[] receiverPermissions) {
+ throw notSupported();
+ }
+
+ @Override
+ public void sendBroadcast(Intent intent, String receiverPermission, int appOp) {
+ throw notSupported();
+ }
+
+ @Override
+ public void sendOrderedBroadcast(Intent intent, String receiverPermission) {
+ throw notSupported();
+ }
+
+ @Override
+ public void sendOrderedBroadcast(Intent intent, String receiverPermission,
+ BroadcastReceiver resultReceiver, Handler scheduler, int initialCode,
+ String initialData, Bundle initialExtras) {
+ throw notSupported();
+ }
+
+ @Override
+ public void sendOrderedBroadcast(Intent intent, String receiverPermission,
+ int appOp, BroadcastReceiver resultReceiver, Handler scheduler, int initialCode,
+ String initialData, Bundle initialExtras) {
+ throw notSupported();
+ }
+
+ @Override
+ public void sendBroadcastAsUser(Intent intent, UserHandle user) {
+ throw notSupported();
+ }
+
+ @Override
+ public void sendBroadcastAsUser(Intent intent, UserHandle user, String receiverPermission) {
+ throw notSupported();
+ }
+
+ @Override
+ public void sendBroadcastAsUser(Intent intent, UserHandle user, String receiverPermission,
+ Bundle options) {
+ throw notSupported();
+ }
+
+ @Override
+ public void sendBroadcastAsUser(Intent intent, UserHandle user, String receiverPermission,
+ int appOp) {
+ throw notSupported();
+ }
+
+ @Override
+ public void sendOrderedBroadcastAsUser(Intent intent, UserHandle user,
+ String receiverPermission, BroadcastReceiver resultReceiver, Handler scheduler,
+ int initialCode, String initialData, Bundle initialExtras) {
+ throw notSupported();
+ }
+
+ @Override
+ public void sendOrderedBroadcastAsUser(Intent intent, UserHandle user,
+ String receiverPermission, int appOp, BroadcastReceiver resultReceiver,
+ Handler scheduler, int initialCode, String initialData, Bundle initialExtras) {
+ throw notSupported();
+ }
+
+ @Override
+ public void sendOrderedBroadcastAsUser(Intent intent, UserHandle user,
+ String receiverPermission, int appOp, Bundle options,
+ BroadcastReceiver resultReceiver, Handler scheduler, int initialCode,
+ String initialData, Bundle initialExtras) {
+ throw notSupported();
+ }
+
+ @Override
+ public void sendStickyBroadcast(Intent intent) {
+ throw notSupported();
+ }
+
+ @Override
+ public void sendStickyOrderedBroadcast(Intent intent, BroadcastReceiver resultReceiver,
+ Handler scheduler, int initialCode, String initialData, Bundle initialExtras) {
+ throw notSupported();
+
+ }
+
+ @Override
+ public void removeStickyBroadcast(Intent intent) {
+ throw notSupported();
+
+ }
+
+ @Override
+ public void sendStickyBroadcastAsUser(Intent intent, UserHandle user) {
+ throw notSupported();
+ }
+
+ @Override
+ public void sendStickyBroadcastAsUser(Intent intent, UserHandle user, Bundle options) {
+ throw notSupported();
+
+ }
+
+ @Override
+ public void sendStickyOrderedBroadcastAsUser(Intent intent, UserHandle user,
+ BroadcastReceiver resultReceiver, Handler scheduler, int initialCode,
+ String initialData, Bundle initialExtras) {
+ throw notSupported();
+ }
+
+ @Override
+ public void removeStickyBroadcastAsUser(Intent intent, UserHandle user) {
+ throw notSupported();
+ }
+
+ @Override
+ public Intent registerReceiver(BroadcastReceiver receiver, IntentFilter filter) {
+ throw notSupported();
+ }
+
+ @Override
+ public Intent registerReceiver(BroadcastReceiver receiver, IntentFilter filter, int flags) {
+ throw notSupported();
+ }
+
+ @Override
+ public Intent registerReceiver(BroadcastReceiver receiver, IntentFilter filter,
+ String broadcastPermission, Handler scheduler) {
+ throw notSupported();
+ }
+
+ @Override
+ public Intent registerReceiver(BroadcastReceiver receiver, IntentFilter filter,
+ String broadcastPermission, Handler scheduler, int flags) {
+ throw notSupported();
+ }
+
+ @Override
+ public Intent registerReceiverAsUser(BroadcastReceiver receiver, UserHandle user,
+ IntentFilter filter, String broadcastPermission, Handler scheduler) {
+ throw notSupported();
+ }
+
+ @Override
+ public Intent registerReceiverAsUser(BroadcastReceiver receiver, UserHandle user,
+ IntentFilter filter, String broadcastPermission, Handler scheduler, int flags) {
+ throw notSupported();
+ }
+
+ @Override
+ public void unregisterReceiver(BroadcastReceiver receiver) {
+ throw notSupported();
+ }
+
+ @Override
+ public ComponentName startService(Intent service) {
+ throw notSupported();
+ }
+
+ @Override
+ public ComponentName startForegroundService(Intent service) {
+ throw notSupported();
+ }
+
+ @Override
+ public ComponentName startForegroundServiceAsUser(Intent service, UserHandle user) {
+ throw notSupported();
+ }
+
+ @Override
+ public boolean stopService(Intent service) {
+ throw notSupported();
+ }
+
+ @Override
+ public ComponentName startServiceAsUser(Intent service, UserHandle user) {
+ throw notSupported();
+ }
+
+ @Override
+ public boolean stopServiceAsUser(Intent service, UserHandle user) {
+ throw notSupported();
+ }
+
+ @Override
+ public boolean bindService(Intent service, ServiceConnection conn, int flags) {
+ throw notSupported();
+ }
+
+ @Override
+ public void unbindService(ServiceConnection conn) {
+ throw notSupported();
+ }
+
+ @Override
+ public boolean startInstrumentation(ComponentName className, String profileFile,
+ Bundle arguments) {
+ throw notSupported();
+ }
+
+ @Override
+ public Object getSystemService(String name) {
+ throw notSupported();
+ }
+
+ @Override
+ public String getSystemServiceName(Class<?> serviceClass) {
+ throw notSupported();
+ }
+
+ @Override
+ public int checkPermission(String permission, int pid, int uid) {
+ throw notSupported();
+ }
+
+ @Override
+ public int checkPermission(String permission, int pid, int uid, IBinder callerToken) {
+ throw notSupported();
+ }
+
+ @Override
+ public int checkCallingPermission(String permission) {
+ throw notSupported();
+ }
+
+ @Override
+ public int checkCallingOrSelfPermission(String permission) {
+ throw notSupported();
+ }
+
+ @Override
+ public int checkSelfPermission(String permission) {
+ throw notSupported();
+ }
+
+ @Override
+ public void enforcePermission(String permission, int pid, int uid, String message) {
+ throw notSupported();
+ }
+
+ @Override
+ public void enforceCallingPermission(String permission, String message) {
+ throw notSupported();
+ }
+
+ @Override
+ public void enforceCallingOrSelfPermission(String permission, String message) {
+ throw notSupported();
+ }
+
+ @Override
+ public void grantUriPermission(String toPackage, Uri uri, int modeFlags) {
+ throw notSupported();
+ }
+
+ @Override
+ public void revokeUriPermission(Uri uri, int modeFlags) {
+ throw notSupported();
+ }
+
+ @Override
+ public void revokeUriPermission(String toPackage, Uri uri, int modeFlags) {
+ throw notSupported();
+ }
+
+ @Override
+ public int checkUriPermission(Uri uri, int pid, int uid, int modeFlags) {
+ throw notSupported();
+ }
+
+ @Override
+ public int checkUriPermission(Uri uri, int pid, int uid, int modeFlags, IBinder callerToken) {
+ throw notSupported();
+ }
+
+ @Override
+ public int checkCallingUriPermission(Uri uri, int modeFlags) {
+ throw notSupported();
+ }
+
+ @Override
+ public int checkCallingOrSelfUriPermission(Uri uri, int modeFlags) {
+ throw notSupported();
+ }
+
+ @Override
+ public int checkUriPermission(Uri uri, String readPermission, String writePermission,
+ int pid, int uid, int modeFlags) {
+ throw notSupported();
+ }
+
+ @Override
+ public void enforceUriPermission(Uri uri, int pid, int uid, int modeFlags, String message) {
+ throw notSupported();
+ }
+
+ @Override
+ public void enforceCallingUriPermission(Uri uri, int modeFlags, String message) {
+ throw notSupported();
+ }
+
+ @Override
+ public void enforceCallingOrSelfUriPermission(Uri uri, int modeFlags, String message) {
+ throw notSupported();
+ }
+
+ @Override
+ public void enforceUriPermission(Uri uri, String readPermission, String writePermission,
+ int pid, int uid, int modeFlags, String message) {
+ throw notSupported();
+ }
+
+ @Override
+ public Context createPackageContext(String packageName, int flags)
+ throws NameNotFoundException {
+ throw notSupported();
+ }
+
+ @Override
+ public Context createApplicationContext(ApplicationInfo application, int flags)
+ throws NameNotFoundException {
+ throw notSupported();
+ }
+
+ @Override
+ public Context createContextForSplit(String splitName) throws NameNotFoundException {
+ throw notSupported();
+ }
+
+ @Override
+ public Context createConfigurationContext(Configuration overrideConfiguration) {
+ throw notSupported();
+ }
+
+ @Override
+ public Context createDisplayContext(Display display) {
+ throw notSupported();
+ }
+
+ @Override
+ public Context createDeviceProtectedStorageContext() {
+ throw notSupported();
+ }
+
+ @Override
+ public Context createCredentialProtectedStorageContext() {
+ throw notSupported();
+ }
+
+ @Override
+ public DisplayAdjustments getDisplayAdjustments(int displayId) {
+ throw notSupported();
+ }
+
+ @Override
+ public int getDisplayId() {
+ throw notSupported();
+ }
+
+ @Override
+ public void updateDisplay(int displayId) {
+ throw notSupported();
+ }
+
+ @Override
+ public boolean isDeviceProtectedStorage() {
+ throw notSupported();
+ }
+
+ @Override
+ public boolean isCredentialProtectedStorage() {
+ throw notSupported();
+ }
+
+ @Override
+ public boolean canLoadUnsafeResources() {
+ throw notSupported();
+ }
+}
diff --git a/ravenwood/junit-impl-src/android/platform/test/ravenwood/RavenwoodContext.java b/ravenwood/junit-impl-src/android/platform/test/ravenwood/RavenwoodContext.java
index 109ef76..1dd5e1d 100644
--- a/ravenwood/junit-impl-src/android/platform/test/ravenwood/RavenwoodContext.java
+++ b/ravenwood/junit-impl-src/android/platform/test/ravenwood/RavenwoodContext.java
@@ -28,7 +28,6 @@
import android.os.UserHandle;
import android.ravenwood.example.BlueManager;
import android.ravenwood.example.RedManager;
-import android.test.mock.MockContext;
import android.util.ArrayMap;
import android.util.Singleton;
@@ -36,7 +35,7 @@
import java.util.concurrent.Executor;
import java.util.function.Supplier;
-public class RavenwoodContext extends MockContext {
+public class RavenwoodContext extends RavenwoodBaseContext {
private final String mPackageName;
private final HandlerThread mMainThread;
diff --git a/ravenwood/runtime-helper-src/framework/com/android/platform/test/ravenwood/runtimehelper/ClassLoadHook.java b/ravenwood/runtime-helper-src/framework/com/android/platform/test/ravenwood/runtimehelper/ClassLoadHook.java
index 96b7057..69ff262 100644
--- a/ravenwood/runtime-helper-src/framework/com/android/platform/test/ravenwood/runtimehelper/ClassLoadHook.java
+++ b/ravenwood/runtime-helper-src/framework/com/android/platform/test/ravenwood/runtimehelper/ClassLoadHook.java
@@ -162,20 +162,23 @@
android.graphics.Interpolator.class,
android.graphics.Matrix.class,
android.graphics.Path.class,
+ android.graphics.Color.class,
+ android.graphics.ColorSpace.class,
};
/**
- * @return if a given class has any native method or not.
+ * @return if a given class and its nested classes, if any, have any native method or not.
*/
private static boolean hasNativeMethod(Class<?> clazz) {
- for (var method : clazz.getDeclaredMethods()) {
- if (Modifier.isNative(method.getModifiers())) {
- return true;
+ for (var nestedClass : clazz.getNestMembers()) {
+ for (var method : nestedClass.getDeclaredMethods()) {
+ if (Modifier.isNative(method.getModifiers())) {
+ return true;
+ }
}
}
return false;
}
-
/**
* Create a list of classes as comma-separated that require JNI methods to be set up from
* a given class list, ignoring classes with no native methods.
diff --git a/ravenwood/scripts/ravenwood-stats-collector.sh b/ravenwood/scripts/ravenwood-stats-collector.sh
index cf58bd2..43b61a4 100755
--- a/ravenwood/scripts/ravenwood-stats-collector.sh
+++ b/ravenwood/scripts/ravenwood-stats-collector.sh
@@ -18,8 +18,14 @@
set -e
# Output files
-stats=/tmp/ravenwood-stats-all.csv
-apis=/tmp/ravenwood-apis-all.csv
+out_dir=/tmp/ravenwood
+stats=$out_dir/ravenwood-stats-all.csv
+apis=$out_dir/ravenwood-apis-all.csv
+keep_all_dir=$out_dir/ravenwood-keep-all/
+
+rm -fr $out_dir
+mkdir -p $out_dir
+mkdir -p $keep_all_dir
# Where the input files are.
path=$ANDROID_BUILD_TOP/out/host/linux-x86/testcases/ravenwood-stats-checker/x86_64/
@@ -76,3 +82,7 @@
collect_stats $stats
collect_apis $apis
+
+cp *keep_all.txt $keep_all_dir
+echo "Keep all files created at:"
+find $keep_all_dir -type f
\ No newline at end of file
diff --git a/ravenwood/texts/ravenwood-annotation-allowed-classes.txt b/ravenwood/texts/ravenwood-annotation-allowed-classes.txt
index e452299..f3172ae 100644
--- a/ravenwood/texts/ravenwood-annotation-allowed-classes.txt
+++ b/ravenwood/texts/ravenwood-annotation-allowed-classes.txt
@@ -1,5 +1,7 @@
# Only classes listed here can use the Ravenwood annotations.
+com.android.internal.ravenwood.*
+
com.android.internal.display.BrightnessSynchronizer
com.android.internal.util.ArrayUtils
com.android.internal.logging.MetricsLogger
@@ -237,6 +239,8 @@
android.accounts.Account
android.graphics.Bitmap$Config
+android.graphics.Color
+android.graphics.ColorSpace
android.graphics.Insets
android.graphics.Interpolator
android.graphics.Matrix
diff --git a/ravenwood/texts/ravenwood-framework-policies.txt b/ravenwood/texts/ravenwood-framework-policies.txt
index 371c3ac..9d29a05 100644
--- a/ravenwood/texts/ravenwood-framework-policies.txt
+++ b/ravenwood/texts/ravenwood-framework-policies.txt
@@ -1,59 +1,59 @@
# Ravenwood "policy" file for framework-minus-apex.
# Keep all AIDL interfaces
-class :aidl stubclass
+class :aidl keepclass
# Keep all feature flag implementations
-class :feature_flags stubclass
+class :feature_flags keepclass
# Keep all sysprops generated code implementations
-class :sysprops stubclass
+class :sysprops keepclass
# Exported to Mainline modules; cannot use annotations
-class com.android.internal.util.FastXmlSerializer stubclass
-class com.android.internal.util.FileRotator stubclass
-class com.android.internal.util.HexDump stubclass
-class com.android.internal.util.IndentingPrintWriter stubclass
-class com.android.internal.util.LocalLog stubclass
-class com.android.internal.util.MessageUtils stubclass
-class com.android.internal.util.TokenBucket stubclass
-class android.os.HandlerExecutor stubclass
-class android.util.BackupUtils stubclass
-class android.util.IndentingPrintWriter stubclass
-class android.util.LocalLog stubclass
-class android.util.Pair stubclass
-class android.util.Rational stubclass
+class com.android.internal.util.FastXmlSerializer keepclass
+class com.android.internal.util.FileRotator keepclass
+class com.android.internal.util.HexDump keepclass
+class com.android.internal.util.IndentingPrintWriter keepclass
+class com.android.internal.util.LocalLog keepclass
+class com.android.internal.util.MessageUtils keepclass
+class com.android.internal.util.TokenBucket keepclass
+class android.os.HandlerExecutor keepclass
+class android.util.BackupUtils keepclass
+class android.util.IndentingPrintWriter keepclass
+class android.util.LocalLog keepclass
+class android.util.Pair keepclass
+class android.util.Rational keepclass
# From modules-utils; cannot use annotations
-class com.android.internal.util.Preconditions stubclass
-class com.android.internal.logging.InstanceId stubclass
-class com.android.internal.logging.InstanceIdSequence stubclass
-class com.android.internal.logging.UiEvent stubclass
-class com.android.internal.logging.UiEventLogger stubclass
+class com.android.internal.util.Preconditions keepclass
+class com.android.internal.logging.InstanceId keepclass
+class com.android.internal.logging.InstanceIdSequence keepclass
+class com.android.internal.logging.UiEvent keepclass
+class com.android.internal.logging.UiEventLogger keepclass
# From modules-utils; cannot use annotations
-class com.android.modules.utils.BinaryXmlPullParser stubclass
-class com.android.modules.utils.BinaryXmlSerializer stubclass
-class com.android.modules.utils.FastDataInput stubclass
-class com.android.modules.utils.FastDataOutput stubclass
-class com.android.modules.utils.ModifiedUtf8 stubclass
-class com.android.modules.utils.TypedXmlPullParser stubclass
-class com.android.modules.utils.TypedXmlSerializer stubclass
+class com.android.modules.utils.BinaryXmlPullParser keepclass
+class com.android.modules.utils.BinaryXmlSerializer keepclass
+class com.android.modules.utils.FastDataInput keepclass
+class com.android.modules.utils.FastDataOutput keepclass
+class com.android.modules.utils.ModifiedUtf8 keepclass
+class com.android.modules.utils.TypedXmlPullParser keepclass
+class com.android.modules.utils.TypedXmlSerializer keepclass
# Uri
-class android.net.Uri stubclass
-class android.net.UriCodec stubclass
+class android.net.Uri keepclass
+class android.net.UriCodec keepclass
# Telephony
-class android.telephony.PinResult stubclass
+class android.telephony.PinResult keepclass
# Just enough to support mocking, no further functionality
-class android.content.BroadcastReceiver stub
- method <init> ()V stub
-class android.content.Context stub
- method <init> ()V stub
- method getSystemService (Ljava/lang/Class;)Ljava/lang/Object; stub
-class android.content.pm.PackageManager stub
- method <init> ()V stub
-class android.text.ClipboardManager stub
- method <init> ()V stub
+class android.content.BroadcastReceiver keep
+ method <init> ()V keep
+class android.content.Context keep
+ method <init> ()V keep
+ method getSystemService (Ljava/lang/Class;)Ljava/lang/Object; keep
+class android.content.pm.PackageManager keep
+ method <init> ()V keep
+class android.text.ClipboardManager keep
+ method <init> ()V keep
diff --git a/ravenwood/texts/ravenwood-services-policies.txt b/ravenwood/texts/ravenwood-services-policies.txt
index d8d563e..5cdb4f7 100644
--- a/ravenwood/texts/ravenwood-services-policies.txt
+++ b/ravenwood/texts/ravenwood-services-policies.txt
@@ -1,7 +1,7 @@
# Ravenwood "policy" file for services.core.
# Keep all AIDL interfaces
-class :aidl stubclass
+class :aidl keepclass
# Keep all feature flag implementations
-class :feature_flags stubclass
+class :feature_flags keepclass
diff --git a/services/accessibility/accessibility.aconfig b/services/accessibility/accessibility.aconfig
index 82579d8..a50fb9a 100644
--- a/services/accessibility/accessibility.aconfig
+++ b/services/accessibility/accessibility.aconfig
@@ -138,6 +138,16 @@
}
flag {
+ name: "manager_package_monitor_logic_fix"
+ namespace: "accessibility"
+ description: "Corrects the return values of the HandleForceStop function"
+ bug: "337392123"
+ metadata {
+ purpose: PURPOSE_BUGFIX
+ }
+}
+
+flag {
name: "pinch_zoom_zero_min_span"
namespace: "accessibility"
description: "Whether to set min span of ScaleGestureDetector to zero."
@@ -152,6 +162,16 @@
}
flag {
+ name: "remove_on_window_infos_changed_handler"
+ namespace: "accessibility"
+ description: "Updates onWindowInfosChanged() to run without posting to a handler."
+ bug: "333834990"
+ metadata {
+ purpose: PURPOSE_BUGFIX
+ }
+}
+
+flag {
name: "reset_hover_event_timer_on_action_up"
namespace: "accessibility"
description: "Reset the timer for sending hover events on receiving ACTION_UP to guarantee the correct amount of time is available between taps."
@@ -183,3 +203,10 @@
purpose: PURPOSE_BUGFIX
}
}
+
+flag {
+ name: "enable_color_correction_saturation"
+ namespace: "accessibility"
+ description: "Feature allows users to change color correction saturation for daltonizer."
+ bug: "322829049"
+}
\ No newline at end of file
diff --git a/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java b/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
index c70b641..d7ed867 100644
--- a/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
+++ b/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
@@ -697,7 +697,7 @@
/**
* Returns the lock object for any synchronized test blocks.
- * Should not be used outside of testing.
+ * External classes should only use for testing.
* @return lock object.
*/
@VisibleForTesting
@@ -801,7 +801,7 @@
*
* @param packages list of packages that have stopped.
* @param userState user state to be read & modified.
- * @return {@code true} if a service was enabled or a button target was removed,
+ * @return {@code true} if the lists of enabled services or buttons were changed,
* {@code false} otherwise.
*/
@VisibleForTesting
@@ -824,6 +824,7 @@
userState.getBindingServicesLocked().remove(comp);
userState.getCrashedServicesLocked().remove(comp);
enabledServicesChanged = true;
+ break;
}
}
}
@@ -851,132 +852,14 @@
return mPackageMonitor;
}
+ @VisibleForTesting
+ void setPackageMonitor(PackageMonitor monitor) {
+ mPackageMonitor = monitor;
+ }
+
private void registerBroadcastReceivers() {
- mPackageMonitor = new PackageMonitor(true) {
- @Override
- public void onSomePackagesChanged() {
- if (mTraceManager.isA11yTracingEnabledForTypes(FLAGS_PACKAGE_BROADCAST_RECEIVER)) {
- mTraceManager.logTrace(LOG_TAG + ".PM.onSomePackagesChanged",
- FLAGS_PACKAGE_BROADCAST_RECEIVER);
- }
-
- final int userId = getChangingUserId();
- List<AccessibilityServiceInfo> parsedAccessibilityServiceInfos = null;
- List<AccessibilityShortcutInfo> parsedAccessibilityShortcutInfos = null;
- parsedAccessibilityServiceInfos = parseAccessibilityServiceInfos(userId);
- parsedAccessibilityShortcutInfos = parseAccessibilityShortcutInfos(userId);
- synchronized (mLock) {
- // Only the profile parent can install accessibility services.
- // Therefore we ignore packages from linked profiles.
- if (userId != mCurrentUserId) {
- return;
- }
- onSomePackagesChangedLocked(parsedAccessibilityServiceInfos,
- parsedAccessibilityShortcutInfos);
- }
- }
-
- @Override
- public void onPackageUpdateFinished(String packageName, int uid) {
- // The package should already be removed from mBoundServices, and added into
- // mBindingServices in binderDied() during updating. Remove services from this
- // package from mBindingServices, and then update the user state to re-bind new
- // versions of them.
- if (mTraceManager.isA11yTracingEnabledForTypes(FLAGS_PACKAGE_BROADCAST_RECEIVER)) {
- mTraceManager.logTrace(LOG_TAG + ".PM.onPackageUpdateFinished",
- FLAGS_PACKAGE_BROADCAST_RECEIVER,
- "packageName=" + packageName + ";uid=" + uid);
- }
- final int userId = getChangingUserId();
- List<AccessibilityServiceInfo> parsedAccessibilityServiceInfos = null;
- List<AccessibilityShortcutInfo> parsedAccessibilityShortcutInfos = null;
- parsedAccessibilityServiceInfos = parseAccessibilityServiceInfos(userId);
- parsedAccessibilityShortcutInfos = parseAccessibilityShortcutInfos(userId);
- synchronized (mLock) {
- if (userId != mCurrentUserId) {
- return;
- }
- final AccessibilityUserState userState = getUserStateLocked(userId);
- final boolean reboundAService = userState.getBindingServicesLocked().removeIf(
- component -> component != null
- && component.getPackageName().equals(packageName))
- || userState.mCrashedServices.removeIf(component -> component != null
- && component.getPackageName().equals(packageName));
- // Reloads the installed services info to make sure the rebound service could
- // get a new one.
- userState.mInstalledServices.clear();
- final boolean configurationChanged;
- configurationChanged = readConfigurationForUserStateLocked(userState,
- parsedAccessibilityServiceInfos, parsedAccessibilityShortcutInfos);
- if (reboundAService || configurationChanged) {
- onUserStateChangedLocked(userState);
- }
- // Passing 0 for restoreFromSdkInt to have this migration check execute each
- // time. It can make sure a11y button settings are correctly if there's an a11y
- // service updated and modifies the a11y button configuration.
- migrateAccessibilityButtonSettingsIfNecessaryLocked(userState, packageName,
- /* restoreFromSdkInt = */0);
- }
- }
-
- @Override
- public void onPackageRemoved(String packageName, int uid) {
- if (mTraceManager.isA11yTracingEnabledForTypes(FLAGS_PACKAGE_BROADCAST_RECEIVER)) {
- mTraceManager.logTrace(LOG_TAG + ".PM.onPackageRemoved",
- FLAGS_PACKAGE_BROADCAST_RECEIVER,
- "packageName=" + packageName + ";uid=" + uid);
- }
-
- synchronized (mLock) {
- final int userId = getChangingUserId();
- // Only the profile parent can install accessibility services.
- // Therefore we ignore packages from linked profiles.
- if (userId != mCurrentUserId) {
- return;
- }
- onPackageRemovedLocked(packageName);
- }
- }
-
- /**
- * Handles instances in which a package or packages have forcibly stopped.
- *
- * @param intent intent containing package event information.
- * @param uid linux process user id (different from Android user id).
- * @param packages array of package names that have stopped.
- * @param doit whether to try and handle the stop or just log the trace.
- *
- * @return {@code true} if package should be restarted, {@code false} otherwise.
- */
- @Override
- public boolean onHandleForceStop(Intent intent, String[] packages,
- int uid, boolean doit) {
- if (mTraceManager.isA11yTracingEnabledForTypes(FLAGS_PACKAGE_BROADCAST_RECEIVER)) {
- mTraceManager.logTrace(LOG_TAG + ".PM.onHandleForceStop",
- FLAGS_PACKAGE_BROADCAST_RECEIVER,
- "intent=" + intent + ";packages=" + Arrays.toString(packages)
- + ";uid=" + uid + ";doit=" + doit);
- }
- synchronized (mLock) {
- final int userId = getChangingUserId();
- // Only the profile parent can install accessibility services.
- // Therefore we ignore packages from linked profiles.
- if (userId != mCurrentUserId) {
- return false;
- }
- final AccessibilityUserState userState = getUserStateLocked(userId);
-
- if (doit && onPackagesForceStoppedLocked(packages, userState)) {
- onUserStateChangedLocked(userState);
- return false;
- } else {
- return true;
- }
- }
- }
- };
-
// package changes
+ mPackageMonitor = new ManagerPackageMonitor(this);
mPackageMonitor.register(mContext, null, UserHandle.ALL, true);
// user change and unlock
@@ -992,7 +875,9 @@
@Override
public void onReceive(Context context, Intent intent) {
if (mTraceManager.isA11yTracingEnabledForTypes(FLAGS_USER_BROADCAST_RECEIVER)) {
- mTraceManager.logTrace(LOG_TAG + ".BR.onReceive", FLAGS_USER_BROADCAST_RECEIVER,
+ mTraceManager.logTrace(
+ LOG_TAG + ".BR.onReceive",
+ FLAGS_USER_BROADCAST_RECEIVER,
"context=" + context + ";intent=" + intent);
}
@@ -1045,7 +930,8 @@
setNonA11yToolNotificationToMatchSafetyCenter();
}
};
- mContext.registerReceiverAsUser(receiver, UserHandle.ALL, filter, null, mMainHandler,
+ mContext.registerReceiverAsUser(
+ receiver, UserHandle.ALL, filter, null, mMainHandler,
Context.RECEIVER_EXPORTED);
if (!android.companion.virtual.flags.Flags.vdmPublicApis()) {
@@ -4371,7 +4257,7 @@
);
if (!targetWithNoTile.isEmpty()) {
- throw new IllegalArgumentException(
+ Slog.e(LOG_TAG,
"Unable to add/remove Tiles for a11y features: " + targetWithNoTile
+ "as the Tiles aren't provided");
}
@@ -6223,6 +6109,169 @@
}
}
+ @VisibleForTesting
+ public static class ManagerPackageMonitor extends PackageMonitor {
+ private final AccessibilityManagerService mManagerService;
+ public ManagerPackageMonitor(AccessibilityManagerService managerService) {
+ super(/* supportsPackageRestartQuery = */ true);
+ mManagerService = managerService;
+ }
+
+ @Override
+ public void onSomePackagesChanged() {
+ if (mManagerService.mTraceManager.isA11yTracingEnabledForTypes(
+ FLAGS_PACKAGE_BROADCAST_RECEIVER)) {
+ mManagerService.mTraceManager.logTrace(LOG_TAG + ".PM.onSomePackagesChanged",
+ FLAGS_PACKAGE_BROADCAST_RECEIVER);
+ }
+
+ final int userId = getChangingUserId();
+ List<AccessibilityServiceInfo> parsedAccessibilityServiceInfos = mManagerService
+ .parseAccessibilityServiceInfos(userId);
+ List<AccessibilityShortcutInfo> parsedAccessibilityShortcutInfos = mManagerService
+ .parseAccessibilityShortcutInfos(userId);
+ synchronized (mManagerService.getLock()) {
+ // Only the profile parent can install accessibility services.
+ // Therefore we ignore packages from linked profiles.
+ if (userId != mManagerService.getCurrentUserIdLocked()) {
+ return;
+ }
+ mManagerService.onSomePackagesChangedLocked(parsedAccessibilityServiceInfos,
+ parsedAccessibilityShortcutInfos);
+ }
+ }
+
+ @Override
+ public void onPackageUpdateFinished(String packageName, int uid) {
+ // The package should already be removed from mBoundServices, and added into
+ // mBindingServices in binderDied() during updating. Remove services from this
+ // package from mBindingServices, and then update the user state to re-bind new
+ // versions of them.
+ if (mManagerService.mTraceManager.isA11yTracingEnabledForTypes(
+ FLAGS_PACKAGE_BROADCAST_RECEIVER)) {
+ mManagerService.mTraceManager.logTrace(
+ LOG_TAG + ".PM.onPackageUpdateFinished",
+ FLAGS_PACKAGE_BROADCAST_RECEIVER,
+ "packageName=" + packageName + ";uid=" + uid);
+ }
+ final int userId = getChangingUserId();
+ List<AccessibilityServiceInfo> parsedAccessibilityServiceInfos = mManagerService
+ .parseAccessibilityServiceInfos(userId);
+ List<AccessibilityShortcutInfo> parsedAccessibilityShortcutInfos =
+ mManagerService.parseAccessibilityShortcutInfos(userId);
+ synchronized (mManagerService.getLock()) {
+ if (userId != mManagerService.getCurrentUserIdLocked()) {
+ return;
+ }
+ final AccessibilityUserState userState = mManagerService.getUserStateLocked(userId);
+ final boolean reboundAService = userState.getBindingServicesLocked().removeIf(
+ component -> component != null
+ && component.getPackageName().equals(packageName))
+ || userState.mCrashedServices.removeIf(component -> component != null
+ && component.getPackageName().equals(packageName));
+ // Reloads the installed services info to make sure the rebound service could
+ // get a new one.
+ userState.mInstalledServices.clear();
+ final boolean configurationChanged;
+ configurationChanged = mManagerService.readConfigurationForUserStateLocked(
+ userState, parsedAccessibilityServiceInfos,
+ parsedAccessibilityShortcutInfos);
+ if (reboundAService || configurationChanged) {
+ mManagerService.onUserStateChangedLocked(userState);
+ }
+ // Passing 0 for restoreFromSdkInt to have this migration check execute each
+ // time. It can make sure a11y button settings are correctly if there's an a11y
+ // service updated and modifies the a11y button configuration.
+ mManagerService.migrateAccessibilityButtonSettingsIfNecessaryLocked(
+ userState, packageName, /* restoreFromSdkInt = */0);
+ }
+ }
+
+ @Override
+ public void onPackageRemoved(String packageName, int uid) {
+ if (mManagerService.mTraceManager.isA11yTracingEnabledForTypes(
+ FLAGS_PACKAGE_BROADCAST_RECEIVER)) {
+ mManagerService.mTraceManager.logTrace(LOG_TAG + ".PM.onPackageRemoved",
+ FLAGS_PACKAGE_BROADCAST_RECEIVER,
+ "packageName=" + packageName + ";uid=" + uid);
+ }
+
+ synchronized (mManagerService.getLock()) {
+ final int userId = getChangingUserId();
+ // Only the profile parent can install accessibility services.
+ // Therefore we ignore packages from linked profiles.
+ if (userId != mManagerService.getCurrentUserIdLocked()) {
+ return;
+ }
+ mManagerService.onPackageRemovedLocked(packageName);
+ }
+ }
+
+ /**
+ * Handles instances in which a package or packages have forcibly stopped.
+ *
+ * @param intent intent containing package event information.
+ * @param uid linux process user id (different from Android user id).
+ * @param packages array of package names that have stopped.
+ * @param doit whether to try and handle the stop or just log the trace.
+ *
+ * @return {@code true} if doit == {@code false}
+ * and at least one of the provided packages is enabled.
+ * In any other case, returns {@code false}.
+ * This is to indicate whether further action is necessary.
+ */
+ @Override
+ public boolean onHandleForceStop(Intent intent, String[] packages,
+ int uid, boolean doit) {
+ if (mManagerService.mTraceManager.isA11yTracingEnabledForTypes(
+ FLAGS_PACKAGE_BROADCAST_RECEIVER)) {
+ mManagerService.mTraceManager.logTrace(LOG_TAG + ".PM.onHandleForceStop",
+ FLAGS_PACKAGE_BROADCAST_RECEIVER,
+ "intent=" + intent + ";packages=" + Arrays.toString(packages)
+ + ";uid=" + uid + ";doit=" + doit);
+ }
+ synchronized (mManagerService.getLock()) {
+ final int userId = getChangingUserId();
+ // Only the profile parent can install accessibility services.
+ // Therefore we ignore packages from linked profiles.
+ if (userId != mManagerService.getCurrentUserIdLocked()) {
+ return false;
+ }
+ final AccessibilityUserState userState = mManagerService.getUserStateLocked(userId);
+
+ if (Flags.managerPackageMonitorLogicFix()) {
+ if (!doit) {
+ // if we're not handling the stop here, then we only need to know
+ // if any of the force-stopped packages are currently enabled.
+ return userState.mEnabledServices.stream().anyMatch(
+ (comp) -> Arrays.stream(packages).anyMatch(
+ (pkg) -> pkg.equals(comp.getPackageName()))
+ );
+ } else if (mManagerService.onPackagesForceStoppedLocked(packages, userState)) {
+ mManagerService.onUserStateChangedLocked(userState);
+ }
+ return false;
+ } else {
+ // this old logic did not properly indicate when base packageMonitor's routine
+ // should handle stopping the package.
+ if (doit && mManagerService.onPackagesForceStoppedLocked(packages, userState)) {
+ mManagerService.onUserStateChangedLocked(userState);
+ return false;
+ } else {
+ return true;
+ }
+ }
+ }
+ }
+
+ @Override
+ public boolean onPackageChanged(String packageName, int uid, String[] components) {
+ // We care about all package changes, not just the whole package itself which is
+ // default behavior.
+ return true;
+ }
+ }
+
void sendPendingWindowStateChangedEventsForAvailableWindowLocked(int windowId) {
final int eventSize = mSendWindowStateChangedEventRunnables.size();
for (int i = eventSize - 1; i >= 0; i--) {
diff --git a/services/appwidget/java/com/android/server/appwidget/AppWidgetServiceImpl.java b/services/appwidget/java/com/android/server/appwidget/AppWidgetServiceImpl.java
index 06a0297..5567707 100644
--- a/services/appwidget/java/com/android/server/appwidget/AppWidgetServiceImpl.java
+++ b/services/appwidget/java/com/android/server/appwidget/AppWidgetServiceImpl.java
@@ -172,8 +172,7 @@
OnCrossProfileWidgetProvidersChangeListener {
private static final String TAG = "AppWidgetServiceImpl";
- private static final boolean DEBUG = false;
- private static final boolean DEBUG_NULL_PROVIDER_INFO = Build.IS_DEBUGGABLE;
+ private static final boolean DEBUG = Build.IS_DEBUGGABLE;
private static final String OLD_KEYGUARD_HOST_PACKAGE = "android";
private static final String NEW_KEYGUARD_HOST_PACKAGE = "com.android.keyguard";
@@ -522,9 +521,13 @@
false);
final boolean packageRemovedPermanently =
(extras == null || !isReplacing || (isReplacing && isArchival));
-
if (packageRemovedPermanently) {
for (String pkgName : pkgList) {
+ if (DEBUG) {
+ Slog.i(TAG, "calling removeHostsAndProvidersForPackageLocked() "
+ + "because package removed permanently. extras=" + extras
+ + " isReplacing=" + isReplacing + " isArchival=" + isArchival);
+ }
componentsModified |= removeHostsAndProvidersForPackageLocked(
pkgName, userId);
}
@@ -1573,9 +1576,36 @@
Binder.getCallingUid(), callingPackage);
if (widget != null && widget.provider != null && !widget.provider.zombie) {
- return cloneIfLocalBinder(widget.provider.getInfoLocked(mContext));
+ final AppWidgetProviderInfo info = widget.provider.getInfoLocked(mContext);
+ if (info == null) {
+ Slog.e(TAG, "getAppWidgetInfo() returns null because"
+ + " widget.provider.getInfoLocked() returned null."
+ + " appWidgetId=" + appWidgetId + " userId=" + userId
+ + " widget=" + widget);
+ return null;
+ }
+ final AppWidgetProviderInfo ret = cloneIfLocalBinder(info);
+ if (ret == null) {
+ Slog.e(TAG, "getAppWidgetInfo() returns null because"
+ + " cloneIfLocalBinder() returned null."
+ + " appWidgetId=" + appWidgetId + " userId=" + userId
+ + " widget=" + widget + " appWidgetProviderInfo=" + info);
+ }
+ return ret;
+ } else {
+ if (widget == null) {
+ Slog.e(TAG, "getAppWidgetInfo() returns null because widget is null."
+ + " appWidgetId=" + appWidgetId + " userId=" + userId);
+ } else if (widget.provider == null) {
+ Slog.e(TAG, "getAppWidgetInfo() returns null because widget.provider is null."
+ + " appWidgetId=" + appWidgetId + " userId=" + userId
+ + " widget=" + widget);
+ } else {
+ Slog.e(TAG, "getAppWidgetInfo() returns null because widget.provider is zombie."
+ + " appWidgetId=" + appWidgetId + " userId=" + userId
+ + " widget=" + widget);
+ }
}
-
return null;
}
}
@@ -2027,6 +2057,9 @@
}
private void deleteHostLocked(Host host) {
+ if (DEBUG) {
+ Slog.i(TAG, "deleteHostLocked() " + host);
+ }
final int N = host.widgets.size();
for (int i = N - 1; i >= 0; i--) {
Widget widget = host.widgets.remove(i);
@@ -2039,6 +2072,9 @@
}
private void deleteAppWidgetLocked(Widget widget) {
+ if (DEBUG) {
+ Slog.i(TAG, "deleteAppWidgetLocked() " + widget);
+ }
// We first unbind all services that are bound to this id
// Check if we need to destroy any services (if no other app widgets are
// referencing the same service)
@@ -2506,6 +2542,10 @@
return widget;
}
}
+ if (DEBUG) {
+ Slog.i(TAG, "cannot find widget for appWidgetId=" + appWidgetId + " uid=" + uid
+ + " packageName=" + packageName);
+ }
return null;
}
@@ -2623,6 +2663,9 @@
// Remove widgets for provider that are hosted in userId.
private void deleteWidgetsLocked(Provider provider, int userId) {
+ if (DEBUG) {
+ Slog.i(TAG, "deleteWidgetsLocked() provider=" + provider + " userId=" + userId);
+ }
final int N = provider.widgets.size();
for (int i = N - 1; i >= 0; i--) {
Widget widget = provider.widgets.get(i);
@@ -2960,7 +3003,7 @@
AppWidgetProviderInfo info = new AppWidgetProviderInfo();
info.provider = providerId.componentName;
info.providerInfo = ri.activityInfo;
- if (DEBUG_NULL_PROVIDER_INFO) {
+ if (DEBUG) {
Objects.requireNonNull(ri.activityInfo);
}
return info;
@@ -2997,7 +3040,7 @@
AppWidgetProviderInfo info = new AppWidgetProviderInfo();
info.provider = providerId.componentName;
info.providerInfo = activityInfo;
- if (DEBUG_NULL_PROVIDER_INFO) {
+ if (DEBUG) {
Objects.requireNonNull(activityInfo);
}
@@ -3300,6 +3343,9 @@
* Adds the widget to mWidgets and tracks the package name in mWidgetPackages.
*/
void addWidgetLocked(Widget widget) {
+ if (DEBUG) {
+ Slog.i(TAG, "addWidgetLocked() " + widget);
+ }
mWidgets.add(widget);
onWidgetProviderAddedOrChangedLocked(widget);
@@ -3336,6 +3382,9 @@
* removes the associated package from the cache.
*/
void removeWidgetLocked(Widget widget) {
+ if (DEBUG) {
+ Slog.i(TAG, "removeWidgetLocked() " + widget);
+ }
mWidgets.remove(widget);
onWidgetRemovedLocked(widget);
scheduleNotifyAppWidgetRemovedLocked(widget);
@@ -3370,6 +3419,9 @@
* Clears all widgets and associated cache of packages with bound widgets.
*/
void clearWidgetsLocked() {
+ if (DEBUG) {
+ Slog.i(TAG, "clearWidgetsLocked()");
+ }
mWidgets.clear();
onWidgetsClearedLocked();
@@ -3575,7 +3627,7 @@
AppWidgetProviderInfo info = new AppWidgetProviderInfo();
info.provider = providerId.componentName;
info.providerInfo = providerInfo;
- if (DEBUG_NULL_PROVIDER_INFO) {
+ if (DEBUG) {
Objects.requireNonNull(providerInfo);
}
@@ -3594,7 +3646,7 @@
if (info != null) {
info.provider = providerId.componentName;
info.providerInfo = providerInfo;
- if (DEBUG_NULL_PROVIDER_INFO) {
+ if (DEBUG) {
Objects.requireNonNull(providerInfo);
}
provider.setInfoLocked(info);
@@ -3731,6 +3783,9 @@
}
void onUserStopped(int userId) {
+ if (DEBUG) {
+ Slog.i(TAG, "onUserStopped() " + userId);
+ }
synchronized (mLock) {
boolean crossProfileWidgetsChanged = false;
@@ -3968,6 +4023,10 @@
}
private boolean removeHostsAndProvidersForPackageLocked(String pkgName, int userId) {
+ if (DEBUG) {
+ Slog.i(TAG, "removeHostsAndProvidersForPackageLocked() pkg=" + pkgName
+ + " userId=" + userId);
+ }
boolean removed = removeProvidersForPackageLocked(pkgName, userId);
// Delete the hosts for this package too
@@ -4526,6 +4585,10 @@
// have the bind widget permission have access to the widget.
return true;
}
+ if (DEBUG) {
+ Slog.i(TAG, "canAccessAppWidget() failed. packageName=" + packageName
+ + " uid=" + uid + " userId=" + userId + " widget=" + widget);
+ }
return false;
}
@@ -4678,6 +4741,9 @@
}
if (newInfo != null) {
info = newInfo;
+ if (DEBUG) {
+ Objects.requireNonNull(info);
+ }
updateGeneratedPreviewCategoriesLocked();
}
}
@@ -4699,12 +4765,18 @@
@GuardedBy("AppWidgetServiceImpl.mLock")
public void setPartialInfoLocked(AppWidgetProviderInfo info) {
this.info = info;
+ if (DEBUG) {
+ Objects.requireNonNull(this.info);
+ }
mInfoParsed = false;
}
@GuardedBy("AppWidgetServiceImpl.mLock")
public void setInfoLocked(AppWidgetProviderInfo info) {
this.info = info;
+ if (DEBUG) {
+ Objects.requireNonNull(this.info);
+ }
mInfoParsed = true;
}
diff --git a/services/autofill/java/com/android/server/autofill/AutofillInlineSuggestionsRequestSession.java b/services/autofill/java/com/android/server/autofill/AutofillInlineSuggestionsRequestSession.java
index 468b9ab..219b788 100644
--- a/services/autofill/java/com/android/server/autofill/AutofillInlineSuggestionsRequestSession.java
+++ b/services/autofill/java/com/android/server/autofill/AutofillInlineSuggestionsRequestSession.java
@@ -36,6 +36,7 @@
import com.android.internal.annotations.GuardedBy;
import com.android.internal.inputmethod.IInlineSuggestionsRequestCallback;
import com.android.internal.inputmethod.IInlineSuggestionsResponseCallback;
+import com.android.internal.inputmethod.InlineSuggestionsRequestCallback;
import com.android.internal.inputmethod.InlineSuggestionsRequestInfo;
import com.android.server.autofill.ui.InlineFillUi;
import com.android.server.inputmethod.InputMethodManagerInternal;
@@ -376,8 +377,8 @@
/**
* Internal implementation of {@link IInlineSuggestionsRequestCallback}.
*/
- private static final class InlineSuggestionsRequestCallbackImpl extends
- IInlineSuggestionsRequestCallback.Stub {
+ private static final class InlineSuggestionsRequestCallbackImpl
+ implements InlineSuggestionsRequestCallback {
private final WeakReference<AutofillInlineSuggestionsRequestSession> mSession;
@@ -388,7 +389,7 @@
@BinderThread
@Override
- public void onInlineSuggestionsUnsupported() throws RemoteException {
+ public void onInlineSuggestionsUnsupported() {
if (sDebug) Slog.d(TAG, "onInlineSuggestionsUnsupported() called.");
final AutofillInlineSuggestionsRequestSession session = mSession.get();
if (session != null) {
@@ -412,7 +413,7 @@
}
@Override
- public void onInputMethodStartInput(AutofillId imeFieldId) throws RemoteException {
+ public void onInputMethodStartInput(AutofillId imeFieldId) {
if (sVerbose) Slog.v(TAG, "onInputMethodStartInput() received on " + imeFieldId);
final AutofillInlineSuggestionsRequestSession session = mSession.get();
if (session != null) {
@@ -423,7 +424,7 @@
}
@Override
- public void onInputMethodShowInputRequested(boolean requestResult) throws RemoteException {
+ public void onInputMethodShowInputRequested(boolean requestResult) {
if (sVerbose) {
Slog.v(TAG, "onInputMethodShowInputRequested() received: " + requestResult);
}
@@ -454,7 +455,7 @@
}
@Override
- public void onInputMethodFinishInput() throws RemoteException {
+ public void onInputMethodFinishInput() {
if (sVerbose) Slog.v(TAG, "onInputMethodFinishInput() received");
final AutofillInlineSuggestionsRequestSession session = mSession.get();
if (session != null) {
@@ -466,7 +467,7 @@
@BinderThread
@Override
- public void onInlineSuggestionsSessionInvalidated() throws RemoteException {
+ public void onInlineSuggestionsSessionInvalidated() {
if (sDebug) Slog.d(TAG, "onInlineSuggestionsSessionInvalidated() called.");
final AutofillInlineSuggestionsRequestSession session = mSession.get();
if (session != null) {
diff --git a/services/autofill/java/com/android/server/autofill/Session.java b/services/autofill/java/com/android/server/autofill/Session.java
index 07b16c5..a8b1235 100644
--- a/services/autofill/java/com/android/server/autofill/Session.java
+++ b/services/autofill/java/com/android/server/autofill/Session.java
@@ -1168,6 +1168,7 @@
return null;
}
+ @GuardedBy("mLock")
@Nullable
private AutofillValue findValueFromThisSessionOnlyLocked(@NonNull AutofillId autofillId) {
final ViewState state = mViewStates.get(autofillId);
@@ -1176,9 +1177,25 @@
return null;
}
AutofillValue value = state.getCurrentValue();
+
+ // Some app clears the form before navigating to another activities. In this case, use the
+ // cached value instead.
+ if (value == null || value.isEmpty()) {
+ AutofillValue candidateSaveValue = state.getCandidateSaveValue();
+ if (candidateSaveValue != null && !candidateSaveValue.isEmpty()) {
+ if (sDebug) {
+ Slog.d(TAG, "findValueLocked(): current value for " + autofillId
+ + " is empty, using candidateSaveValue instead.");
+ }
+ return candidateSaveValue;
+ }
+ }
if (value == null) {
- if (sDebug) Slog.d(TAG, "findValueLocked(): no current value for " + autofillId);
- value = getValueFromContextsLocked(autofillId);
+ if (sDebug) {
+ Slog.d(TAG, "findValueLocked(): no current value for " + autofillId
+ + ", checking value from previous fill contexts");
+ value = getValueFromContextsLocked(autofillId);
+ }
}
return value;
}
@@ -3717,19 +3734,34 @@
AutofillValue value = viewState.getCurrentValue();
if (value == null || value.isEmpty()) {
- final AutofillValue initialValue = getValueFromContextsLocked(id);
- if (initialValue != null) {
- if (sDebug) {
- Slog.d(TAG, "Value of required field " + id + " didn't change; "
- + "using initial value (" + initialValue + ") instead");
+ // Some apps clear the form before navigating to other activities.
+ // If current value is empty, consider fall back to last cached
+ // non-empty result first.
+ final AutofillValue candidateSaveValue =
+ viewState.getCandidateSaveValue();
+ if (candidateSaveValue != null && !candidateSaveValue.isEmpty()) {
+ if (sVerbose) {
+ Slog.v(TAG, "current value is empty, using cached last non-empty "
+ + "value instead");
}
- value = initialValue;
+ value = candidateSaveValue;
} else {
- if (sDebug) {
- Slog.d(TAG, "empty value for required " + id );
+ // If candidate save value is also empty, consider falling back to initial
+ // value in context.
+ final AutofillValue initialValue = getValueFromContextsLocked(id);
+ if (initialValue != null) {
+ if (sDebug) {
+ Slog.d(TAG, "Value of required field " + id + " didn't change; "
+ + "using initial value (" + initialValue + ") instead");
+ }
+ value = initialValue;
+ } else {
+ if (sDebug) {
+ Slog.d(TAG, "empty value for required " + id);
+ }
+ allRequiredAreNotEmpty = false;
+ break;
}
- allRequiredAreNotEmpty = false;
- break;
}
}
@@ -3801,7 +3833,21 @@
continue;
}
if ((viewState.getState() & ViewState.STATE_CHANGED) != 0) {
- final AutofillValue currentValue = viewState.getCurrentValue();
+ AutofillValue currentValue = viewState.getCurrentValue();
+ if (currentValue == null || currentValue.isEmpty()) {
+ // Some apps clear the form before navigating to other activities.
+ // If current value is empty, consider fall back to last cached
+ // non-empty result instead.
+ final AutofillValue candidateSaveValue =
+ viewState.getCandidateSaveValue();
+ if (candidateSaveValue != null && !candidateSaveValue.isEmpty()) {
+ if (sVerbose) {
+ Slog.v(TAG, "current value is empty, using cached last "
+ + "non-empty value instead");
+ }
+ currentValue = candidateSaveValue;
+ }
+ }
final AutofillValue value = getSanitizedValue(sanitizers, id, currentValue);
if (value == null) {
if (sDebug) {
@@ -4714,14 +4760,18 @@
@GuardedBy("mLock")
private void updateViewStateAndUiOnValueChangedLocked(AutofillId id, AutofillValue value,
ViewState viewState, int flags) {
+ // Cache the last non-empty value for save purpose. Some apps clear the form before
+ // navigating to other activities.
if (mIgnoreViewStateResetToEmpty && (value == null || value.isEmpty())
&& viewState.getCurrentValue() != null && viewState.getCurrentValue().isText()
&& viewState.getCurrentValue().getTextValue() != null
&& viewState.getCurrentValue().getTextValue().length() > 1) {
if (sVerbose) {
- Slog.v(TAG, "Ignoring view state reset to empty on id " + id);
+ Slog.v(TAG, "value is resetting to empty, caching the last non-empty value");
}
- return;
+ viewState.setCandidateSaveValue(viewState.getCurrentValue());
+ } else {
+ viewState.setCandidateSaveValue(null);
}
final String textValue;
if (value == null || !value.isText()) {
diff --git a/services/autofill/java/com/android/server/autofill/ViewState.java b/services/autofill/java/com/android/server/autofill/ViewState.java
index fec5aa5..6ad0eb6 100644
--- a/services/autofill/java/com/android/server/autofill/ViewState.java
+++ b/services/autofill/java/com/android/server/autofill/ViewState.java
@@ -106,6 +106,15 @@
*/
private FillResponse mSecondaryFillResponse;
private AutofillValue mCurrentValue;
+
+ /**
+ * Some apps clear the form before navigating to another activity. The mCandidateSaveValue
+ * caches the value when a field with string longer than 2 characters are cleared.
+ *
+ * When showing save UI, if mCurrentValue of view state is empty, session would use
+ * mCandidateSaveValue to prompt save instead.
+ */
+ private AutofillValue mCandidateSaveValue;
private AutofillValue mAutofilledValue;
private AutofillValue mSanitizedValue;
private Rect mVirtualBounds;
@@ -139,6 +148,18 @@
mCurrentValue = value;
}
+ /**
+ * Gets the candidate save value of the view.
+ */
+ @Nullable
+ AutofillValue getCandidateSaveValue() {
+ return mCandidateSaveValue;
+ }
+
+ void setCandidateSaveValue(AutofillValue value) {
+ mCandidateSaveValue = value;
+ }
+
@Nullable
AutofillValue getAutofilledValue() {
return mAutofilledValue;
@@ -268,6 +289,9 @@
if (mCurrentValue != null) {
builder.append(", currentValue:" ).append(mCurrentValue);
}
+ if (mCandidateSaveValue != null) {
+ builder.append(", candidateSaveValue:").append(mCandidateSaveValue);
+ }
if (mAutofilledValue != null) {
builder.append(", autofilledValue:" ).append(mAutofilledValue);
}
@@ -302,6 +326,9 @@
if (mAutofilledValue != null) {
pw.print(prefix); pw.print("autofilledValue:" ); pw.println(mAutofilledValue);
}
+ if (mCandidateSaveValue != null) {
+ pw.print(prefix); pw.print("candidateSaveValue:"); pw.println(mCandidateSaveValue);
+ }
if (mSanitizedValue != null) {
pw.print(prefix); pw.print("sanitizedValue:" ); pw.println(mSanitizedValue);
}
diff --git a/services/backup/java/com/android/server/backup/transport/TransportConnection.java b/services/backup/java/com/android/server/backup/transport/TransportConnection.java
index 1009787..67ebb3e 100644
--- a/services/backup/java/com/android/server/backup/transport/TransportConnection.java
+++ b/services/backup/java/com/android/server/backup/transport/TransportConnection.java
@@ -658,11 +658,13 @@
* This class is a proxy to TransportClient methods that doesn't hold a strong reference to the
* TransportClient, allowing it to be GC'ed. If the reference was lost it logs a message.
*/
- private static class TransportConnectionMonitor implements ServiceConnection {
+ @VisibleForTesting
+ static class TransportConnectionMonitor implements ServiceConnection {
private final Context mContext;
private final WeakReference<TransportConnection> mTransportClientRef;
- private TransportConnectionMonitor(Context context,
+ @VisibleForTesting
+ TransportConnectionMonitor(Context context,
TransportConnection transportConnection) {
mContext = context;
mTransportClientRef = new WeakReference<>(transportConnection);
@@ -704,7 +706,13 @@
/** @see TransportConnection#finalize() */
private void referenceLost(String caller) {
- mContext.unbindService(this);
+ try {
+ mContext.unbindService(this);
+ } catch (IllegalArgumentException e) {
+ TransportUtils.log(Priority.WARN, TAG,
+ caller + " called but unbindService failed: " + e.getMessage());
+ return;
+ }
TransportUtils.log(
Priority.INFO,
TAG,
diff --git a/services/companion/java/com/android/server/companion/securechannel/SecureChannel.java b/services/companion/java/com/android/server/companion/securechannel/SecureChannel.java
index 0e66fbc..71a1822 100644
--- a/services/companion/java/com/android/server/companion/securechannel/SecureChannel.java
+++ b/services/companion/java/com/android/server/companion/securechannel/SecureChannel.java
@@ -23,6 +23,7 @@
import android.os.Build;
import android.util.Slog;
+import com.google.security.cryptauth.lib.securegcm.ukey2.AlertException;
import com.google.security.cryptauth.lib.securegcm.ukey2.BadHandleException;
import com.google.security.cryptauth.lib.securegcm.ukey2.CryptoException;
import com.google.security.cryptauth.lib.securegcm.ukey2.D2DConnectionContextV1;
@@ -203,7 +204,8 @@
*
* This method must only be called from one of the two participants.
*/
- public void establishSecureConnection() throws IOException, SecureChannelException {
+ public void establishSecureConnection() throws IOException,
+ SecureChannelException, HandshakeException {
if (isSecured()) {
Slog.d(TAG, "Channel is already secure.");
return;
@@ -334,7 +336,7 @@
}
}
- private void initiateHandshake() throws IOException, BadHandleException {
+ private void initiateHandshake() throws IOException, BadHandleException , HandshakeException {
if (mConnectionContext != null) {
Slog.d(TAG, "Ukey2 handshake is already completed.");
return;
@@ -394,8 +396,8 @@
}
}
- private void exchangeHandshake()
- throws IOException, HandshakeException, BadHandleException, CryptoException {
+ private void exchangeHandshake() throws IOException, HandshakeException,
+ BadHandleException, CryptoException, AlertException {
if (mConnectionContext != null) {
Slog.d(TAG, "Ukey2 handshake is already completed.");
return;
diff --git a/services/companion/java/com/android/server/companion/virtual/TEST_MAPPING b/services/companion/java/com/android/server/companion/virtual/TEST_MAPPING
index 340bc32..caa877c 100644
--- a/services/companion/java/com/android/server/companion/virtual/TEST_MAPPING
+++ b/services/companion/java/com/android/server/companion/virtual/TEST_MAPPING
@@ -81,9 +81,6 @@
"name": "CtsPermissionTestCases",
"options": [
{
- "include-filter": "android.permissionmultidevice.cts.DeviceAwarePermissionGrantTest"
- },
- {
"include-filter": "android.permission.cts.DevicePermissionsTest"
},
{
@@ -93,6 +90,14 @@
"exclude-annotation": "androidx.test.filters.FlakyTest"
}
]
+ },
+ {
+ "name": "CtsPermissionMultiDeviceTestCases",
+ "options": [
+ {
+ "exclude-annotation": "androidx.test.filters.FlakyTest"
+ }
+ ]
}
]
}
diff --git a/services/contextualsearch/java/com/android/server/contextualsearch/ContextualSearchManagerService.java b/services/contextualsearch/java/com/android/server/contextualsearch/ContextualSearchManagerService.java
index 9a73a2d..f5db6e9 100644
--- a/services/contextualsearch/java/com/android/server/contextualsearch/ContextualSearchManagerService.java
+++ b/services/contextualsearch/java/com/android/server/contextualsearch/ContextualSearchManagerService.java
@@ -27,9 +27,9 @@
import static android.content.pm.PackageManager.PERMISSION_GRANTED;
import static android.view.WindowManager.LayoutParams.TYPE_NAVIGATION_BAR;
import static android.view.WindowManager.LayoutParams.TYPE_NAVIGATION_BAR_PANEL;
+import static android.view.WindowManager.LayoutParams.TYPE_POINTER;
import static android.view.WindowManager.LayoutParams.TYPE_STATUS_BAR;
-import static com.android.server.contextualsearch.flags.Flags.enableExcludePersistentUi;
import static com.android.server.wm.ActivityTaskManagerInternal.ASSIST_KEY_CONTENT;
import static com.android.server.wm.ActivityTaskManagerInternal.ASSIST_KEY_STRUCTURE;
@@ -286,13 +286,11 @@
}
final ScreenCapture.ScreenshotHardwareBuffer shb;
if (mWmInternal != null) {
- if (enableExcludePersistentUi()) {
- shb = mWmInternal.takeAssistScreenshot(
- Set.of(TYPE_STATUS_BAR, TYPE_NAVIGATION_BAR, TYPE_NAVIGATION_BAR_PANEL));
- } else {
- shb = mWmInternal.takeAssistScreenshot(/* windowTypesToExclude= */ Set.of());
- }
-
+ shb = mWmInternal.takeAssistScreenshot(Set.of(
+ TYPE_STATUS_BAR,
+ TYPE_NAVIGATION_BAR,
+ TYPE_NAVIGATION_BAR_PANEL,
+ TYPE_POINTER));
} else {
shb = null;
}
diff --git a/services/core/Android.bp b/services/core/Android.bp
index d153c18..0fdf6d0 100644
--- a/services/core/Android.bp
+++ b/services/core/Android.bp
@@ -232,6 +232,7 @@
"android.hardware.rebootescrow-V1-java",
"android.hardware.power.stats-V2-java",
"android.hidl.manager-V1.2-java",
+ "audio-permission-aidl-java",
"cbor-java",
"com.android.media.audio-aconfig-java",
"icu4j_calendar_astronomer",
@@ -259,7 +260,6 @@
"connectivity_flags_lib",
"dreams_flags_lib",
"aconfig_new_storage_flags_lib",
- "aconfigd_java_proto_lib",
],
javac_shard_size: 50,
javacflags: [
diff --git a/services/core/java/com/android/server/BootReceiver.java b/services/core/java/com/android/server/BootReceiver.java
index 9f279b1..f69a521 100644
--- a/services/core/java/com/android/server/BootReceiver.java
+++ b/services/core/java/com/android/server/BootReceiver.java
@@ -34,6 +34,7 @@
import android.provider.Downloads;
import android.system.ErrnoException;
import android.system.Os;
+import android.system.OsConstants;
import android.text.TextUtils;
import android.util.AtomicFile;
import android.util.EventLog;
@@ -230,16 +231,23 @@
}
private static String getCurrentBootHeaders() throws IOException {
- return new StringBuilder(512)
- .append("Build: ").append(Build.FINGERPRINT).append("\n")
- .append("Hardware: ").append(Build.BOARD).append("\n")
- .append("Revision: ")
- .append(SystemProperties.get("ro.revision", "")).append("\n")
- .append("Bootloader: ").append(Build.BOOTLOADER).append("\n")
- .append("Radio: ").append(Build.getRadioVersion()).append("\n")
- .append("Kernel: ")
- .append(FileUtils.readTextFile(new File("/proc/version"), 1024, "...\n"))
- .append("\n").toString();
+ StringBuilder builder = new StringBuilder(512)
+ .append("Build: ").append(Build.FINGERPRINT).append("\n")
+ .append("Hardware: ").append(Build.BOARD).append("\n")
+ .append("Revision: ")
+ .append(SystemProperties.get("ro.revision", "")).append("\n")
+ .append("Bootloader: ").append(Build.BOOTLOADER).append("\n")
+ .append("Radio: ").append(Build.getRadioVersion()).append("\n")
+ .append("Kernel: ")
+ .append(FileUtils.readTextFile(new File("/proc/version"), 1024, "...\n"));
+
+ // If device is not using 4KB pages, add the PageSize
+ long pageSize = Os.sysconf(OsConstants._SC_PAGESIZE);
+ if (pageSize != 4096) {
+ builder.append("PageSize: ").append(pageSize).append("\n");
+ }
+ builder.append("\n");
+ return builder.toString();
}
diff --git a/services/core/java/com/android/server/PinnerService.java b/services/core/java/com/android/server/PinnerService.java
index d04dbc2..ef03888 100644
--- a/services/core/java/com/android/server/PinnerService.java
+++ b/services/core/java/com/android/server/PinnerService.java
@@ -21,6 +21,7 @@
import static android.os.Process.SYSTEM_UID;
import static com.android.server.flags.Flags.pinWebview;
+import static com.android.server.flags.Flags.skipHomeArtPins;
import android.annotation.EnforcePermission;
import android.annotation.IntDef;
@@ -851,6 +852,9 @@
}
int apkPinSizeLimit = pinSizeLimit;
+
+ boolean shouldSkipArtPins = key == KEY_HOME && skipHomeArtPins();
+
for (String apk: apks) {
if (apkPinSizeLimit <= 0) {
Slog.w(TAG, "Reached to the pin size limit. Skipping: " + apk);
@@ -870,11 +874,12 @@
}
synchronized (this) {
pinnedApp.mFiles.add(pf);
+ mPinnedFiles.put(pf.fileName, pf);
}
apkPinSizeLimit -= pf.bytesPinned;
- if (apk.equals(appInfo.sourceDir)) {
- pinOptimizedDexDependencies(pf, apkPinSizeLimit, appInfo);
+ if (apk.equals(appInfo.sourceDir) && !shouldSkipArtPins) {
+ pinOptimizedDexDependencies(pf, Integer.MAX_VALUE, appInfo);
}
}
}
@@ -920,8 +925,8 @@
}
pf.groupName = groupName != null ? groupName : "";
- maxBytesToPin -= bytesPinned;
bytesPinned += pf.bytesPinned;
+ maxBytesToPin -= bytesPinned;
synchronized (this) {
mPinnedFiles.put(pf.fileName, pf);
@@ -969,7 +974,7 @@
// Unpin if it was already pinned prior to re-pinning.
unpinFile(file);
- PinnedFile df = mInjector.pinFileInternal(file, Integer.MAX_VALUE,
+ PinnedFile df = mInjector.pinFileInternal(file, maxBytesToPin,
/*attemptPinIntrospection=*/false);
if (df == null) {
Slog.i(TAG, "Failed to pin ART file = " + file);
@@ -1341,18 +1346,6 @@
public List<PinnedFileStat> getPinnerStats() {
ArrayList<PinnedFileStat> stats = new ArrayList<>();
- Collection<PinnedApp> pinnedApps;
- synchronized(this) {
- pinnedApps = mPinnedApps.values();
- }
- for (PinnedApp pinnedApp : pinnedApps) {
- for (PinnedFile pf : pinnedApp.mFiles) {
- PinnedFileStat stat =
- new PinnedFileStat(pf.fileName, pf.bytesPinned, pf.groupName);
- stats.add(stat);
- }
- }
-
Collection<PinnedFile> pinnedFiles;
synchronized(this) {
pinnedFiles = mPinnedFiles.values();
diff --git a/services/core/java/com/android/server/SystemConfig.java b/services/core/java/com/android/server/SystemConfig.java
index a508ebf..8c1bb3b 100644
--- a/services/core/java/com/android/server/SystemConfig.java
+++ b/services/core/java/com/android/server/SystemConfig.java
@@ -1783,7 +1783,13 @@
String gidStr = parser.getAttributeValue(null, "gid");
if (gidStr != null) {
int gid = Process.getGidForName(gidStr);
- perm.gids = appendInt(perm.gids, gid);
+ if (gid != -1) {
+ perm.gids = appendInt(perm.gids, gid);
+ } else {
+ Slog.w(TAG, "<group> with unknown gid \""
+ + gidStr + " for permission " + name + " in "
+ + parser.getPositionDescription());
+ }
} else {
Slog.w(TAG, "<group> without gid at "
+ parser.getPositionDescription());
diff --git a/services/core/java/com/android/server/TEST_MAPPING b/services/core/java/com/android/server/TEST_MAPPING
index 5933639..a3b6d80 100644
--- a/services/core/java/com/android/server/TEST_MAPPING
+++ b/services/core/java/com/android/server/TEST_MAPPING
@@ -134,6 +134,13 @@
},
{
"name": "CtsSuspendAppsTestCases"
+ },
+ {
+ "name": "CtsWindowManagerBackgroundActivityTestCases",
+ "file_patterns": [
+ "Background.*\\.java",
+ "Activity.*\\.java"
+ ]
}
],
"presubmit-large": [
@@ -187,6 +194,18 @@
},
{
"name": "SelinuxFrameworksTests"
+ },
+ {
+ "name": "WmTests",
+ "file_patterns": [
+ "Background.*\\.java",
+ "Activity.*\\.java"
+ ],
+ "options": [
+ {
+ "include-filter": "com.android.server.wm.BackgroundActivityStart*"
+ }
+ ]
}
- ]
+ ]
}
diff --git a/services/core/java/com/android/server/WallpaperUpdateReceiver.java b/services/core/java/com/android/server/WallpaperUpdateReceiver.java
index 2812233..42391a5 100644
--- a/services/core/java/com/android/server/WallpaperUpdateReceiver.java
+++ b/services/core/java/com/android/server/WallpaperUpdateReceiver.java
@@ -24,7 +24,6 @@
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
-import android.graphics.Bitmap;
import android.os.AsyncTask;
import android.os.ParcelFileDescriptor;
import android.util.Slog;
@@ -59,10 +58,10 @@
return;
}
if (DEBUG) Slog.d(TAG, "Set customized default_wallpaper.");
- Bitmap blank = Bitmap.createBitmap(1, 1, Bitmap.Config.ALPHA_8);
- // set a blank wallpaper to force a redraw of default_wallpaper
- wallpaperManager.setBitmap(blank);
- wallpaperManager.setResource(com.android.internal.R.drawable.default_wallpaper);
+ // Check if it is not a live wallpaper set
+ if (wallpaperManager.getWallpaperInfo() == null) {
+ wallpaperManager.clearWallpaper();
+ }
} catch (Exception e) {
Slog.w(TAG, "Failed to customize system wallpaper." + e);
}
diff --git a/services/core/java/com/android/server/am/ActiveServices.java b/services/core/java/com/android/server/am/ActiveServices.java
index 94bf813..9be0e1f 100644
--- a/services/core/java/com/android/server/am/ActiveServices.java
+++ b/services/core/java/com/android/server/am/ActiveServices.java
@@ -3783,6 +3783,14 @@
return fgsInfo.getLastFgsStartTime() + Math.max(0, timeLimit - fgsInfo.getTotalRuntime());
}
+ private TimeLimitedFgsInfo getFgsTimeLimitedInfo(int uid, int fgsType) {
+ final SparseArray<TimeLimitedFgsInfo> fgsInfo = mTimeLimitedFgsInfo.get(uid);
+ if (fgsInfo != null) {
+ return fgsInfo.get(fgsType);
+ }
+ return null;
+ }
+
private void maybeUpdateFgsTrackingLocked(ServiceRecord sr, int previousFgsType) {
final int previouslyTimeLimitedType = getTimeLimitedFgsType(previousFgsType);
if (previouslyTimeLimitedType == ServiceInfo.FOREGROUND_SERVICE_TYPE_NONE
@@ -3793,16 +3801,12 @@
if (previouslyTimeLimitedType != ServiceInfo.FOREGROUND_SERVICE_TYPE_NONE) {
// FGS is switching types and the previous type was time-limited so update the runtime.
- final SparseArray<TimeLimitedFgsInfo> fgsInfo = mTimeLimitedFgsInfo.get(sr.appInfo.uid);
- if (fgsInfo != null) {
- final TimeLimitedFgsInfo fgsTypeInfo = fgsInfo.get(previouslyTimeLimitedType);
- if (fgsTypeInfo != null) {
- // Update the total runtime for the previous time-limited fgs type.
- fgsTypeInfo.updateTotalRuntime();
- // TODO(b/330399444): handle the case where an app is running 2 services of the
- // same time-limited type in parallel and stops one of them which leads to the
- // second running one gaining additional runtime.
- }
+ final TimeLimitedFgsInfo fgsTypeInfo = getFgsTimeLimitedInfo(
+ sr.appInfo.uid, previouslyTimeLimitedType);
+ if (fgsTypeInfo != null) {
+ // Update the total runtime for the previous time-limited fgs type.
+ fgsTypeInfo.updateTotalRuntime(SystemClock.uptimeMillis());
+ fgsTypeInfo.decNumParallelServices();
}
if (!sr.isFgsTimeLimited()) {
@@ -3825,10 +3829,10 @@
final int timeLimitedFgsType = getTimeLimitedFgsType(sr.foregroundServiceType);
TimeLimitedFgsInfo fgsTypeInfo = fgsInfo.get(timeLimitedFgsType);
if (fgsTypeInfo == null) {
- fgsTypeInfo = sr.createTimeLimitedFgsInfo(nowUptime);
+ fgsTypeInfo = sr.createTimeLimitedFgsInfo();
fgsInfo.put(timeLimitedFgsType, fgsTypeInfo);
}
- fgsTypeInfo.setLastFgsStartTime(nowUptime);
+ fgsTypeInfo.noteFgsFgsStart(nowUptime);
// We'll cancel the previous ANR timer and start a fresh one below.
mFGSAnrTimer.cancel(sr);
@@ -3852,13 +3856,12 @@
return; // if the current fgs type is not time-limited, return.
}
- final SparseArray<TimeLimitedFgsInfo> fgsInfo = mTimeLimitedFgsInfo.get(sr.appInfo.uid);
- if (fgsInfo != null) {
- final TimeLimitedFgsInfo fgsTypeInfo = fgsInfo.get(timeLimitedType);
- if (fgsTypeInfo != null) {
- // Update the total runtime for the previous time-limited fgs type.
- fgsTypeInfo.updateTotalRuntime();
- }
+ final TimeLimitedFgsInfo fgsTypeInfo = getFgsTimeLimitedInfo(
+ sr.appInfo.uid, timeLimitedType);
+ if (fgsTypeInfo != null) {
+ // Update the total runtime for the previous time-limited fgs type.
+ fgsTypeInfo.updateTotalRuntime(SystemClock.uptimeMillis());
+ fgsTypeInfo.decNumParallelServices();
}
Slog.d(TAG_SERVICE, "Stop FGS timeout: " + sr);
mFGSAnrTimer.cancel(sr);
@@ -3913,24 +3916,21 @@
mFGSAnrTimer.accept(sr);
traceInstant("FGS timed out: ", sr);
- final SparseArray<TimeLimitedFgsInfo> fgsInfo = mTimeLimitedFgsInfo.get(sr.appInfo.uid);
- if (fgsInfo != null) {
- final TimeLimitedFgsInfo fgsTypeInfo = fgsInfo.get(fgsType);
- if (fgsTypeInfo != null) {
- // Update total runtime for the time-limited fgs type and mark it as timed out.
- fgsTypeInfo.updateTotalRuntime();
- fgsTypeInfo.setTimeLimitExceededAt(nowUptime);
+ final TimeLimitedFgsInfo fgsTypeInfo = getFgsTimeLimitedInfo(sr.appInfo.uid, fgsType);
+ if (fgsTypeInfo != null) {
+ // Update total runtime for the time-limited fgs type and mark it as timed out.
+ fgsTypeInfo.updateTotalRuntime(nowUptime);
+ fgsTypeInfo.setTimeLimitExceededAt(nowUptime);
- logFGSStateChangeLocked(sr,
- FOREGROUND_SERVICE_STATE_CHANGED__STATE__TIMED_OUT,
- nowUptime > fgsTypeInfo.getLastFgsStartTime()
- ? (int) (nowUptime - fgsTypeInfo.getLastFgsStartTime()) : 0,
- FGS_STOP_REASON_UNKNOWN,
- FGS_TYPE_POLICY_CHECK_UNKNOWN,
- FOREGROUND_SERVICE_STATE_CHANGED__FGS_START_API__FGSSTARTAPI_NA,
- false /* fgsRestrictionRecalculated */
- );
- }
+ logFGSStateChangeLocked(sr,
+ FOREGROUND_SERVICE_STATE_CHANGED__STATE__TIMED_OUT,
+ nowUptime > fgsTypeInfo.getFirstFgsStartUptime()
+ ? (int) (nowUptime - fgsTypeInfo.getFirstFgsStartUptime()) : 0,
+ FGS_STOP_REASON_UNKNOWN,
+ FGS_TYPE_POLICY_CHECK_UNKNOWN,
+ FOREGROUND_SERVICE_STATE_CHANGED__FGS_START_API__FGSSTARTAPI_NA,
+ false /* fgsRestrictionRecalculated */
+ );
}
try {
@@ -3949,6 +3949,16 @@
if (fgsType == ServiceInfo.FOREGROUND_SERVICE_TYPE_NONE) {
return; // no timed out FGS type was found (either it was stopped or it switched types)
}
+
+ synchronized (mAm) {
+ final TimeLimitedFgsInfo fgsTypeInfo = getFgsTimeLimitedInfo(sr.appInfo.uid, fgsType);
+ if (fgsTypeInfo != null) {
+ // Runtime is already updated when the service times out - if the app didn't
+ // stop the service, decrement the number of parallel running services here.
+ fgsTypeInfo.decNumParallelServices();
+ }
+ }
+
final String reason = "A foreground service of type "
+ ServiceInfo.foregroundServiceTypeToLabel(fgsType)
+ " did not stop within its timeout: " + sr.getComponentName();
@@ -3958,7 +3968,6 @@
Slog.wtf(TAG, reason);
return;
}
-
if (android.app.Flags.enableFgsTimeoutCrashBehavior()) {
// Crash the app
synchronized (mAm) {
@@ -4005,7 +4014,6 @@
private void stopServiceAndUpdateAllowlistManagerLocked(ServiceRecord service) {
maybeStopShortFgsTimeoutLocked(service);
- maybeStopFgsTimeoutLocked(service);
final ProcessServiceRecord psr = service.app.mServices;
psr.stopService(service);
psr.updateBoundClientUids();
@@ -6291,7 +6299,6 @@
Slog.w(TAG_SERVICE, "Short FGS brought down without stopping: " + r);
maybeStopShortFgsTimeoutLocked(r);
}
- maybeStopFgsTimeoutLocked(r);
// Report to all of the connections that the service is no longer
// available.
@@ -6416,7 +6423,6 @@
final boolean exitingFg = r.isForeground;
if (exitingFg) {
maybeStopShortFgsTimeoutLocked(r);
- maybeStopFgsTimeoutLocked(r);
decActiveForegroundAppLocked(smap, r);
synchronized (mAm.mProcessStats.mLock) {
ServiceState stracker = r.getTracker();
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index 46ed1fd..58855ea 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -372,6 +372,8 @@
import android.provider.Settings;
import android.server.ServerProtoEnums;
import android.sysprop.InitProperties;
+import android.system.Os;
+import android.system.OsConstants;
import android.telephony.TelephonyManager;
import android.text.TextUtils;
import android.text.style.SuggestionSpan;
@@ -598,6 +600,9 @@
// as one line, but close enough for now.
static final int RESERVED_BYTES_PER_LOGCAT_LINE = 100;
+ // How many seconds should the system wait before terminating the spawned logcat process.
+ static final int LOGCAT_TIMEOUT_SEC = 10;
+
// Necessary ApplicationInfo flags to mark an app as persistent
static final int PERSISTENT_MASK =
ApplicationInfo.FLAG_SYSTEM|ApplicationInfo.FLAG_PERSISTENT;
@@ -9908,6 +9913,13 @@
sb.append("ErrorId: ").append(errorId.toString()).append("\n");
}
sb.append("Build: ").append(Build.FINGERPRINT).append("\n");
+
+ // If device is not using 4KB pages, add the PageSize
+ long pageSize = Os.sysconf(OsConstants._SC_PAGESIZE);
+ if (pageSize != 4096) {
+ sb.append("PageSize: ").append(pageSize).append("\n");
+ }
+
if (Debug.isDebuggerConnected()) {
sb.append("Debugger: Connected\n");
}
@@ -9939,126 +9951,70 @@
// If process is null, we are being called from some internal code
// and may be about to die -- run this synchronously.
final boolean runSynchronously = process == null;
- Thread worker =
- new Thread("Error dump: " + dropboxTag) {
- @Override
- public void run() {
- if (report != null) {
- sb.append(report);
+ Thread worker = new Thread("Error dump: " + dropboxTag) {
+ @Override
+ public void run() {
+ if (report != null) {
+ sb.append(report);
+ }
+
+ String logcatSetting = Settings.Global.ERROR_LOGCAT_PREFIX + dropboxTag;
+ String kerLogSetting = Settings.Global.ERROR_KERNEL_LOG_PREFIX + dropboxTag;
+ String maxBytesSetting = Settings.Global.MAX_ERROR_BYTES_PREFIX + dropboxTag;
+ int logcatLines = Build.IS_USER
+ ? 0
+ : Settings.Global.getInt(mContext.getContentResolver(), logcatSetting, 0);
+ int kernelLogLines = Build.IS_USER
+ ? 0
+ : Settings.Global.getInt(mContext.getContentResolver(), kerLogSetting, 0);
+ int dropboxMaxSize = Settings.Global.getInt(
+ mContext.getContentResolver(), maxBytesSetting, DROPBOX_DEFAULT_MAX_SIZE);
+
+ if (dataFile != null) {
+ // Attach the stack traces file to the report so collectors can load them
+ // by file if they have access.
+ sb.append(DATA_FILE_PATH_HEADER)
+ .append(dataFile.getAbsolutePath()).append('\n');
+
+ int maxDataFileSize = dropboxMaxSize
+ - sb.length()
+ - logcatLines * RESERVED_BYTES_PER_LOGCAT_LINE
+ - kernelLogLines * RESERVED_BYTES_PER_LOGCAT_LINE
+ - DATA_FILE_PATH_FOOTER.length();
+
+ if (maxDataFileSize > 0) {
+ // Inline dataFile contents if there is room.
+ try {
+ sb.append(FileUtils.readTextFile(dataFile, maxDataFileSize,
+ "\n\n[[TRUNCATED]]\n"));
+ } catch (IOException e) {
+ Slog.e(TAG, "Error reading " + dataFile, e);
}
-
- String logcatSetting = Settings.Global.ERROR_LOGCAT_PREFIX + dropboxTag;
- String maxBytesSetting =
- Settings.Global.MAX_ERROR_BYTES_PREFIX + dropboxTag;
- int lines =
- Build.IS_USER
- ? 0
- : Settings.Global.getInt(
- mContext.getContentResolver(), logcatSetting, 0);
- int dropboxMaxSize =
- Settings.Global.getInt(
- mContext.getContentResolver(),
- maxBytesSetting,
- DROPBOX_DEFAULT_MAX_SIZE);
-
- if (dataFile != null) {
- // Attach the stack traces file to the report so collectors can load
- // them
- // by file if they have access.
- sb.append(DATA_FILE_PATH_HEADER)
- .append(dataFile.getAbsolutePath())
- .append('\n');
-
- int maxDataFileSize =
- dropboxMaxSize
- - sb.length()
- - lines * RESERVED_BYTES_PER_LOGCAT_LINE
- - DATA_FILE_PATH_FOOTER.length();
-
- if (maxDataFileSize > 0) {
- // Inline dataFile contents if there is room.
- try {
- sb.append(
- FileUtils.readTextFile(
- dataFile,
- maxDataFileSize,
- "\n\n[[TRUNCATED]]\n"));
- } catch (IOException e) {
- Slog.e(TAG, "Error reading " + dataFile, e);
- }
- }
-
- // Always append the footer, even there wasn't enough space to inline
- // the
- // dataFile contents.
- sb.append(DATA_FILE_PATH_FOOTER);
- }
-
- if (crashInfo != null && crashInfo.stackTrace != null) {
- sb.append(crashInfo.stackTrace);
- }
-
- if (lines > 0 && !runSynchronously) {
- sb.append("\n");
-
- InputStreamReader input = null;
- try {
- java.lang.Process logcat =
- new ProcessBuilder(
- // Time out after 10s of inactivity, but
- // kill logcat with SEGV
- // so we can investigate why it didn't
- // finish.
- "/system/bin/timeout",
- "-i",
- "-s",
- "SEGV",
- "10s",
- // Merge several logcat streams, and take
- // the last N lines.
- "/system/bin/logcat",
- "-v",
- "threadtime",
- "-b",
- "events",
- "-b",
- "system",
- "-b",
- "main",
- "-b",
- "crash",
- "-t",
- String.valueOf(lines))
- .redirectErrorStream(true)
- .start();
-
- try {
- logcat.getOutputStream().close();
- } catch (IOException e) {
- }
- try {
- logcat.getErrorStream().close();
- } catch (IOException e) {
- }
- input = new InputStreamReader(logcat.getInputStream());
-
- int num;
- char[] buf = new char[8192];
- while ((num = input.read(buf)) > 0) sb.append(buf, 0, num);
- } catch (IOException e) {
- Slog.e(TAG, "Error running logcat", e);
- } finally {
- if (input != null)
- try {
- input.close();
- } catch (IOException e) {
- }
- }
- }
-
- dbox.addText(dropboxTag, sb.toString());
}
- };
+ // Always append the footer, even there wasn't enough space to inline the
+ // dataFile contents.
+ sb.append(DATA_FILE_PATH_FOOTER);
+ }
+
+ if (crashInfo != null && crashInfo.stackTrace != null) {
+ sb.append(crashInfo.stackTrace);
+ }
+ boolean shouldAddLogs = logcatLines > 0 || kernelLogLines > 0;
+ if (!runSynchronously && shouldAddLogs) {
+ sb.append("\n");
+ if (logcatLines > 0) {
+ fetchLogcatBuffers(sb, logcatLines, LOGCAT_TIMEOUT_SEC,
+ List.of("events", "system", "main", "crash"));
+ }
+ if (kernelLogLines > 0) {
+ fetchLogcatBuffers(sb, kernelLogLines, LOGCAT_TIMEOUT_SEC / 2,
+ List.of("kernel"));
+ }
+ }
+
+ dbox.addText(dropboxTag, sb.toString());
+ }
+ };
if (runSynchronously) {
final int oldMask = StrictMode.allowThreadDiskWritesMask();
@@ -10321,6 +10277,67 @@
}
/**
+ * Retrieves logs from specified logcat buffers and appends them to a StringBuilder
+ * in the supplied order. The method executes a logcat command to fetch specific
+ * log entries from the supplied buffers.
+ *
+ * @param sb the StringBuilder to append the logcat output to.
+ * @param lines the number of lines to retrieve.
+ * @param timeout the maximum allowed time in seconds for logcat to run before being terminated.
+ * @param buffers the list of log buffers from which to retrieve logs.
+ */
+ private static void fetchLogcatBuffers(StringBuilder sb, int lines,
+ int timeout, List<String> buffers) {
+
+ if (buffers.size() == 0 || lines <= 0 || timeout <= 0) {
+ return;
+ }
+
+ List<String> command = new ArrayList<>(10 + (2 * buffers.size()));
+ // Time out after 10s of inactivity, but kill logcat with SEGV
+ // so we can investigate why it didn't finish.
+ command.add("/system/bin/timeout");
+ command.add("-i");
+ command.add("-s");
+ command.add("SEGV");
+ command.add(timeout + "s");
+
+ // Merge several logcat streams, and take the last N lines.
+ command.add("/system/bin/logcat");
+ command.add("-v");
+ // This adds a timestamp and thread info to each log line.
+ command.add("threadtime");
+ for (String buffer : buffers) {
+ command.add("-b");
+ command.add(buffer);
+ }
+ // Limit the output to the last N lines.
+ command.add("-t");
+ command.add(String.valueOf(lines));
+
+ try {
+ java.lang.Process proc =
+ new ProcessBuilder(command).redirectErrorStream(true).start();
+
+ // Close the output stream immediately as we do not send input to the process.
+ try {
+ proc.getOutputStream().close();
+ } catch (IOException e) {
+ }
+
+ try (InputStreamReader reader = new InputStreamReader(proc.getInputStream())) {
+ char[] buffer = new char[8192];
+ int numRead;
+ while ((numRead = reader.read(buffer, 0, buffer.length)) > 0) {
+ sb.append(buffer, 0, numRead);
+ }
+ }
+ } catch (IOException e) {
+ Slog.e(TAG, "Error running logcat", e);
+ }
+ }
+
+ /**
* Check if the calling process has the permission to dump given package,
* throw SecurityException if it doesn't have the permission.
*
@@ -12326,8 +12343,8 @@
ProcessList.SYSTEM_ADJ, ProcessList.PERSISTENT_PROC_ADJ,
ProcessList.PERSISTENT_SERVICE_ADJ, ProcessList.FOREGROUND_APP_ADJ,
ProcessList.VISIBLE_APP_ADJ,
- ProcessList.PERCEPTIBLE_APP_ADJ, ProcessList.PERCEPTIBLE_LOW_APP_ADJ,
- ProcessList.PERCEPTIBLE_MEDIUM_APP_ADJ,
+ ProcessList.PERCEPTIBLE_APP_ADJ,
+ ProcessList.PERCEPTIBLE_MEDIUM_APP_ADJ, ProcessList.PERCEPTIBLE_LOW_APP_ADJ,
ProcessList.BACKUP_APP_ADJ, ProcessList.HEAVY_WEIGHT_APP_ADJ,
ProcessList.SERVICE_ADJ, ProcessList.HOME_APP_ADJ,
ProcessList.PREVIOUS_APP_ADJ, ProcessList.SERVICE_B_ADJ, ProcessList.CACHED_APP_MIN_ADJ
@@ -12335,7 +12352,7 @@
static final String[] DUMP_MEM_OOM_LABEL = new String[] {
"Native",
"System", "Persistent", "Persistent Service", "Foreground",
- "Visible", "Perceptible", "Perceptible Low", "Perceptible Medium",
+ "Visible", "Perceptible", "Perceptible Medium", "Perceptible Low",
"Backup", "Heavy Weight",
"A Services", "Home",
"Previous", "B Services", "Cached"
@@ -12343,7 +12360,7 @@
static final String[] DUMP_MEM_OOM_COMPACT_LABEL = new String[] {
"native",
"sys", "pers", "persvc", "fore",
- "vis", "percept", "perceptl", "perceptm",
+ "vis", "percept", "perceptm", "perceptl",
"backup", "heavy",
"servicea", "home",
"prev", "serviceb", "cached"
@@ -19967,6 +19984,26 @@
addStartInfoTimestampInternal(key, timestampNs, userId, uid);
}
+
+ @Override
+ public void killApplicationSync(String pkgName, int appId, int userId,
+ String reason, int exitInfoReason) {
+ if (pkgName == null) {
+ return;
+ }
+ // Make sure the uid is valid.
+ if (appId < 0) {
+ Slog.w(TAG, "Invalid appid specified for pkg : " + pkgName);
+ return;
+ }
+ synchronized (ActivityManagerService.this) {
+ ActivityManagerService.this.forceStopPackageLocked(pkgName, appId,
+ /* callerWillRestart= */ false, /*purgeCache= */ false,
+ /* doit= */ true, /* evenPersistent= */ false,
+ /* uninstalling= */ false, /* packageStateStopped= */ false,
+ userId, reason, exitInfoReason);
+ }
+ }
}
long inputDispatchingTimedOut(int pid, final boolean aboveSystem, TimeoutRecord timeoutRecord) {
diff --git a/services/core/java/com/android/server/am/ActivityManagerShellCommand.java b/services/core/java/com/android/server/am/ActivityManagerShellCommand.java
index a182a10..bbd4323 100644
--- a/services/core/java/com/android/server/am/ActivityManagerShellCommand.java
+++ b/services/core/java/com/android/server/am/ActivityManagerShellCommand.java
@@ -130,6 +130,7 @@
import com.android.server.am.nano.Capability;
import com.android.server.am.nano.FrameworkCapability;
import com.android.server.am.nano.VMCapability;
+import com.android.server.am.nano.VMInfo;
import com.android.server.compat.PlatformCompat;
import com.android.server.pm.UserManagerInternal;
import com.android.server.utils.Slogf;
@@ -460,6 +461,8 @@
return -1;
}
}
+ String vmName = System.getProperty("java.vm.name", "?");
+ String vmVersion = System.getProperty("java.vm.version", "?");
if (outputAsProtobuf) {
Capabilities capabilities = new Capabilities();
@@ -486,6 +489,11 @@
capabilities.frameworkCapabilities[i] = cap;
}
+ VMInfo vmInfo = new VMInfo();
+ vmInfo.name = vmName;
+ vmInfo.version = vmVersion;
+ capabilities.vmInfo = vmInfo;
+
try {
getRawOutputStream().write(Capabilities.toByteArray(capabilities));
} catch (IOException e) {
@@ -505,6 +513,8 @@
for (String capability : Debug.getFeatureList()) {
pw.println("framework:" + capability);
}
+ pw.println("vm_name:" + vmName);
+ pw.println("vm_version:" + vmVersion);
}
return 0;
}
diff --git a/services/core/java/com/android/server/am/BatteryStatsService.java b/services/core/java/com/android/server/am/BatteryStatsService.java
index 4f84149..58732fd 100644
--- a/services/core/java/com/android/server/am/BatteryStatsService.java
+++ b/services/core/java/com/android/server/am/BatteryStatsService.java
@@ -159,6 +159,8 @@
import java.util.concurrent.ExecutionException;
import java.util.concurrent.Future;
import java.util.concurrent.TimeUnit;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
/**
* All information we are collecting about things that can happen that impact
@@ -409,26 +411,14 @@
com.android.internal.R.bool.config_batteryStatsResetOnUnplugHighBatteryLevel);
final boolean resetOnUnplugAfterSignificantCharge = context.getResources().getBoolean(
com.android.internal.R.bool.config_batteryStatsResetOnUnplugAfterSignificantCharge);
- final long powerStatsThrottlePeriodCpu = context.getResources().getInteger(
- com.android.internal.R.integer.config_defaultPowerStatsThrottlePeriodCpu);
- final long powerStatsThrottlePeriodMobileRadio = context.getResources().getInteger(
- com.android.internal.R.integer.config_defaultPowerStatsThrottlePeriodMobileRadio);
- final long powerStatsThrottlePeriodWifi = context.getResources().getInteger(
- com.android.internal.R.integer.config_defaultPowerStatsThrottlePeriodWifi);
- mBatteryStatsConfig =
+ BatteryStatsImpl.BatteryStatsConfig.Builder batteryStatsConfigBuilder =
new BatteryStatsImpl.BatteryStatsConfig.Builder()
.setResetOnUnplugHighBatteryLevel(resetOnUnplugHighBatteryLevel)
- .setResetOnUnplugAfterSignificantCharge(resetOnUnplugAfterSignificantCharge)
- .setPowerStatsThrottlePeriodMillis(
- BatteryConsumer.POWER_COMPONENT_CPU,
- powerStatsThrottlePeriodCpu)
- .setPowerStatsThrottlePeriodMillis(
- BatteryConsumer.POWER_COMPONENT_MOBILE_RADIO,
- powerStatsThrottlePeriodMobileRadio)
- .setPowerStatsThrottlePeriodMillis(
- BatteryConsumer.POWER_COMPONENT_WIFI,
- powerStatsThrottlePeriodWifi)
- .build();
+ .setResetOnUnplugAfterSignificantCharge(
+ resetOnUnplugAfterSignificantCharge);
+ setPowerStatsThrottlePeriods(batteryStatsConfigBuilder, context.getResources().getString(
+ com.android.internal.R.string.config_powerStatsThrottlePeriods));
+ mBatteryStatsConfig = batteryStatsConfigBuilder.build();
mPowerStatsUidResolver = new PowerStatsUidResolver();
mStats = new BatteryStatsImpl(mBatteryStatsConfig, Clock.SYSTEM_CLOCK, mMonotonicClock,
systemDir, mHandler, this, this, mUserManagerUserInfoProvider, mPowerProfile,
@@ -515,6 +505,26 @@
return config;
}
+ private void setPowerStatsThrottlePeriods(BatteryStatsImpl.BatteryStatsConfig.Builder builder,
+ String configString) {
+ Matcher matcher = Pattern.compile("([^:]+):(\\d+)\\s*").matcher(configString);
+ while (matcher.find()) {
+ String powerComponentName = matcher.group(1);
+ long throttlePeriod;
+ try {
+ throttlePeriod = Long.parseLong(matcher.group(2));
+ } catch (NumberFormatException nfe) {
+ throw new IllegalArgumentException(
+ "Invalid config_powerStatsThrottlePeriods format: " + configString);
+ }
+ if (powerComponentName.equals("*")) {
+ builder.setDefaultPowerStatsThrottlePeriodMillis(throttlePeriod);
+ } else {
+ builder.setPowerStatsThrottlePeriodMillis(powerComponentName, throttlePeriod);
+ }
+ }
+ }
+
/**
* Creates an instance of BatteryStatsService and restores data from stored state.
*/
diff --git a/services/core/java/com/android/server/am/OomAdjusterModernImpl.java b/services/core/java/com/android/server/am/OomAdjusterModernImpl.java
index 9600317..a67af89 100644
--- a/services/core/java/com/android/server/am/OomAdjusterModernImpl.java
+++ b/services/core/java/com/android/server/am/OomAdjusterModernImpl.java
@@ -84,6 +84,7 @@
import java.util.Arrays;
import java.util.function.BiConsumer;
import java.util.function.Consumer;
+import java.util.function.ToIntFunction;
/**
* A modern implementation of the oom adjuster.
@@ -271,11 +272,31 @@
// The last node besides the tail.
private final ProcessRecordNode[] mLastNode;
+ private final ToIntFunction<ProcessRecord> mSlotFunction;
+ // Cache of the most important slot with a node in it.
+ private int mFirstPopulatedSlot = 0;
+
ProcessRecordNodes(@ProcessRecordNode.NodeType int type, int size) {
mType = type;
+ final ToIntFunction<ProcessRecord> valueFunction;
+ switch (mType) {
+ case ProcessRecordNode.NODE_TYPE_PROC_STATE:
+ valueFunction = (proc) -> proc.mState.getCurProcState();
+ mSlotFunction = (proc) -> processStateToSlot(proc.mState.getCurProcState());
+ break;
+ case ProcessRecordNode.NODE_TYPE_ADJ:
+ valueFunction = (proc) -> proc.mState.getCurRawAdj();
+ mSlotFunction = (proc) -> adjToSlot(proc.mState.getCurRawAdj());
+ break;
+ default:
+ valueFunction = (proc) -> 0;
+ mSlotFunction = (proc) -> 0;
+ break;
+ }
+
mProcessRecordNodes = new LinkedProcessRecordList[size];
for (int i = 0; i < size; i++) {
- mProcessRecordNodes[i] = new LinkedProcessRecordList(type);
+ mProcessRecordNodes[i] = new LinkedProcessRecordList(valueFunction);
}
mLastNode = new ProcessRecordNode[size];
resetLastNodes();
@@ -294,6 +315,11 @@
}
void resetLastNodes() {
+ if (Flags.simplifyProcessTraversal()) {
+ // Last nodes are no longer used. Just reset instead.
+ reset();
+ return;
+ }
for (int i = 0; i < mProcessRecordNodes.length; i++) {
mLastNode[i] = mProcessRecordNodes[i].getLastNodeBeforeTail();
}
@@ -308,6 +334,36 @@
final ProcessRecordNode tail = mProcessRecordNodes[slot].TAIL;
while (node != tail) {
mTmpOomAdjusterArgs.mApp = node.mApp;
+ if (node.mApp == null) {
+ // TODO(b/336178916) - Temporary logging for root causing b/336178916.
+ StringBuilder sb = new StringBuilder();
+ sb.append("Iterating null process during OomAdjuster traversal!!!\n");
+ sb.append("Type:");
+ switch (mType) {
+ case ProcessRecordNode.NODE_TYPE_PROC_STATE -> sb.append(
+ "NODE_TYPE_PROC_STATE");
+ case ProcessRecordNode.NODE_TYPE_ADJ -> sb.append("NODE_TYPE_ADJ");
+ default -> sb.append("UNKNOWN");
+ }
+ sb.append(", Slot:");
+ sb.append(slot);
+ sb.append("\nLAST:");
+ ProcessRecordNode last = mLastNode[slot];
+ if (last.mApp == null) {
+ sb.append("null");
+ } else {
+ sb.append(last);
+ sb.append("\nSetProcState:");
+ sb.append(last.mApp.getSetProcState());
+ sb.append(", CurProcState:");
+ sb.append(last.mApp.mState.getCurProcState());
+ sb.append(", SetAdj:");
+ sb.append(last.mApp.getSetAdj());
+ sb.append(", CurRawAdj:");
+ sb.append(last.mApp.mState.getCurRawAdj());
+ }
+ Slog.wtfStack(TAG, sb.toString());
+ }
// Save the next before calling callback, since that may change the node.mNext.
final ProcessRecordNode next = node.mNext;
callback.accept(mTmpOomAdjusterArgs);
@@ -325,6 +381,33 @@
}
}
+ ProcessRecord poll() {
+ ProcessRecordNode node = null;
+ final int size = mProcessRecordNodes.length;
+ // Find the next node.
+ while (node == null && mFirstPopulatedSlot < size) {
+ node = mProcessRecordNodes[mFirstPopulatedSlot].poll();
+ if (node == null) {
+ // This slot is now empty, move on to the next.
+ mFirstPopulatedSlot++;
+ }
+ }
+ if (node == null) return null;
+ return node.mApp;
+ }
+
+ void offer(ProcessRecord proc) {
+ ProcessRecordNode node = proc.mLinkedNodes[mType];
+ // Find which slot to add the node to.
+ final int newSlot = mSlotFunction.applyAsInt(proc);
+ if (newSlot < mFirstPopulatedSlot) {
+ // node is being added to a more important slot.
+ mFirstPopulatedSlot = newSlot;
+ }
+ node.unlink();
+ mProcessRecordNodes[newSlot].offer(node);
+ }
+
int getNumberOfSlots() {
return mProcessRecordNodes.length;
}
@@ -423,12 +506,35 @@
// Sentinel head/tail, to make bookkeeping work easier.
final ProcessRecordNode HEAD = new ProcessRecordNode(null);
final ProcessRecordNode TAIL = new ProcessRecordNode(null);
- final @ProcessRecordNode.NodeType int mNodeType;
+ final ToIntFunction<ProcessRecord> mValueFunction;
- LinkedProcessRecordList(@ProcessRecordNode.NodeType int nodeType) {
+ LinkedProcessRecordList(ToIntFunction<ProcessRecord> valueFunction) {
HEAD.mNext = TAIL;
TAIL.mPrev = HEAD;
- mNodeType = nodeType;
+ mValueFunction = valueFunction;
+ }
+
+ ProcessRecordNode poll() {
+ final ProcessRecordNode next = HEAD.mNext;
+ if (next == TAIL) return null;
+ next.unlink();
+ return next;
+ }
+
+ void offer(@NonNull ProcessRecordNode node) {
+ final int newValue = mValueFunction.applyAsInt(node.mApp);
+
+ // Find the last node with less than or equal value to the new node.
+ ProcessRecordNode curNode = TAIL.mPrev;
+ while (curNode != HEAD && mValueFunction.applyAsInt(curNode.mApp) > newValue) {
+ curNode = curNode.mPrev;
+ }
+
+ // Insert the new node after the found node.
+ node.mPrev = curNode;
+ node.mNext = curNode.mNext;
+ curNode.mNext.mPrev = node;
+ curNode.mNext = node;
}
void append(@NonNull ProcessRecordNode node) {
@@ -727,34 +833,50 @@
private void updateAdjSlotIfNecessary(ProcessRecord app, int prevRawAdj) {
if (app.mState.getCurRawAdj() != prevRawAdj) {
- final int slot = adjToSlot(app.mState.getCurRawAdj());
- final int prevSlot = adjToSlot(prevRawAdj);
- if (slot != prevSlot && slot != ADJ_SLOT_INVALID) {
- mProcessRecordAdjNodes.moveAppTo(app, prevSlot, slot);
+ if (Flags.simplifyProcessTraversal()) {
+ mProcessRecordAdjNodes.offer(app);
+ } else {
+ final int slot = adjToSlot(app.mState.getCurRawAdj());
+ final int prevSlot = adjToSlot(prevRawAdj);
+ if (slot != prevSlot && slot != ADJ_SLOT_INVALID) {
+ mProcessRecordAdjNodes.moveAppTo(app, prevSlot, slot);
+ }
}
}
}
private void updateAdjSlot(ProcessRecord app, int prevRawAdj) {
- final int slot = adjToSlot(app.mState.getCurRawAdj());
- final int prevSlot = adjToSlot(prevRawAdj);
- mProcessRecordAdjNodes.moveAppTo(app, prevSlot, slot);
+ if (Flags.simplifyProcessTraversal()) {
+ mProcessRecordAdjNodes.offer(app);
+ } else {
+ final int slot = adjToSlot(app.mState.getCurRawAdj());
+ final int prevSlot = adjToSlot(prevRawAdj);
+ mProcessRecordAdjNodes.moveAppTo(app, prevSlot, slot);
+ }
}
private void updateProcStateSlotIfNecessary(ProcessRecord app, int prevProcState) {
if (app.mState.getCurProcState() != prevProcState) {
- final int slot = processStateToSlot(app.mState.getCurProcState());
- final int prevSlot = processStateToSlot(prevProcState);
- if (slot != prevSlot) {
- mProcessRecordProcStateNodes.moveAppTo(app, prevSlot, slot);
+ if (Flags.simplifyProcessTraversal()) {
+ mProcessRecordProcStateNodes.offer(app);
+ } else {
+ final int slot = processStateToSlot(app.mState.getCurProcState());
+ final int prevSlot = processStateToSlot(prevProcState);
+ if (slot != prevSlot) {
+ mProcessRecordProcStateNodes.moveAppTo(app, prevSlot, slot);
+ }
}
}
}
private void updateProcStateSlot(ProcessRecord app, int prevProcState) {
- final int slot = processStateToSlot(app.mState.getCurProcState());
- final int prevSlot = processStateToSlot(prevProcState);
- mProcessRecordProcStateNodes.moveAppTo(app, prevSlot, slot);
+ if (Flags.simplifyProcessTraversal()) {
+ mProcessRecordProcStateNodes.offer(app);
+ } else {
+ final int slot = processStateToSlot(app.mState.getCurProcState());
+ final int prevSlot = processStateToSlot(prevProcState);
+ mProcessRecordProcStateNodes.moveAppTo(app, prevSlot, slot);
+ }
}
@Override
@@ -832,8 +954,15 @@
// Compute initial values, the procState and adj priority queues will be populated here.
computeOomAdjLSP(app, UNKNOWN_ADJ, topApp, true, now, false, false, oomAdjReason,
false);
- updateProcStateSlot(app, prevProcState);
- updateAdjSlot(app, prevAdj);
+
+ if (Flags.simplifyProcessTraversal()) {
+ // Just add to the procState priority queue. The adj priority queue should be
+ // empty going into the traversal step.
+ mProcessRecordProcStateNodes.offer(app);
+ } else {
+ updateProcStateSlot(app, prevProcState);
+ updateAdjSlot(app, prevAdj);
+ }
}
// Set adj last nodes now, this way a process will only be reevaluated during the adj node
@@ -851,14 +980,32 @@
*/
@GuardedBy({"mService", "mProcLock"})
private void computeConnectionsLSP() {
- // 1st pass, scan each slot in the procstate node list.
- for (int i = 0, end = mProcessRecordProcStateNodes.size() - 1; i < end; i++) {
- mProcessRecordProcStateNodes.forEachNewNode(i, mComputeConnectionsConsumer);
- }
+ if (Flags.simplifyProcessTraversal()) {
+ // 1st pass, iterate all nodes in order of procState importance.
+ ProcessRecord proc = mProcessRecordProcStateNodes.poll();
+ while (proc != null) {
+ mTmpOomAdjusterArgs.mApp = proc;
+ mComputeConnectionsConsumer.accept(mTmpOomAdjusterArgs);
+ proc = mProcessRecordProcStateNodes.poll();
+ }
- // 2nd pass, scan each slot in the adj node list.
- for (int i = 0, end = mProcessRecordAdjNodes.size() - 1; i < end; i++) {
- mProcessRecordAdjNodes.forEachNewNode(i, mComputeConnectionsConsumer);
+ // 2st pass, iterate all nodes in order of procState importance.
+ proc = mProcessRecordAdjNodes.poll();
+ while (proc != null) {
+ mTmpOomAdjusterArgs.mApp = proc;
+ mComputeConnectionsConsumer.accept(mTmpOomAdjusterArgs);
+ proc = mProcessRecordAdjNodes.poll();
+ }
+ } else {
+ // 1st pass, scan each slot in the procstate node list.
+ for (int i = 0, end = mProcessRecordProcStateNodes.size() - 1; i < end; i++) {
+ mProcessRecordProcStateNodes.forEachNewNode(i, mComputeConnectionsConsumer);
+ }
+
+ // 2nd pass, scan each slot in the adj node list.
+ for (int i = 0, end = mProcessRecordAdjNodes.size() - 1; i < end; i++) {
+ mProcessRecordAdjNodes.forEachNewNode(i, mComputeConnectionsConsumer);
+ }
}
}
@@ -987,8 +1134,14 @@
args.mApp = reachable;
computeOomAdjIgnoringReachablesLSP(args);
- updateProcStateSlot(reachable, prevProcState);
- updateAdjSlot(reachable, prevAdj);
+ if (Flags.simplifyProcessTraversal()) {
+ // Just add to the procState priority queue. The adj priority queue should be
+ // empty going into the traversal step.
+ mProcessRecordProcStateNodes.offer(reachable);
+ } else {
+ updateProcStateSlot(reachable, prevProcState);
+ updateAdjSlot(reachable, prevAdj);
+ }
}
}
diff --git a/services/core/java/com/android/server/am/PendingIntentRecord.java b/services/core/java/com/android/server/am/PendingIntentRecord.java
index da45a77..8d7a1c9 100644
--- a/services/core/java/com/android/server/am/PendingIntentRecord.java
+++ b/services/core/java/com/android/server/am/PendingIntentRecord.java
@@ -18,6 +18,10 @@
import static android.app.ActivityManager.PROCESS_STATE_TOP;
import static android.app.ActivityManager.START_SUCCESS;
+import static android.app.ActivityOptions.MODE_BACKGROUND_ACTIVITY_START_ALLOWED;
+import static android.app.ActivityOptions.MODE_BACKGROUND_ACTIVITY_START_COMPAT;
+import static android.app.ActivityOptions.MODE_BACKGROUND_ACTIVITY_START_DENIED;
+import static android.app.ActivityOptions.MODE_BACKGROUND_ACTIVITY_START_SYSTEM_DEFINED;
import static com.android.server.am.ActivityManagerDebugConfig.TAG_AM;
import static com.android.server.am.ActivityManagerDebugConfig.TAG_WITH_CLASS_NAME;
@@ -389,13 +393,20 @@
private static BackgroundStartPrivileges getBackgroundStartPrivilegesAllowedByCaller(
@Nullable Bundle options, int callingUid, @Nullable String callingPackage) {
- if (options == null || !options.containsKey(
- ActivityOptions.KEY_PENDING_INTENT_BACKGROUND_ACTIVITY_ALLOWED)) {
+ if (options == null) {
return getDefaultBackgroundStartPrivileges(callingUid, callingPackage);
}
- return options.getBoolean(ActivityOptions.KEY_PENDING_INTENT_BACKGROUND_ACTIVITY_ALLOWED)
- ? BackgroundStartPrivileges.ALLOW_BAL
- : BackgroundStartPrivileges.NONE;
+ switch (options.getInt(ActivityOptions.KEY_PENDING_INTENT_BACKGROUND_ACTIVITY_ALLOWED,
+ MODE_BACKGROUND_ACTIVITY_START_SYSTEM_DEFINED)) {
+ case MODE_BACKGROUND_ACTIVITY_START_DENIED:
+ return BackgroundStartPrivileges.NONE;
+ case MODE_BACKGROUND_ACTIVITY_START_SYSTEM_DEFINED:
+ return getDefaultBackgroundStartPrivileges(callingUid, callingPackage);
+ case MODE_BACKGROUND_ACTIVITY_START_ALLOWED:
+ case MODE_BACKGROUND_ACTIVITY_START_COMPAT:
+ default:
+ return BackgroundStartPrivileges.ALLOW_BAL;
+ }
}
/**
diff --git a/services/core/java/com/android/server/am/ServiceRecord.java b/services/core/java/com/android/server/am/ServiceRecord.java
index 8eca4fc..2184340 100644
--- a/services/core/java/com/android/server/am/ServiceRecord.java
+++ b/services/core/java/com/android/server/am/ServiceRecord.java
@@ -690,10 +690,14 @@
private long mTimeLimitExceededAt = Long.MIN_VALUE;
@UptimeMillisLong
private long mTotalRuntime = 0;
+ private int mNumParallelServices = 0;
- TimeLimitedFgsInfo(@UptimeMillisLong long startTime) {
- mFirstFgsStartUptime = startTime;
- mFirstFgsStartRealtime = SystemClock.elapsedRealtime();
+ public void noteFgsFgsStart(@UptimeMillisLong long startTime) {
+ mNumParallelServices++;
+ if (mNumParallelServices == 1) {
+ mFirstFgsStartUptime = startTime;
+ mFirstFgsStartRealtime = SystemClock.elapsedRealtime();
+ }
mLastFgsStartTime = startTime;
}
@@ -707,17 +711,23 @@
return mFirstFgsStartRealtime;
}
- public void setLastFgsStartTime(@UptimeMillisLong long startTime) {
- mLastFgsStartTime = startTime;
- }
-
@UptimeMillisLong
public long getLastFgsStartTime() {
return mLastFgsStartTime;
}
- public void updateTotalRuntime() {
- mTotalRuntime += SystemClock.uptimeMillis() - mLastFgsStartTime;
+ public void decNumParallelServices() {
+ if (mNumParallelServices > 0) {
+ mNumParallelServices--;
+ }
+ if (mNumParallelServices == 0) {
+ mLastFgsStartTime = 0;
+ }
+ }
+
+ public void updateTotalRuntime(@UptimeMillisLong long nowUptime) {
+ mTotalRuntime += nowUptime - mLastFgsStartTime;
+ mLastFgsStartTime = nowUptime;
}
@UptimeMillisLong
@@ -735,6 +745,7 @@
}
public void reset() {
+ mNumParallelServices = 0;
mFirstFgsStartUptime = 0;
mFirstFgsStartRealtime = 0;
mLastFgsStartTime = 0;
@@ -1872,8 +1883,8 @@
/**
* Called when a time-limited FGS starts.
*/
- public TimeLimitedFgsInfo createTimeLimitedFgsInfo(@UptimeMillisLong long nowUptime) {
- return new TimeLimitedFgsInfo(nowUptime);
+ public TimeLimitedFgsInfo createTimeLimitedFgsInfo() {
+ return new TimeLimitedFgsInfo();
}
/**
diff --git a/services/core/java/com/android/server/am/SettingsToPropertiesMapper.java b/services/core/java/com/android/server/am/SettingsToPropertiesMapper.java
index 827db57..9bf5c21 100644
--- a/services/core/java/com/android/server/am/SettingsToPropertiesMapper.java
+++ b/services/core/java/com/android/server/am/SettingsToPropertiesMapper.java
@@ -29,6 +29,8 @@
import android.provider.Settings;
import android.text.TextUtils;
import android.util.Slog;
+import android.util.proto.ProtoInputStream;
+import android.util.proto.ProtoOutputStream;
import com.android.internal.annotations.VisibleForTesting;
@@ -155,6 +157,7 @@
"car_telemetry",
"codec_fwk",
"companion",
+ "com_android_adbd",
"content_protection",
"context_hub",
"core_experiments_team_internal",
@@ -262,11 +265,11 @@
Uri settingUri = Settings.Global.getUriFor(globalSetting);
String propName = makePropertyName(GLOBAL_SETTINGS_CATEGORY, globalSetting);
if (settingUri == null) {
- log("setting uri is null for globalSetting " + globalSetting);
+ logErr("setting uri is null for globalSetting " + globalSetting);
continue;
}
if (propName == null) {
- log("invalid prop name for globalSetting " + globalSetting);
+ logErr("invalid prop name for globalSetting " + globalSetting);
continue;
}
@@ -294,7 +297,7 @@
for (String key : properties.getKeyset()) {
String propertyName = makePropertyName(scope, key);
if (propertyName == null) {
- log("unable to construct system property for " + scope + "/"
+ logErr("unable to construct system property for " + scope + "/"
+ key);
return;
}
@@ -306,7 +309,7 @@
// sys prop slot can be removed.
String aconfigPropertyName = makeAconfigFlagPropertyName(scope, key);
if (aconfigPropertyName == null) {
- log("unable to construct system property for " + scope + "/"
+ logErr("unable to construct system property for " + scope + "/"
+ key);
return;
}
@@ -324,7 +327,7 @@
for (String key : properties.getKeyset()) {
String aconfigPropertyName = makeAconfigFlagPropertyName(scope, key);
if (aconfigPropertyName == null) {
- log("unable to construct system property for " + scope + "/"
+ logErr("unable to construct system property for " + scope + "/"
+ key);
return;
}
@@ -354,7 +357,7 @@
if (!propertyName.matches(SYSTEM_PROPERTY_VALID_CHARACTERS_REGEX)
|| propertyName.contains(SYSTEM_PROPERTY_INVALID_SUBSTRING)) {
- log("unable to construct system property for " + actualNamespace
+ logErr("unable to construct system property for " + actualNamespace
+ "/" + flagName);
continue;
}
@@ -383,18 +386,18 @@
/**
* apply flag local override in aconfig new storage
- * @param props
- * @return aconfigd socket return
+ * @param requests: request proto output stream
+ * @return aconfigd socket return as proto input stream
*/
- public static StorageReturnMessages sendAconfigdRequests(StorageRequestMessages requests) {
+ static ProtoInputStream sendAconfigdRequests(ProtoOutputStream requests) {
// connect to aconfigd socket
LocalSocket client = new LocalSocket();
try{
client.connect(new LocalSocketAddress(
"aconfigd", LocalSocketAddress.Namespace.RESERVED));
- log("connected to aconfigd socket");
+ Slog.d(TAG, "connected to aconfigd socket");
} catch (IOException ioe) {
- log("failed to connect to aconfigd socket", ioe);
+ logErr("failed to connect to aconfigd socket", ioe);
return null;
}
@@ -404,43 +407,93 @@
inputStream = new DataInputStream(client.getInputStream());
outputStream = new DataOutputStream(client.getOutputStream());
} catch (IOException ioe) {
- log("failed to get local socket iostreams", ioe);
+ logErr("failed to get local socket iostreams", ioe);
return null;
}
// send requests
try {
- byte[] requests_bytes = requests.toByteArray();
+ byte[] requests_bytes = requests.getBytes();
outputStream.writeInt(requests_bytes.length);
outputStream.write(requests_bytes, 0, requests_bytes.length);
- log(requests.getMsgsCount() + " flag override requests sent to aconfigd");
+ Slog.d(TAG, "flag override requests sent to aconfigd");
} catch (IOException ioe) {
- log("failed to send requests to aconfigd", ioe);
+ logErr("failed to send requests to aconfigd", ioe);
return null;
}
// read return
- StorageReturnMessages return_msgs = null;
try {
int num_bytes = inputStream.readInt();
- byte[] buffer = new byte[num_bytes];
- inputStream.read(buffer, 0, num_bytes);
- return_msgs = StorageReturnMessages.parseFrom(buffer);
- log(return_msgs.getMsgsCount() + " acknowledgement received from aconfigd");
+ ProtoInputStream returns = new ProtoInputStream(inputStream);
+ Slog.d(TAG, "received " + num_bytes + " bytes back from aconfigd");
+ return returns;
} catch (IOException ioe) {
- log("failed to read requests return from aconfigd", ioe);
+ logErr("failed to read requests return from aconfigd", ioe);
return null;
}
+ }
- return return_msgs;
+ /**
+ * serialize a flag override request
+ * @param proto
+ */
+ static void writeFlagOverrideRequest(
+ ProtoOutputStream proto, String packageName, String flagName, String flagValue,
+ boolean isLocal) {
+ long msgsToken = proto.start(StorageRequestMessages.MSGS);
+ long msgToken = proto.start(StorageRequestMessage.FLAG_OVERRIDE_MESSAGE);
+ proto.write(StorageRequestMessage.FlagOverrideMessage.PACKAGE_NAME, packageName);
+ proto.write(StorageRequestMessage.FlagOverrideMessage.FLAG_NAME, flagName);
+ proto.write(StorageRequestMessage.FlagOverrideMessage.FLAG_VALUE, flagValue);
+ proto.write(StorageRequestMessage.FlagOverrideMessage.IS_LOCAL, isLocal);
+ proto.end(msgToken);
+ proto.end(msgsToken);
+ }
+
+ /**
+ * deserialize a flag input proto stream and log
+ * @param proto
+ */
+ static void parseAndLogAconfigdReturn(ProtoInputStream proto) throws IOException {
+ while (true) {
+ switch (proto.nextField()) {
+ case (int) StorageReturnMessages.MSGS:
+ long msgsToken = proto.start(StorageReturnMessages.MSGS);
+ switch (proto.nextField()) {
+ case (int) StorageReturnMessage.FLAG_OVERRIDE_MESSAGE:
+ Slog.d(TAG, "successfully handled override requests");
+ long msgToken = proto.start(StorageReturnMessage.FLAG_OVERRIDE_MESSAGE);
+ proto.end(msgToken);
+ break;
+ case (int) StorageReturnMessage.ERROR_MESSAGE:
+ String errmsg = proto.readString(StorageReturnMessage.ERROR_MESSAGE);
+ Slog.d(TAG, "override request failed: " + errmsg);
+ break;
+ case ProtoInputStream.NO_MORE_FIELDS:
+ break;
+ default:
+ logErr("invalid message type, expecting only flag override return or error message");
+ break;
+ }
+ proto.end(msgsToken);
+ break;
+ case ProtoInputStream.NO_MORE_FIELDS:
+ return;
+ default:
+ logErr("invalid message type, expect storage return message");
+ break;
+ }
+ }
}
/**
* apply flag local override in aconfig new storage
* @param props
*/
- public static void setLocalOverridesInNewStorage(DeviceConfig.Properties props) {
- StorageRequestMessages.Builder requests_builder = StorageRequestMessages.newBuilder();
+ static void setLocalOverridesInNewStorage(DeviceConfig.Properties props) {
+ int num_requests = 0;
+ ProtoOutputStream requests = new ProtoOutputStream();
for (String flagName : props.getKeyset()) {
String flagValue = props.getString(flagName, null);
if (flagName == null || flagValue == null) {
@@ -449,32 +502,35 @@
int idx = flagName.indexOf(":");
if (idx == -1 || idx == flagName.length() - 1 || idx == 0) {
- log("invalid local flag override: " + flagName);
+ logErr("invalid local flag override: " + flagName);
continue;
}
String actualNamespace = flagName.substring(0, idx);
String fullFlagName = flagName.substring(idx+1);
idx = fullFlagName.lastIndexOf(".");
if (idx == -1) {
- log("invalid flag name: " + fullFlagName);
+ logErr("invalid flag name: " + fullFlagName);
continue;
}
String packageName = fullFlagName.substring(0, idx);
String realFlagName = fullFlagName.substring(idx+1);
-
- StorageRequestMessage.FlagOverrideMessage.Builder override_msg_builder =
- StorageRequestMessage.FlagOverrideMessage.newBuilder();
- override_msg_builder.setPackageName(packageName);
- override_msg_builder.setFlagName(realFlagName);
- override_msg_builder.setFlagValue(flagValue);
- override_msg_builder.setIsLocal(true);
-
- StorageRequestMessage.Builder request_builder = StorageRequestMessage.newBuilder();
- request_builder.setFlagOverrideMessage(override_msg_builder.build());
- requests_builder.addMsgs(request_builder.build());
+ writeFlagOverrideRequest(requests, packageName, realFlagName, flagValue, true);
+ ++num_requests;
}
- StorageRequestMessages requests = requests_builder.build();
- StorageReturnMessages acks = sendAconfigdRequests(requests);
+
+ if (num_requests == 0) {
+ return;
+ }
+
+ // send requests to aconfigd and obtain the return byte buffer
+ ProtoInputStream returns = sendAconfigdRequests(requests);
+
+ // deserialize back using proto input stream
+ try {
+ parseAndLogAconfigdReturn(returns);
+ } catch (IOException ioe) {
+ logErr("failed to parse aconfigd return", ioe);
+ }
}
public static SettingsToPropertiesMapper start(ContentResolver contentResolver) {
@@ -517,7 +573,7 @@
for (String property_name : property_names) {
String[] segments = property_name.split("\\.");
if (segments.length < 3) {
- log("failed to extract category name from property " + property_name);
+ logErr("failed to extract category name from property " + property_name);
continue;
}
categories.add(segments[2]);
@@ -545,14 +601,16 @@
return propertyName;
}
+
/**
* stage flags in aconfig new storage
* @param propsToStage
*/
@VisibleForTesting
static void stageFlagsInNewStorage(HashMap<String, HashMap<String, String>> propsToStage) {
- // create storage request proto
- StorageRequestMessages.Builder requests_builder = StorageRequestMessages.newBuilder();
+ // write aconfigd requests proto to proto output stream
+ int num_requests = 0;
+ ProtoOutputStream requests = new ProtoOutputStream();
for (HashMap.Entry<String, HashMap<String, String>> entry : propsToStage.entrySet()) {
String actualNamespace = entry.getKey();
HashMap<String, String> flagValuesToStage = entry.getValue();
@@ -560,26 +618,29 @@
String stagedValue = flagValuesToStage.get(fullFlagName);
int idx = fullFlagName.lastIndexOf(".");
if (idx == -1) {
- log("invalid flag name: " + fullFlagName);
+ logErr("invalid flag name: " + fullFlagName);
continue;
}
String packageName = fullFlagName.substring(0, idx);
String flagName = fullFlagName.substring(idx+1);
-
- StorageRequestMessage.FlagOverrideMessage.Builder override_msg_builder =
- StorageRequestMessage.FlagOverrideMessage.newBuilder();
- override_msg_builder.setPackageName(packageName);
- override_msg_builder.setFlagName(flagName);
- override_msg_builder.setFlagValue(stagedValue);
- override_msg_builder.setIsLocal(false);
-
- StorageRequestMessage.Builder request_builder = StorageRequestMessage.newBuilder();
- request_builder.setFlagOverrideMessage(override_msg_builder.build());
- requests_builder.addMsgs(request_builder.build());
+ writeFlagOverrideRequest(requests, packageName, flagName, stagedValue, false);
+ ++num_requests;
}
}
- StorageRequestMessages requests = requests_builder.build();
- StorageReturnMessages acks = sendAconfigdRequests(requests);
+
+ if (num_requests == 0) {
+ return;
+ }
+
+ // send requests to aconfigd and obtain the return
+ ProtoInputStream returns = sendAconfigdRequests(requests);
+
+ // deserialize back using proto input stream
+ try {
+ parseAndLogAconfigdReturn(returns);
+ } catch (IOException ioe) {
+ logErr("failed to parse aconfigd return", ioe);
+ }
}
/**
@@ -620,7 +681,7 @@
for (String flagName : properties.getKeyset()) {
int idx = flagName.indexOf(NAMESPACE_REBOOT_STAGING_DELIMITER);
if (idx == -1 || idx == flagName.length() - 1 || idx == 0) {
- log("invalid staged flag: " + flagName);
+ logErr("invalid staged flag: " + flagName);
continue;
}
String actualNamespace = flagName.substring(0, idx);
@@ -671,7 +732,7 @@
}
value = "";
} else if (value.length() > SYSTEM_PROPERTY_MAX_LENGTH) {
- log("key=" + key + " value=" + value + " exceeds system property max length.");
+ logErr("key=" + key + " value=" + value + " exceeds system property max length.");
return;
}
@@ -681,11 +742,11 @@
// Failure to set a property can be caused by SELinux denial. This usually indicates
// that the property wasn't allowlisted in sepolicy.
// No need to report it on all user devices, only on debug builds.
- log("Unable to set property " + key + " value '" + value + "'", e);
+ logErr("Unable to set property " + key + " value '" + value + "'", e);
}
}
- private static void log(String msg, Exception e) {
+ private static void logErr(String msg, Exception e) {
if (Build.IS_DEBUGGABLE) {
Slog.wtf(TAG, msg, e);
} else {
@@ -693,7 +754,7 @@
}
}
- private static void log(String msg) {
+ private static void logErr(String msg) {
if (Build.IS_DEBUGGABLE) {
Slog.wtf(TAG, msg);
} else {
@@ -711,7 +772,7 @@
br.close();
} catch (IOException ioe) {
- log("failed to read file " + RESET_RECORD_FILE_PATH, ioe);
+ logErr("failed to read file " + RESET_RECORD_FILE_PATH, ioe);
}
return content;
}
diff --git a/services/core/java/com/android/server/am/flags.aconfig b/services/core/java/com/android/server/am/flags.aconfig
index b7108df..afde4f7 100644
--- a/services/core/java/com/android/server/am/flags.aconfig
+++ b/services/core/java/com/android/server/am/flags.aconfig
@@ -123,3 +123,14 @@
description: "Avoid OomAdjuster calculations for connections that won't change importance"
bug: "323376416"
}
+
+flag {
+ name: "simplify_process_traversal"
+ namespace: "backstage_power"
+ description: "Simplify the OomAdjuster's process traversal mechanism."
+ bug: "336178916"
+ is_fixed_read_only: true
+ metadata {
+ purpose: PURPOSE_BUGFIX
+ }
+}
diff --git a/services/core/java/com/android/server/appop/AppOpsService.java b/services/core/java/com/android/server/appop/AppOpsService.java
index 1db3483..147c8d7 100644
--- a/services/core/java/com/android/server/appop/AppOpsService.java
+++ b/services/core/java/com/android/server/appop/AppOpsService.java
@@ -55,6 +55,7 @@
import static android.app.AppOpsManager.SAMPLING_STRATEGY_UNIFORM;
import static android.app.AppOpsManager.SAMPLING_STRATEGY_UNIFORM_OPS;
import static android.app.AppOpsManager.SECURITY_EXCEPTION_ON_INVALID_ATTRIBUTION_TAG_CHANGE;
+import static android.app.AppOpsManager.UID_STATE_NONEXISTENT;
import static android.app.AppOpsManager.WATCH_FOREGROUND_CHANGES;
import static android.app.AppOpsManager._NUM_OP;
import static android.app.AppOpsManager.extractFlagsFromKey;
@@ -70,7 +71,6 @@
import static android.content.Intent.EXTRA_REPLACING;
import static android.content.pm.PermissionInfo.PROTECTION_DANGEROUS;
import static android.content.pm.PermissionInfo.PROTECTION_FLAG_APPOP;
-import static android.permission.flags.Flags.runtimePermissionAppopsMappingEnabled;
import static com.android.server.appop.AppOpsService.ModeCallback.ALL_OPS;
@@ -130,6 +130,7 @@
import android.os.UserHandle;
import android.os.storage.StorageManagerInternal;
import android.permission.PermissionManager;
+import android.permission.flags.Flags;
import android.provider.Settings;
import android.util.ArrayMap;
import android.util.ArraySet;
@@ -140,6 +141,7 @@
import android.util.SparseArray;
import android.util.SparseBooleanArray;
import android.util.SparseIntArray;
+import android.util.SparseLongArray;
import android.util.TimeUtils;
import android.util.Xml;
@@ -153,7 +155,6 @@
import com.android.internal.app.IAppOpsService;
import com.android.internal.app.IAppOpsStartedCallback;
import com.android.internal.app.MessageSamplingConfig;
-import com.android.internal.camera.flags.Flags;
import com.android.internal.compat.IPlatformCompat;
import com.android.internal.os.Clock;
import com.android.internal.pm.pkg.component.ParsedAttribution;
@@ -1109,12 +1110,12 @@
final String changedPkg = changedPkgs[i];
// We trust packagemanager to insert matching uid and packageNames in the
// extras
- Set<String> devices;
+ Set<String> devices = new ArraySet<>();
+ devices.add(PERSISTENT_DEVICE_ID_DEFAULT);
+
if (mVirtualDeviceManagerInternal != null) {
- devices = mVirtualDeviceManagerInternal.getAllPersistentDeviceIds();
- } else {
- devices = new ArraySet<>();
- devices.add(PERSISTENT_DEVICE_ID_DEFAULT);
+ devices.addAll(
+ mVirtualDeviceManagerInternal.getAllPersistentDeviceIds());
}
for (String device: devices) {
notifyOpChanged(onModeChangedListeners, code, changedUid, changedPkg,
@@ -1421,6 +1422,9 @@
// The callback method from AppOpsUidStateTracker
private void onUidStateChanged(int uid, int state, boolean foregroundModeMayChange) {
synchronized (this) {
+ if (state == UID_STATE_NONEXISTENT) {
+ onUidProcessDeathLocked(uid);
+ }
UidState uidState = getUidStateLocked(uid, false);
boolean hasForegroundWatchers = false;
@@ -1508,6 +1512,11 @@
}
}
+ if (state == UID_STATE_NONEXISTENT) {
+ // For UID_STATE_NONEXISTENT, we don't call onUidStateChanged for AttributedOps
+ return;
+ }
+
if (uidState != null) {
int numPkgs = uidState.pkgOps.size();
for (int pkgNum = 0; pkgNum < numPkgs; pkgNum++) {
@@ -1532,6 +1541,81 @@
}
}
+ @GuardedBy("this")
+ private void onUidProcessDeathLocked(int uid) {
+ if (!mUidStates.contains(uid) || !Flags.finishRunningOpsForKilledPackages()) {
+ return;
+ }
+ final SparseLongArray chainsToFinish = new SparseLongArray();
+ doForAllAttributedOpsInUidLocked(uid, (attributedOp) -> {
+ attributedOp.doForAllInProgressStartOpEvents((event) -> {
+ int chainId = event.getAttributionChainId();
+ if (chainId != ATTRIBUTION_CHAIN_ID_NONE) {
+ long currentEarliestStartTime =
+ chainsToFinish.get(chainId, Long.MAX_VALUE);
+ if (event.getStartTime() < currentEarliestStartTime) {
+ // Store the earliest chain link we're finishing, so that we can go back
+ // and finish any links in the chain that started after this one
+ chainsToFinish.put(chainId, event.getStartTime());
+ }
+ }
+ attributedOp.finished(event.getClientId());
+ });
+ });
+ finishChainsLocked(chainsToFinish);
+ }
+
+ @GuardedBy("this")
+ private void finishChainsLocked(SparseLongArray chainsToFinish) {
+ doForAllAttributedOpsLocked((attributedOp) -> {
+ attributedOp.doForAllInProgressStartOpEvents((event) -> {
+ int chainId = event.getAttributionChainId();
+ // If this event is part of a chain, and this event started after the event in the
+ // chain we already finished, then finish this event, too
+ long earliestEventStart = chainsToFinish.get(chainId, Long.MAX_VALUE);
+ if (chainId != ATTRIBUTION_CHAIN_ID_NONE
+ && event.getStartTime() >= earliestEventStart) {
+ attributedOp.finished(event.getClientId());
+ }
+ });
+ });
+ }
+
+ @GuardedBy("this")
+ private void doForAllAttributedOpsLocked(Consumer<AttributedOp> action) {
+ int numUids = mUidStates.size();
+ for (int uidNum = 0; uidNum < numUids; uidNum++) {
+ int uid = mUidStates.keyAt(uidNum);
+ doForAllAttributedOpsInUidLocked(uid, action);
+ }
+ }
+
+ @GuardedBy("this")
+ private void doForAllAttributedOpsInUidLocked(int uid, Consumer<AttributedOp> action) {
+ UidState uidState = mUidStates.get(uid);
+ if (uidState == null) {
+ return;
+ }
+
+ int numPkgs = uidState.pkgOps.size();
+ for (int pkgNum = 0; pkgNum < numPkgs; pkgNum++) {
+ Ops ops = uidState.pkgOps.valueAt(pkgNum);
+ int numOps = ops.size();
+ for (int opNum = 0; opNum < numOps; opNum++) {
+ Op op = ops.valueAt(opNum);
+ int numDevices = op.mDeviceAttributedOps.size();
+ for (int deviceNum = 0; deviceNum < numDevices; deviceNum++) {
+ ArrayMap<String, AttributedOp> attrOps =
+ op.mDeviceAttributedOps.valueAt(deviceNum);
+ int numAttributions = attrOps.size();
+ for (int attrNum = 0; attrNum < numAttributions; attrNum++) {
+ action.accept(attrOps.valueAt(attrNum));
+ }
+ }
+ }
+ }
+ }
+
/**
* Notify the proc state or capability has changed for a certain UID.
*/
@@ -2525,12 +2609,10 @@
ArrayList<ChangeRec> reports = ent.getValue();
for (int i=0; i<reports.size(); i++) {
ChangeRec rep = reports.get(i);
- Set<String> devices;
+ Set<String> devices = new ArraySet<>();
+ devices.add(PERSISTENT_DEVICE_ID_DEFAULT);
if (mVirtualDeviceManagerInternal != null) {
- devices = mVirtualDeviceManagerInternal.getAllPersistentDeviceIds();
- } else {
- devices = new ArraySet<>();
- devices.add(PERSISTENT_DEVICE_ID_DEFAULT);
+ devices.addAll(mVirtualDeviceManagerInternal.getAllPersistentDeviceIds());
}
for (String device: devices) {
mHandler.sendMessage(PooledLambda.obtainMessage(
@@ -2702,7 +2784,7 @@
* have information on them.
*/
private static boolean isOpAllowedForUid(int uid) {
- return runtimePermissionAppopsMappingEnabled()
+ return Flags.runtimePermissionAppopsMappingEnabled()
&& (uid == Process.ROOT_UID || uid == Process.SYSTEM_UID);
}
@@ -4775,8 +4857,8 @@
if ((code == OP_CAMERA) && isAutomotive()) {
final long identity = Binder.clearCallingIdentity();
try {
- if ((Flags.cameraPrivacyAllowlist())
- && (mSensorPrivacyManager.isCameraPrivacyEnabled(packageName))) {
+ if (com.android.internal.camera.flags.Flags.cameraPrivacyAllowlist()
+ && mSensorPrivacyManager.isCameraPrivacyEnabled(packageName)) {
return true;
}
} finally {
diff --git a/services/core/java/com/android/server/appop/AppOpsUidStateTracker.java b/services/core/java/com/android/server/appop/AppOpsUidStateTracker.java
index 18ea8cf..268b286 100644
--- a/services/core/java/com/android/server/appop/AppOpsUidStateTracker.java
+++ b/services/core/java/com/android/server/appop/AppOpsUidStateTracker.java
@@ -68,6 +68,7 @@
return UID_STATE_BACKGROUND;
}
+ // UID_STATE_NONEXISTENT is deliberately excluded here
return UID_STATE_CACHED;
}
diff --git a/services/core/java/com/android/server/appop/AppOpsUidStateTrackerImpl.java b/services/core/java/com/android/server/appop/AppOpsUidStateTrackerImpl.java
index bc6ef20..03c8156 100644
--- a/services/core/java/com/android/server/appop/AppOpsUidStateTrackerImpl.java
+++ b/services/core/java/com/android/server/appop/AppOpsUidStateTrackerImpl.java
@@ -34,7 +34,9 @@
import static android.app.AppOpsManager.OP_TAKE_AUDIO_FOCUS;
import static android.app.AppOpsManager.UID_STATE_FOREGROUND_SERVICE;
import static android.app.AppOpsManager.UID_STATE_MAX_LAST_NON_RESTRICTED;
+import static android.app.AppOpsManager.UID_STATE_NONEXISTENT;
import static android.app.AppOpsManager.UID_STATE_TOP;
+import static android.permission.flags.Flags.finishRunningOpsForKilledPackages;
import static com.android.server.appop.AppOpsUidStateTracker.processStateToUidState;
@@ -343,13 +345,14 @@
int capability = mCapability.get(uid, PROCESS_CAPABILITY_NONE);
boolean appWidgetVisible = mAppWidgetVisible.get(uid, false);
+ boolean foregroundChange = uidState <= UID_STATE_MAX_LAST_NON_RESTRICTED
+ != pendingUidState <= UID_STATE_MAX_LAST_NON_RESTRICTED
+ || capability != pendingCapability
+ || appWidgetVisible != pendingAppWidgetVisible;
+
if (uidState != pendingUidState
|| capability != pendingCapability
|| appWidgetVisible != pendingAppWidgetVisible) {
- boolean foregroundChange = uidState <= UID_STATE_MAX_LAST_NON_RESTRICTED
- != pendingUidState <= UID_STATE_MAX_LAST_NON_RESTRICTED
- || capability != pendingCapability
- || appWidgetVisible != pendingAppWidgetVisible;
if (foregroundChange) {
// To save on memory usage, log only interesting changes.
@@ -372,6 +375,16 @@
mCapability.delete(uid);
mAppWidgetVisible.delete(uid);
mPendingGone.delete(uid);
+ if (finishRunningOpsForKilledPackages()) {
+ for (int i = 0; i < mUidStateChangedCallbacks.size(); i++) {
+ UidStateChangedCallback cb = mUidStateChangedCallbacks.keyAt(i);
+ Executor executor = mUidStateChangedCallbacks.valueAt(i);
+
+ executor.execute(PooledLambda.obtainRunnable(
+ UidStateChangedCallback::onUidStateChanged, cb, uid,
+ UID_STATE_NONEXISTENT, foregroundChange));
+ }
+ }
} else {
mUidStates.put(uid, pendingUidState);
mCapability.put(uid, pendingCapability);
diff --git a/services/core/java/com/android/server/appop/AttributedOp.java b/services/core/java/com/android/server/appop/AttributedOp.java
index 2760ccf..02fc993 100644
--- a/services/core/java/com/android/server/appop/AttributedOp.java
+++ b/services/core/java/com/android/server/appop/AttributedOp.java
@@ -38,6 +38,7 @@
import java.util.ArrayList;
import java.util.List;
import java.util.NoSuchElementException;
+import java.util.function.Consumer;
final class AttributedOp {
private final @NonNull AppOpsService mAppOpsService;
@@ -256,6 +257,19 @@
}
}
+ public void doForAllInProgressStartOpEvents(Consumer<InProgressStartOpEvent> action) {
+ ArrayMap<IBinder, AttributedOp.InProgressStartOpEvent> events = isPaused()
+ ? mPausedInProgressEvents : mInProgressEvents;
+ if (events == null) {
+ return;
+ }
+
+ int numStartedOps = events.size();
+ for (int i = 0; i < numStartedOps; i++) {
+ action.accept(events.valueAt(i));
+ }
+ }
+
/**
* Update state when finishOp was called. Will finish started ops, and delete paused ops.
*
diff --git a/services/core/java/com/android/server/audio/AudioService.java b/services/core/java/com/android/server/audio/AudioService.java
index c310822..15c5c10 100644
--- a/services/core/java/com/android/server/audio/AudioService.java
+++ b/services/core/java/com/android/server/audio/AudioService.java
@@ -4440,7 +4440,8 @@
|| usage == AudioAttributes.USAGE_VOICE_COMMUNICATION_SIGNALLING) {
voiceActive = true;
}
- if (usage == AudioAttributes.USAGE_MEDIA || usage == AudioAttributes.USAGE_GAME) {
+ if (usage == AudioAttributes.USAGE_MEDIA || usage == AudioAttributes.USAGE_GAME
+ || usage == AudioAttributes.USAGE_UNKNOWN) {
mediaActive = true;
}
}
diff --git a/services/core/java/com/android/server/audio/SpatializerHelper.java b/services/core/java/com/android/server/audio/SpatializerHelper.java
index e2c4b46..cae1695 100644
--- a/services/core/java/com/android/server/audio/SpatializerHelper.java
+++ b/services/core/java/com/android/server/audio/SpatializerHelper.java
@@ -347,9 +347,6 @@
//------------------------------------------------------
// routing monitoring
synchronized void onRoutingUpdated() {
- if (!mFeatureEnabled) {
- return;
- }
switch (mState) {
case STATE_UNINITIALIZED:
case STATE_NOT_SUPPORTED:
@@ -393,7 +390,7 @@
setDispatchAvailableState(false);
}
- boolean enabled = able && enabledAvailable.first;
+ boolean enabled = mFeatureEnabled && able && enabledAvailable.first;
if (enabled) {
loglogi("Enabling Spatial Audio since enabled for media device:"
+ currentDevice);
diff --git a/services/core/java/com/android/server/biometrics/sensors/BiometricNotificationUtils.java b/services/core/java/com/android/server/biometrics/sensors/BiometricNotificationUtils.java
index eaa5e2a..53e6bdb 100644
--- a/services/core/java/com/android/server/biometrics/sensors/BiometricNotificationUtils.java
+++ b/services/core/java/com/android/server/biometrics/sensors/BiometricNotificationUtils.java
@@ -238,7 +238,7 @@
showNotificationHelper(context, name, title, content, setupPendingIntent, setupAction,
notNowAction, Notification.CATEGORY_SYSTEM, channel, tag,
- Notification.VISIBILITY_SECRET, false);
+ Notification.VISIBILITY_SECRET, false, Notification.FLAG_NO_CLEAR);
}
private static String getFingerprintDanglingContentString(Context context,
@@ -296,13 +296,13 @@
String notificationTag, int visibility, boolean listenToDismissEvent) {
showNotificationHelper(context, name, title, content, pendingIntent,
null /* positiveAction */, null /* negativeAction */, category, channelName,
- notificationTag, visibility, listenToDismissEvent);
+ notificationTag, visibility, listenToDismissEvent, 0);
}
private static void showNotificationHelper(Context context, String name, String title,
String content, PendingIntent pendingIntent, Notification.Action positiveAction,
Notification.Action negativeAction, String category, String channelName,
- String notificationTag, int visibility, boolean listenToDismissEvent) {
+ String notificationTag, int visibility, boolean listenToDismissEvent, int flags) {
Slog.v(TAG," listenToDismissEvent = " + listenToDismissEvent);
final PendingIntent dismissIntent = PendingIntent.getActivityAsUser(context,
0 /* requestCode */, DISMISS_FRR_INTENT, PendingIntent.FLAG_IMMUTABLE /* flags */,
@@ -324,6 +324,9 @@
.setContentIntent(pendingIntent)
.setVisibility(visibility);
+ if (flags > 0) {
+ builder.setFlag(flags, true);
+ }
if (positiveAction != null) {
builder.addAction(positiveAction);
}
diff --git a/services/core/java/com/android/server/display/AutomaticBrightnessController.java b/services/core/java/com/android/server/display/AutomaticBrightnessController.java
index d9c3ab8..30d12e6 100644
--- a/services/core/java/com/android/server/display/AutomaticBrightnessController.java
+++ b/services/core/java/com/android/server/display/AutomaticBrightnessController.java
@@ -1189,7 +1189,11 @@
update();
}
- void switchMode(@AutomaticBrightnessMode int mode) {
+ /**
+ * Responsible for switching the AutomaticBrightnessMode of the associated display. Also takes
+ * care of resetting the short term model wherever required
+ */
+ public void switchMode(@AutomaticBrightnessMode int mode) {
if (!mBrightnessMappingStrategyMap.contains(mode)) {
return;
}
diff --git a/services/core/java/com/android/server/display/DisplayPowerController.java b/services/core/java/com/android/server/display/DisplayPowerController.java
index 70a1014..0fcdf19 100644
--- a/services/core/java/com/android/server/display/DisplayPowerController.java
+++ b/services/core/java/com/android/server/display/DisplayPowerController.java
@@ -84,6 +84,7 @@
import com.android.server.display.brightness.DisplayBrightnessController;
import com.android.server.display.brightness.clamper.BrightnessClamperController;
import com.android.server.display.brightness.strategy.AutomaticBrightnessStrategy2;
+import com.android.server.display.brightness.strategy.DisplayBrightnessStrategyConstants;
import com.android.server.display.color.ColorDisplayService.ColorDisplayServiceInternal;
import com.android.server.display.color.ColorDisplayService.ReduceBrightColorsListener;
import com.android.server.display.config.HysteresisLevels;
@@ -797,7 +798,7 @@
return;
}
mDisplayStateController.overrideDozeScreenState(displayState, reason);
- sendUpdatePowerState();
+ updatePowerState();
}, mClock.uptimeMillis());
}
@@ -1333,12 +1334,6 @@
mDisplayStateController.shouldPerformScreenOffTransition());
state = mPowerState.getScreenState();
- // Switch to doze auto-brightness mode if needed
- if (mFlags.areAutoBrightnessModesEnabled() && mAutomaticBrightnessController != null
- && !mAutomaticBrightnessController.isInIdleMode()) {
- mAutomaticBrightnessController.switchMode(Display.isDozeState(state)
- ? AUTO_BRIGHTNESS_MODE_DOZE : AUTO_BRIGHTNESS_MODE_DEFAULT);
- }
DisplayBrightnessState displayBrightnessState = mDisplayBrightnessController
.updateBrightness(mPowerRequest, state);
@@ -1372,6 +1367,13 @@
final boolean wasShortTermModelActive =
mAutomaticBrightnessStrategy.isShortTermModelActive();
if (!mFlags.isRefactorDisplayPowerControllerEnabled()) {
+ // Switch to doze auto-brightness mode if needed
+ if (mFlags.areAutoBrightnessModesEnabled() && mAutomaticBrightnessController != null
+ && !mAutomaticBrightnessController.isInIdleMode()) {
+ mAutomaticBrightnessController.switchMode(Display.isDozeState(state)
+ ? AUTO_BRIGHTNESS_MODE_DOZE : AUTO_BRIGHTNESS_MODE_DEFAULT);
+ }
+
mAutomaticBrightnessStrategy.setAutoBrightnessState(state,
mDisplayBrightnessController.isAllowAutoBrightnessWhileDozingConfig(),
mBrightnessReasonTemp.getReason(), mPowerRequest.policy,
@@ -1440,45 +1442,52 @@
brightnessState = clampScreenBrightness(brightnessState);
}
- // If there's an offload session, we need to set the initial doze brightness before
- // the offload session starts controlling the brightness.
- // During the transition DOZE_SUSPEND -> DOZE -> DOZE_SUSPEND, this brightness strategy
- // will be selected again, meaning that no new brightness will be sent to the hardware and
- // the display will stay at the brightness level set by the offload session.
- if (Float.isNaN(brightnessState) && mFlags.isDisplayOffloadEnabled()
- && Display.isDozeState(state) && mDisplayOffloadSession != null) {
- if (mAutomaticBrightnessController != null
- && mAutomaticBrightnessStrategy.shouldUseAutoBrightness()) {
- // Use the auto-brightness curve and the last observed lux
- rawBrightnessState = mAutomaticBrightnessController
- .getAutomaticScreenBrightnessBasedOnLastUsedLux(
- mTempBrightnessEvent);
- } else {
- rawBrightnessState = getDozeBrightnessForOffload();
- mTempBrightnessEvent.setFlags(mTempBrightnessEvent.getFlags()
- | BrightnessEvent.FLAG_DOZE_SCALE);
- }
-
- if (BrightnessUtils.isValidBrightnessValue(rawBrightnessState)) {
- brightnessState = clampScreenBrightness(rawBrightnessState);
- mBrightnessReasonTemp.setReason(BrightnessReason.REASON_DOZE_INITIAL);
-
+ if (Display.isDozeState(state)) {
+ // If there's an offload session, we need to set the initial doze brightness before
+ // the offload session starts controlling the brightness.
+ // During the transition DOZE_SUSPEND -> DOZE -> DOZE_SUSPEND, this brightness strategy
+ // will be selected again, meaning that no new brightness will be sent to the hardware
+ // and the display will stay at the brightness level set by the offload session.
+ if ((Float.isNaN(brightnessState)
+ || displayBrightnessState.getDisplayBrightnessStrategyName()
+ .equals(DisplayBrightnessStrategyConstants.FALLBACK_BRIGHTNESS_STRATEGY_NAME))
+ && mFlags.isDisplayOffloadEnabled()
+ && mDisplayOffloadSession != null) {
if (mAutomaticBrightnessController != null
&& mAutomaticBrightnessStrategy.shouldUseAutoBrightness()) {
- // Keep the brightness in the setting so that we can use it after the screen
- // turns on, until a lux sample becomes available. We don't do this when
- // auto-brightness is disabled - in that situation we still want to use
- // the last brightness from when the screen was on.
- updateScreenBrightnessSetting = currentBrightnessSetting != brightnessState;
+ // Use the auto-brightness curve and the last observed lux
+ rawBrightnessState = mAutomaticBrightnessController
+ .getAutomaticScreenBrightnessBasedOnLastUsedLux(
+ mTempBrightnessEvent);
+ } else {
+ rawBrightnessState = getDozeBrightnessForOffload();
+ mTempBrightnessEvent.setFlags(mTempBrightnessEvent.getFlags()
+ | BrightnessEvent.FLAG_DOZE_SCALE);
+ }
+
+ if (BrightnessUtils.isValidBrightnessValue(rawBrightnessState)) {
+ brightnessState = clampScreenBrightness(rawBrightnessState);
+ mBrightnessReasonTemp.setReason(BrightnessReason.REASON_DOZE_INITIAL);
+
+ if (mAutomaticBrightnessController != null
+ && mAutomaticBrightnessStrategy.shouldUseAutoBrightness()) {
+ // Keep the brightness in the setting so that we can use it after the screen
+ // turns on, until a lux sample becomes available. We don't do this when
+ // auto-brightness is disabled - in that situation we still want to use
+ // the last brightness from when the screen was on.
+ updateScreenBrightnessSetting = currentBrightnessSetting != brightnessState;
+ }
}
}
- }
- // Use default brightness when dozing unless overridden.
- if (Float.isNaN(brightnessState) && Display.isDozeState(state)) {
- rawBrightnessState = mScreenBrightnessDozeConfig;
- brightnessState = clampScreenBrightness(rawBrightnessState);
- mBrightnessReasonTemp.setReason(BrightnessReason.REASON_DOZE_DEFAULT);
+ // Use default brightness when dozing unless overridden.
+ if (Float.isNaN(brightnessState)
+ || displayBrightnessState.getDisplayBrightnessStrategyName()
+ .equals(DisplayBrightnessStrategyConstants.FALLBACK_BRIGHTNESS_STRATEGY_NAME)) {
+ rawBrightnessState = mScreenBrightnessDozeConfig;
+ brightnessState = clampScreenBrightness(rawBrightnessState);
+ mBrightnessReasonTemp.setReason(BrightnessReason.REASON_DOZE_DEFAULT);
+ }
}
if (!mFlags.isRefactorDisplayPowerControllerEnabled()) {
@@ -1502,7 +1511,7 @@
}
// Apply manual brightness.
- if (Float.isNaN(brightnessState)) {
+ if (Float.isNaN(brightnessState) && !mFlags.isRefactorDisplayPowerControllerEnabled()) {
rawBrightnessState = currentBrightnessSetting;
brightnessState = clampScreenBrightness(rawBrightnessState);
if (brightnessState != currentBrightnessSetting) {
diff --git a/services/core/java/com/android/server/display/brightness/DisplayBrightnessStrategySelector.java b/services/core/java/com/android/server/display/brightness/DisplayBrightnessStrategySelector.java
index 22a21a6..feec4e6 100644
--- a/services/core/java/com/android/server/display/brightness/DisplayBrightnessStrategySelector.java
+++ b/services/core/java/com/android/server/display/brightness/DisplayBrightnessStrategySelector.java
@@ -32,6 +32,7 @@
import com.android.server.display.brightness.strategy.BoostBrightnessStrategy;
import com.android.server.display.brightness.strategy.DisplayBrightnessStrategy;
import com.android.server.display.brightness.strategy.DozeBrightnessStrategy;
+import com.android.server.display.brightness.strategy.FallbackBrightnessStrategy;
import com.android.server.display.brightness.strategy.FollowerBrightnessStrategy;
import com.android.server.display.brightness.strategy.InvalidBrightnessStrategy;
import com.android.server.display.brightness.strategy.OffloadBrightnessStrategy;
@@ -85,6 +86,9 @@
@Nullable
private final AutoBrightnessFallbackStrategy mAutoBrightnessFallbackStrategy;
+ @Nullable
+ private final FallbackBrightnessStrategy mFallbackBrightnessStrategy;
+
// A collective representation of all the strategies that the selector is aware of. This is
// non null, but the strategies this is tracking can be null
@NonNull
@@ -118,7 +122,8 @@
mInvalidBrightnessStrategy = injector.getInvalidBrightnessStrategy();
mAutomaticBrightnessStrategy1 =
(!mDisplayManagerFlags.isRefactorDisplayPowerControllerEnabled()) ? null
- : injector.getAutomaticBrightnessStrategy1(context, displayId);
+ : injector.getAutomaticBrightnessStrategy1(context, displayId,
+ mDisplayManagerFlags);
mAutomaticBrightnessStrategy2 =
(mDisplayManagerFlags.isRefactorDisplayPowerControllerEnabled()) ? null
: injector.getAutomaticBrightnessStrategy2(context, displayId);
@@ -134,11 +139,14 @@
} else {
mOffloadBrightnessStrategy = null;
}
+ mFallbackBrightnessStrategy = (mDisplayManagerFlags
+ .isRefactorDisplayPowerControllerEnabled())
+ ? injector.getFallbackBrightnessStrategy() : null;
mDisplayBrightnessStrategies = new DisplayBrightnessStrategy[]{mInvalidBrightnessStrategy,
mScreenOffBrightnessStrategy, mDozeBrightnessStrategy, mFollowerBrightnessStrategy,
mBoostBrightnessStrategy, mOverrideBrightnessStrategy, mTemporaryBrightnessStrategy,
mAutomaticBrightnessStrategy1, mOffloadBrightnessStrategy,
- mAutoBrightnessFallbackStrategy};
+ mAutoBrightnessFallbackStrategy, mFallbackBrightnessStrategy};
mAllowAutoBrightnessWhileDozingConfig = context.getResources().getBoolean(
R.bool.config_allowAutoBrightnessWhileDozing);
mOldBrightnessStrategyName = mInvalidBrightnessStrategy.getName();
@@ -179,6 +187,12 @@
displayBrightnessStrategy = mOffloadBrightnessStrategy;
} else if (isAutoBrightnessFallbackStrategyValid()) {
displayBrightnessStrategy = mAutoBrightnessFallbackStrategy;
+ } else {
+ // This will become the ultimate fallback strategy once the flag has been fully rolled
+ // out
+ if (mDisplayManagerFlags.isRefactorDisplayPowerControllerEnabled()) {
+ displayBrightnessStrategy = mFallbackBrightnessStrategy;
+ }
}
if (mDisplayManagerFlags.isRefactorDisplayPowerControllerEnabled()) {
@@ -330,8 +344,8 @@
}
AutomaticBrightnessStrategy getAutomaticBrightnessStrategy1(Context context,
- int displayId) {
- return new AutomaticBrightnessStrategy(context, displayId);
+ int displayId, DisplayManagerFlags displayManagerFlags) {
+ return new AutomaticBrightnessStrategy(context, displayId, displayManagerFlags);
}
AutomaticBrightnessStrategy2 getAutomaticBrightnessStrategy2(Context context,
@@ -347,5 +361,9 @@
AutoBrightnessFallbackStrategy getAutoBrightnessFallbackStrategy() {
return new AutoBrightnessFallbackStrategy(/* injector= */ null);
}
+
+ FallbackBrightnessStrategy getFallbackBrightnessStrategy() {
+ return new FallbackBrightnessStrategy();
+ }
}
}
diff --git a/services/core/java/com/android/server/display/brightness/strategy/AutomaticBrightnessStrategy.java b/services/core/java/com/android/server/display/brightness/strategy/AutomaticBrightnessStrategy.java
index 2305228..37b6931 100644
--- a/services/core/java/com/android/server/display/brightness/strategy/AutomaticBrightnessStrategy.java
+++ b/services/core/java/com/android/server/display/brightness/strategy/AutomaticBrightnessStrategy.java
@@ -17,6 +17,9 @@
import static android.hardware.display.DisplayManagerInternal.DisplayPowerRequest.POLICY_DOZE;
+import static com.android.server.display.AutomaticBrightnessController.AUTO_BRIGHTNESS_MODE_DEFAULT;
+import static com.android.server.display.AutomaticBrightnessController.AUTO_BRIGHTNESS_MODE_DOZE;
+
import android.annotation.Nullable;
import android.content.Context;
import android.hardware.display.BrightnessConfiguration;
@@ -33,6 +36,7 @@
import com.android.server.display.brightness.BrightnessUtils;
import com.android.server.display.brightness.StrategyExecutionRequest;
import com.android.server.display.brightness.StrategySelectionNotifyRequest;
+import com.android.server.display.feature.DisplayManagerFlags;
import java.io.PrintWriter;
@@ -98,19 +102,24 @@
private Injector mInjector;
+ private DisplayManagerFlags mDisplayManagerFlags;
+
@VisibleForTesting
- AutomaticBrightnessStrategy(Context context, int displayId, Injector injector) {
+ AutomaticBrightnessStrategy(Context context, int displayId, Injector injector,
+ DisplayManagerFlags displayManagerFlags) {
super(context, displayId);
mContext = context;
mDisplayId = displayId;
mAutoBrightnessAdjustment = getAutoBrightnessAdjustmentSetting();
mPendingAutoBrightnessAdjustment = PowerManager.BRIGHTNESS_INVALID_FLOAT;
mTemporaryAutoBrightnessAdjustment = PowerManager.BRIGHTNESS_INVALID_FLOAT;
+ mDisplayManagerFlags = displayManagerFlags;
mInjector = (injector == null) ? new RealInjector() : injector;
}
- public AutomaticBrightnessStrategy(Context context, int displayId) {
- this(context, displayId, null);
+ public AutomaticBrightnessStrategy(Context context, int displayId,
+ DisplayManagerFlags displayManagerFlags) {
+ this(context, displayId, null, displayManagerFlags);
}
/**
@@ -120,6 +129,7 @@
public void setAutoBrightnessState(int targetDisplayState,
boolean allowAutoBrightnessWhileDozingConfig, int brightnessReason, int policy,
float lastUserSetScreenBrightness, boolean userSetBrightnessChanged) {
+ switchMode(targetDisplayState);
final boolean autoBrightnessEnabledInDoze =
allowAutoBrightnessWhileDozingConfig && policy == POLICY_DOZE;
mIsAutoBrightnessEnabled = shouldUseAutoBrightness()
@@ -155,9 +165,8 @@
public boolean isAutoBrightnessValid() {
boolean isValid = false;
if (isAutoBrightnessEnabled()) {
- float brightness = (mAutomaticBrightnessController != null)
- ? mAutomaticBrightnessController.getAutomaticScreenBrightness(null)
- : PowerManager.BRIGHTNESS_INVALID_FLOAT;
+ float brightness = getAutomaticScreenBrightness(null,
+ /* isAutomaticBrightnessAdjusted = */ false);
if (BrightnessUtils.isValidBrightnessValue(brightness)
|| brightness == PowerManager.BRIGHTNESS_OFF_FLOAT) {
isValid = true;
@@ -264,7 +273,12 @@
BrightnessReason brightnessReason = new BrightnessReason();
brightnessReason.setReason(BrightnessReason.REASON_AUTOMATIC);
BrightnessEvent brightnessEvent = mInjector.getBrightnessEvent(mDisplayId);
- float brightness = getAutomaticScreenBrightness(brightnessEvent);
+
+ // AutoBrightness adjustments were already applied while checking the validity of this
+ // strategy. Reapplying them again will result in incorrect adjustment reason flags as we
+ // might end up assuming no adjustments are applied
+ float brightness = getAutomaticScreenBrightness(brightnessEvent,
+ /* isAutomaticBrightnessAdjusted = */ true);
return new DisplayBrightnessState.Builder()
.setBrightness(brightness)
.setSdrBrightness(brightness)
@@ -345,11 +359,14 @@
* @param brightnessEvent Event object to populate with details about why the specific
* brightness was chosen.
*/
- public float getAutomaticScreenBrightness(BrightnessEvent brightnessEvent) {
+ public float getAutomaticScreenBrightness(BrightnessEvent brightnessEvent,
+ boolean isAutomaticBrightnessAdjusted) {
float brightness = (mAutomaticBrightnessController != null)
? mAutomaticBrightnessController.getAutomaticScreenBrightness(brightnessEvent)
: PowerManager.BRIGHTNESS_INVALID_FLOAT;
- adjustAutomaticBrightnessStateIfValid(brightness);
+ if (!isAutomaticBrightnessAdjusted) {
+ adjustAutomaticBrightnessStateIfValid(brightness);
+ }
return brightness;
}
@@ -479,6 +496,16 @@
}
}
+
+ private void switchMode(int state) {
+ if (mDisplayManagerFlags.areAutoBrightnessModesEnabled()
+ && mAutomaticBrightnessController != null
+ && !mAutomaticBrightnessController.isInIdleMode()) {
+ mAutomaticBrightnessController.switchMode(Display.isDozeState(state)
+ ? AUTO_BRIGHTNESS_MODE_DOZE : AUTO_BRIGHTNESS_MODE_DEFAULT);
+ }
+ }
+
/**
* Evaluates if there are any temporary auto-brightness adjustments which is not applied yet.
* Temporary brightness adjustments happen when the user moves the brightness slider in the
diff --git a/services/core/java/com/android/server/display/brightness/strategy/DisplayBrightnessStrategyConstants.java b/services/core/java/com/android/server/display/brightness/strategy/DisplayBrightnessStrategyConstants.java
index 504683a..7b2f2b9 100644
--- a/services/core/java/com/android/server/display/brightness/strategy/DisplayBrightnessStrategyConstants.java
+++ b/services/core/java/com/android/server/display/brightness/strategy/DisplayBrightnessStrategyConstants.java
@@ -18,4 +18,5 @@
public class DisplayBrightnessStrategyConstants {
static final String INVALID_BRIGHTNESS_STRATEGY_NAME = "InvalidBrightnessStrategy";
+ public static final String FALLBACK_BRIGHTNESS_STRATEGY_NAME = "FallbackBrightnessStrategy";
}
diff --git a/services/core/java/com/android/server/display/brightness/strategy/FallbackBrightnessStrategy.java b/services/core/java/com/android/server/display/brightness/strategy/FallbackBrightnessStrategy.java
new file mode 100644
index 0000000..3463649a
--- /dev/null
+++ b/services/core/java/com/android/server/display/brightness/strategy/FallbackBrightnessStrategy.java
@@ -0,0 +1,72 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.display.brightness.strategy;
+
+import android.annotation.NonNull;
+
+import com.android.server.display.DisplayBrightnessState;
+import com.android.server.display.brightness.BrightnessReason;
+import com.android.server.display.brightness.StrategyExecutionRequest;
+import com.android.server.display.brightness.StrategySelectionNotifyRequest;
+
+import java.io.PrintWriter;
+
+/**
+ * Manages the brightness of the associated display when no other strategy qualifies for
+ * setting up the brightness state. This strategy is also being used for evaluating the
+ * display brightness state when we have a manually set brightness. This is a temporary state, and
+ * the logic for evaluating the manual brightness will be moved to a separate strategy
+ */
+public class FallbackBrightnessStrategy implements DisplayBrightnessStrategy{
+ @Override
+ public DisplayBrightnessState updateBrightness(
+ StrategyExecutionRequest strategyExecutionRequest) {
+ BrightnessReason brightnessReason = new BrightnessReason();
+ brightnessReason.setReason(BrightnessReason.REASON_MANUAL);
+ return new DisplayBrightnessState.Builder()
+ .setBrightness(strategyExecutionRequest.getCurrentScreenBrightness())
+ .setSdrBrightness(strategyExecutionRequest.getCurrentScreenBrightness())
+ .setBrightnessReason(brightnessReason)
+ .setDisplayBrightnessStrategyName(getName())
+ // The fallback brightness might change due to clamping. Make sure we tell the rest
+ // of the system by updating the setting
+ .setShouldUpdateScreenBrightnessSetting(true)
+ .build();
+ }
+
+ @NonNull
+ @Override
+ public String getName() {
+ return DisplayBrightnessStrategyConstants.FALLBACK_BRIGHTNESS_STRATEGY_NAME;
+ }
+
+ @Override
+ public int getReason() {
+ return BrightnessReason.REASON_MANUAL;
+ }
+
+ @Override
+ public void dump(PrintWriter writer) {
+
+ }
+
+ @Override
+ public void strategySelectionPostProcessor(
+ StrategySelectionNotifyRequest strategySelectionNotifyRequest) {
+
+ }
+}
diff --git a/services/core/java/com/android/server/display/color/ColorDisplayService.java b/services/core/java/com/android/server/display/color/ColorDisplayService.java
index 0bb93a9..3883604 100644
--- a/services/core/java/com/android/server/display/color/ColorDisplayService.java
+++ b/services/core/java/com/android/server/display/color/ColorDisplayService.java
@@ -78,6 +78,7 @@
import com.android.server.DisplayThread;
import com.android.server.LocalServices;
import com.android.server.SystemService;
+import com.android.server.accessibility.Flags;
import com.android.server.display.feature.DisplayManagerFlags;
import com.android.server.twilight.TwilightListener;
import com.android.server.twilight.TwilightManager;
@@ -356,6 +357,11 @@
case Secure.ACCESSIBILITY_DISPLAY_DALTONIZER:
onAccessibilityDaltonizerChanged();
break;
+ case Secure.ACCESSIBILITY_DISPLAY_DALTONIZER_SATURATION_LEVEL:
+ if (Flags.enableColorCorrectionSaturation()) {
+ onAccessibilityDaltonizerChanged();
+ }
+ break;
case Secure.DISPLAY_WHITE_BALANCE_ENABLED:
updateDisplayWhiteBalanceStatus();
break;
@@ -398,6 +404,11 @@
false /* notifyForDescendants */, mContentObserver, mCurrentUser);
cr.registerContentObserver(Secure.getUriFor(Secure.REDUCE_BRIGHT_COLORS_LEVEL),
false /* notifyForDescendants */, mContentObserver, mCurrentUser);
+ if (Flags.enableColorCorrectionSaturation()) {
+ cr.registerContentObserver(
+ Secure.getUriFor(Secure.ACCESSIBILITY_DISPLAY_DALTONIZER_SATURATION_LEVEL),
+ false /* notifyForDescendants */, mContentObserver, mCurrentUser);
+ }
// Apply the accessibility settings first, since they override most other settings.
onAccessibilityInversionChanged();
@@ -597,21 +608,31 @@
if (mCurrentUser == UserHandle.USER_NULL) {
return;
}
+ var contentResolver = getContext().getContentResolver();
final int daltonizerMode = isAccessiblityDaltonizerEnabled()
- ? Secure.getIntForUser(getContext().getContentResolver(),
- Secure.ACCESSIBILITY_DISPLAY_DALTONIZER,
- AccessibilityManager.DALTONIZER_CORRECT_DEUTERANOMALY, mCurrentUser)
+ ? Secure.getIntForUser(contentResolver,
+ Secure.ACCESSIBILITY_DISPLAY_DALTONIZER,
+ AccessibilityManager.DALTONIZER_CORRECT_DEUTERANOMALY, mCurrentUser)
: AccessibilityManager.DALTONIZER_DISABLED;
+ int saturation = NOT_SET;
+ if (Flags.enableColorCorrectionSaturation()) {
+ saturation = Secure.getIntForUser(
+ contentResolver,
+ Secure.ACCESSIBILITY_DISPLAY_DALTONIZER_SATURATION_LEVEL,
+ NOT_SET,
+ mCurrentUser);
+ }
+
final DisplayTransformManager dtm = getLocalService(DisplayTransformManager.class);
if (daltonizerMode == AccessibilityManager.DALTONIZER_SIMULATE_MONOCHROMACY) {
// Monochromacy isn't supported by the native Daltonizer implementation; use grayscale.
dtm.setColorMatrix(DisplayTransformManager.LEVEL_COLOR_MATRIX_GRAYSCALE,
MATRIX_GRAYSCALE);
- dtm.setDaltonizerMode(AccessibilityManager.DALTONIZER_DISABLED);
+ dtm.setDaltonizerMode(AccessibilityManager.DALTONIZER_DISABLED, saturation);
} else {
dtm.setColorMatrix(DisplayTransformManager.LEVEL_COLOR_MATRIX_GRAYSCALE, null);
- dtm.setDaltonizerMode(daltonizerMode);
+ dtm.setDaltonizerMode(daltonizerMode, saturation);
}
}
diff --git a/services/core/java/com/android/server/display/color/DisplayTransformManager.java b/services/core/java/com/android/server/display/color/DisplayTransformManager.java
index 0dba9e1..a76c427 100644
--- a/services/core/java/com/android/server/display/color/DisplayTransformManager.java
+++ b/services/core/java/com/android/server/display/color/DisplayTransformManager.java
@@ -16,6 +16,7 @@
package com.android.server.display.color;
+import android.annotation.IntRange;
import android.app.ActivityTaskManager;
import android.hardware.display.ColorDisplayManager;
import android.opengl.Matrix;
@@ -111,9 +112,15 @@
/**
* Lock used for synchronize access to {@link #mDaltonizerMode}.
*/
- private final Object mDaltonizerModeLock = new Object();
+ @VisibleForTesting
+ final Object mDaltonizerModeLock = new Object();
+ @VisibleForTesting
@GuardedBy("mDaltonizerModeLock")
- private int mDaltonizerMode = -1;
+ int mDaltonizerMode = -1;
+
+ @VisibleForTesting
+ @GuardedBy("mDaltonizerModeLock")
+ int mDaltonizerLevel = -1;
private static final IBinder sFlinger = ServiceManager.getService(SURFACE_FLINGER);
@@ -168,12 +175,15 @@
* various types of color blindness.
*
* @param mode the new Daltonization mode, or -1 to disable
+ * @param level the level of saturation for color correction [-1,10] inclusive. -1 for when
+ * it is not set.
*/
- public void setDaltonizerMode(int mode) {
+ public void setDaltonizerMode(int mode, @IntRange(from = -1, to = 10) int level) {
synchronized (mDaltonizerModeLock) {
- if (mDaltonizerMode != mode) {
+ if (mDaltonizerMode != mode || mDaltonizerLevel != level) {
mDaltonizerMode = mode;
- applyDaltonizerMode(mode);
+ mDaltonizerLevel = level;
+ applyDaltonizerMode(mode, level);
}
}
}
@@ -223,10 +233,11 @@
/**
* Propagates the provided Daltonization mode to the SurfaceFlinger.
*/
- private static void applyDaltonizerMode(int mode) {
+ private static void applyDaltonizerMode(int mode, int level) {
final Parcel data = Parcel.obtain();
data.writeInterfaceToken("android.ui.ISurfaceComposer");
data.writeInt(mode);
+ data.writeInt(level);
try {
sFlinger.transact(SURFACE_FLINGER_TRANSACTION_DALTONIZER, data, null, 0);
} catch (RemoteException ex) {
diff --git a/services/core/java/com/android/server/display/config/RefreshRateData.java b/services/core/java/com/android/server/display/config/RefreshRateData.java
index b186fd5..d7ed904 100644
--- a/services/core/java/com/android/server/display/config/RefreshRateData.java
+++ b/services/core/java/com/android/server/display/config/RefreshRateData.java
@@ -20,6 +20,10 @@
import android.content.res.Resources;
import com.android.internal.R;
+import com.android.internal.annotations.VisibleForTesting;
+
+import java.util.Collections;
+import java.util.List;
/**
* RefreshRates config for display
@@ -58,12 +62,17 @@
*/
public final int defaultRefreshRateInHbmSunlight;
+ public final List<SupportedModeData> lowPowerSupportedModes;
+
+ @VisibleForTesting
public RefreshRateData(int defaultRefreshRate, int defaultPeakRefreshRate,
- int defaultRefreshRateInHbmHdr, int defaultRefreshRateInHbmSunlight) {
+ int defaultRefreshRateInHbmHdr, int defaultRefreshRateInHbmSunlight,
+ List<SupportedModeData> lowPowerSupportedModes) {
this.defaultRefreshRate = defaultRefreshRate;
this.defaultPeakRefreshRate = defaultPeakRefreshRate;
this.defaultRefreshRateInHbmHdr = defaultRefreshRateInHbmHdr;
this.defaultRefreshRateInHbmSunlight = defaultRefreshRateInHbmSunlight;
+ this.lowPowerSupportedModes = Collections.unmodifiableList(lowPowerSupportedModes);
}
@@ -71,9 +80,10 @@
public String toString() {
return "RefreshRateData {"
+ "defaultRefreshRate: " + defaultRefreshRate
- + "defaultPeakRefreshRate: " + defaultPeakRefreshRate
- + "defaultRefreshRateInHbmHdr: " + defaultRefreshRateInHbmHdr
- + "defaultRefreshRateInHbmSunlight: " + defaultRefreshRateInHbmSunlight
+ + ", defaultPeakRefreshRate: " + defaultPeakRefreshRate
+ + ", defaultRefreshRateInHbmHdr: " + defaultRefreshRateInHbmHdr
+ + ", defaultRefreshRateInHbmSunlight: " + defaultRefreshRateInHbmSunlight
+ + ", lowPowerSupportedModes=" + lowPowerSupportedModes
+ "} ";
}
@@ -90,8 +100,13 @@
int defaultRefreshRateInHbmSunlight = loadDefaultRefreshRateInHbmSunlight(
refreshRateConfigs, resources);
+ NonNegativeFloatToFloatMap modes =
+ refreshRateConfigs == null ? null : refreshRateConfigs.getLowPowerSupportedModes();
+ List<SupportedModeData> lowPowerSupportedModes = SupportedModeData.load(modes);
+
return new RefreshRateData(defaultRefreshRate, defaultPeakRefreshRate,
- defaultRefreshRateInHbmHdr, defaultRefreshRateInHbmSunlight);
+ defaultRefreshRateInHbmHdr, defaultRefreshRateInHbmSunlight,
+ lowPowerSupportedModes);
}
private static int loadDefaultRefreshRate(
diff --git a/services/core/java/com/android/server/display/config/SensorData.java b/services/core/java/com/android/server/display/config/SensorData.java
index 6ad13c3..8bfc4a3 100644
--- a/services/core/java/com/android/server/display/config/SensorData.java
+++ b/services/core/java/com/android/server/display/config/SensorData.java
@@ -24,7 +24,6 @@
import com.android.internal.annotations.VisibleForTesting;
import com.android.server.display.feature.DisplayManagerFlags;
-import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
@@ -42,7 +41,7 @@
public final String name;
public final float minRefreshRate;
public final float maxRefreshRate;
- public final List<SupportedMode> supportedModes;
+ public final List<SupportedModeData> supportedModes;
@VisibleForTesting
public SensorData() {
@@ -61,7 +60,7 @@
@VisibleForTesting
public SensorData(String type, String name, float minRefreshRate, float maxRefreshRate,
- List<SupportedMode> supportedModes) {
+ List<SupportedModeData> supportedModes) {
this.type = type;
this.name = name;
this.minRefreshRate = minRefreshRate;
@@ -214,26 +213,11 @@
minRefreshRate = rr.getMinimum().floatValue();
maxRefreshRate = rr.getMaximum().floatValue();
}
- ArrayList<SupportedMode> supportedModes = new ArrayList<>();
- NonNegativeFloatToFloatMap configSupportedModes = sensorDetails.getSupportedModes();
- if (configSupportedModes != null) {
- for (NonNegativeFloatToFloatPoint supportedMode : configSupportedModes.getPoint()) {
- supportedModes.add(new SupportedMode(supportedMode.getFirst().floatValue(),
- supportedMode.getSecond().floatValue()));
- }
- }
+ List<SupportedModeData> supportedModes = SupportedModeData.load(
+ sensorDetails.getSupportedModes());
return new SensorData(sensorDetails.getType(), sensorDetails.getName(), minRefreshRate,
maxRefreshRate, supportedModes);
}
- public static class SupportedMode {
- public final float refreshRate;
- public final float vsyncRate;
-
- public SupportedMode(float refreshRate, float vsyncRate) {
- this.refreshRate = refreshRate;
- this.vsyncRate = vsyncRate;
- }
- }
}
diff --git a/services/core/java/com/android/server/display/config/SupportedModeData.java b/services/core/java/com/android/server/display/config/SupportedModeData.java
new file mode 100644
index 0000000..3c82884
--- /dev/null
+++ b/services/core/java/com/android/server/display/config/SupportedModeData.java
@@ -0,0 +1,54 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.display.config;
+
+import android.annotation.Nullable;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Supported display mode data. Display mode is uniquely identified by refreshRate-vsync pair
+ */
+public class SupportedModeData {
+ public final float refreshRate;
+ public final float vsyncRate;
+
+ public SupportedModeData(float refreshRate, float vsyncRate) {
+ this.refreshRate = refreshRate;
+ this.vsyncRate = vsyncRate;
+ }
+
+ @Override
+ public String toString() {
+ return "SupportedModeData{"
+ + "refreshRate= " + refreshRate
+ + ", vsyncRate= " + vsyncRate
+ + '}';
+ }
+
+ static List<SupportedModeData> load(@Nullable NonNegativeFloatToFloatMap configMap) {
+ ArrayList<SupportedModeData> supportedModes = new ArrayList<>();
+ if (configMap != null) {
+ for (NonNegativeFloatToFloatPoint supportedMode : configMap.getPoint()) {
+ supportedModes.add(new SupportedModeData(supportedMode.getFirst().floatValue(),
+ supportedMode.getSecond().floatValue()));
+ }
+ }
+ return supportedModes;
+ }
+}
diff --git a/services/core/java/com/android/server/display/mode/DisplayModeDirector.java b/services/core/java/com/android/server/display/mode/DisplayModeDirector.java
index 91bd80e..846ee23 100644
--- a/services/core/java/com/android/server/display/mode/DisplayModeDirector.java
+++ b/services/core/java/com/android/server/display/mode/DisplayModeDirector.java
@@ -78,6 +78,7 @@
import com.android.server.display.DisplayDeviceConfig;
import com.android.server.display.config.IdleScreenRefreshRateTimeoutLuxThresholdPoint;
import com.android.server.display.config.RefreshRateData;
+import com.android.server.display.config.SupportedModeData;
import com.android.server.display.feature.DeviceConfigParameterProvider;
import com.android.server.display.feature.DisplayManagerFlags;
import com.android.server.display.utils.AmbientFilter;
@@ -451,15 +452,6 @@
return config != null && config.isVrrSupportEnabled();
}
- private boolean isVrrSupportedByAnyDisplayLocked() {
- for (int i = 0; i < mDisplayDeviceConfigByDisplay.size(); i++) {
- if (mDisplayDeviceConfigByDisplay.valueAt(i).isVrrSupportEnabled()) {
- return true;
- }
- }
- return false;
- }
-
/**
* Sets the desiredDisplayModeSpecsListener for changes to display mode and refresh rate ranges.
*/
@@ -939,18 +931,44 @@
private final Uri mMatchContentFrameRateSetting =
Settings.Secure.getUriFor(Settings.Secure.MATCH_CONTENT_FRAME_RATE);
- private final boolean mVsynLowPowerVoteEnabled;
+ private final boolean mVsyncLowPowerVoteEnabled;
private final boolean mPeakRefreshRatePhysicalLimitEnabled;
private final Context mContext;
+ private final Handler mHandler;
private float mDefaultPeakRefreshRate;
private float mDefaultRefreshRate;
+ private boolean mIsLowPower = false;
+
+ private final DisplayManager.DisplayListener mDisplayListener =
+ new DisplayManager.DisplayListener() {
+ @Override
+ public void onDisplayAdded(int displayId) {
+ synchronized (mLock) {
+ updateLowPowerModeAllowedModesLocked();
+ }
+ }
+
+ @Override
+ public void onDisplayRemoved(int displayId) {
+ mVotesStorage.updateVote(displayId, Vote.PRIORITY_LOW_POWER_MODE_MODES,
+ null);
+ }
+
+ @Override
+ public void onDisplayChanged(int displayId) {
+ synchronized (mLock) {
+ updateLowPowerModeAllowedModesLocked();
+ }
+ }
+ };
SettingsObserver(@NonNull Context context, @NonNull Handler handler,
DisplayManagerFlags flags) {
super(handler);
mContext = context;
- mVsynLowPowerVoteEnabled = flags.isVsyncLowPowerVoteEnabled();
+ mHandler = handler;
+ mVsyncLowPowerVoteEnabled = flags.isVsyncLowPowerVoteEnabled();
mPeakRefreshRatePhysicalLimitEnabled = flags.isPeakRefreshRatePhysicalLimitEnabled();
// We don't want to load from the DeviceConfig while constructing since this leads to
// a spike in the latency of DisplayManagerService startup. This happens because
@@ -983,6 +1001,7 @@
UserHandle.USER_SYSTEM);
cr.registerContentObserver(mMatchContentFrameRateSetting, false /*notifyDescendants*/,
this);
+ mInjector.registerDisplayListener(mDisplayListener, mHandler);
float deviceConfigDefaultPeakRefresh =
mConfigParameterProvider.getPeakRefreshRateDefault();
@@ -995,6 +1014,7 @@
updateLowPowerModeSettingLocked();
updateModeSwitchingTypeSettingLocked();
}
+
}
public void setDefaultRefreshRate(float refreshRate) {
@@ -1061,23 +1081,36 @@
}
private void updateLowPowerModeSettingLocked() {
- boolean inLowPowerMode = Settings.Global.getInt(mContext.getContentResolver(),
+ mIsLowPower = Settings.Global.getInt(mContext.getContentResolver(),
Settings.Global.LOW_POWER_MODE, 0 /*default*/) != 0;
final Vote vote;
- if (inLowPowerMode && mVsynLowPowerVoteEnabled && isVrrSupportedByAnyDisplayLocked()) {
- vote = Vote.forSupportedRefreshRates(List.of(
- new SupportedRefreshRatesVote.RefreshRates(/* peakRefreshRate= */ 60f,
- /* vsyncRate= */ 240f),
- new SupportedRefreshRatesVote.RefreshRates(/* peakRefreshRate= */ 60f,
- /* vsyncRate= */ 60f)
- ));
- } else if (inLowPowerMode) {
+ if (mIsLowPower) {
vote = Vote.forRenderFrameRates(0f, 60f);
} else {
vote = null;
}
- mVotesStorage.updateGlobalVote(Vote.PRIORITY_LOW_POWER_MODE, vote);
- mBrightnessObserver.onLowPowerModeEnabledLocked(inLowPowerMode);
+ mVotesStorage.updateGlobalVote(Vote.PRIORITY_LOW_POWER_MODE_RENDER_RATE, vote);
+ mBrightnessObserver.onLowPowerModeEnabledLocked(mIsLowPower);
+ updateLowPowerModeAllowedModesLocked();
+ }
+
+ private void updateLowPowerModeAllowedModesLocked() {
+ if (!mVsyncLowPowerVoteEnabled) {
+ return;
+ }
+ if (mIsLowPower) {
+ for (int i = 0; i < mDisplayDeviceConfigByDisplay.size(); i++) {
+ DisplayDeviceConfig config = mDisplayDeviceConfigByDisplay.valueAt(i);
+ List<SupportedModeData> supportedModes = config
+ .getRefreshRateData().lowPowerSupportedModes;
+ mVotesStorage.updateVote(
+ mDisplayDeviceConfigByDisplay.keyAt(i),
+ Vote.PRIORITY_LOW_POWER_MODE_MODES,
+ Vote.forSupportedRefreshRates(supportedModes));
+ }
+ } else {
+ mVotesStorage.removeAllVotesForPriority(Vote.PRIORITY_LOW_POWER_MODE_MODES);
+ }
}
/**
diff --git a/services/core/java/com/android/server/display/mode/Vote.java b/services/core/java/com/android/server/display/mode/Vote.java
index ddb334e..8167c1f 100644
--- a/services/core/java/com/android/server/display/mode/Vote.java
+++ b/services/core/java/com/android/server/display/mode/Vote.java
@@ -18,6 +18,9 @@
import android.annotation.NonNull;
+import com.android.server.display.config.SupportedModeData;
+
+import java.util.ArrayList;
import java.util.List;
interface Vote {
@@ -102,9 +105,15 @@
// For internal application to limit display modes to specific ids
int PRIORITY_SYSTEM_REQUESTED_MODES = 14;
- // LOW_POWER_MODE force the render frame rate to [0, 60HZ] if
+ // PRIORITY_LOW_POWER_MODE_MODES limits display modes to specific refreshRate-vsync pairs if
// Settings.Global.LOW_POWER_MODE is on.
- int PRIORITY_LOW_POWER_MODE = 15;
+ // Lower priority that PRIORITY_LOW_POWER_MODE_RENDER_RATE and if discarded (due to other
+ // higher priority votes), render rate limit can still apply
+ int PRIORITY_LOW_POWER_MODE_MODES = 14;
+
+ // PRIORITY_LOW_POWER_MODE_RENDER_RATE force the render frame rate to [0, 60HZ] if
+ // Settings.Global.LOW_POWER_MODE is on.
+ int PRIORITY_LOW_POWER_MODE_RENDER_RATE = 15;
// PRIORITY_FLICKER_REFRESH_RATE_SWITCH votes for disabling refresh rate switching. If the
// higher priority voters' result is a range, it will fix the rate to a single choice.
@@ -177,22 +186,26 @@
return new BaseModeRefreshRateVote(baseModeRefreshRate);
}
- static Vote forSupportedRefreshRates(
- List<SupportedRefreshRatesVote.RefreshRates> refreshRates) {
- return new SupportedRefreshRatesVote(refreshRates);
+ static Vote forSupportedRefreshRates(List<SupportedModeData> supportedModes) {
+ if (supportedModes.isEmpty()) {
+ return null;
+ }
+ List<SupportedRefreshRatesVote.RefreshRates> rates = new ArrayList<>();
+ for (SupportedModeData data : supportedModes) {
+ rates.add(new SupportedRefreshRatesVote.RefreshRates(data.refreshRate, data.vsyncRate));
+ }
+ return new SupportedRefreshRatesVote(rates);
}
static Vote forSupportedModes(List<Integer> modeIds) {
return new SupportedModesVote(modeIds);
}
-
-
static Vote forSupportedRefreshRatesAndDisableSwitching(
List<SupportedRefreshRatesVote.RefreshRates> supportedRefreshRates) {
return new CombinedVote(
List.of(forDisableRefreshRateSwitching(),
- forSupportedRefreshRates(supportedRefreshRates)));
+ new SupportedRefreshRatesVote(supportedRefreshRates)));
}
static String priorityToString(int priority) {
@@ -213,8 +226,10 @@
return "PRIORITY_HIGH_BRIGHTNESS_MODE";
case PRIORITY_PROXIMITY:
return "PRIORITY_PROXIMITY";
- case PRIORITY_LOW_POWER_MODE:
- return "PRIORITY_LOW_POWER_MODE";
+ case PRIORITY_LOW_POWER_MODE_MODES:
+ return "PRIORITY_LOW_POWER_MODE_MODES";
+ case PRIORITY_LOW_POWER_MODE_RENDER_RATE:
+ return "PRIORITY_LOW_POWER_MODE_RENDER_RATE";
case PRIORITY_SKIN_TEMPERATURE:
return "PRIORITY_SKIN_TEMPERATURE";
case PRIORITY_UDFPS:
@@ -227,6 +242,8 @@
return "PRIORITY_LIMIT_MODE";
case PRIORITY_SYNCHRONIZED_REFRESH_RATE:
return "PRIORITY_SYNCHRONIZED_REFRESH_RATE";
+ case PRIORITY_USER_SETTING_PEAK_REFRESH_RATE:
+ return "PRIORITY_USER_SETTING_PEAK_REFRESH_RATE";
case PRIORITY_USER_SETTING_PEAK_RENDER_FRAME_RATE:
return "PRIORITY_USER_SETTING_PEAK_RENDER_FRAME_RATE";
case PRIORITY_AUTH_OPTIMIZER_RENDER_FRAME_RATE:
diff --git a/services/core/java/com/android/server/dreams/DreamController.java b/services/core/java/com/android/server/dreams/DreamController.java
index 816242d..c6aef7f 100644
--- a/services/core/java/com/android/server/dreams/DreamController.java
+++ b/services/core/java/com/android/server/dreams/DreamController.java
@@ -249,14 +249,14 @@
mCurrentDream.mAppTask = appTask;
}
- void setDreamHasFocus(boolean hasFocus) {
+ void setDreamIsObscured(boolean isObscured) {
if (mCurrentDream != null) {
- mCurrentDream.mDreamHasFocus = hasFocus;
+ mCurrentDream.mDreamIsObscured = isObscured;
}
}
- boolean dreamHasFocus() {
- return mCurrentDream != null && mCurrentDream.mDreamHasFocus;
+ boolean dreamIsFrontmost() {
+ return mCurrentDream != null && mCurrentDream.dreamIsFrontmost();
}
/**
@@ -451,7 +451,7 @@
private String mStopReason;
private long mDreamStartTime;
public boolean mWakingGently;
- public boolean mDreamHasFocus;
+ private boolean mDreamIsObscured;
private final Runnable mStopPreviousDreamsIfNeeded = this::stopPreviousDreamsIfNeeded;
private final Runnable mReleaseWakeLockIfNeeded = this::releaseWakeLockIfNeeded;
@@ -549,5 +549,9 @@
mHandler.removeCallbacks(mReleaseWakeLockIfNeeded);
}
}
+
+ boolean dreamIsFrontmost() {
+ return !mDreamIsObscured;
+ }
}
}
diff --git a/services/core/java/com/android/server/dreams/DreamManagerService.java b/services/core/java/com/android/server/dreams/DreamManagerService.java
index 2def5ae..18a9986 100644
--- a/services/core/java/com/android/server/dreams/DreamManagerService.java
+++ b/services/core/java/com/android/server/dreams/DreamManagerService.java
@@ -20,7 +20,7 @@
import static android.app.WindowConfiguration.ACTIVITY_TYPE_ASSISTANT;
import static android.app.WindowConfiguration.ACTIVITY_TYPE_DREAM;
import static android.app.WindowConfiguration.ACTIVITY_TYPE_HOME;
-import static android.service.dreams.Flags.dreamTracksFocus;
+import static android.service.dreams.Flags.dreamHandlesBeingObscured;
import static com.android.server.wm.ActivityInterceptorCallback.DREAM_MANAGER_ORDERED_ID;
@@ -428,7 +428,7 @@
// Can't start dreaming if we are already dreaming and the dream has focus. If we are
// dreaming but the dream does not have focus, then the dream can be brought to the
// front so it does have focus.
- if (isScreenOn && isDreamingInternal() && dreamHasFocus()) {
+ if (isScreenOn && isDreamingInternal() && dreamIsFrontmost()) {
return false;
}
@@ -463,9 +463,10 @@
}
}
- private boolean dreamHasFocus() {
- // Dreams always had focus before they were able to track it.
- return !dreamTracksFocus() || mController.dreamHasFocus();
+ private boolean dreamIsFrontmost() {
+ // Dreams were always considered frontmost before they began tracking whether they are
+ // obscured.
+ return !dreamHandlesBeingObscured() || mController.dreamIsFrontmost();
}
protected void requestStartDreamFromShell() {
@@ -473,7 +474,7 @@
}
private void requestDreamInternal() {
- if (isDreamingInternal() && !dreamHasFocus() && mController.bringDreamToFront()) {
+ if (isDreamingInternal() && !dreamIsFrontmost() && mController.bringDreamToFront()) {
return;
}
@@ -1159,10 +1160,16 @@
}
@Override
- public void onDreamFocusChanged(boolean hasFocus) {
+ public void setDreamIsObscured(boolean isObscured) {
+ if (!dreamHandlesBeingObscured()) {
+ return;
+ }
+
+ checkPermission(android.Manifest.permission.WRITE_DREAM_STATE);
+
final long ident = Binder.clearCallingIdentity();
try {
- mController.setDreamHasFocus(hasFocus);
+ mHandler.post(() -> mController.setDreamIsObscured(isObscured));
} finally {
Binder.restoreCallingIdentity(ident);
}
diff --git a/services/core/java/com/android/server/flags/pinner.aconfig b/services/core/java/com/android/server/flags/pinner.aconfig
index 16a45cd..2f817db 100644
--- a/services/core/java/com/android/server/flags/pinner.aconfig
+++ b/services/core/java/com/android/server/flags/pinner.aconfig
@@ -6,4 +6,11 @@
namespace: "system_performance"
description: "This flag controls if webview should be pinned in memory."
bug: "307594624"
+}
+
+flag {
+ name: "skip_home_art_pins"
+ namespace: "system_performance"
+ description: "Ablation study flag that controls if home app odex/vdex files should be pinned in memory."
+ bug: "340935152"
}
\ No newline at end of file
diff --git a/services/core/java/com/android/server/hdmi/HdmiCecLocalDevicePlayback.java b/services/core/java/com/android/server/hdmi/HdmiCecLocalDevicePlayback.java
index d21fc85..5db17bb 100644
--- a/services/core/java/com/android/server/hdmi/HdmiCecLocalDevicePlayback.java
+++ b/services/core/java/com/android/server/hdmi/HdmiCecLocalDevicePlayback.java
@@ -29,7 +29,6 @@
import android.os.Handler;
import android.os.PowerManager;
import android.os.SystemProperties;
-import android.os.UserHandle;
import android.sysprop.HdmiProperties;
import android.util.Slog;
@@ -278,8 +277,7 @@
void dismissUiOnActiveSourceStatusRecovered() {
assertRunOnServiceThread();
Intent intent = new Intent(HdmiControlManager.ACTION_ON_ACTIVE_SOURCE_RECOVERED_DISMISS_UI);
- mService.getContext().sendBroadcastAsUser(intent, UserHandle.ALL,
- HdmiControlService.PERMISSION);
+ mService.sendBroadcastAsUser(intent);
}
@Override
diff --git a/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceTv.java b/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceTv.java
index 46061a5..275c930 100644
--- a/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceTv.java
+++ b/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceTv.java
@@ -206,6 +206,10 @@
launchDeviceDiscovery();
startQueuedActions();
if (!mDelayedMessageBuffer.isBuffered(Constants.MESSAGE_ACTIVE_SOURCE)) {
+ if (hasAction(RequestActiveSourceAction.class)) {
+ Slog.i(TAG, "RequestActiveSourceAction is in progress. Restarting.");
+ removeAction(RequestActiveSourceAction.class);
+ }
addAndStartAction(new RequestActiveSourceAction(this, new IHdmiControlCallback.Stub() {
@Override
public void onComplete(int result) {
@@ -1308,6 +1312,8 @@
mService.sendCecCommand(
HdmiCecMessageBuilder.buildActiveSource(
getDeviceInfo().getLogicalAddress(), activePath));
+ updateActiveSource(getDeviceInfo().getLogicalAddress(), activePath,
+ "HdmiCecLocalDeviceTv#launchRoutingControl()");
}
}
}
diff --git a/services/core/java/com/android/server/hdmi/HdmiControlService.java b/services/core/java/com/android/server/hdmi/HdmiControlService.java
index d2d0279..cca73b5 100644
--- a/services/core/java/com/android/server/hdmi/HdmiControlService.java
+++ b/services/core/java/com/android/server/hdmi/HdmiControlService.java
@@ -40,6 +40,7 @@
import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.annotation.RequiresPermission;
import android.content.BroadcastReceiver;
import android.content.ContentResolver;
import android.content.Context;
@@ -1645,6 +1646,13 @@
case Constants.MESSAGE_ROUTING_CHANGE:
case Constants.MESSAGE_SET_STREAM_PATH:
case Constants.MESSAGE_TEXT_VIEW_ON:
+ // RequestActiveSourceAction is started after the TV finished logical address
+ // allocation. This action is used by the TV to get the active source from the CEC
+ // network. If the TV sent a source changing CEC message, this action does not have
+ // to continue anymore.
+ if (isTvDeviceEnabled()) {
+ tv().removeAction(RequestActiveSourceAction.class);
+ }
sendCecCommandWithRetries(command, callback);
break;
default:
@@ -4392,8 +4400,7 @@
assertRunOnServiceThread();
Intent intent = new Intent(HdmiControlManager.ACTION_OSD_MESSAGE);
intent.putExtra(HdmiControlManager.EXTRA_MESSAGE_ID, messageId);
- getContext().sendBroadcastAsUser(intent, UserHandle.ALL,
- HdmiControlService.PERMISSION);
+ sendBroadcastAsUser(intent);
}
@ServiceThreadOnly
@@ -4402,8 +4409,17 @@
Intent intent = new Intent(HdmiControlManager.ACTION_OSD_MESSAGE);
intent.putExtra(HdmiControlManager.EXTRA_MESSAGE_ID, messageId);
intent.putExtra(HdmiControlManager.EXTRA_MESSAGE_EXTRA_PARAM1, extra);
- getContext().sendBroadcastAsUser(intent, UserHandle.ALL,
- HdmiControlService.PERMISSION);
+ sendBroadcastAsUser(intent);
+ }
+
+ // This method is used such that we can override it inside unit tests to avoid a
+ // SecurityException.
+ @ServiceThreadOnly
+ @VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE)
+ @RequiresPermission(android.Manifest.permission.INTERACT_ACROSS_USERS)
+ protected void sendBroadcastAsUser(@RequiresPermission Intent intent) {
+ assertRunOnServiceThread();
+ getContext().sendBroadcastAsUser(intent, UserHandle.ALL, HdmiControlService.PERMISSION);
}
@VisibleForTesting
diff --git a/services/core/java/com/android/server/hdmi/RequestActiveSourceAction.java b/services/core/java/com/android/server/hdmi/RequestActiveSourceAction.java
index d250416..539a00d 100644
--- a/services/core/java/com/android/server/hdmi/RequestActiveSourceAction.java
+++ b/services/core/java/com/android/server/hdmi/RequestActiveSourceAction.java
@@ -21,13 +21,20 @@
import android.util.Slog;
/**
- * Feature action that sends <Request Active Source> message and waits for <Active Source>.
+ * Feature action that sends <Request Active Source> message and waits for <Active Source> on TV
+ * panels.
+ * This action has a delay before sending <Request Active Source>. This is because it should wait
+ * for a possible request from LauncherX and can be cancelled if an <Active Source> message was
+ * received or the TV switched to another input.
*/
public class RequestActiveSourceAction extends HdmiCecFeatureAction {
private static final String TAG = "RequestActiveSourceAction";
+ // State to wait for the LauncherX to call the CEC API.
+ private static final int STATE_WAIT_FOR_LAUNCHERX_API_CALL = 1;
+
// State to wait for the <Active Source> message.
- private static final int STATE_WAIT_FOR_ACTIVE_SOURCE = 1;
+ private static final int STATE_WAIT_FOR_ACTIVE_SOURCE = 2;
// Number of retries <Request Active Source> is sent if no device answers this message.
private static final int MAX_SEND_RETRY_COUNT = 1;
@@ -43,10 +50,12 @@
boolean start() {
Slog.v(TAG, "RequestActiveSourceAction started.");
- sendCommand(HdmiCecMessageBuilder.buildRequestActiveSource(getSourceAddress()));
+ mState = STATE_WAIT_FOR_LAUNCHERX_API_CALL;
- mState = STATE_WAIT_FOR_ACTIVE_SOURCE;
- addTimer(mState, HdmiConfig.TIMEOUT_MS);
+ // We wait for default timeout to allow the message triggered by the LauncherX API call to
+ // be sent by the TV and another default timeout in case the message has to be answered
+ // (e.g. TV sent a <Set Stream Path> or <Routing Change>).
+ addTimer(mState, HdmiConfig.TIMEOUT_MS * 2);
return true;
}
@@ -65,13 +74,23 @@
if (mState != state) {
return;
}
- if (mState == STATE_WAIT_FOR_ACTIVE_SOURCE) {
- if (mSendRetryCount++ < MAX_SEND_RETRY_COUNT) {
+
+ switch (mState) {
+ case STATE_WAIT_FOR_LAUNCHERX_API_CALL:
+ mState = STATE_WAIT_FOR_ACTIVE_SOURCE;
sendCommand(HdmiCecMessageBuilder.buildRequestActiveSource(getSourceAddress()));
addTimer(mState, HdmiConfig.TIMEOUT_MS);
- } else {
- finishWithCallback(HdmiControlManager.RESULT_TIMEOUT);
- }
+ return;
+ case STATE_WAIT_FOR_ACTIVE_SOURCE:
+ if (mSendRetryCount++ < MAX_SEND_RETRY_COUNT) {
+ sendCommand(HdmiCecMessageBuilder.buildRequestActiveSource(getSourceAddress()));
+ addTimer(mState, HdmiConfig.TIMEOUT_MS);
+ } else {
+ finishWithCallback(HdmiControlManager.RESULT_TIMEOUT);
+ }
+ return;
+ default:
+ return;
}
}
}
diff --git a/services/core/java/com/android/server/input/InputManagerInternal.java b/services/core/java/com/android/server/input/InputManagerInternal.java
index b47631c3..d32a5ed 100644
--- a/services/core/java/com/android/server/input/InputManagerInternal.java
+++ b/services/core/java/com/android/server/input/InputManagerInternal.java
@@ -218,4 +218,13 @@
* display, external peripherals, fingerprint sensor, etc.
*/
public abstract void notifyUserActivity();
+
+ /**
+ * Get the device ID of the {@link InputDevice} that used most recently.
+ *
+ * @return the last used input device ID, or
+ * {@link android.os.IInputConstants#INVALID_INPUT_DEVICE_ID} if no device has been used
+ * since boot.
+ */
+ public abstract int getLastUsedInputDeviceId();
}
diff --git a/services/core/java/com/android/server/input/InputManagerService.java b/services/core/java/com/android/server/input/InputManagerService.java
index 8317991..48cccd5 100644
--- a/services/core/java/com/android/server/input/InputManagerService.java
+++ b/services/core/java/com/android/server/input/InputManagerService.java
@@ -960,12 +960,6 @@
// Binder call
@Override
- public boolean isInputDeviceEnabled(int deviceId) {
- return mNative.isInputDeviceEnabled(deviceId);
- }
-
- // Binder call
- @Override
public void enableInputDevice(int deviceId) {
if (!checkCallingPermission(android.Manifest.permission.DISABLE_INPUT_DEVICE,
"enableInputDevice()")) {
@@ -2671,24 +2665,6 @@
return null;
}
- private static class PointerDisplayIdChangedArgs {
- final int mPointerDisplayId;
- final float mXPosition;
- final float mYPosition;
- PointerDisplayIdChangedArgs(int pointerDisplayId, float xPosition, float yPosition) {
- mPointerDisplayId = pointerDisplayId;
- mXPosition = xPosition;
- mYPosition = yPosition;
- }
- }
-
- // Native callback.
- @SuppressWarnings("unused")
- @VisibleForTesting
- void onPointerDisplayIdChanged(int pointerDisplayId, float xPosition, float yPosition) {
- // TODO(b/311416205): Remove.
- }
-
@Override
@EnforcePermission(Manifest.permission.MONITOR_STICKY_MODIFIER_STATE)
public void registerStickyModifierStateListener(
@@ -3204,6 +3180,11 @@
public void setStylusButtonMotionEventsEnabled(boolean enabled) {
mNative.setStylusButtonMotionEventsEnabled(enabled);
}
+
+ @Override
+ public int getLastUsedInputDeviceId() {
+ return mNative.getLastUsedInputDeviceId();
+ }
}
@Override
diff --git a/services/core/java/com/android/server/input/NativeInputManagerService.java b/services/core/java/com/android/server/input/NativeInputManagerService.java
index f742360..a9d40bb 100644
--- a/services/core/java/com/android/server/input/NativeInputManagerService.java
+++ b/services/core/java/com/android/server/input/NativeInputManagerService.java
@@ -183,8 +183,6 @@
void monitor();
- boolean isInputDeviceEnabled(int deviceId);
-
void enableInputDevice(int deviceId);
void disableInputDevice(int deviceId);
@@ -271,6 +269,15 @@
void setInputMethodConnectionIsActive(boolean isActive);
+ /**
+ * Get the device ID of the InputDevice that used most recently.
+ *
+ * @return the last used input device ID, or
+ * {@link android.os.IInputConstants#INVALID_INPUT_DEVICE_ID} if no device has been used
+ * since boot.
+ */
+ int getLastUsedInputDeviceId();
+
/** The native implementation of InputManagerService methods. */
class NativeImpl implements NativeInputManagerService {
/** Pointer to native input manager service object, used by native code. */
@@ -454,9 +461,6 @@
public native void monitor();
@Override
- public native boolean isInputDeviceEnabled(int deviceId);
-
- @Override
public native void enableInputDevice(int deviceId);
@Override
@@ -544,5 +548,8 @@
@Override
public native void setInputMethodConnectionIsActive(boolean isActive);
+
+ @Override
+ public native int getLastUsedInputDeviceId();
}
}
diff --git a/services/core/java/com/android/server/inputmethod/AutofillSuggestionsController.java b/services/core/java/com/android/server/inputmethod/AutofillSuggestionsController.java
index 00bc751..ad98b4a 100644
--- a/services/core/java/com/android/server/inputmethod/AutofillSuggestionsController.java
+++ b/services/core/java/com/android/server/inputmethod/AutofillSuggestionsController.java
@@ -29,6 +29,7 @@
import com.android.internal.annotations.GuardedBy;
import com.android.internal.inputmethod.IInlineSuggestionsRequestCallback;
import com.android.internal.inputmethod.IInlineSuggestionsResponseCallback;
+import com.android.internal.inputmethod.InlineSuggestionsRequestCallback;
import com.android.internal.inputmethod.InlineSuggestionsRequestInfo;
/**
@@ -49,12 +50,12 @@
private static final class CreateInlineSuggestionsRequest {
@NonNull final InlineSuggestionsRequestInfo mRequestInfo;
- @NonNull final IInlineSuggestionsRequestCallback mCallback;
+ @NonNull final InlineSuggestionsRequestCallback mCallback;
@NonNull final String mPackageName;
CreateInlineSuggestionsRequest(
@NonNull InlineSuggestionsRequestInfo requestInfo,
- @NonNull IInlineSuggestionsRequestCallback callback,
+ @NonNull InlineSuggestionsRequestCallback callback,
@NonNull String packageName) {
mRequestInfo = requestInfo;
mCallback = callback;
@@ -78,7 +79,7 @@
*/
@GuardedBy("ImfLock.class")
@Nullable
- private IInlineSuggestionsRequestCallback mInlineSuggestionsRequestCallback;
+ private InlineSuggestionsRequestCallback mInlineSuggestionsRequestCallback;
AutofillSuggestionsController(@NonNull InputMethodManagerService service) {
mService = service;
@@ -97,33 +98,30 @@
@GuardedBy("ImfLock.class")
void onCreateInlineSuggestionsRequest(@UserIdInt int userId,
- InlineSuggestionsRequestInfo requestInfo, IInlineSuggestionsRequestCallback callback,
+ InlineSuggestionsRequestInfo requestInfo, InlineSuggestionsRequestCallback callback,
boolean touchExplorationEnabled) {
clearPendingInlineSuggestionsRequest();
mInlineSuggestionsRequestCallback = callback;
final InputMethodInfo imi = mService.queryInputMethodForCurrentUserLocked(
mService.getSelectedMethodIdLocked());
- try {
- if (userId == mService.getCurrentImeUserIdLocked()
- && imi != null && isInlineSuggestionsEnabled(imi, touchExplorationEnabled)) {
- mPendingInlineSuggestionsRequest = new CreateInlineSuggestionsRequest(
- requestInfo, callback, imi.getPackageName());
- if (mService.getCurMethodLocked() != null) {
- // In the normal case when the IME is connected, we can make the request here.
- performOnCreateInlineSuggestionsRequest();
- } else {
- // Otherwise, the next time the IME connection is established,
- // InputMethodBindingController.mMainConnection#onServiceConnected() will call
- // into #performOnCreateInlineSuggestionsRequestLocked() to make the request.
- if (DEBUG) {
- Slog.d(TAG, "IME not connected. Delaying inline suggestions request.");
- }
- }
+
+ if (userId == mService.getCurrentImeUserIdLocked()
+ && imi != null && isInlineSuggestionsEnabled(imi, touchExplorationEnabled)) {
+ mPendingInlineSuggestionsRequest = new CreateInlineSuggestionsRequest(
+ requestInfo, callback, imi.getPackageName());
+ if (mService.getCurMethodLocked() != null) {
+ // In the normal case when the IME is connected, we can make the request here.
+ performOnCreateInlineSuggestionsRequest();
} else {
- callback.onInlineSuggestionsUnsupported();
+ // Otherwise, the next time the IME connection is established,
+ // InputMethodBindingController.mMainConnection#onServiceConnected() will call
+ // into #performOnCreateInlineSuggestionsRequestLocked() to make the request.
+ if (DEBUG) {
+ Slog.d(TAG, "IME not connected. Delaying inline suggestions request.");
+ }
}
- } catch (RemoteException e) {
- Slog.w(TAG, "RemoteException calling onCreateInlineSuggestionsRequest(): " + e);
+ } else {
+ callback.onInlineSuggestionsUnsupported();
}
}
@@ -166,11 +164,7 @@
@GuardedBy("ImfLock.class")
void invalidateAutofillSession() {
if (mInlineSuggestionsRequestCallback != null) {
- try {
- mInlineSuggestionsRequestCallback.onInlineSuggestionsSessionInvalidated();
- } catch (RemoteException e) {
- Slog.e(TAG, "Cannot invalidate autofill session.", e);
- }
+ mInlineSuggestionsRequestCallback.onInlineSuggestionsSessionInvalidated();
}
}
@@ -180,13 +174,13 @@
*/
private final class InlineSuggestionsRequestCallbackDecorator
extends IInlineSuggestionsRequestCallback.Stub {
- @NonNull private final IInlineSuggestionsRequestCallback mCallback;
+ @NonNull private final InlineSuggestionsRequestCallback mCallback;
@NonNull private final String mImePackageName;
private final int mImeDisplayId;
@NonNull private final IBinder mImeToken;
InlineSuggestionsRequestCallbackDecorator(
- @NonNull IInlineSuggestionsRequestCallback callback, @NonNull String imePackageName,
+ @NonNull InlineSuggestionsRequestCallback callback, @NonNull String imePackageName,
int displayId, @NonNull IBinder imeToken) {
mCallback = callback;
mImePackageName = imePackageName;
@@ -195,7 +189,7 @@
}
@Override
- public void onInlineSuggestionsUnsupported() throws RemoteException {
+ public void onInlineSuggestionsUnsupported() {
mCallback.onInlineSuggestionsUnsupported();
}
@@ -220,32 +214,32 @@
}
@Override
- public void onInputMethodStartInput(AutofillId imeFieldId) throws RemoteException {
+ public void onInputMethodStartInput(AutofillId imeFieldId) {
mCallback.onInputMethodStartInput(imeFieldId);
}
@Override
- public void onInputMethodShowInputRequested(boolean requestResult) throws RemoteException {
+ public void onInputMethodShowInputRequested(boolean requestResult) {
mCallback.onInputMethodShowInputRequested(requestResult);
}
@Override
- public void onInputMethodStartInputView() throws RemoteException {
+ public void onInputMethodStartInputView() {
mCallback.onInputMethodStartInputView();
}
@Override
- public void onInputMethodFinishInputView() throws RemoteException {
+ public void onInputMethodFinishInputView() {
mCallback.onInputMethodFinishInputView();
}
@Override
- public void onInputMethodFinishInput() throws RemoteException {
+ public void onInputMethodFinishInput() {
mCallback.onInputMethodFinishInput();
}
@Override
- public void onInlineSuggestionsSessionInvalidated() throws RemoteException {
+ public void onInlineSuggestionsSessionInvalidated() {
mCallback.onInlineSuggestionsSessionInvalidated();
}
}
diff --git a/services/core/java/com/android/server/inputmethod/ImeBindingState.java b/services/core/java/com/android/server/inputmethod/ImeBindingState.java
index 4c20c3b..f78ea84 100644
--- a/services/core/java/com/android/server/inputmethod/ImeBindingState.java
+++ b/services/core/java/com/android/server/inputmethod/ImeBindingState.java
@@ -21,7 +21,9 @@
import static android.view.WindowManager.LayoutParams.SOFT_INPUT_STATE_UNSPECIFIED;
import android.annotation.Nullable;
+import android.annotation.UserIdInt;
import android.os.IBinder;
+import android.os.UserHandle;
import android.util.Printer;
import android.util.proto.ProtoOutputStream;
import android.view.WindowManager;
@@ -36,6 +38,9 @@
*/
final class ImeBindingState {
+ @UserIdInt
+ final int mUserId;
+
/**
* The last window token that we confirmed to be focused. This is always updated upon
* reports from the input method client. If the window state is already changed before the
@@ -90,6 +95,7 @@
static ImeBindingState newEmptyState() {
return new ImeBindingState(
+ /*userId=*/ UserHandle.USER_NULL,
/*focusedWindow=*/ null,
/*focusedWindowSoftInputMode=*/ SOFT_INPUT_STATE_UNSPECIFIED,
/*focusedWindowClient=*/ null,
@@ -97,10 +103,12 @@
);
}
- ImeBindingState(@Nullable IBinder focusedWindow,
+ ImeBindingState(@UserIdInt int userId,
+ @Nullable IBinder focusedWindow,
@SoftInputModeFlags int focusedWindowSoftInputMode,
@Nullable ClientState focusedWindowClient,
@Nullable EditorInfo focusedWindowEditorInfo) {
+ mUserId = userId;
mFocusedWindow = focusedWindow;
mFocusedWindowSoftInputMode = focusedWindowSoftInputMode;
mFocusedWindowClient = focusedWindowClient;
diff --git a/services/core/java/com/android/server/inputmethod/ImeTrackerService.java b/services/core/java/com/android/server/inputmethod/ImeTrackerService.java
index 1c14fc1..fff0e6e 100644
--- a/services/core/java/com/android/server/inputmethod/ImeTrackerService.java
+++ b/services/core/java/com/android/server/inputmethod/ImeTrackerService.java
@@ -133,6 +133,13 @@
}
}
+ @Override
+ public void onDispatched(@NonNull ImeTracker.Token statsToken) {
+ synchronized (mLock) {
+ mHistory.setFinished(statsToken, ImeTracker.STATUS_SUCCESS, ImeTracker.PHASE_NOT_SET);
+ }
+ }
+
/**
* Updates the IME request tracking token with new information available in IMMS.
*
diff --git a/services/core/java/com/android/server/inputmethod/InputMethodBindingController.java b/services/core/java/com/android/server/inputmethod/InputMethodBindingController.java
index b709174..ad99950 100644
--- a/services/core/java/com/android/server/inputmethod/InputMethodBindingController.java
+++ b/services/core/java/com/android/server/inputmethod/InputMethodBindingController.java
@@ -39,6 +39,7 @@
import android.provider.Settings;
import android.util.EventLog;
import android.util.Slog;
+import android.view.Display;
import android.view.WindowManager;
import android.view.inputmethod.InputMethod;
import android.view.inputmethod.InputMethodInfo;
@@ -78,6 +79,7 @@
@GuardedBy("ImfLock.class") @Nullable private IInputMethodInvoker mCurMethod;
@GuardedBy("ImfLock.class") private int mCurMethodUid = Process.INVALID_UID;
@GuardedBy("ImfLock.class") @Nullable private IBinder mCurToken;
+ @GuardedBy("ImfLock.class") private int mCurTokenDisplayId = Display.INVALID_DISPLAY;
@GuardedBy("ImfLock.class") private int mCurSeq;
@GuardedBy("ImfLock.class") private boolean mVisibleBound;
@GuardedBy("ImfLock.class") private boolean mSupportsStylusHw;
@@ -193,6 +195,17 @@
}
/**
+ * Returns the displayId associated with {@link #getCurToken()}.
+ *
+ * @return the displayId associated with {@link #getCurToken()}. {@link Display#INVALID_DISPLAY}
+ * while {@link #getCurToken()} returns {@code null}
+ */
+ @GuardedBy("ImfLock.class")
+ int getCurTokenDisplayId() {
+ return mCurTokenDisplayId;
+ }
+
+ /**
* The Intent used to connect to the current input method.
*/
@GuardedBy("ImfLock.class")
@@ -412,15 +425,14 @@
@GuardedBy("ImfLock.class")
private void removeCurrentToken() {
- int curTokenDisplayId = mService.getCurTokenDisplayIdLocked();
-
if (DEBUG) {
Slog.v(TAG,
- "Removing window token: " + mCurToken + " for display: " + curTokenDisplayId);
+ "Removing window token: " + mCurToken + " for display: " + mCurTokenDisplayId);
}
mWindowManagerInternal.removeWindowToken(mCurToken, true /* removeWindows */,
- false /* animateExit */, curTokenDisplayId);
+ false /* animateExit */, mCurTokenDisplayId);
mCurToken = null;
+ mCurTokenDisplayId = Display.INVALID_DISPLAY;
}
@GuardedBy("ImfLock.class")
@@ -443,7 +455,16 @@
mCurId = info.getId();
mLastBindTime = SystemClock.uptimeMillis();
- addFreshWindowToken();
+ final int displayIdToShowIme = mService.getDisplayIdToShowImeLocked();
+ mCurToken = new Binder();
+ mCurTokenDisplayId = displayIdToShowIme;
+ if (DEBUG) {
+ Slog.v(TAG, "Adding window token: " + mCurToken + " for display: "
+ + displayIdToShowIme);
+ }
+ mWindowManagerInternal.addWindowToken(mCurToken,
+ WindowManager.LayoutParams.TYPE_INPUT_METHOD,
+ displayIdToShowIme, null /* options */);
return new InputBindResult(
InputBindResult.ResultCode.SUCCESS_WAITING_IME_BINDING,
null, null, null, mCurId, mCurSeq, false);
@@ -471,22 +492,6 @@
}
@GuardedBy("ImfLock.class")
- private void addFreshWindowToken() {
- int displayIdToShowIme = mService.getDisplayIdToShowImeLocked();
- mCurToken = new Binder();
-
- mService.setCurTokenDisplayIdLocked(displayIdToShowIme);
-
- if (DEBUG) {
- Slog.v(TAG, "Adding window token: " + mCurToken + " for display: "
- + displayIdToShowIme);
- }
- mWindowManagerInternal.addWindowToken(mCurToken,
- WindowManager.LayoutParams.TYPE_INPUT_METHOD,
- displayIdToShowIme, null /* options */);
- }
-
- @GuardedBy("ImfLock.class")
private void unbindMainConnection() {
mContext.unbindService(mMainConnection);
mHasMainConnection = false;
diff --git a/services/core/java/com/android/server/inputmethod/InputMethodInfoUtils.java b/services/core/java/com/android/server/inputmethod/InputMethodInfoUtils.java
index 6339686..458c359 100644
--- a/services/core/java/com/android/server/inputmethod/InputMethodInfoUtils.java
+++ b/services/core/java/com/android/server/inputmethod/InputMethodInfoUtils.java
@@ -22,6 +22,7 @@
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.content.Context;
+import android.os.Parcel;
import android.text.TextUtils;
import android.util.Slog;
import android.view.inputmethod.InputMethodInfo;
@@ -323,4 +324,24 @@
return SubtypeUtils.containsSubtypeOf(imi, requiredLocale, checkCountry,
requiredSubtypeMode);
}
+
+ /**
+ * Marshals the given {@link InputMethodInfo} into a byte array.
+ *
+ * @param imi {@link InputMethodInfo} to be marshalled
+ * @return a byte array where the given {@link InputMethodInfo} is marshalled
+ */
+ @NonNull
+ static byte[] marshal(@NonNull InputMethodInfo imi) {
+ Parcel parcel = null;
+ try {
+ parcel = Parcel.obtain();
+ parcel.writeTypedObject(imi, 0);
+ return parcel.marshall();
+ } finally {
+ if (parcel != null) {
+ parcel.recycle();
+ }
+ }
+ }
}
diff --git a/services/core/java/com/android/server/inputmethod/InputMethodManagerInternal.java b/services/core/java/com/android/server/inputmethod/InputMethodManagerInternal.java
index e8543f2..dace67f 100644
--- a/services/core/java/com/android/server/inputmethod/InputMethodManagerInternal.java
+++ b/services/core/java/com/android/server/inputmethod/InputMethodManagerInternal.java
@@ -25,7 +25,7 @@
import android.view.inputmethod.InputMethodInfo;
import com.android.internal.inputmethod.IAccessibilityInputMethodSession;
-import com.android.internal.inputmethod.IInlineSuggestionsRequestCallback;
+import com.android.internal.inputmethod.InlineSuggestionsRequestCallback;
import com.android.internal.inputmethod.InlineSuggestionsRequestInfo;
import com.android.internal.inputmethod.SoftInputShowHideReason;
import com.android.server.LocalServices;
@@ -86,11 +86,11 @@
*
* @param userId the user ID to be queried
* @param requestInfo information needed to create an {@link InlineSuggestionsRequest}.
- * @param cb {@link IInlineSuggestionsRequestCallback} used to pass back the request
+ * @param cb {@link InlineSuggestionsRequestCallback} used to pass back the request
* object
*/
public abstract void onCreateInlineSuggestionsRequest(@UserIdInt int userId,
- InlineSuggestionsRequestInfo requestInfo, IInlineSuggestionsRequestCallback cb);
+ InlineSuggestionsRequestInfo requestInfo, InlineSuggestionsRequestCallback cb);
/**
* Force switch to the enabled input method by {@code imeId} for current user. If the input
@@ -263,7 +263,7 @@
@Override
public void onCreateInlineSuggestionsRequest(@UserIdInt int userId,
InlineSuggestionsRequestInfo requestInfo,
- IInlineSuggestionsRequestCallback cb) {
+ InlineSuggestionsRequestCallback cb) {
}
@Override
diff --git a/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java b/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
index 848f74e..9477380 100644
--- a/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
+++ b/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
@@ -147,7 +147,6 @@
import com.android.internal.inputmethod.IBooleanListener;
import com.android.internal.inputmethod.IConnectionlessHandwritingCallback;
import com.android.internal.inputmethod.IImeTracker;
-import com.android.internal.inputmethod.IInlineSuggestionsRequestCallback;
import com.android.internal.inputmethod.IInputContentUriToken;
import com.android.internal.inputmethod.IInputMethod;
import com.android.internal.inputmethod.IInputMethodClient;
@@ -157,6 +156,7 @@
import com.android.internal.inputmethod.IRemoteAccessibilityInputConnection;
import com.android.internal.inputmethod.IRemoteInputConnection;
import com.android.internal.inputmethod.ImeTracing;
+import com.android.internal.inputmethod.InlineSuggestionsRequestCallback;
import com.android.internal.inputmethod.InlineSuggestionsRequestInfo;
import com.android.internal.inputmethod.InputBindResult;
import com.android.internal.inputmethod.InputMethodDebug;
@@ -262,6 +262,7 @@
private static final int NOT_A_SUBTYPE_ID = InputMethodUtils.NOT_A_SUBTYPE_ID;
private static final String TAG_TRY_SUPPRESSING_IME_SWITCHER = "TrySuppressingImeSwitcher";
private static final String HANDLER_THREAD_NAME = "android.imms";
+ private static final String PACKAGE_MONITOR_THREAD_NAME = "android.imms2";
/**
* When set, {@link #startInputUncheckedLocked} will return
@@ -281,10 +282,35 @@
@NonNull
private final String[] mNonPreemptibleInputMethods;
+ /**
+ * See {@link #shouldEnableExperimentalConcurrentMultiUserMode(Context)} about when set to be
+ * {@code true}.
+ */
+ private final boolean mExperimentalConcurrentMultiUserModeEnabled;
+
+ /**
+ * Returns {@code true} if experimental concurrent multi-user mode is enabled.
+ *
+ * <p>Currently not compatible with profiles (e.g. work profile).</p>
+ *
+ * @param context {@link Context} to be used to query
+ * {@link PackageManager#FEATURE_AUTOMOTIVE}
+ * @return {@code true} if experimental concurrent multi-user mode is enabled.
+ */
+ static boolean shouldEnableExperimentalConcurrentMultiUserMode(@NonNull Context context) {
+ return context.getPackageManager().hasSystemFeature(PackageManager.FEATURE_AUTOMOTIVE)
+ && UserManager.isVisibleBackgroundUsersEnabled()
+ && context.getResources().getBoolean(android.R.bool.config_perDisplayFocusEnabled)
+ && Flags.concurrentInputMethods();
+ }
+
final Context mContext;
final Resources mRes;
private final Handler mHandler;
+ @NonNull
+ private final Handler mPackageMonitorHandler;
+
@MultiUserUnawareField
@UserIdInt
@GuardedBy("ImfLock.class")
@@ -485,16 +511,6 @@
return userData.mBindingController.getSelectedMethodId();
}
- /**
- * The current binding sequence number, incremented every time there is
- * a new bind performed.
- */
- @GuardedBy("ImfLock.class")
- private int getSequenceNumberLocked() {
- final var userData = mUserDataRepository.getOrCreate(mCurrentUserId);
- return userData.mBindingController.getSequenceNumber();
- }
-
@GuardedBy("ImfLock.class")
@Nullable
InputMethodInfo queryInputMethodForCurrentUserLocked(@NonNull String imeId) {
@@ -543,21 +559,6 @@
EditorInfo mCurEditorInfo;
/**
- * Id obtained with {@link InputMethodInfo#getId()} for the input method that we are currently
- * connected to or in the process of connecting to.
- *
- * <p>This can be {@code null} when no input method is connected.</p>
- *
- * @see #getSelectedMethodIdLocked()
- */
- @GuardedBy("ImfLock.class")
- @Nullable
- private String getCurIdLocked() {
- final var userData = mUserDataRepository.getOrCreate(mCurrentUserId);
- return userData.mBindingController.getCurId();
- }
-
- /**
* The current subtype of the current input method.
*/
@MultiUserUnawareField
@@ -587,16 +588,6 @@
boolean mInFullscreenMode;
/**
- * The Intent used to connect to the current input method.
- */
- @GuardedBy("ImfLock.class")
- @Nullable
- private Intent getCurIntentLocked() {
- final var userData = mUserDataRepository.getOrCreate(mCurrentUserId);
- return userData.mBindingController.getCurIntent();
- }
-
- /**
* The token we have made for the currently active input method, to
* identify it in the future.
*/
@@ -612,18 +603,10 @@
*/
@GuardedBy("ImfLock.class")
int getCurTokenDisplayIdLocked() {
- return mCurTokenDisplayId;
+ final var userData = mUserDataRepository.getOrCreate(mCurrentUserId);
+ return userData.mBindingController.getCurTokenDisplayId();
}
- @GuardedBy("ImfLock.class")
- void setCurTokenDisplayIdLocked(int curTokenDisplayId) {
- mCurTokenDisplayId = curTokenDisplayId;
- }
-
- @GuardedBy("ImfLock.class")
- @MultiUserUnawareField
- private int mCurTokenDisplayId = INVALID_DISPLAY;
-
/**
* The display ID of the input method indicates the fallback display which returned by
* {@link #computeImeDisplayIdForTarget}.
@@ -642,15 +625,6 @@
}
/**
- * If not {@link Process#INVALID_UID}, then the UID of {@link #getCurIntentLocked()}.
- */
- @GuardedBy("ImfLock.class")
- private int getCurMethodUidLocked() {
- final var userData = mUserDataRepository.getOrCreate(mCurrentUserId);
- return userData.mBindingController.getCurMethodUid();
- }
-
- /**
* Have we called mCurMethod.bindInput()?
*/
@MultiUserUnawareField
@@ -880,37 +854,6 @@
final class MyPackageMonitor extends PackageMonitor {
/**
- * Package names that are known to contain {@link InputMethodService}.
- *
- * <p>No need to include packages because of direct-boot unaware IMEs since we always rescan
- * all the packages when the user is unlocked, and direct-boot awareness will not be changed
- * dynamically unless the entire package is updated, which also always triggers package
- * rescanning.</p>
- */
- @GuardedBy("ImfLock.class")
- private final ArraySet<String> mKnownImePackageNames = new ArraySet<>();
-
- /**
- * Packages that are appeared, disappeared, or modified for whatever reason.
- *
- * <p>Note: For now we intentionally use {@link ArrayList} instead of {@link ArraySet}
- * because 1) the number of elements is almost always 1 or so, and 2) we do not care
- * duplicate elements for our use case.</p>
- *
- * <p>This object must be accessed only from callback methods in {@link PackageMonitor},
- * which should be bound to {@link #getRegisteredHandler()}.</p>
- */
- private final ArrayList<String> mChangedPackages = new ArrayList<>();
-
- /**
- * {@code true} if one or more packages that contain {@link InputMethodService} appeared.
- *
- * <p>This field must be accessed only from callback methods in {@link PackageMonitor},
- * which should be bound to {@link #getRegisteredHandler()}.</p>
- */
- private boolean mImePackageAppeared = false;
-
- /**
* Remembers package names passed to {@link #onPackageDataCleared(String, int)}.
*
* <p>This field must be accessed only from callback methods in {@link PackageMonitor},
@@ -923,16 +866,6 @@
}
@GuardedBy("ImfLock.class")
- void clearKnownImePackageNamesLocked() {
- mKnownImePackageNames.clear();
- }
-
- @GuardedBy("ImfLock.class")
- void addKnownImePackageNameLocked(@NonNull String packageName) {
- mKnownImePackageNames.add(packageName);
- }
-
- @GuardedBy("ImfLock.class")
private boolean isChangingPackagesOfCurrentUserLocked() {
final int userId = getChangingUserId();
final boolean retval = userId == mCurrentUserId;
@@ -982,52 +915,7 @@
}
@Override
- public void onPackageAppeared(String packageName, int reason) {
- if (!mImePackageAppeared) {
- final PackageManager pm = mContext.getPackageManager();
- final List<ResolveInfo> services = pm.queryIntentServicesAsUser(
- new Intent(InputMethod.SERVICE_INTERFACE).setPackage(packageName),
- PackageManager.MATCH_DISABLED_COMPONENTS, getChangingUserId());
- // No need to lock this because we access it only on getRegisteredHandler().
- if (!services.isEmpty()) {
- mImePackageAppeared = true;
- }
- }
- // No need to lock this because we access it only on getRegisteredHandler().
- mChangedPackages.add(packageName);
- }
-
- @Override
- public void onPackageDisappeared(String packageName, int reason) {
- // No need to lock this because we access it only on getRegisteredHandler().
- mChangedPackages.add(packageName);
- }
-
- @Override
- public void onPackageModified(String packageName) {
- // No need to lock this because we access it only on getRegisteredHandler().
- mChangedPackages.add(packageName);
- }
-
- @Override
- public void onPackagesSuspended(String[] packages) {
- // No need to lock this because we access it only on getRegisteredHandler().
- for (String packageName : packages) {
- mChangedPackages.add(packageName);
- }
- }
-
- @Override
- public void onPackagesUnsuspended(String[] packages) {
- // No need to lock this because we access it only on getRegisteredHandler().
- for (String packageName : packages) {
- mChangedPackages.add(packageName);
- }
- }
-
- @Override
public void onPackageDataCleared(String packageName, int uid) {
- mChangedPackages.add(packageName);
mDataClearedPackages.add(packageName);
}
@@ -1039,37 +927,20 @@
private void clearPackageChangeState() {
// No need to lock them because we access these fields only on getRegisteredHandler().
- mChangedPackages.clear();
mDataClearedPackages.clear();
- mImePackageAppeared = false;
- }
-
- @GuardedBy("ImfLock.class")
- private boolean shouldRebuildInputMethodListLocked() {
- // This method is guaranteed to be called only by getRegisteredHandler().
-
- // If there is any new package that contains at least one IME, then rebuilt the list
- // of IMEs.
- if (mImePackageAppeared) {
- return true;
- }
-
- // Otherwise, check if mKnownImePackageNames and mChangedPackages have any intersection.
- // TODO: Consider to create a utility method to do the following test. List.retainAll()
- // is an option, but it may still do some extra operations that we do not need here.
- final int numPackages = mChangedPackages.size();
- for (int i = 0; i < numPackages; ++i) {
- final String packageName = mChangedPackages.get(i);
- if (mKnownImePackageNames.contains(packageName)) {
- return true;
- }
- }
- return false;
}
private void onFinishPackageChangesInternal() {
+ final int userId = getChangingUserId();
+
+ // Instantiating InputMethodInfo requires disk I/O.
+ // Do them before acquiring the lock to minimize the chances of ANR (b/340221861).
+ final var newMethodMapWithoutAdditionalSubtypes =
+ queryInputMethodServicesInternal(mContext, userId,
+ AdditionalSubtypeMap.EMPTY_MAP, DirectBootAwareness.AUTO)
+ .getMethodMap();
+
synchronized (ImfLock.class) {
- final int userId = getChangingUserId();
final boolean isCurrentUser = (userId == mCurrentUserId);
final AdditionalSubtypeMap additionalSubtypeMap =
AdditionalSubtypeMapRepository.get(userId);
@@ -1117,13 +988,17 @@
AdditionalSubtypeMapRepository.putAndSave(userId, newAdditionalSubtypeMap,
settings.getMethodMap());
}
- if (isCurrentUser
- && !(additionalSubtypeChanged || shouldRebuildInputMethodListLocked())) {
+
+ final var newMethodMap = newMethodMapWithoutAdditionalSubtypes
+ .applyAdditionalSubtypes(newAdditionalSubtypeMap);
+
+ if (InputMethodMap.areSame(settings.getMethodMap(), newMethodMap)) {
+ // No update in the actual IME map.
return;
}
- final InputMethodSettings newSettings = queryInputMethodServicesInternal(mContext,
- userId, newAdditionalSubtypeMap, DirectBootAwareness.AUTO);
+ final InputMethodSettings newSettings =
+ InputMethodSettings.create(newMethodMap, userId);
InputMethodSettingsRepository.put(userId, newSettings);
if (!isCurrentUser) {
return;
@@ -1222,8 +1097,10 @@
public static final class Lifecycle extends SystemService {
private final InputMethodManagerService mService;
+
public Lifecycle(Context context) {
- this(context, new InputMethodManagerService(context));
+ this(context, new InputMethodManagerService(context,
+ shouldEnableExperimentalConcurrentMultiUserMode(context)));
}
public Lifecycle(
@@ -1250,6 +1127,11 @@
public void onUserSwitching(@Nullable TargetUser from, @NonNull TargetUser to) {
// Called on ActivityManager thread.
synchronized (ImfLock.class) {
+ if (mService.mExperimentalConcurrentMultiUserModeEnabled) {
+ // In concurrent multi-user mode, we in general do not rely on the concept of
+ // current user.
+ return;
+ }
mService.scheduleSwitchUserTaskLocked(to.getUserIdentifier(),
/* clientToBeReset= */ null);
}
@@ -1275,9 +1157,15 @@
@Override
public void onUserStarting(TargetUser user) {
// Called on ActivityManager thread.
- SecureSettingsWrapper.onUserStarting(user.getUserIdentifier());
+ final int userId = user.getUserIdentifier();
+ SecureSettingsWrapper.onUserStarting(userId);
synchronized (ImfLock.class) {
- mService.mUserDataRepository.getOrCreate(user.getUserIdentifier());
+ mService.mUserDataRepository.getOrCreate(userId);
+ if (mService.mExperimentalConcurrentMultiUserModeEnabled) {
+ if (mService.mCurrentUserId != userId) {
+ mService.experimentalInitializeVisibleBackgroundUserLocked(userId);
+ }
+ }
}
}
@@ -1298,6 +1186,8 @@
// We need to rebuild IMEs.
postInputMethodSettingUpdatedLocked(false /* resetDefaultEnabledIme */);
updateInputMethodsFromSettingsLocked(true /* enabledChanged */);
+ } else if (mExperimentalConcurrentMultiUserModeEnabled) {
+ experimentalInitializeVisibleBackgroundUserLocked(userId);
}
}
}
@@ -1322,16 +1212,21 @@
mHandler.post(task);
}
- public InputMethodManagerService(Context context) {
- this(context, null, null);
+ public InputMethodManagerService(Context context,
+ boolean experimentalConcurrentMultiUserModeEnabled) {
+ this(context, experimentalConcurrentMultiUserModeEnabled, null, null, null);
}
@VisibleForTesting
InputMethodManagerService(
Context context,
+ boolean experimentalConcurrentMultiUserModeEnabled,
@Nullable ServiceThread serviceThreadForTesting,
+ @Nullable ServiceThread packageMonitorThreadForTesting,
@Nullable IntFunction<InputMethodBindingController> bindingControllerForTesting) {
synchronized (ImfLock.class) {
+ mExperimentalConcurrentMultiUserModeEnabled =
+ experimentalConcurrentMultiUserModeEnabled;
mContext = context;
mRes = context.getResources();
SecureSettingsWrapper.onStart(mContext);
@@ -1347,6 +1242,17 @@
true /* allowIo */);
thread.start();
mHandler = Handler.createAsync(thread.getLooper(), this);
+ {
+ final ServiceThread packageMonitorThread =
+ packageMonitorThreadForTesting != null
+ ? packageMonitorThreadForTesting
+ : new ServiceThread(
+ PACKAGE_MONITOR_THREAD_NAME,
+ Process.THREAD_PRIORITY_FOREGROUND,
+ true /* allowIo */);
+ packageMonitorThread.start();
+ mPackageMonitorHandler = Handler.createAsync(packageMonitorThread.getLooper());
+ }
SystemLocaleWrapper.onStart(context, this::onActionLocaleChanged, mHandler);
mImeTrackerService = new ImeTrackerService(serviceThreadForTesting != null
? serviceThreadForTesting.getLooper() : Looper.getMainLooper());
@@ -1522,7 +1428,8 @@
// Note that in b/197848765 we want to see if we can keep the binding alive for better
// profile switching.
final var userData = mUserDataRepository.getOrCreate(mCurrentUserId);
- userData.mBindingController.unbindCurrentMethod();
+ final var bindingController = userData.mBindingController;
+ bindingController.unbindCurrentMethod();
unbindCurrentClientLocked(UnbindReason.SWITCH_USER);
@@ -1620,7 +1527,7 @@
}
}, "Lazily initialize IMMS#mImeDrawsImeNavBarRes");
- mMyPackageMonitor.register(mContext, UserHandle.ALL, mHandler);
+ mMyPackageMonitor.register(mContext, UserHandle.ALL, mPackageMonitorHandler);
mSettingsObserver.registerContentObserverLocked(currentUserId);
final IntentFilter broadcastFilterForAllUsers = new IntentFilter();
@@ -1648,8 +1555,9 @@
/**
* Returns true iff the caller is identified to be the current input method with the token.
- * @param token The window token given to the input method when it was started.
- * @return true if and only if non-null valid token is specified.
+ *
+ * @param token the window token given to the input method when it was started
+ * @return true if and only if non-null valid token is specified
*/
@GuardedBy("ImfLock.class")
private boolean calledWithValidTokenLocked(@NonNull IBinder token) {
@@ -1741,9 +1649,10 @@
// Check if selected IME of current user supports handwriting.
if (userId == mCurrentUserId) {
final var userData = mUserDataRepository.getOrCreate(userId);
- return userData.mBindingController.supportsStylusHandwriting()
+ final var bindingController = userData.mBindingController;
+ return bindingController.supportsStylusHandwriting()
&& (!connectionless
- || userData.mBindingController.supportsConnectionlessStylusHandwriting());
+ || bindingController.supportsConnectionlessStylusHandwriting());
}
final InputMethodSettings settings = InputMethodSettingsRepository.get(userId);
final InputMethodInfo imi = settings.getMethodMap().get(
@@ -1804,10 +1713,11 @@
/**
* Gets enabled subtypes of the specified {@link InputMethodInfo}.
*
- * @param imiId if null, returns enabled subtypes for the current {@link InputMethodInfo}.
+ * @param imiId if null, returns enabled subtypes for the current
+ * {@link InputMethodInfo}
* @param allowsImplicitlyEnabledSubtypes {@code true} to return the implicitly enabled
- * subtypes.
- * @param userId the user ID to be queried about.
+ * subtypes
+ * @param userId the user ID to be queried about
*/
@Override
public List<InputMethodSubtype> getEnabledInputMethodSubtypeList(String imiId,
@@ -1851,10 +1761,11 @@
* <p>As a general principle, IPCs from the application process that take
* {@link IInputMethodClient} will be rejected without this step.</p>
*
- * @param client {@link android.os.Binder} proxy that is associated with the singleton instance
- * of {@link android.view.inputmethod.InputMethodManager} that runs on the client
- * process
- * @param inputConnection communication channel for the fallback {@link InputConnection}
+ * @param client {@link android.os.Binder} proxy that is associated with the
+ * singleton instance of
+ * {@link android.view.inputmethod.InputMethodManager} that runs
+ * on the client process
+ * @param inputConnection communication channel for the fallback {@link InputConnection}
* @param selfReportedDisplayId self-reported display ID to which the client is associated.
* Whether the client is still allowed to access to this display
* or not needs to be evaluated every time the client interacts
@@ -1879,10 +1790,10 @@
}
}
- // TODO(b/325515685): Move this method to InputMethodBindingController
/**
* Hide the IME if the removed user is the current user.
*/
+ // TODO(b/325515685): Move this method to InputMethodBindingController
@GuardedBy("ImfLock.class")
private void onClientRemoved(ClientState client) {
clearClientSessionLocked(client);
@@ -1915,7 +1826,6 @@
return mClientController.getClient(client.asBinder());
}
- // TODO(b/314150112): Move this to ClientController.
@GuardedBy("ImfLock.class")
void unbindCurrentClientLocked(@UnbindReason int unbindClientReason) {
if (mCurClient != null) {
@@ -1935,7 +1845,14 @@
// all accessibility too. That means, when input method get disconnected (including
// switching ime), we also unbind accessibility
mCurClient.mClient.setActive(false /* active */, false /* fullscreen */);
- mCurClient.mClient.onUnbindMethod(getSequenceNumberLocked(), unbindClientReason);
+
+ // TODO(b/325515685): make binding controller user independent. Before this change, the
+ // following dependencies also need to be user independent: mCurClient, mBoundToMethod,
+ // getCurMethodLocked(), and mMenuController.
+ final var userData = mUserDataRepository.getOrCreate(mCurrentUserId);
+ final var bindingController = userData.mBindingController;
+ mCurClient.mClient.onUnbindMethod(bindingController.getSequenceNumber(),
+ unbindClientReason);
mCurClient.mSessionRequested = false;
mCurClient.mSessionRequestedForAccessibility = false;
mCurClient = null;
@@ -2013,12 +1930,15 @@
final boolean restarting = !initial;
final Binder startInputToken = new Binder();
+ final var userData = mUserDataRepository.getOrCreate(mCurrentUserId);
+ final var bindingController = userData.mBindingController;
final StartInputInfo info = new StartInputInfo(mCurrentUserId,
getCurTokenLocked(),
- mCurTokenDisplayId, getCurIdLocked(), startInputReason, restarting,
- UserHandle.getUserId(mCurClient.mUid),
+ getCurTokenDisplayIdLocked(), bindingController.getCurId(), startInputReason,
+ restarting, UserHandle.getUserId(mCurClient.mUid),
mCurClient.mSelfReportedDisplayId, mImeBindingState.mFocusedWindow, mCurEditorInfo,
- mImeBindingState.mFocusedWindowSoftInputMode, getSequenceNumberLocked());
+ mImeBindingState.mFocusedWindowSoftInputMode,
+ bindingController.getSequenceNumber());
mImeTargetWindowMap.put(startInputToken, mImeBindingState.mFocusedWindow);
mStartInputHistory.addEntry(info);
@@ -2029,8 +1949,8 @@
// INTERACT_ACROSS_USERS(_FULL) permissions, which is actually almost always the case.
if (mCurrentUserId == UserHandle.getUserId(
mCurClient.mUid)) {
- mPackageManagerInternal.grantImplicitAccess(mCurrentUserId,
- null /* intent */, UserHandle.getAppId(getCurMethodUidLocked()),
+ mPackageManagerInternal.grantImplicitAccess(mCurrentUserId, null /* intent */,
+ UserHandle.getAppId(bindingController.getCurMethodUid()),
mCurClient.mUid, true /* direct */);
}
@@ -2051,21 +1971,20 @@
null /* resultReceiver */, SoftInputShowHideReason.ATTACH_NEW_INPUT);
}
- String curId = getCurIdLocked();
+ final var curId = bindingController.getCurId();
final InputMethodInfo curInputMethodInfo = InputMethodSettingsRepository.get(mCurrentUserId)
.getMethodMap().get(curId);
final boolean suppressesSpellChecker =
curInputMethodInfo != null && curInputMethodInfo.suppressesSpellChecker();
final SparseArray<IAccessibilityInputMethodSession> accessibilityInputMethodSessions =
createAccessibilityInputMethodSessions(mCurClient.mAccessibilitySessions);
- final var userData = mUserDataRepository.getOrCreate(mCurrentUserId);
- if (userData.mBindingController.supportsStylusHandwriting() && hasSupportedStylusLocked()) {
+ if (bindingController.supportsStylusHandwriting() && hasSupportedStylusLocked()) {
mHwController.setInkWindowInitializer(new InkWindowInitializer());
}
return new InputBindResult(InputBindResult.ResultCode.SUCCESS_WITH_IME_SESSION,
session.mSession, accessibilityInputMethodSessions,
(session.mChannel != null ? session.mChannel.dup() : null),
- curId, getSequenceNumberLocked(), suppressesSpellChecker);
+ curId, bindingController.getSequenceNumber(), suppressesSpellChecker);
}
@GuardedBy("ImfLock.class")
@@ -2159,7 +2078,8 @@
final boolean connectionWasActive = mCurInputConnection != null;
// Bump up the sequence for this client and attach it.
- userData.mBindingController.advanceSequenceNumber();
+ final var bindingController = userData.mBindingController;
+ bindingController.advanceSequenceNumber();
mCurClient = cs;
mCurInputConnection = inputConnection;
@@ -2182,7 +2102,6 @@
if (connectionIsActive != connectionWasActive) {
mInputManagerInternal.notifyInputMethodConnectionActive(connectionIsActive);
}
- final var bindingController = userData.mBindingController;
// If configured, we want to avoid starting up the IME if it is not supposed to be showing
if (shouldPreventImeStartupLocked(selectedMethodId, startInputFlags,
@@ -2200,7 +2119,7 @@
// display ID.
final String curId = bindingController.getCurId();
if (curId != null && curId.equals(bindingController.getSelectedMethodId())
- && mDisplayIdToShowIme == mCurTokenDisplayId) {
+ && mDisplayIdToShowIme == getCurTokenDisplayIdLocked()) {
if (cs.mCurSession != null) {
// Fast case: if we are already connected to the input method,
// then just return it.
@@ -2231,11 +2150,13 @@
/**
* Update the current deviceId and return the relevant imeId for this device.
- * 1. If the device changes to virtual and its custom IME is not available, then disable IME.
- * 2. If the device changes to virtual with valid custom IME, then return the custom IME. If
- * the old device was default, then store the current imeId so it can be restored.
- * 3. If the device changes to default, restore the default device IME.
- * 4. Otherwise keep the current imeId.
+ *
+ * <p>1. If the device changes to virtual and its custom IME is not available, then disable
+ * IME.</p>
+ * <p>2. If the device changes to virtual with valid custom IME, then return the custom IME. If
+ * the old device was default, then store the current imeId so it can be restored.</p>
+ * <p>3. If the device changes to default, restore the default device IME.</p>
+ * <p>4. Otherwise keep the current imeId.</p>
*/
@GuardedBy("ImfLock.class")
private String computeCurrentDeviceMethodIdLocked(String currentMethodId) {
@@ -2333,7 +2254,8 @@
@Nullable
private InputBindResult tryReuseConnectionLocked(@NonNull UserDataRepository.UserData userData,
@NonNull ClientState cs) {
- if (userData.mBindingController.hasMainConnection()) {
+ final var bindingController = userData.mBindingController;
+ if (bindingController.hasMainConnection()) {
if (getCurMethodLocked() != null) {
// Return to client, and we will get back with it when
// we have had a session made for it.
@@ -2341,9 +2263,11 @@
requestClientSessionForAccessibilityLocked(cs);
return new InputBindResult(
InputBindResult.ResultCode.SUCCESS_WAITING_IME_SESSION,
- null, null, null, getCurIdLocked(), getSequenceNumberLocked(), false);
+ null, null, null,
+ bindingController.getCurId(),
+ bindingController.getSequenceNumber(), false);
} else {
- final long lastBindTime = userData.mBindingController.getLastBindTime();
+ final long lastBindTime = bindingController.getLastBindTime();
long bindingDuration = SystemClock.uptimeMillis() - lastBindTime;
if (bindingDuration < TIME_TO_RECONNECT) {
// In this case we have connected to the service, but
@@ -2355,7 +2279,9 @@
// to see if we can get back in touch with the service.
return new InputBindResult(
InputBindResult.ResultCode.SUCCESS_WAITING_IME_BINDING,
- null, null, null, getCurIdLocked(), getSequenceNumberLocked(), false);
+ null, null, null,
+ bindingController.getCurId(),
+ bindingController.getSequenceNumber(), false);
} else {
EventLog.writeEvent(EventLogTags.IMF_FORCE_RECONNECT_IME,
getSelectedMethodIdLocked(), bindingDuration, 0);
@@ -2374,12 +2300,12 @@
/**
* Find the display where the IME should be shown.
*
- * @param displayId the ID of the display where the IME client target is.
- * @param checker instance of {@link ImeDisplayValidator} which is used for
- * checking display config to adjust the final target display.
- * @return The ID of the display where the IME should be shown or
- * {@link android.view.Display#INVALID_DISPLAY} if the display has an ImePolicy of
- * {@link WindowManager#DISPLAY_IME_POLICY_HIDE}.
+ * @param displayId the ID of the display where the IME client target is
+ * @param checker instance of {@link ImeDisplayValidator} which is used for
+ * checking display config to adjust the final target display
+ * @return the ID of the display where the IME should be shown or
+ * {@link android.view.Display#INVALID_DISPLAY} if the display has an ImePolicy of
+ * {@link WindowManager#DISPLAY_IME_POLICY_HIDE}
*/
static int computeImeDisplayIdForTarget(int displayId, @NonNull ImeDisplayValidator checker) {
if (displayId == DEFAULT_DISPLAY || displayId == INVALID_DISPLAY) {
@@ -2402,7 +2328,7 @@
void initializeImeLocked(@NonNull IInputMethodInvoker inputMethod, @NonNull IBinder token) {
if (DEBUG) {
Slog.v(TAG, "Sending attach of token: " + token + " for display: "
- + mCurTokenDisplayId);
+ + getCurTokenDisplayIdLocked());
}
inputMethod.initializeInternal(token, new InputMethodPrivilegedOperationsImpl(this, token),
getInputMethodNavButtonFlagsLocked());
@@ -2476,19 +2402,19 @@
mImeWindowVis = 0;
mBackDisposition = InputMethodService.BACK_DISPOSITION_DEFAULT;
updateSystemUiLocked(mImeWindowVis, mBackDisposition);
- mCurTokenDisplayId = INVALID_DISPLAY;
mAutofillController.onResetSystemUi();
}
@GuardedBy("ImfLock.class")
void resetCurrentMethodAndClientLocked(@UnbindReason int unbindClientReason) {
- final var userData = mUserDataRepository.getOrCreate(mCurrentUserId);
- userData.mBindingController.setSelectedMethodId(null);
+ final var bindingController =
+ mUserDataRepository.getOrCreate(mCurrentUserId).mBindingController;
+ bindingController.setSelectedMethodId(null);
// Callback before clean-up binding states.
// TODO(b/338461930): Check if this is still necessary or not.
onUnbindCurrentMethodByReset();
- userData.mBindingController.unbindCurrentMethod();
+ bindingController.unbindCurrentMethod();
unbindCurrentClientLocked(unbindClientReason);
}
@@ -2691,9 +2617,10 @@
}
// Whether the current display has a navigation bar. When this is false (e.g. emulator),
// the IME should not draw the IME navigation bar.
+ final int tokenDisplayId = getCurTokenDisplayIdLocked();
final boolean hasNavigationBar = mWindowManagerInternal
- .hasNavigationBar(mCurTokenDisplayId != INVALID_DISPLAY
- ? mCurTokenDisplayId : DEFAULT_DISPLAY);
+ .hasNavigationBar(tokenDisplayId != INVALID_DISPLAY
+ ? tokenDisplayId : DEFAULT_DISPLAY);
final boolean canImeDrawsImeNavBar =
mImeDrawsImeNavBarRes != null && mImeDrawsImeNavBarRes.get() && hasNavigationBar;
final boolean shouldShowImeSwitcherWhenImeIsShown = shouldShowImeSwitcherLocked(
@@ -2709,7 +2636,8 @@
// When the IME switcher dialog is shown, the IME switcher button should be hidden.
if (mMenuController.getSwitchingDialogLocked() != null) return false;
// When we are switching IMEs, the IME switcher button should be hidden.
- if (!Objects.equals(getCurIdLocked(), getSelectedMethodIdLocked())) {
+ final var userData = mUserDataRepository.getOrCreate(mCurrentUserId);
+ if (!Objects.equals(userData.mBindingController.getCurId(), getSelectedMethodIdLocked())) {
return false;
}
if (mWindowManagerInternal.isKeyguardShowingAndNotOccluded()
@@ -2788,8 +2716,8 @@
// Note that we still need to update IME status when focusing external display
// that does not support system decoration and fallback to show IME on default
// display since it is intentional behavior.
- if (mCurTokenDisplayId != topFocusedDisplayId
- && mCurTokenDisplayId != FALLBACK_DISPLAY_ID) {
+ final int tokenDisplayId = getCurTokenDisplayIdLocked();
+ if (tokenDisplayId != topFocusedDisplayId && tokenDisplayId != FALLBACK_DISPLAY_ID) {
return;
}
mImeWindowVis = vis;
@@ -2852,7 +2780,7 @@
Slog.d(TAG, "IME window vis: " + vis
+ " active: " + (vis & InputMethodService.IME_ACTIVE)
+ " inv: " + (vis & InputMethodService.IME_INVISIBLE)
- + " displayId: " + mCurTokenDisplayId);
+ + " displayId: " + getCurTokenDisplayIdLocked());
}
final IBinder focusedWindowToken = mImeBindingState != null
? mImeBindingState.mFocusedWindow : null;
@@ -2871,15 +2799,17 @@
} else {
vis &= ~InputMethodService.IME_VISIBLE_IMPERCEPTIBLE;
}
+ final var userData = mUserDataRepository.getOrCreate(mCurrentUserId);
+ final var curId = userData.mBindingController.getCurId();
if (mMenuController.getSwitchingDialogLocked() != null
- || !Objects.equals(getCurIdLocked(), getSelectedMethodIdLocked())) {
+ || !Objects.equals(curId, getSelectedMethodIdLocked())) {
// When the IME switcher dialog is shown, or we are switching IMEs,
// the back button should be in the default state (as if the IME is not shown).
backDisposition = InputMethodService.BACK_DISPOSITION_ADJUST_NOTHING;
}
final boolean needsToShowImeSwitcher = shouldShowImeSwitcherLocked(vis);
if (mStatusBarManagerInternal != null) {
- mStatusBarManagerInternal.setImeWindowStatus(mCurTokenDisplayId,
+ mStatusBarManagerInternal.setImeWindowStatus(getCurTokenDisplayIdLocked(),
getCurTokenLocked(), vis, backDisposition, needsToShowImeSwitcher);
}
} finally {
@@ -2893,6 +2823,49 @@
mMenuController.updateKeyboardFromSettingsLocked();
}
+ /**
+ * This is an experimental implementation used when and only when
+ * {@link #mExperimentalConcurrentMultiUserModeEnabled}.
+ *
+ * <p>Never assume what this method is doing is officially supported. For the canonical and
+ * desired behaviors always refer to single-user code paths such as
+ * {@link #updateInputMethodsFromSettingsLocked(boolean)}.</p>
+ *
+ * <p>Here are examples of missing features.</p>
+ * <ul>
+ * <li>Subtypes are not supported at all!</li>
+ * <li>Profiles are not supported.</li>
+ * <li>
+ * {@link PackageManager#COMPONENT_ENABLED_STATE_DISABLED_UNTIL_USED} is not updated.
+ * </li>
+ * <li>{@link #mDeviceIdToShowIme} is ignored.</li>
+ * <li>{@link #mSwitchingController} is ignored.</li>
+ * <li>{@link #mHardwareKeyboardShortcutController} is ignored.</li>
+ * <li>{@link #mPreventImeStartupUnlessTextEditor} is ignored.</li>
+ * <li>and so on.</li>
+ * </ul>
+ */
+ @GuardedBy("ImfLock.class")
+ void experimentalInitializeVisibleBackgroundUserLocked(@UserIdInt int userId) {
+ if (!mUserManagerInternal.isUserVisible(userId)) {
+ return;
+ }
+ final var settings = InputMethodSettingsRepository.get(userId);
+ String id = settings.getSelectedInputMethod();
+ if (TextUtils.isEmpty(id)) {
+ final InputMethodInfo imi = InputMethodInfoUtils.getMostApplicableDefaultIME(
+ settings.getEnabledInputMethodList());
+ if (imi == null) {
+ return;
+ }
+ id = imi.getId();
+ settings.putSelectedInputMethod(id);
+ }
+ final var userData = mUserDataRepository.getOrCreate(userId);
+ final var bindingController = userData.mBindingController;
+ bindingController.setSelectedMethodId(id);
+ }
+
@GuardedBy("ImfLock.class")
void updateInputMethodsFromSettingsLocked(boolean enabledMayChange) {
final InputMethodSettings settings = InputMethodSettingsRepository.get(mCurrentUserId);
@@ -3567,12 +3540,15 @@
"InputMethodManagerService#startInputOrWindowGainedFocus", mDumper);
final InputBindResult result;
synchronized (ImfLock.class) {
+ final var userData = mUserDataRepository.getOrCreate(userId);
+ final var bindingController = userData.mBindingController;
// If the system is not yet ready, we shouldn't be running third party code.
if (!mSystemReady) {
return new InputBindResult(
InputBindResult.ResultCode.ERROR_SYSTEM_NOT_READY,
null /* method */, null /* accessibilitySessions */, null /* channel */,
- getSelectedMethodIdLocked(), getSequenceNumberLocked(),
+ getSelectedMethodIdLocked(),
+ bindingController.getSequenceNumber(),
false /* isInputMethodSuppressingSpellChecker */);
}
final ClientState cs = mClientController.getClient(client.asBinder());
@@ -3582,7 +3558,8 @@
final long ident = Binder.clearCallingIdentity();
try {
// Verify if IMMS is in the process of switching user.
- if (mUserSwitchHandlerTask != null) {
+ if (!mExperimentalConcurrentMultiUserModeEnabled
+ && mUserSwitchHandlerTask != null) {
// There is already an on-going pending user switch task.
final int nextUserId = mUserSwitchHandlerTask.mToUserId;
if (userId == nextUserId) {
@@ -3636,7 +3613,7 @@
}
// Verify if caller is a background user.
- if (userId != mCurrentUserId) {
+ if (!mExperimentalConcurrentMultiUserModeEnabled && userId != mCurrentUserId) {
if (ArrayUtils.contains(
mUserManagerInternal.getProfileIds(mCurrentUserId, false),
userId)) {
@@ -3664,7 +3641,7 @@
result = startInputOrWindowGainedFocusInternalLocked(startInputReason,
client, windowToken, startInputFlags, softInputMode, windowFlags,
editorInfo, inputConnection, remoteAccessibilityInputConnection,
- unverifiedTargetSdkVersion, userId, imeDispatcher, cs);
+ unverifiedTargetSdkVersion, userData, imeDispatcher, cs);
} finally {
Binder.restoreCallingIdentity(ident);
}
@@ -3692,7 +3669,7 @@
@SoftInputModeFlags int softInputMode, int windowFlags, EditorInfo editorInfo,
IRemoteInputConnection inputContext,
@Nullable IRemoteAccessibilityInputConnection remoteAccessibilityInputConnection,
- int unverifiedTargetSdkVersion, @UserIdInt int userId,
+ int unverifiedTargetSdkVersion, @NonNull UserDataRepository.UserData userData,
@NonNull ImeOnBackInvokedDispatcher imeDispatcher, @NonNull ClientState cs) {
if (DEBUG) {
Slog.v(TAG, "startInputOrWindowGainedFocusInternalLocked: reason="
@@ -3705,7 +3682,7 @@
+ " softInputMode=" + InputMethodDebug.softInputModeToString(softInputMode)
+ " windowFlags=#" + Integer.toHexString(windowFlags)
+ " unverifiedTargetSdkVersion=" + unverifiedTargetSdkVersion
- + " userId=" + userId
+ + " userData=" + userData
+ " imeDispatcher=" + imeDispatcher
+ " cs=" + cs);
}
@@ -3724,7 +3701,6 @@
startInputByWinGainedFocus, toolType);
mVisibilityStateComputer.setWindowState(windowToken, windowState);
- final var userData = mUserDataRepository.getOrCreate(userId);
if (sameWindowFocused && isTextEditor) {
if (DEBUG) {
Slog.w(TAG, "Window already focused, ignoring focus gain of: " + client
@@ -3742,7 +3718,8 @@
null, null, null, null, -1, false);
}
- mImeBindingState = new ImeBindingState(windowToken, softInputMode, cs, editorInfo);
+ mImeBindingState = new ImeBindingState(userData.mUserId, windowToken, softInputMode, cs,
+ editorInfo);
mFocusedWindowPerceptible.put(windowToken, true);
// We want to start input before showing the IME, but after closing
@@ -3781,7 +3758,7 @@
// window token removed.
// Note that we can trust client's display ID as long as it matches
// to the display ID obtained from the window.
- if (cs.mSelfReportedDisplayId != mCurTokenDisplayId) {
+ if (cs.mSelfReportedDisplayId != getCurTokenDisplayIdLocked()) {
userData.mBindingController.unbindCurrentMethod();
}
}
@@ -3832,10 +3809,10 @@
if (mCurrentUserId != UserHandle.getUserId(uid)) {
return false;
}
- if (getCurIntentLocked() != null && InputMethodUtils.checkIfPackageBelongsToUid(
- mPackageManagerInternal,
- uid,
- getCurIntentLocked().getComponent().getPackageName())) {
+ final var userData = mUserDataRepository.getOrCreate(mCurrentUserId);
+ final var curIntent = userData.mBindingController.getCurIntent();
+ if (curIntent != null && InputMethodUtils.checkIfPackageBelongsToUid(
+ mPackageManagerInternal, uid, curIntent.getComponent().getPackageName())) {
return true;
}
return false;
@@ -4145,7 +4122,6 @@
* This is kept due to {@code @UnsupportedAppUsage} in
* {@link InputMethodManager#getInputMethodWindowVisibleHeight()} and a dependency in
* {@link InputMethodService#onCreate()}.
- *
* @return {@link WindowManagerInternal#getInputMethodWindowVisibleHeight(int)}
*
* @deprecated TODO(b/113914148): Check if we can remove this
@@ -4163,7 +4139,7 @@
}
// This should probably use the caller's display id, but because this is unsupported
// and maintained only for compatibility, there's no point in fixing it.
- curTokenDisplayId = mCurTokenDisplayId;
+ curTokenDisplayId = getCurTokenDisplayIdLocked();
}
return mWindowManagerInternal.getInputMethodWindowVisibleHeight(curTokenDisplayId);
});
@@ -4244,8 +4220,9 @@
// a new Stylus is detected. If IME supports handwriting, and we don't have
// handwriting initialized, lets do it now.
final var userData = mUserDataRepository.getOrCreate(mCurrentUserId);
+ final var bindingController = userData.mBindingController;
if (!mHwController.getCurrentRequestId().isPresent()
- && userData.mBindingController.supportsStylusHandwriting()) {
+ && bindingController.supportsStylusHandwriting()) {
scheduleResetStylusHandwriting();
}
}
@@ -4303,7 +4280,8 @@
/**
* Helper method to set a stylus idle-timeout after which handwriting {@code InkWindow}
* will be removed.
- * @param timeout to set in milliseconds. To reset to default, use a value <= zero.
+ *
+ * @param timeout to set in milliseconds. To reset to default, use a value <= zero
*/
@BinderThread
@IInputMethodManagerImpl.PermissionVerified(Manifest.permission.TEST_INPUT_METHOD)
@@ -4334,11 +4312,8 @@
private static IntArray getStylusInputDeviceIds(InputManager im) {
IntArray stylusIds = new IntArray();
for (int id : im.getInputDeviceIds()) {
- if (!im.isInputDeviceEnabled(id)) {
- continue;
- }
InputDevice device = im.getInputDevice(id);
- if (device != null && isStylusDevice(device)) {
+ if (device != null && device.isEnabled() && isStylusDevice(device)) {
stylusIds.add(id);
}
}
@@ -4427,9 +4402,10 @@
private void dumpDebug(ProtoOutputStream proto, long fieldId) {
synchronized (ImfLock.class) {
final var userData = mUserDataRepository.getOrCreate(mCurrentUserId);
+ final var bindingController = userData.mBindingController;
final long token = proto.start(fieldId);
proto.write(CUR_METHOD_ID, getSelectedMethodIdLocked());
- proto.write(CUR_SEQ, getSequenceNumberLocked());
+ proto.write(CUR_SEQ, bindingController.getSequenceNumber());
proto.write(CUR_CLIENT, Objects.toString(mCurClient));
mImeBindingState.dumpDebug(proto, mWindowManagerInternal);
proto.write(LAST_IME_TARGET_WINDOW_NAME,
@@ -4439,13 +4415,13 @@
if (mCurEditorInfo != null) {
mCurEditorInfo.dumpDebug(proto, CUR_ATTRIBUTE);
}
- proto.write(CUR_ID, getCurIdLocked());
+ proto.write(CUR_ID, bindingController.getCurId());
mVisibilityStateComputer.dumpDebug(proto, fieldId);
proto.write(IN_FULLSCREEN_MODE, mInFullscreenMode);
proto.write(CUR_TOKEN, Objects.toString(getCurTokenLocked()));
- proto.write(CUR_TOKEN_DISPLAY_ID, mCurTokenDisplayId);
+ proto.write(CUR_TOKEN_DISPLAY_ID, getCurTokenDisplayIdLocked());
proto.write(SYSTEM_READY, mSystemReady);
- proto.write(HAVE_CONNECTION, userData.mBindingController.hasMainConnection());
+ proto.write(HAVE_CONNECTION, bindingController.hasMainConnection());
proto.write(BOUND_TO_METHOD, mBoundToMethod);
proto.write(IS_INTERACTIVE, mIsInteractive);
proto.write(BACK_DISPOSITION, mBackDisposition);
@@ -4557,7 +4533,8 @@
final IBinder requestToken = mVisibilityStateComputer.getWindowTokenFrom(requestImeToken);
final WindowManagerInternal.ImeTargetInfo info =
mWindowManagerInternal.onToggleImeRequested(
- show, mImeBindingState.mFocusedWindow, requestToken, mCurTokenDisplayId);
+ show, mImeBindingState.mFocusedWindow, requestToken,
+ getCurTokenDisplayIdLocked());
mSoftInputShowHideHistory.addEntry(new SoftInputShowHideHistory.Entry(
mImeBindingState.mFocusedWindowClient, mImeBindingState.mFocusedWindowEditorInfo,
info.focusedWindowName, mImeBindingState.mFocusedWindowSoftInputMode, reason,
@@ -4816,10 +4793,11 @@
case MSG_RESET_HANDWRITING: {
synchronized (ImfLock.class) {
final var userData = mUserDataRepository.getOrCreate(mCurrentUserId);
- if (userData.mBindingController.supportsStylusHandwriting()
+ final var bindingController = userData.mBindingController;
+ if (bindingController.supportsStylusHandwriting()
&& getCurMethodLocked() != null && hasSupportedStylusLocked()) {
Slog.d(TAG, "Initializing Handwriting Spy");
- mHwController.initializeHandwritingSpy(mCurTokenDisplayId);
+ mHwController.initializeHandwritingSpy(getCurTokenDisplayIdLocked());
} else {
mHwController.reset();
}
@@ -4842,11 +4820,12 @@
return true;
}
final var userData = mUserDataRepository.getOrCreate(mCurrentUserId);
+ final var bindingController = userData.mBindingController;
final HandwritingModeController.HandwritingSession session =
mHwController.startHandwritingSession(
msg.arg1 /*requestId*/,
msg.arg2 /*pid*/,
- userData.mBindingController.getCurMethodUid(),
+ bindingController.getCurMethodUid(),
mImeBindingState.mFocusedWindow);
if (session == null) {
Slog.e(TAG,
@@ -4896,7 +4875,11 @@
if (mCurClient == null || mCurClient.mClient == null) {
return;
}
- if (mImePlatformCompatUtils.shouldUseSetInteractiveProtocol(getCurMethodUidLocked())) {
+ // TODO(b/325515685): user data must be retrieved by a userId parameter
+ final var userData = mUserDataRepository.getOrCreate(mCurrentUserId);
+ final var bindingController = userData.mBindingController;
+ if (mImePlatformCompatUtils.shouldUseSetInteractiveProtocol(
+ bindingController.getCurMethodUid())) {
// Handle IME visibility when interactive changed before finishing the input to
// ensure we preserve the last state as possible.
final ImeVisibilityResult imeVisRes = mVisibilityStateComputer.onInteractiveChanged(
@@ -5031,30 +5014,9 @@
return;
}
mMethodMapUpdateCount++;
- mMyPackageMonitor.clearKnownImePackageNamesLocked();
final InputMethodSettings settings = InputMethodSettingsRepository.get(mCurrentUserId);
- // Construct the set of possible IME packages for onPackageChanged() to avoid false
- // negatives when the package state remains to be the same but only the component state is
- // changed.
- {
- // Here we intentionally use PackageManager.MATCH_DISABLED_COMPONENTS since the purpose
- // of this query is to avoid false negatives. PackageManager.MATCH_ALL could be more
- // conservative, but it seems we cannot use it for now (Issue 35176630).
- final List<ResolveInfo> allInputMethodServices =
- mContext.getPackageManager().queryIntentServicesAsUser(
- new Intent(InputMethod.SERVICE_INTERFACE),
- PackageManager.MATCH_DISABLED_COMPONENTS, settings.getUserId());
- final int numImes = allInputMethodServices.size();
- for (int i = 0; i < numImes; ++i) {
- final ServiceInfo si = allInputMethodServices.get(i).serviceInfo;
- if (android.Manifest.permission.BIND_INPUT_METHOD.equals(si.permission)) {
- mMyPackageMonitor.addKnownImePackageNameLocked(si.packageName);
- }
- }
- }
-
boolean reenableMinimumNonAuxSystemImes = false;
// TODO: The following code should find better place to live.
if (!resetDefaultEnabledIme) {
@@ -5141,7 +5103,8 @@
@GuardedBy("ImfLock.class")
void sendOnNavButtonFlagsChangedLocked() {
final var userData = mUserDataRepository.getOrCreate(mCurrentUserId);
- final IInputMethodInvoker curMethod = userData.mBindingController.getCurMethod();
+ final var bindingController = userData.mBindingController;
+ final IInputMethodInvoker curMethod = bindingController.getCurMethod();
if (curMethod == null) {
// No need to send the data if the IME is not yet bound.
return;
@@ -5186,10 +5149,10 @@
/**
* Enable or disable the given IME by updating {@link Settings.Secure#ENABLED_INPUT_METHODS}.
*
- * @param id ID of the IME is to be manipulated. It is OK to pass IME ID that is currently not
- * recognized by the system.
- * @param enabled {@code true} if {@code id} needs to be enabled.
- * @return {@code true} if the IME was previously enabled. {@code false} otherwise.
+ * @param id ID of the IME is to be manipulated. It is OK to pass IME ID that is currently
+ * not recognized by the system
+ * @param enabled {@code true} if {@code id} needs to be enabled
+ * @return {@code true} if the IME was previously enabled
*/
@GuardedBy("ImfLock.class")
private boolean setInputMethodEnabledLocked(String id, boolean enabled) {
@@ -5296,8 +5259,8 @@
/**
* Gets the current subtype of this input method.
*
- * @param userId User ID to be queried about.
- * @return The current {@link InputMethodSubtype} for the specified user.
+ * @param userId User ID to be queried about
+ * @return the current {@link InputMethodSubtype} for the specified user
*/
@Nullable
@Override
@@ -5371,7 +5334,8 @@
/**
* Returns the default {@link InputMethodInfo} for the specific userId.
- * @param userId user ID to query.
+ *
+ * @param userId user ID to query
*/
@GuardedBy("ImfLock.class")
private InputMethodInfo queryDefaultInputMethodForUserIdLocked(@UserIdInt int userId) {
@@ -5405,11 +5369,11 @@
* Filter the access to the input method by rules of the package visibility. Return {@code true}
* if the given input method is the currently selected one or visible to the caller.
*
- * @param targetPkgName The package name of input method to check.
- * @param callingUid The caller that is going to access the input method.
- * @param userId The user ID where the input method resides.
- * @param settings The input method settings under the given user ID.
- * @return {@code true} if caller is able to access the input method.
+ * @param targetPkgName the package name of input method to check
+ * @param callingUid the caller that is going to access the input method
+ * @param userId the user ID where the input method resides
+ * @param settings the input method settings under the given user ID
+ * @return {@code true} if caller is able to access the input method
*/
private boolean canCallerAccessInputMethod(@NonNull String targetPkgName, int callingUid,
@UserIdInt int userId, @NonNull InputMethodSettings settings) {
@@ -5503,7 +5467,7 @@
@Override
public void onCreateInlineSuggestionsRequest(@UserIdInt int userId,
- InlineSuggestionsRequestInfo requestInfo, IInlineSuggestionsRequestCallback cb) {
+ InlineSuggestionsRequestInfo requestInfo, InlineSuggestionsRequestCallback cb) {
// Get the device global touch exploration state before lock to avoid deadlock.
final boolean touchExplorationEnabled = AccessibilityManagerInternal.get()
.isTouchExplorationEnabled(userId);
@@ -5575,7 +5539,7 @@
//TODO(b/150843766): Check if Input Token is valid.
final IBinder curHostInputToken;
synchronized (ImfLock.class) {
- if (displayId != mCurTokenDisplayId) {
+ if (displayId != getCurTokenDisplayIdLocked()) {
return false;
}
curHostInputToken = mAutofillController.getCurHostInputToken();
@@ -5626,6 +5590,8 @@
public void onSessionForAccessibilityCreated(int accessibilityConnectionId,
IAccessibilityInputMethodSession session, @UserIdInt int userId) {
synchronized (ImfLock.class) {
+ final var userData = mUserDataRepository.getOrCreate(mCurrentUserId);
+ final var bindingController = userData.mBindingController;
// TODO(b/305829876): Implement user ID verification
if (mCurClient != null) {
clearClientSessionForAccessibilityLocked(mCurClient, accessibilityConnectionId);
@@ -5647,8 +5613,10 @@
mCurClient.mAccessibilitySessions);
final InputBindResult res = new InputBindResult(
InputBindResult.ResultCode.SUCCESS_WITH_ACCESSIBILITY_SESSION,
- imeSession, accessibilityInputMethodSessions, null, getCurIdLocked(),
- getSequenceNumberLocked(), false);
+ imeSession, accessibilityInputMethodSessions, /* channel= */ null,
+ bindingController.getCurId(),
+ bindingController.getSequenceNumber(),
+ /* isInputMethodSuppressingSpellChecker= */ false);
mCurClient.mClient.onBindAccessibilityService(res, accessibilityConnectionId);
}
}
@@ -5658,6 +5626,8 @@
public void unbindAccessibilityFromCurrentClient(int accessibilityConnectionId,
@UserIdInt int userId) {
synchronized (ImfLock.class) {
+ final var userData = mUserDataRepository.getOrCreate(mCurrentUserId);
+ final var bindingController = userData.mBindingController;
// TODO(b/305829876): Implement user ID verification
if (mCurClient != null) {
if (DEBUG) {
@@ -5667,7 +5637,7 @@
// A11yManagerService unbinds the disabled accessibility service. We don't need
// to do it here.
mCurClient.mClient.onUnbindAccessibilityService(
- getSequenceNumberLocked(),
+ bindingController.getSequenceNumber(),
accessibilityConnectionId);
}
// We only have sessions when we bound to an input method. Remove this session
@@ -5877,13 +5847,11 @@
@SuppressWarnings("GuardedBy") Consumer<ClientState> clientControllerDump = c -> {
p.println(" " + c + ":");
p.println(" client=" + c.mClient);
-
p.println(" fallbackInputConnection="
+ c.mFallbackInputConnection);
p.println(" sessionRequested="
+ c.mSessionRequested);
- p.println(
- " sessionRequestedForAccessibility="
+ p.println(" sessionRequestedForAccessibility="
+ c.mSessionRequestedForAccessibility);
p.println(" curSession=" + c.mCurSession);
p.println(" selfReportedDisplayId=" + c.mSelfReportedDisplayId);
@@ -5891,17 +5859,20 @@
p.println(" pid=" + c.mPid);
};
mClientController.forAllClients(clientControllerDump);
+ final var userData = mUserDataRepository.getOrCreate(mCurrentUserId);
+ final var bindingController = userData.mBindingController;
p.println(" mCurrentUserId=" + mCurrentUserId);
p.println(" mCurMethodId=" + getSelectedMethodIdLocked());
client = mCurClient;
- p.println(" mCurClient=" + client + " mCurSeq=" + getSequenceNumberLocked());
+ p.println(" mCurClient=" + client + " mCurSeq="
+ + bindingController.getSequenceNumber());
p.println(" mFocusedWindowPerceptible=" + mFocusedWindowPerceptible);
mImeBindingState.dump(/* prefix= */ " ", p);
- final var userData = mUserDataRepository.getOrCreate(mCurrentUserId);
- p.println(" mCurId=" + getCurIdLocked()
- + " mHaveConnection=" + userData.mBindingController.hasMainConnection()
+
+ p.println(" mCurId=" + bindingController.getCurId()
+ + " mHaveConnection=" + bindingController.hasMainConnection()
+ " mBoundToMethod=" + mBoundToMethod + " mVisibleBound="
- + userData.mBindingController.isVisibleBound());
+ + bindingController.isVisibleBound());
p.println(" mUserDataRepository=");
// TODO(b/324907325): Remove the suppress warnings once b/324907325 is fixed.
@@ -5915,15 +5886,17 @@
mUserDataRepository.forAllUserData(userDataDump);
p.println(" mCurToken=" + getCurTokenLocked());
- p.println(" mCurTokenDisplayId=" + mCurTokenDisplayId);
+ p.println(" mCurTokenDisplayId=" + getCurTokenDisplayIdLocked());
p.println(" mCurHostInputToken=" + mAutofillController.getCurHostInputToken());
- p.println(" mCurIntent=" + getCurIntentLocked());
+ p.println(" mCurIntent=" + bindingController.getCurIntent());
method = getCurMethodLocked();
p.println(" mCurMethod=" + getCurMethodLocked());
p.println(" mEnabledSession=" + mEnabledSession);
mVisibilityStateComputer.dump(pw, " ");
p.println(" mInFullscreenMode=" + mInFullscreenMode);
p.println(" mSystemReady=" + mSystemReady + " mInteractive=" + mIsInteractive);
+ p.println(" mExperimentalConcurrentMultiUserModeEnabled="
+ + mExperimentalConcurrentMultiUserModeEnabled);
p.println(" ENABLE_HIDE_IME_CAPTION_BAR="
+ InputMethodService.ENABLE_HIDE_IME_CAPTION_BAR);
p.println(" mSettingsObserver=" + mSettingsObserver);
@@ -6156,8 +6129,9 @@
/**
* Handles {@code adb shell ime list}.
- * @param shellCommand {@link ShellCommand} object that is handling this command.
- * @return Exit code of the command.
+ *
+ * @param shellCommand {@link ShellCommand} object that is handling this command
+ * @return exit code of the command
*/
@BinderThread
@ShellCommandResult
@@ -6215,9 +6189,9 @@
/**
* Handles {@code adb shell ime enable} and {@code adb shell ime disable}.
*
- * @param shellCommand {@link ShellCommand} object that is handling this command.
- * @param enabled {@code true} if the command was {@code adb shell ime enable}.
- * @return Exit code of the command.
+ * @param shellCommand {@link ShellCommand} object that is handling this command
+ * @param enabled {@code true} if the command was {@code adb shell ime enable}
+ * @return exit code of the command
*/
@BinderThread
@ShellCommandResult
@@ -6252,8 +6226,8 @@
* {@link ShellCommand#getNextArg()} and {@link ShellCommand#getNextArgRequired()} for the
* main arguments.</p>
*
- * @param shellCommand {@link ShellCommand} from which options should be obtained.
- * @return User ID to be resolved. {@link UserHandle#CURRENT} if not specified.
+ * @param shellCommand {@link ShellCommand} from which options should be obtained
+ * @return user ID to be resolved. {@link UserHandle#CURRENT} if not specified
*/
@BinderThread
@UserIdInt
@@ -6275,12 +6249,12 @@
/**
* Handles core logic of {@code adb shell ime enable} and {@code adb shell ime disable}.
*
- * @param userId user ID specified to the command. Pseudo user IDs are not supported.
- * @param imeId IME ID specified to the command.
- * @param enabled {@code true} for {@code adb shell ime enable}. {@code false} otherwise.
- * @param out {@link PrintWriter} to output standard messages.
- * @param error {@link PrintWriter} to output error messages.
- * @return {@code false} if it fails to enable the IME. {@code false} otherwise.
+ * @param userId user ID specified to the command (pseudo user IDs are not supported)
+ * @param imeId IME ID specified to the command
+ * @param enabled {@code true} for {@code adb shell ime enable}
+ * @param out {@link PrintWriter} to output standard messages
+ * @param error {@link PrintWriter} to output error messages
+ * @return {@code false} if it fails to enable the IME
*/
@BinderThread
@GuardedBy("ImfLock.class")
@@ -6338,7 +6312,7 @@
/**
* Handles {@code adb shell ime set}.
*
- * @param shellCommand {@link ShellCommand} object that is handling this command.
+ * @param shellCommand {@link ShellCommand} object that is handling this command
* @return Exit code of the command.
*/
@BinderThread
@@ -6381,7 +6355,8 @@
/**
* Handles {@code adb shell ime reset-ime}.
- * @param shellCommand {@link ShellCommand} object that is handling this command.
+ *
+ * @param shellCommand {@link ShellCommand} object that is handling this command
* @return Exit code of the command.
*/
@BinderThread
@@ -6397,8 +6372,8 @@
continue;
}
// Skip on headless user
- if (USER_TYPE_SYSTEM_HEADLESS.equals(
- mUserManagerInternal.getUserInfo(userId).userType)) {
+ final var userInfo = mUserManagerInternal.getUserInfo(userId);
+ if (userInfo != null && USER_TYPE_SYSTEM_HEADLESS.equals(userInfo.userType)) {
continue;
}
final String nextIme;
@@ -6408,7 +6383,8 @@
hideCurrentInputLocked(mImeBindingState.mFocusedWindow, 0 /* flags */,
SoftInputShowHideReason.HIDE_RESET_SHELL_COMMAND);
final var userData = mUserDataRepository.getOrCreate(userId);
- userData.mBindingController.unbindCurrentMethod();
+ final var bindingController = userData.mBindingController;
+ bindingController.unbindCurrentMethod();
// Enable default IMEs, disable others
var toDisable = settings.getEnabledInputMethodList();
@@ -6461,7 +6437,8 @@
/**
* Handles {@code adb shell cmd input_method tracing start/stop/save-for-bugreport}.
- * @param shellCommand {@link ShellCommand} object that is handling this command.
+ *
+ * @param shellCommand {@link ShellCommand} object that is handling this command
* @return Exit code of the command.
*/
@BinderThread
@@ -6506,9 +6483,9 @@
/**
* @param userId the actual user handle obtained by {@link UserHandle#getIdentifier()}
- * and *not* pseudo ids like {@link UserHandle#USER_ALL etc}.
- * @return {@code true} if userId has debugging privileges.
- * i.e. {@link UserManager#DISALLOW_DEBUGGING_FEATURES} is {@code false}.
+ * and *not* pseudo ids like {@link UserHandle#USER_ALL etc}
+ * @return {@code true} if userId has debugging privileges
+ * i.e. {@link UserManager#DISALLOW_DEBUGGING_FEATURES} is {@code false}
*/
private boolean userHasDebugPriv(@UserIdInt int userId, ShellCommand shellCommand) {
if (mUserManagerInternal.hasUserRestriction(
@@ -6529,8 +6506,8 @@
/**
* Creates an IME request tracking token for the current focused client.
*
- * @param show whether this is a show or a hide request.
- * @param reason the reason why the IME request was created.
+ * @param show whether this is a show or a hide request
+ * @param reason the reason why the IME request was created
*/
@NonNull
private ImeTracker.Token createStatsTokenForFocusedClient(boolean show,
diff --git a/services/core/java/com/android/server/inputmethod/InputMethodMap.java b/services/core/java/com/android/server/inputmethod/InputMethodMap.java
index a8e5e2e..bab21e8 100644
--- a/services/core/java/com/android/server/inputmethod/InputMethodMap.java
+++ b/services/core/java/com/android/server/inputmethod/InputMethodMap.java
@@ -23,6 +23,7 @@
import android.util.ArrayMap;
import android.view.inputmethod.InputMethodInfo;
+import java.util.Arrays;
import java.util.List;
/**
@@ -75,4 +76,61 @@
int size() {
return mMap.size();
}
+
+ @AnyThread
+ @NonNull
+ public InputMethodMap applyAdditionalSubtypes(
+ @NonNull AdditionalSubtypeMap additionalSubtypeMap) {
+ if (additionalSubtypeMap.isEmpty()) {
+ return this;
+ }
+ final int size = size();
+ final ArrayMap<String, InputMethodInfo> newMethodMap = new ArrayMap<>(size);
+ boolean updated = false;
+ for (int i = 0; i < size; ++i) {
+ final var imi = valueAt(i);
+ final var imeId = imi.getId();
+ final var newAdditionalSubtypes = additionalSubtypeMap.get(imeId);
+ if (newAdditionalSubtypes == null || newAdditionalSubtypes.isEmpty()) {
+ newMethodMap.put(imi.getId(), imi);
+ } else {
+ newMethodMap.put(imi.getId(), new InputMethodInfo(imi, newAdditionalSubtypes));
+ updated = true;
+ }
+ }
+ return updated ? InputMethodMap.of(newMethodMap) : this;
+ }
+
+ /**
+ * Compares the given two {@link InputMethodMap} instances to see if they contain the same data
+ * or not.
+ *
+ * @param map1 {@link InputMethodMap} to be compared with
+ * @param map2 {@link InputMethodMap} to be compared with
+ * @return {@code true} if both {@link InputMethodMap} instances contain exactly the same data
+ */
+ @AnyThread
+ static boolean areSame(@NonNull InputMethodMap map1, @NonNull InputMethodMap map2) {
+ if (map1 == map2) {
+ return true;
+ }
+ final int size = map1.size();
+ if (size != map2.size()) {
+ return false;
+ }
+ for (int i = 0; i < size; ++i) {
+ final var imi1 = map1.valueAt(i);
+ final var imeId = imi1.getId();
+ final var imi2 = map2.get(imeId);
+ if (imi2 == null) {
+ return false;
+ }
+ final var marshaled1 = InputMethodInfoUtils.marshal(imi1);
+ final var marshaled2 = InputMethodInfoUtils.marshal(imi2);
+ if (!Arrays.equals(marshaled1, marshaled2)) {
+ return false;
+ }
+ }
+ return true;
+ }
}
diff --git a/services/core/java/com/android/server/inputmethod/UserDataRepository.java b/services/core/java/com/android/server/inputmethod/UserDataRepository.java
index 825cfcb..2b19d3e 100644
--- a/services/core/java/com/android/server/inputmethod/UserDataRepository.java
+++ b/services/core/java/com/android/server/inputmethod/UserDataRepository.java
@@ -96,5 +96,10 @@
mUserId = userId;
mBindingController = bindingController;
}
+
+ @Override
+ public String toString() {
+ return "UserData{" + "mUserId=" + mUserId + '}';
+ }
}
}
diff --git a/services/core/java/com/android/server/location/contexthub/ContextHubService.java b/services/core/java/com/android/server/location/contexthub/ContextHubService.java
index 17f8abe..b3fb147 100644
--- a/services/core/java/com/android/server/location/contexthub/ContextHubService.java
+++ b/services/core/java/com/android/server/location/contexthub/ContextHubService.java
@@ -317,9 +317,6 @@
*/
private static final int MAX_PROBABILITY_PERCENT = 100;
- /**
- * Random number generator.
- */
private Random mRandom = new Random();
/**
@@ -998,50 +995,75 @@
return;
}
- if (message.isReliable()) {
- byte errorCode = ErrorCode.OK;
- synchronized (mReliableMessageRecordQueue) {
- Optional<ReliableMessageRecord> record = Optional.empty();
- for (ReliableMessageRecord r: mReliableMessageRecordQueue) {
- if (r.getContextHubId() == contextHubId
- && r.getMessageSequenceNumber() == message.getMessageSequenceNumber()) {
- record = Optional.of(r);
- break;
- }
- }
-
- if (record.isPresent()) {
- errorCode = record.get().getErrorCode();
- if (errorCode == ErrorCode.TRANSIENT_ERROR) {
- Log.w(TAG, "Found duplicate reliable message with message sequence number: "
- + record.get().getMessageSequenceNumber() + ": retrying");
- errorCode = mClientManager.onMessageFromNanoApp(
- contextHubId, hostEndpointId, message,
- nanoappPermissions, messagePermissions);
- record.get().setErrorCode(errorCode);
- } else {
- Log.w(TAG, "Found duplicate reliable message with message sequence number: "
- + record.get().getMessageSequenceNumber());
- }
- } else {
- errorCode = mClientManager.onMessageFromNanoApp(
- contextHubId, hostEndpointId, message,
- nanoappPermissions, messagePermissions);
- mReliableMessageRecordQueue.add(
- new ReliableMessageRecord(contextHubId,
- SystemClock.elapsedRealtimeNanos(),
- message.getMessageSequenceNumber(),
- errorCode));
- }
- }
- sendMessageDeliveryStatusToContextHub(contextHubId,
- message.getMessageSequenceNumber(), errorCode);
- } else {
+ if (!message.isReliable()) {
mClientManager.onMessageFromNanoApp(
contextHubId, hostEndpointId, message,
nanoappPermissions, messagePermissions);
+ cleanupReliableMessageRecordQueue();
+ return;
}
+ byte errorCode = ErrorCode.OK;
+ synchronized (mReliableMessageRecordQueue) {
+ Optional<ReliableMessageRecord> record =
+ findReliableMessageRecord(contextHubId,
+ message.getMessageSequenceNumber());
+
+ if (record.isPresent()) {
+ errorCode = record.get().getErrorCode();
+ if (errorCode == ErrorCode.TRANSIENT_ERROR) {
+ Log.w(TAG, "Found duplicate reliable message with message sequence number: "
+ + record.get().getMessageSequenceNumber() + ": retrying");
+ errorCode = mClientManager.onMessageFromNanoApp(
+ contextHubId, hostEndpointId, message,
+ nanoappPermissions, messagePermissions);
+ record.get().setErrorCode(errorCode);
+ } else {
+ Log.w(TAG, "Found duplicate reliable message with message sequence number: "
+ + record.get().getMessageSequenceNumber());
+ }
+ } else {
+ errorCode = mClientManager.onMessageFromNanoApp(
+ contextHubId, hostEndpointId, message,
+ nanoappPermissions, messagePermissions);
+ mReliableMessageRecordQueue.add(
+ new ReliableMessageRecord(contextHubId,
+ SystemClock.elapsedRealtimeNanos(),
+ message.getMessageSequenceNumber(),
+ errorCode));
+ }
+ }
+
+ sendMessageDeliveryStatusToContextHub(contextHubId,
+ message.getMessageSequenceNumber(), errorCode);
+ cleanupReliableMessageRecordQueue();
+ }
+
+ /**
+ * Finds a reliable message record in the queue that matches the given
+ * context hub ID and message sequence number. This function assumes
+ * the caller is synchronized on mReliableMessageRecordQueue.
+ *
+ * @param contextHubId the ID of the hub
+ * @param messageSequenceNumber the message sequence number
+ *
+ * @return the record if found, or empty if not found
+ */
+ private Optional<ReliableMessageRecord> findReliableMessageRecord(
+ int contextHubId, int messageSequenceNumber) {
+ for (ReliableMessageRecord record: mReliableMessageRecordQueue) {
+ if (record.getContextHubId() == contextHubId
+ && record.getMessageSequenceNumber() == messageSequenceNumber) {
+ return Optional.of(record);
+ }
+ }
+ return Optional.empty();
+ }
+
+ /**
+ * Removes old entries from the reliable message record queue.
+ */
+ private void cleanupReliableMessageRecordQueue() {
synchronized (mReliableMessageRecordQueue) {
while (mReliableMessageRecordQueue.peek() != null
&& mReliableMessageRecordQueue.peek().isExpired()) {
diff --git a/services/core/java/com/android/server/location/contexthub/ContextHubTransactionManager.java b/services/core/java/com/android/server/location/contexthub/ContextHubTransactionManager.java
index a0dbfa0..ec94e2b 100644
--- a/services/core/java/com/android/server/location/contexthub/ContextHubTransactionManager.java
+++ b/services/core/java/com/android/server/location/contexthub/ContextHubTransactionManager.java
@@ -412,10 +412,15 @@
/* package */
synchronized void addTransaction(
ContextHubServiceTransaction transaction) throws IllegalStateException {
+ if (transaction == null) {
+ return;
+ }
+
if (mTransactionQueue.size() == MAX_PENDING_REQUESTS) {
throw new IllegalStateException("Transaction queue is full (capacity = "
+ MAX_PENDING_REQUESTS + ")");
}
+
mTransactionQueue.add(transaction);
mTransactionRecordDeque.add(new TransactionRecord(transaction.toString()));
@@ -517,7 +522,10 @@
* the caller has obtained a lock on this ContextHubTransactionManager object.
*/
private void removeTransactionAndStartNext() {
- mTimeoutFuture.cancel(false /* mayInterruptIfRunning */);
+ if (mTimeoutFuture != null) {
+ mTimeoutFuture.cancel(/* mayInterruptIfRunning= */ false);
+ mTimeoutFuture = null;
+ }
ContextHubServiceTransaction transaction = mTransactionQueue.remove();
transaction.setComplete();
diff --git a/services/core/java/com/android/server/media/MediaRoute2ProviderServiceProxy.java b/services/core/java/com/android/server/media/MediaRoute2ProviderServiceProxy.java
index a7fd750..386657e 100644
--- a/services/core/java/com/android/server/media/MediaRoute2ProviderServiceProxy.java
+++ b/services/core/java/com/android/server/media/MediaRoute2ProviderServiceProxy.java
@@ -81,6 +81,7 @@
MediaRoute2ProviderServiceProxy(
@NonNull Context context,
+ @NonNull Looper looper,
@NonNull ComponentName componentName,
boolean isSelfScanOnlyProvider,
int userId) {
@@ -88,7 +89,7 @@
mContext = Objects.requireNonNull(context, "Context must not be null.");
mIsSelfScanOnlyProvider = isSelfScanOnlyProvider;
mUserId = userId;
- mHandler = new Handler(Looper.myLooper());
+ mHandler = new Handler(looper);
}
public void setManagerScanning(boolean managerScanning) {
diff --git a/services/core/java/com/android/server/media/MediaRoute2ProviderWatcher.java b/services/core/java/com/android/server/media/MediaRoute2ProviderWatcher.java
index 178eb71..7c1a5e1 100644
--- a/services/core/java/com/android/server/media/MediaRoute2ProviderWatcher.java
+++ b/services/core/java/com/android/server/media/MediaRoute2ProviderWatcher.java
@@ -142,6 +142,7 @@
MediaRoute2ProviderServiceProxy proxy =
new MediaRoute2ProviderServiceProxy(
mContext,
+ mHandler.getLooper(),
new ComponentName(serviceInfo.packageName, serviceInfo.name),
isSelfScanOnlyProvider,
mUserId);
diff --git a/services/core/java/com/android/server/media/MediaRouter2ServiceImpl.java b/services/core/java/com/android/server/media/MediaRouter2ServiceImpl.java
index 73647db..c03497e 100644
--- a/services/core/java/com/android/server/media/MediaRouter2ServiceImpl.java
+++ b/services/core/java/com/android/server/media/MediaRouter2ServiceImpl.java
@@ -404,37 +404,17 @@
long managerRequestId,
@NonNull RoutingSessionInfo oldSession,
@NonNull MediaRoute2Info route,
- Bundle sessionHints,
- @Nullable UserHandle transferInitiatorUserHandle,
- @Nullable String transferInitiatorPackageName) {
+ Bundle sessionHints) {
Objects.requireNonNull(router, "router must not be null");
Objects.requireNonNull(oldSession, "oldSession must not be null");
Objects.requireNonNull(route, "route must not be null");
- synchronized (mLock) {
- if (managerRequestId == MediaRoute2ProviderService.REQUEST_ID_NONE
- || transferInitiatorUserHandle == null
- || transferInitiatorPackageName == null) {
- final IBinder binder = router.asBinder();
- final RouterRecord routerRecord = mAllRouterRecords.get(binder);
-
- transferInitiatorUserHandle = Binder.getCallingUserHandle();
- if (routerRecord != null) {
- transferInitiatorPackageName = routerRecord.mPackageName;
- } else {
- transferInitiatorPackageName = mContext.getPackageName();
- }
- }
- }
-
final long token = Binder.clearCallingIdentity();
try {
synchronized (mLock) {
requestCreateSessionWithRouter2Locked(
requestId,
managerRequestId,
- transferInitiatorUserHandle,
- transferInitiatorPackageName,
router,
oldSession,
route,
@@ -1281,8 +1261,6 @@
private void requestCreateSessionWithRouter2Locked(
int requestId,
long managerRequestId,
- @NonNull UserHandle transferInitiatorUserHandle,
- @NonNull String transferInitiatorPackageName,
@NonNull IMediaRouter2 router,
@NonNull RoutingSessionInfo oldSession,
@NonNull MediaRoute2Info route,
@@ -1355,8 +1333,6 @@
userHandler,
uniqueRequestId,
managerRequestId,
- transferInitiatorUserHandle,
- transferInitiatorPackageName,
routerRecord,
oldSession,
route,
@@ -2695,11 +2671,7 @@
route = mSystemProvider.getDefaultRoute();
}
routerRecord.mRouter.requestCreateSessionByManager(
- uniqueRequestId,
- oldSession,
- route,
- transferInitiatorUserHandle,
- transferInitiatorPackageName);
+ uniqueRequestId, oldSession, route);
} catch (RemoteException ex) {
Slog.w(TAG, "getSessionHintsForCreatingSessionOnHandler: "
+ "Failed to request. Router probably died.", ex);
@@ -2711,8 +2683,6 @@
private void requestCreateSessionWithRouter2OnHandler(
long uniqueRequestId,
long managerRequestId,
- @NonNull UserHandle transferInitiatorUserHandle,
- @NonNull String transferInitiatorPackageName,
@NonNull RouterRecord routerRecord,
@NonNull RoutingSessionInfo oldSession,
@NonNull MediaRoute2Info route,
@@ -2732,10 +2702,10 @@
managerRequestId, oldSession, route);
mSessionCreationRequests.add(request);
- int transferReason = RoutingSessionInfo.TRANSFER_REASON_APP;
- if (managerRequestId != MediaRoute2ProviderService.REQUEST_ID_NONE) {
- transferReason = RoutingSessionInfo.TRANSFER_REASON_SYSTEM_REQUEST;
- }
+ int transferReason =
+ managerRequestId != MediaRoute2ProviderService.REQUEST_ID_NONE
+ ? RoutingSessionInfo.TRANSFER_REASON_SYSTEM_REQUEST
+ : RoutingSessionInfo.TRANSFER_REASON_APP;
provider.requestCreateSession(
uniqueRequestId,
@@ -2743,8 +2713,8 @@
route.getOriginalId(),
sessionHints,
transferReason,
- transferInitiatorUserHandle,
- transferInitiatorPackageName);
+ UserHandle.of(routerRecord.mUserRecord.mUserId),
+ routerRecord.mPackageName);
}
// routerRecord can be null if the session is system's or RCN.
@@ -2800,6 +2770,10 @@
final String providerId = route.getProviderId();
final MediaRoute2Provider provider = findProvider(providerId);
if (provider == null) {
+ Slog.w(
+ TAG,
+ "Ignoring transferToRoute due to lack of matching provider for target: "
+ + route);
return;
}
provider.transferToRoute(
diff --git a/services/core/java/com/android/server/media/MediaRouterService.java b/services/core/java/com/android/server/media/MediaRouterService.java
index 064443c..192ac62 100644
--- a/services/core/java/com/android/server/media/MediaRouterService.java
+++ b/services/core/java/com/android/server/media/MediaRouterService.java
@@ -495,18 +495,9 @@
long managerRequestId,
RoutingSessionInfo oldSession,
MediaRoute2Info route,
- Bundle sessionHints,
- @Nullable UserHandle transferInitiatorUserHandle,
- @Nullable String transferInitiatorPackageName) {
+ Bundle sessionHints) {
mService2.requestCreateSessionWithRouter2(
- router,
- requestId,
- managerRequestId,
- oldSession,
- route,
- sessionHints,
- transferInitiatorUserHandle,
- transferInitiatorPackageName);
+ router, requestId, managerRequestId, oldSession, route, sessionHints);
}
// Binder call
diff --git a/services/core/java/com/android/server/media/MediaSessionRecord.java b/services/core/java/com/android/server/media/MediaSessionRecord.java
index a3c5d2d..69f07d5 100644
--- a/services/core/java/com/android/server/media/MediaSessionRecord.java
+++ b/services/core/java/com/android/server/media/MediaSessionRecord.java
@@ -1057,6 +1057,7 @@
return -1;
}
+ @NonNull
private PlaybackInfo getVolumeAttributes() {
int volumeType;
AudioAttributes attributes;
@@ -1850,6 +1851,7 @@
return mFlags;
}
+ @NonNull
@Override
public PlaybackInfo getVolumeAttributes() {
return MediaSessionRecord.this.getVolumeAttributes();
diff --git a/services/core/java/com/android/server/net/NetworkManagementService.java b/services/core/java/com/android/server/net/NetworkManagementService.java
index d25f529..5ea3e70 100644
--- a/services/core/java/com/android/server/net/NetworkManagementService.java
+++ b/services/core/java/com/android/server/net/NetworkManagementService.java
@@ -20,6 +20,9 @@
import static android.net.ConnectivityManager.FIREWALL_CHAIN_BACKGROUND;
import static android.net.ConnectivityManager.FIREWALL_CHAIN_DOZABLE;
import static android.net.ConnectivityManager.FIREWALL_CHAIN_LOW_POWER_STANDBY;
+import static android.net.ConnectivityManager.FIREWALL_CHAIN_METERED_ALLOW;
+import static android.net.ConnectivityManager.FIREWALL_CHAIN_METERED_DENY_ADMIN;
+import static android.net.ConnectivityManager.FIREWALL_CHAIN_METERED_DENY_USER;
import static android.net.ConnectivityManager.FIREWALL_CHAIN_POWERSAVE;
import static android.net.ConnectivityManager.FIREWALL_CHAIN_RESTRICTED;
import static android.net.ConnectivityManager.FIREWALL_CHAIN_STANDBY;
@@ -31,6 +34,9 @@
import static android.net.NetworkPolicyManager.FIREWALL_CHAIN_NAME_BACKGROUND;
import static android.net.NetworkPolicyManager.FIREWALL_CHAIN_NAME_DOZABLE;
import static android.net.NetworkPolicyManager.FIREWALL_CHAIN_NAME_LOW_POWER_STANDBY;
+import static android.net.NetworkPolicyManager.FIREWALL_CHAIN_NAME_METERED_ALLOW;
+import static android.net.NetworkPolicyManager.FIREWALL_CHAIN_NAME_METERED_DENY_ADMIN;
+import static android.net.NetworkPolicyManager.FIREWALL_CHAIN_NAME_METERED_DENY_USER;
import static android.net.NetworkPolicyManager.FIREWALL_CHAIN_NAME_POWERSAVE;
import static android.net.NetworkPolicyManager.FIREWALL_CHAIN_NAME_RESTRICTED;
import static android.net.NetworkPolicyManager.FIREWALL_CHAIN_NAME_STANDBY;
@@ -143,6 +149,8 @@
private final Object mQuotaLock = new Object();
private final Object mRulesLock = new Object();
+ private final boolean mUseMeteredFirewallChains;
+
/** Set of interfaces with active quotas. */
@GuardedBy("mQuotaLock")
private HashMap<String, Long> mActiveQuotas = Maps.newHashMap();
@@ -150,9 +158,11 @@
@GuardedBy("mQuotaLock")
private HashMap<String, Long> mActiveAlerts = Maps.newHashMap();
/** Set of UIDs denied on metered networks. */
+ // TODO: b/336693007 - Remove once NPMS has completely migrated to metered firewall chains.
@GuardedBy("mRulesLock")
private SparseBooleanArray mUidRejectOnMetered = new SparseBooleanArray();
/** Set of UIDs allowed on metered networks. */
+ // TODO: b/336693007 - Remove once NPMS has completely migrated to metered firewall chains.
@GuardedBy("mRulesLock")
private SparseBooleanArray mUidAllowOnMetered = new SparseBooleanArray();
/** Set of UIDs with cleartext penalties. */
@@ -196,10 +206,32 @@
@GuardedBy("mRulesLock")
private final SparseIntArray mUidFirewallBackgroundRules = new SparseIntArray();
+ /**
+ * Contains the per-UID firewall rules that are used to allowlist the app from metered-network
+ * restrictions when data saver is enabled.
+ */
+ @GuardedBy("mRulesLock")
+ private final SparseIntArray mUidMeteredFirewallAllowRules = new SparseIntArray();
+
+ /**
+ * Contains the per-UID firewall rules that are used to deny app access to metered networks
+ * due to user action.
+ */
+ @GuardedBy("mRulesLock")
+ private final SparseIntArray mUidMeteredFirewallDenyUserRules = new SparseIntArray();
+
+ /**
+ * Contains the per-UID firewall rules that are used to deny app access to metered networks
+ * due to admin action.
+ */
+ @GuardedBy("mRulesLock")
+ private final SparseIntArray mUidMeteredFirewallDenyAdminRules = new SparseIntArray();
+
/** Set of states for the child firewall chains. True if the chain is active. */
@GuardedBy("mRulesLock")
final SparseBooleanArray mFirewallChainStates = new SparseBooleanArray();
+ // TODO: b/336693007 - Remove once NPMS has completely migrated to metered firewall chains.
@GuardedBy("mQuotaLock")
private volatile boolean mDataSaverMode;
@@ -217,6 +249,15 @@
mContext = context;
mDeps = deps;
+ mUseMeteredFirewallChains = Flags.useMeteredFirewallChains();
+
+ if (mUseMeteredFirewallChains) {
+ // These firewalls are always on and currently ConnectivityService does not allow
+ // changing their enabled state.
+ mFirewallChainStates.put(FIREWALL_CHAIN_METERED_DENY_USER, true);
+ mFirewallChainStates.put(FIREWALL_CHAIN_METERED_DENY_ADMIN, true);
+ }
+
mDaemonHandler = new Handler(FgThread.get().getLooper());
mNetdUnsolicitedEventListener = new NetdUnsolicitedEventListener();
@@ -410,33 +451,39 @@
}
}
- SparseBooleanArray uidRejectOnQuota = null;
- SparseBooleanArray uidAcceptOnQuota = null;
- synchronized (mRulesLock) {
- size = mUidRejectOnMetered.size();
- if (size > 0) {
- if (DBG) Slog.d(TAG, "Pushing " + size + " UIDs to metered denylist rules");
- uidRejectOnQuota = mUidRejectOnMetered;
- mUidRejectOnMetered = new SparseBooleanArray();
- }
+ if (!mUseMeteredFirewallChains) {
+ SparseBooleanArray uidRejectOnQuota = null;
+ SparseBooleanArray uidAcceptOnQuota = null;
+ synchronized (mRulesLock) {
+ size = mUidRejectOnMetered.size();
+ if (size > 0) {
+ if (DBG) {
+ Slog.d(TAG, "Pushing " + size + " UIDs to metered denylist rules");
+ }
+ uidRejectOnQuota = mUidRejectOnMetered;
+ mUidRejectOnMetered = new SparseBooleanArray();
+ }
- size = mUidAllowOnMetered.size();
- if (size > 0) {
- if (DBG) Slog.d(TAG, "Pushing " + size + " UIDs to metered allowlist rules");
- uidAcceptOnQuota = mUidAllowOnMetered;
- mUidAllowOnMetered = new SparseBooleanArray();
+ size = mUidAllowOnMetered.size();
+ if (size > 0) {
+ if (DBG) {
+ Slog.d(TAG, "Pushing " + size + " UIDs to metered allowlist rules");
+ }
+ uidAcceptOnQuota = mUidAllowOnMetered;
+ mUidAllowOnMetered = new SparseBooleanArray();
+ }
}
- }
- if (uidRejectOnQuota != null) {
- for (int i = 0; i < uidRejectOnQuota.size(); i++) {
- setUidOnMeteredNetworkDenylist(uidRejectOnQuota.keyAt(i),
- uidRejectOnQuota.valueAt(i));
+ if (uidRejectOnQuota != null) {
+ for (int i = 0; i < uidRejectOnQuota.size(); i++) {
+ setUidOnMeteredNetworkDenylist(uidRejectOnQuota.keyAt(i),
+ uidRejectOnQuota.valueAt(i));
+ }
}
- }
- if (uidAcceptOnQuota != null) {
- for (int i = 0; i < uidAcceptOnQuota.size(); i++) {
- setUidOnMeteredNetworkAllowlist(uidAcceptOnQuota.keyAt(i),
- uidAcceptOnQuota.valueAt(i));
+ if (uidAcceptOnQuota != null) {
+ for (int i = 0; i < uidAcceptOnQuota.size(); i++) {
+ setUidOnMeteredNetworkAllowlist(uidAcceptOnQuota.keyAt(i),
+ uidAcceptOnQuota.valueAt(i));
+ }
}
}
@@ -459,8 +506,16 @@
syncFirewallChainLocked(FIREWALL_CHAIN_RESTRICTED, "restricted ");
syncFirewallChainLocked(FIREWALL_CHAIN_LOW_POWER_STANDBY, "low power standby ");
syncFirewallChainLocked(FIREWALL_CHAIN_BACKGROUND, FIREWALL_CHAIN_NAME_BACKGROUND);
+ if (mUseMeteredFirewallChains) {
+ syncFirewallChainLocked(FIREWALL_CHAIN_METERED_ALLOW,
+ FIREWALL_CHAIN_NAME_METERED_ALLOW);
+ syncFirewallChainLocked(FIREWALL_CHAIN_METERED_DENY_USER,
+ FIREWALL_CHAIN_NAME_METERED_DENY_USER);
+ syncFirewallChainLocked(FIREWALL_CHAIN_METERED_DENY_ADMIN,
+ FIREWALL_CHAIN_NAME_METERED_DENY_ADMIN);
+ }
- final int[] chains = {
+ final int[] chainsToEnable = {
FIREWALL_CHAIN_STANDBY,
FIREWALL_CHAIN_DOZABLE,
FIREWALL_CHAIN_POWERSAVE,
@@ -469,14 +524,13 @@
FIREWALL_CHAIN_BACKGROUND,
};
- for (int chain : chains) {
+ for (int chain : chainsToEnable) {
if (getFirewallChainState(chain)) {
setFirewallChainEnabled(chain, true);
}
}
}
-
try {
getBatteryStats().noteNetworkStatsEnabled();
} catch (RemoteException e) {
@@ -1077,6 +1131,14 @@
mContext.getSystemService(ConnectivityManager.class)
.setDataSaverEnabled(enable);
mDataSaverMode = enable;
+ if (mUseMeteredFirewallChains) {
+ // Copy mDataSaverMode state to FIREWALL_CHAIN_METERED_ALLOW
+ // until ConnectivityService allows manipulation of the data saver mode via
+ // FIREWALL_CHAIN_METERED_ALLOW.
+ synchronized (mRulesLock) {
+ mFirewallChainStates.put(FIREWALL_CHAIN_METERED_ALLOW, enable);
+ }
+ }
return true;
} else {
final boolean changed = mNetdService.bandwidthEnableDataSaver(enable);
@@ -1191,9 +1253,9 @@
setFirewallChainState(chain, enable);
}
- final String chainName = getFirewallChainName(chain);
- if (chain == FIREWALL_CHAIN_NONE) {
- throw new IllegalArgumentException("Bad child chain: " + chainName);
+ if (!isValidFirewallChainForSetEnabled(chain)) {
+ throw new IllegalArgumentException("Invalid chain for setFirewallChainEnabled: "
+ + NetworkPolicyLogger.getFirewallChainName(chain));
}
final ConnectivityManager cm = mContext.getSystemService(ConnectivityManager.class);
@@ -1205,38 +1267,29 @@
}
}
- private String getFirewallChainName(int chain) {
- switch (chain) {
- case FIREWALL_CHAIN_STANDBY:
- return FIREWALL_CHAIN_NAME_STANDBY;
- case FIREWALL_CHAIN_DOZABLE:
- return FIREWALL_CHAIN_NAME_DOZABLE;
- case FIREWALL_CHAIN_POWERSAVE:
- return FIREWALL_CHAIN_NAME_POWERSAVE;
- case FIREWALL_CHAIN_RESTRICTED:
- return FIREWALL_CHAIN_NAME_RESTRICTED;
- case FIREWALL_CHAIN_LOW_POWER_STANDBY:
- return FIREWALL_CHAIN_NAME_LOW_POWER_STANDBY;
- case FIREWALL_CHAIN_BACKGROUND:
- return FIREWALL_CHAIN_NAME_BACKGROUND;
- default:
- throw new IllegalArgumentException("Bad child chain: " + chain);
- }
+ private boolean isValidFirewallChainForSetEnabled(int chain) {
+ return switch (chain) {
+ case FIREWALL_CHAIN_STANDBY, FIREWALL_CHAIN_DOZABLE, FIREWALL_CHAIN_POWERSAVE,
+ FIREWALL_CHAIN_RESTRICTED, FIREWALL_CHAIN_LOW_POWER_STANDBY,
+ FIREWALL_CHAIN_BACKGROUND -> true;
+ // METERED_* firewall chains are not yet supported by
+ // ConnectivityService#setFirewallChainEnabled.
+ default -> false;
+ };
}
private int getFirewallType(int chain) {
switch (chain) {
case FIREWALL_CHAIN_STANDBY:
+ case FIREWALL_CHAIN_METERED_DENY_ADMIN:
+ case FIREWALL_CHAIN_METERED_DENY_USER:
return FIREWALL_DENYLIST;
case FIREWALL_CHAIN_DOZABLE:
- return FIREWALL_ALLOWLIST;
case FIREWALL_CHAIN_POWERSAVE:
- return FIREWALL_ALLOWLIST;
case FIREWALL_CHAIN_RESTRICTED:
- return FIREWALL_ALLOWLIST;
case FIREWALL_CHAIN_LOW_POWER_STANDBY:
- return FIREWALL_ALLOWLIST;
case FIREWALL_CHAIN_BACKGROUND:
+ case FIREWALL_CHAIN_METERED_ALLOW:
return FIREWALL_ALLOWLIST;
default:
return isFirewallEnabled() ? FIREWALL_ALLOWLIST : FIREWALL_DENYLIST;
@@ -1360,6 +1413,12 @@
return mUidFirewallLowPowerStandbyRules;
case FIREWALL_CHAIN_BACKGROUND:
return mUidFirewallBackgroundRules;
+ case FIREWALL_CHAIN_METERED_ALLOW:
+ return mUidMeteredFirewallAllowRules;
+ case FIREWALL_CHAIN_METERED_DENY_USER:
+ return mUidMeteredFirewallDenyUserRules;
+ case FIREWALL_CHAIN_METERED_DENY_ADMIN:
+ return mUidMeteredFirewallDenyAdminRules;
case FIREWALL_CHAIN_NONE:
return mUidFirewallRules;
default:
@@ -1378,6 +1437,10 @@
protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
if (!DumpUtils.checkDumpPermission(mContext, TAG, pw)) return;
+ pw.println("Flags:");
+ pw.println(Flags.FLAG_USE_METERED_FIREWALL_CHAINS + ": " + mUseMeteredFirewallChains);
+ pw.println();
+
synchronized (mQuotaLock) {
pw.print("Active quota ifaces: "); pw.println(mActiveQuotas.toString());
pw.print("Active alert ifaces: "); pw.println(mActiveAlerts.toString());
@@ -1416,6 +1479,27 @@
pw.print("UID firewall background chain enabled: ");
pw.println(getFirewallChainState(FIREWALL_CHAIN_BACKGROUND));
dumpUidFirewallRule(pw, FIREWALL_CHAIN_NAME_BACKGROUND, mUidFirewallBackgroundRules);
+
+ pw.print("UID firewall metered allow chain enabled (Data saver mode): ");
+ // getFirewallChainState should maintain a duplicated state from mDataSaverMode when
+ // mUseMeteredFirewallChains is enabled.
+ pw.println(getFirewallChainState(FIREWALL_CHAIN_METERED_ALLOW));
+ dumpUidFirewallRule(pw, FIREWALL_CHAIN_NAME_METERED_ALLOW,
+ mUidMeteredFirewallAllowRules);
+
+ pw.print("UID firewall metered deny_user chain enabled (always-on): ");
+ // This always-on state should be reflected by getFirewallChainState when
+ // mUseMeteredFirewallChains is enabled.
+ pw.println(getFirewallChainState(FIREWALL_CHAIN_METERED_DENY_USER));
+ dumpUidFirewallRule(pw, FIREWALL_CHAIN_NAME_METERED_DENY_USER,
+ mUidMeteredFirewallDenyUserRules);
+
+ pw.print("UID firewall metered deny_admin chain enabled (always-on): ");
+ // This always-on state should be reflected by getFirewallChainState when
+ // mUseMeteredFirewallChains is enabled.
+ pw.println(getFirewallChainState(FIREWALL_CHAIN_METERED_DENY_ADMIN));
+ dumpUidFirewallRule(pw, FIREWALL_CHAIN_NAME_METERED_DENY_ADMIN,
+ mUidMeteredFirewallDenyAdminRules);
}
pw.print("Firewall enabled: "); pw.println(mFirewallEnabled);
@@ -1520,14 +1604,40 @@
if (DBG) Slog.d(TAG, "Uid " + uid + " restricted because it is in background");
return true;
}
- if (mUidRejectOnMetered.get(uid)) {
- if (DBG) Slog.d(TAG, "Uid " + uid + " restricted because of no metered data"
- + " in the background");
- return true;
- }
- if (mDataSaverMode && !mUidAllowOnMetered.get(uid)) {
- if (DBG) Slog.d(TAG, "Uid " + uid + " restricted because of data saver mode");
- return true;
+ if (mUseMeteredFirewallChains) {
+ if (getFirewallChainState(FIREWALL_CHAIN_METERED_DENY_USER)
+ && mUidMeteredFirewallDenyUserRules.get(uid) == FIREWALL_RULE_DENY) {
+ if (DBG) {
+ Slog.d(TAG, "Uid " + uid + " restricted because of user-restricted metered"
+ + " data in the background");
+ }
+ return true;
+ }
+ if (getFirewallChainState(FIREWALL_CHAIN_METERED_DENY_ADMIN)
+ && mUidMeteredFirewallDenyAdminRules.get(uid) == FIREWALL_RULE_DENY) {
+ if (DBG) {
+ Slog.d(TAG, "Uid " + uid + " restricted because of admin-restricted metered"
+ + " data in the background");
+ }
+ return true;
+ }
+ if (getFirewallChainState(FIREWALL_CHAIN_METERED_ALLOW)
+ && mUidMeteredFirewallAllowRules.get(uid) != FIREWALL_RULE_ALLOW) {
+ if (DBG) Slog.d(TAG, "Uid " + uid + " restricted because of data saver mode");
+ return true;
+ }
+ } else {
+ if (mUidRejectOnMetered.get(uid)) {
+ if (DBG) {
+ Slog.d(TAG, "Uid " + uid
+ + " restricted because of no metered data in the background");
+ }
+ return true;
+ }
+ if (mDataSaverMode && !mUidAllowOnMetered.get(uid)) {
+ if (DBG) Slog.d(TAG, "Uid " + uid + " restricted because of data saver mode");
+ return true;
+ }
}
return false;
}
diff --git a/services/core/java/com/android/server/net/NetworkPolicyLogger.java b/services/core/java/com/android/server/net/NetworkPolicyLogger.java
index 8e2d778..681aa8a 100644
--- a/services/core/java/com/android/server/net/NetworkPolicyLogger.java
+++ b/services/core/java/com/android/server/net/NetworkPolicyLogger.java
@@ -19,6 +19,9 @@
import static android.net.ConnectivityManager.FIREWALL_CHAIN_BACKGROUND;
import static android.net.ConnectivityManager.FIREWALL_CHAIN_DOZABLE;
import static android.net.ConnectivityManager.FIREWALL_CHAIN_LOW_POWER_STANDBY;
+import static android.net.ConnectivityManager.FIREWALL_CHAIN_METERED_ALLOW;
+import static android.net.ConnectivityManager.FIREWALL_CHAIN_METERED_DENY_ADMIN;
+import static android.net.ConnectivityManager.FIREWALL_CHAIN_METERED_DENY_USER;
import static android.net.ConnectivityManager.FIREWALL_CHAIN_POWERSAVE;
import static android.net.ConnectivityManager.FIREWALL_CHAIN_RESTRICTED;
import static android.net.ConnectivityManager.FIREWALL_CHAIN_STANDBY;
@@ -28,6 +31,9 @@
import static android.net.NetworkPolicyManager.FIREWALL_CHAIN_NAME_BACKGROUND;
import static android.net.NetworkPolicyManager.FIREWALL_CHAIN_NAME_DOZABLE;
import static android.net.NetworkPolicyManager.FIREWALL_CHAIN_NAME_LOW_POWER_STANDBY;
+import static android.net.NetworkPolicyManager.FIREWALL_CHAIN_NAME_METERED_ALLOW;
+import static android.net.NetworkPolicyManager.FIREWALL_CHAIN_NAME_METERED_DENY_ADMIN;
+import static android.net.NetworkPolicyManager.FIREWALL_CHAIN_NAME_METERED_DENY_USER;
import static android.net.NetworkPolicyManager.FIREWALL_CHAIN_NAME_POWERSAVE;
import static android.net.NetworkPolicyManager.FIREWALL_CHAIN_NAME_RESTRICTED;
import static android.net.NetworkPolicyManager.FIREWALL_CHAIN_NAME_STANDBY;
@@ -379,7 +385,7 @@
return "Interfaces of netId=" + netId + " changed to " + newIfaces;
}
- private static String getFirewallChainName(int chain) {
+ static String getFirewallChainName(int chain) {
switch (chain) {
case FIREWALL_CHAIN_DOZABLE:
return FIREWALL_CHAIN_NAME_DOZABLE;
@@ -393,6 +399,12 @@
return FIREWALL_CHAIN_NAME_LOW_POWER_STANDBY;
case FIREWALL_CHAIN_BACKGROUND:
return FIREWALL_CHAIN_NAME_BACKGROUND;
+ case FIREWALL_CHAIN_METERED_ALLOW:
+ return FIREWALL_CHAIN_NAME_METERED_ALLOW;
+ case FIREWALL_CHAIN_METERED_DENY_USER:
+ return FIREWALL_CHAIN_NAME_METERED_DENY_USER;
+ case FIREWALL_CHAIN_METERED_DENY_ADMIN:
+ return FIREWALL_CHAIN_NAME_METERED_DENY_ADMIN;
default:
return String.valueOf(chain);
}
diff --git a/services/core/java/com/android/server/net/NetworkPolicyManagerService.java b/services/core/java/com/android/server/net/NetworkPolicyManagerService.java
index 22f5332..c60ac3a 100644
--- a/services/core/java/com/android/server/net/NetworkPolicyManagerService.java
+++ b/services/core/java/com/android/server/net/NetworkPolicyManagerService.java
@@ -60,6 +60,9 @@
import static android.net.ConnectivityManager.FIREWALL_CHAIN_BACKGROUND;
import static android.net.ConnectivityManager.FIREWALL_CHAIN_DOZABLE;
import static android.net.ConnectivityManager.FIREWALL_CHAIN_LOW_POWER_STANDBY;
+import static android.net.ConnectivityManager.FIREWALL_CHAIN_METERED_ALLOW;
+import static android.net.ConnectivityManager.FIREWALL_CHAIN_METERED_DENY_ADMIN;
+import static android.net.ConnectivityManager.FIREWALL_CHAIN_METERED_DENY_USER;
import static android.net.ConnectivityManager.FIREWALL_CHAIN_POWERSAVE;
import static android.net.ConnectivityManager.FIREWALL_CHAIN_RESTRICTED;
import static android.net.ConnectivityManager.FIREWALL_CHAIN_STANDBY;
@@ -514,6 +517,12 @@
*/
private boolean mBackgroundNetworkRestricted;
+ /**
+ * Whether or not metered firewall chains should be used for uid policy controlling access to
+ * metered networks.
+ */
+ private boolean mUseMeteredFirewallChains;
+
// See main javadoc for instructions on how to use these locks.
final Object mUidRulesFirstLock = new Object();
final Object mNetworkPoliciesSecondLock = new Object();
@@ -997,6 +1006,8 @@
mAppStandby = LocalServices.getService(AppStandbyInternal.class);
mActivityManagerInternal = LocalServices.getService(ActivityManagerInternal.class);
+ mUseMeteredFirewallChains = Flags.useMeteredFirewallChains();
+
synchronized (mUidRulesFirstLock) {
synchronized (mNetworkPoliciesSecondLock) {
updatePowerSaveAllowlistUL();
@@ -4030,8 +4041,10 @@
fout.println();
fout.println("Flags:");
- fout.println("Network blocked for TOP_SLEEPING and above: "
+ fout.println(Flags.FLAG_NETWORK_BLOCKED_FOR_TOP_SLEEPING_AND_ABOVE + ": "
+ mBackgroundNetworkRestricted);
+ fout.println(Flags.FLAG_USE_METERED_FIREWALL_CHAINS + ": "
+ + mUseMeteredFirewallChains);
fout.println();
fout.println("mRestrictBackgroundLowPowerMode: " + mRestrictBackgroundLowPowerMode);
@@ -5373,23 +5386,44 @@
postUidRulesChangedMsg(uid, uidRules);
}
- // Note that the conditionals below are for avoiding unnecessary calls to netd.
- // TODO: Measure the performance for doing a no-op call to netd so that we can
- // remove the conditionals to simplify the logic below. We can also further reduce
- // some calls to netd if they turn out to be costly.
- final int denylistReasons = BLOCKED_METERED_REASON_ADMIN_DISABLED
- | BLOCKED_METERED_REASON_USER_RESTRICTED;
- if ((oldEffectiveBlockedReasons & denylistReasons) != BLOCKED_REASON_NONE
- || (newEffectiveBlockedReasons & denylistReasons) != BLOCKED_REASON_NONE) {
- setMeteredNetworkDenylist(uid,
- (newEffectiveBlockedReasons & denylistReasons) != BLOCKED_REASON_NONE);
- }
- final int allowlistReasons = ALLOWED_METERED_REASON_FOREGROUND
- | ALLOWED_METERED_REASON_USER_EXEMPTED;
- if ((oldAllowedReasons & allowlistReasons) != ALLOWED_REASON_NONE
- || (newAllowedReasons & allowlistReasons) != ALLOWED_REASON_NONE) {
- setMeteredNetworkAllowlist(uid,
- (newAllowedReasons & allowlistReasons) != ALLOWED_REASON_NONE);
+ if (mUseMeteredFirewallChains) {
+ if ((newEffectiveBlockedReasons & BLOCKED_METERED_REASON_ADMIN_DISABLED)
+ != BLOCKED_REASON_NONE) {
+ setUidFirewallRuleUL(FIREWALL_CHAIN_METERED_DENY_ADMIN, uid, FIREWALL_RULE_DENY);
+ } else {
+ setUidFirewallRuleUL(FIREWALL_CHAIN_METERED_DENY_ADMIN, uid, FIREWALL_RULE_DEFAULT);
+ }
+ if ((newEffectiveBlockedReasons & BLOCKED_METERED_REASON_USER_RESTRICTED)
+ != BLOCKED_REASON_NONE) {
+ setUidFirewallRuleUL(FIREWALL_CHAIN_METERED_DENY_USER, uid, FIREWALL_RULE_DENY);
+ } else {
+ setUidFirewallRuleUL(FIREWALL_CHAIN_METERED_DENY_USER, uid, FIREWALL_RULE_DEFAULT);
+ }
+ if ((newAllowedReasons & (ALLOWED_METERED_REASON_FOREGROUND
+ | ALLOWED_METERED_REASON_USER_EXEMPTED)) != ALLOWED_REASON_NONE) {
+ setUidFirewallRuleUL(FIREWALL_CHAIN_METERED_ALLOW, uid, FIREWALL_RULE_ALLOW);
+ } else {
+ setUidFirewallRuleUL(FIREWALL_CHAIN_METERED_ALLOW, uid, FIREWALL_RULE_DEFAULT);
+ }
+ } else {
+ // Note that the conditionals below are for avoiding unnecessary calls to netd.
+ // TODO: Measure the performance for doing a no-op call to netd so that we can
+ // remove the conditionals to simplify the logic below. We can also further reduce
+ // some calls to netd if they turn out to be costly.
+ final int denylistReasons = BLOCKED_METERED_REASON_ADMIN_DISABLED
+ | BLOCKED_METERED_REASON_USER_RESTRICTED;
+ if ((oldEffectiveBlockedReasons & denylistReasons) != BLOCKED_REASON_NONE
+ || (newEffectiveBlockedReasons & denylistReasons) != BLOCKED_REASON_NONE) {
+ setMeteredNetworkDenylist(uid,
+ (newEffectiveBlockedReasons & denylistReasons) != BLOCKED_REASON_NONE);
+ }
+ final int allowlistReasons = ALLOWED_METERED_REASON_FOREGROUND
+ | ALLOWED_METERED_REASON_USER_EXEMPTED;
+ if ((oldAllowedReasons & allowlistReasons) != ALLOWED_REASON_NONE
+ || (newAllowedReasons & allowlistReasons) != ALLOWED_REASON_NONE) {
+ setMeteredNetworkAllowlist(uid,
+ (newAllowedReasons & allowlistReasons) != ALLOWED_REASON_NONE);
+ }
}
}
@@ -6149,6 +6183,8 @@
} else if (chain == FIREWALL_CHAIN_BACKGROUND) {
mUidFirewallBackgroundRules.put(uid, rule);
}
+ // Note that we do not need keep a separate cache of uid rules for chains that we do
+ // not call #setUidFirewallRulesUL for.
try {
mNetworkManager.setFirewallUidRule(chain, uid, rule);
@@ -6206,10 +6242,19 @@
FIREWALL_RULE_DEFAULT);
mNetworkManager.setFirewallUidRule(FIREWALL_CHAIN_BACKGROUND, uid,
FIREWALL_RULE_DEFAULT);
- mNetworkManager.setUidOnMeteredNetworkAllowlist(uid, false);
- mLogger.meteredAllowlistChanged(uid, false);
- mNetworkManager.setUidOnMeteredNetworkDenylist(uid, false);
- mLogger.meteredDenylistChanged(uid, false);
+ if (mUseMeteredFirewallChains) {
+ mNetworkManager.setFirewallUidRule(FIREWALL_CHAIN_METERED_DENY_ADMIN, uid,
+ FIREWALL_RULE_DEFAULT);
+ mNetworkManager.setFirewallUidRule(FIREWALL_CHAIN_METERED_DENY_USER, uid,
+ FIREWALL_RULE_DEFAULT);
+ mNetworkManager.setFirewallUidRule(FIREWALL_CHAIN_METERED_ALLOW, uid,
+ FIREWALL_RULE_DEFAULT);
+ } else {
+ mNetworkManager.setUidOnMeteredNetworkAllowlist(uid, false);
+ mLogger.meteredAllowlistChanged(uid, false);
+ mNetworkManager.setUidOnMeteredNetworkDenylist(uid, false);
+ mLogger.meteredDenylistChanged(uid, false);
+ }
} catch (IllegalStateException e) {
Log.wtf(TAG, "problem resetting firewall uid rules for " + uid, e);
} catch (RemoteException e) {
diff --git a/services/core/java/com/android/server/net/flags.aconfig b/services/core/java/com/android/server/net/flags.aconfig
index d9491de..e986dd8 100644
--- a/services/core/java/com/android/server/net/flags.aconfig
+++ b/services/core/java/com/android/server/net/flags.aconfig
@@ -7,3 +7,13 @@
description: "Block network access for apps in a low importance background state"
bug: "304347838"
}
+
+flag {
+ name: "use_metered_firewall_chains"
+ namespace: "backstage_power"
+ description: "Use metered firewall chains to control access to metered networks"
+ bug: "336693007"
+ metadata {
+ purpose: PURPOSE_BUGFIX
+ }
+}
diff --git a/services/core/java/com/android/server/notification/NotificationAttentionHelper.java b/services/core/java/com/android/server/notification/NotificationAttentionHelper.java
index bf49671..13429db 100644
--- a/services/core/java/com/android/server/notification/NotificationAttentionHelper.java
+++ b/services/core/java/com/android/server/notification/NotificationAttentionHelper.java
@@ -22,12 +22,14 @@
import static android.app.NotificationManager.IMPORTANCE_MIN;
import static android.app.NotificationManager.Policy.SUPPRESSED_EFFECT_LIGHTS;
import static android.app.NotificationManager.Policy.SUPPRESSED_EFFECT_STATUS_BAR;
+import static android.content.pm.PackageManager.PERMISSION_GRANTED;
import static android.media.AudioAttributes.USAGE_NOTIFICATION_RINGTONE;
import static android.media.audio.Flags.focusExclusiveWithRecording;
import static android.service.notification.NotificationListenerService.HINT_HOST_DISABLE_CALL_EFFECTS;
import static android.service.notification.NotificationListenerService.HINT_HOST_DISABLE_EFFECTS;
import static android.service.notification.NotificationListenerService.HINT_HOST_DISABLE_NOTIFICATION_EFFECTS;
+import android.Manifest.permission;
import android.annotation.IntDef;
import android.app.ActivityManager;
import android.app.KeyguardManager;
@@ -135,7 +137,7 @@
private LogicalLight mAttentionLight;
private final boolean mUseAttentionLight;
- boolean mHasLight = true;
+ boolean mHasLight;
private final SettingsObserver mSettingsObserver;
@@ -149,7 +151,7 @@
private boolean mInCallStateOffHook = false;
private boolean mScreenOn = true;
private boolean mUserPresent = false;
- boolean mNotificationPulseEnabled;
+ private boolean mNotificationPulseEnabled;
private final Uri mInCallNotificationUri;
private final AudioAttributes mInCallNotificationAudioAttributes;
private final float mInCallNotificationVolume;
@@ -223,7 +225,10 @@
mFlagResolver.getIntValue(NotificationFlags.NOTIF_COOLDOWN_T2),
mFlagResolver.getIntValue(NotificationFlags.NOTIF_VOLUME1),
mFlagResolver.getIntValue(NotificationFlags.NOTIF_VOLUME2),
- mFlagResolver.getIntValue(NotificationFlags.NOTIF_COOLDOWN_COUNTER_RESET));
+ mFlagResolver.getIntValue(NotificationFlags.NOTIF_COOLDOWN_COUNTER_RESET),
+ record -> mPackageManager.checkPermission(
+ permission.RECEIVE_EMERGENCY_BROADCAST,
+ record.getSbn().getPackageName()) == PERMISSION_GRANTED);
return new StrategyAvalanche(
mFlagResolver.getIntValue(NotificationFlags.NOTIF_COOLDOWN_T1),
@@ -231,14 +236,17 @@
mFlagResolver.getIntValue(NotificationFlags.NOTIF_VOLUME1),
mFlagResolver.getIntValue(NotificationFlags.NOTIF_VOLUME2),
mFlagResolver.getIntValue(NotificationFlags.NOTIF_AVALANCHE_TIMEOUT),
- appStrategy);
+ appStrategy, appStrategy.mExemptionProvider);
} else {
return new StrategyPerApp(
mFlagResolver.getIntValue(NotificationFlags.NOTIF_COOLDOWN_T1),
mFlagResolver.getIntValue(NotificationFlags.NOTIF_COOLDOWN_T2),
mFlagResolver.getIntValue(NotificationFlags.NOTIF_VOLUME1),
mFlagResolver.getIntValue(NotificationFlags.NOTIF_VOLUME2),
- mFlagResolver.getIntValue(NotificationFlags.NOTIF_COOLDOWN_COUNTER_RESET));
+ mFlagResolver.getIntValue(NotificationFlags.NOTIF_COOLDOWN_COUNTER_RESET),
+ record -> mPackageManager.checkPermission(
+ permission.RECEIVE_EMERGENCY_BROADCAST,
+ record.getSbn().getPackageName()) == PERMISSION_GRANTED);
}
}
@@ -305,6 +313,13 @@
}
private void loadUserSettings() {
+ boolean pulseEnabled = Settings.System.getIntForUser(mContext.getContentResolver(),
+ Settings.System.NOTIFICATION_LIGHT_PULSE, 0, UserHandle.USER_CURRENT) != 0;
+ if (mNotificationPulseEnabled != pulseEnabled) {
+ mNotificationPulseEnabled = pulseEnabled;
+ updateLightsLocked();
+ }
+
if (Flags.politeNotifications()) {
try {
mCurrentWorkProfileId = getManagedProfileId(ActivityManager.getCurrentUser());
@@ -874,6 +889,9 @@
boolean canShowLightsLocked(final NotificationRecord record, final Signals signals,
boolean aboveThreshold) {
+ if (!mSystemReady) {
+ return false;
+ }
// device lacks light
if (!mHasLight) {
return false;
@@ -1088,6 +1106,11 @@
}
}
+ // Returns true if a notification should be exempted from attenuation
+ private interface ExemptionProvider {
+ boolean isExempted(NotificationRecord record);
+ }
+
@VisibleForTesting
abstract static class PolitenessStrategy {
static final int POLITE_STATE_DEFAULT = 0;
@@ -1118,8 +1141,10 @@
protected boolean mIsActive = true;
+ protected final ExemptionProvider mExemptionProvider;
+
public PolitenessStrategy(int timeoutPolite, int timeoutMuted, int volumePolite,
- int volumeMuted) {
+ int volumeMuted, ExemptionProvider exemptionProvider) {
mVolumeStates = new HashMap<>();
mLastUpdatedTimestampByPackage = new HashMap<>();
@@ -1127,6 +1152,7 @@
this.mTimeoutMuted = timeoutMuted;
this.mVolumePolite = volumePolite / 100.0f;
this.mVolumeMuted = volumeMuted / 100.0f;
+ this.mExemptionProvider = exemptionProvider;
}
abstract void onNotificationPosted(NotificationRecord record);
@@ -1284,8 +1310,8 @@
private final int mMaxPostedForReset;
public StrategyPerApp(int timeoutPolite, int timeoutMuted, int volumePolite,
- int volumeMuted, int maxPosted) {
- super(timeoutPolite, timeoutMuted, volumePolite, volumeMuted);
+ int volumeMuted, int maxPosted, ExemptionProvider exemptionProvider) {
+ super(timeoutPolite, timeoutMuted, volumePolite, volumeMuted, exemptionProvider);
mNumPosted = new HashMap<>();
mMaxPostedForReset = maxPosted;
@@ -1306,7 +1332,12 @@
final String key = getChannelKey(record);
@PolitenessState final int currState = getPolitenessState(record);
- @PolitenessState int nextState = getNextState(currState, timeSinceLastNotif);
+ @PolitenessState int nextState;
+ if (Flags.politeNotificationsAttnUpdate()) {
+ nextState = getNextState(currState, timeSinceLastNotif, record);
+ } else {
+ nextState = getNextState(currState, timeSinceLastNotif);
+ }
// Reset to default state if number of posted notifications exceed this value when muted
int numPosted = mNumPosted.getOrDefault(key, 0) + 1;
@@ -1324,6 +1355,14 @@
mVolumeStates.put(key, nextState);
}
+ @PolitenessState int getNextState(@PolitenessState final int currState,
+ final long timeSinceLastNotif, final NotificationRecord record) {
+ if (mExemptionProvider.isExempted(record)) {
+ return POLITE_STATE_DEFAULT;
+ }
+ return getNextState(currState, timeSinceLastNotif);
+ }
+
@Override
public void onUserInteraction(final NotificationRecord record) {
super.onUserInteraction(record);
@@ -1344,8 +1383,9 @@
private long mLastAvalancheTriggerTimestamp = 0;
StrategyAvalanche(int timeoutPolite, int timeoutMuted, int volumePolite,
- int volumeMuted, int timeoutAvalanche, PolitenessStrategy appStrategy) {
- super(timeoutPolite, timeoutMuted, volumePolite, volumeMuted);
+ int volumeMuted, int timeoutAvalanche, PolitenessStrategy appStrategy,
+ ExemptionProvider exemptionProvider) {
+ super(timeoutPolite, timeoutMuted, volumePolite, volumeMuted, exemptionProvider);
mTimeoutAvalanche = timeoutAvalanche;
mAppStrategy = appStrategy;
@@ -1518,7 +1558,7 @@
return true;
}
- return false;
+ return mExemptionProvider.isExempted(record);
}
private boolean isAvalancheExempted(final NotificationRecord record) {
@@ -1721,8 +1761,6 @@
void setLights(LogicalLight light) {
mNotificationLight = light;
mAttentionLight = light;
- mNotificationPulseEnabled = true;
- mHasLight = true;
}
@VisibleForTesting
diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java
index 61054a9..44e7694 100755
--- a/services/core/java/com/android/server/notification/NotificationManagerService.java
+++ b/services/core/java/com/android/server/notification/NotificationManagerService.java
@@ -593,6 +593,8 @@
static final long NOTIFICATION_TTL = Duration.ofDays(3).toMillis();
+ static final long NOTIFICATION_MAX_AGE_AT_POST = Duration.ofDays(14).toMillis();
+
private IActivityManager mAm;
private ActivityTaskManagerInternal mAtm;
private ActivityManager mActivityManager;
@@ -2637,27 +2639,48 @@
* Cleanup broadcast receivers change listeners.
*/
public void onDestroy() {
- getContext().unregisterReceiver(mIntentReceiver);
- getContext().unregisterReceiver(mPackageIntentReceiver);
- if (Flags.allNotifsNeedTtl()) {
- mTtlHelper.destroy();
- } else {
- getContext().unregisterReceiver(mNotificationTimeoutReceiver);
+ if (mIntentReceiver != null) {
+ getContext().unregisterReceiver(mIntentReceiver);
}
- getContext().unregisterReceiver(mRestoreReceiver);
- getContext().unregisterReceiver(mLocaleChangeReceiver);
-
- mSettingsObserver.destroy();
- mRoleObserver.destroy();
+ if (mPackageIntentReceiver != null) {
+ getContext().unregisterReceiver(mPackageIntentReceiver);
+ }
+ if (Flags.allNotifsNeedTtl()) {
+ if (mTtlHelper != null) {
+ mTtlHelper.destroy();
+ }
+ } else {
+ if (mNotificationTimeoutReceiver != null) {
+ getContext().unregisterReceiver(mNotificationTimeoutReceiver);
+ }
+ }
+ if (mRestoreReceiver != null) {
+ getContext().unregisterReceiver(mRestoreReceiver);
+ }
+ if (mLocaleChangeReceiver != null) {
+ getContext().unregisterReceiver(mLocaleChangeReceiver);
+ }
+ if (mSettingsObserver != null) {
+ mSettingsObserver.destroy();
+ }
+ if (mRoleObserver != null) {
+ mRoleObserver.destroy();
+ }
if (mShortcutHelper != null) {
mShortcutHelper.destroy();
}
- mStatsManager.clearPullAtomCallback(PACKAGE_NOTIFICATION_PREFERENCES);
- mStatsManager.clearPullAtomCallback(PACKAGE_NOTIFICATION_CHANNEL_PREFERENCES);
- mStatsManager.clearPullAtomCallback(PACKAGE_NOTIFICATION_CHANNEL_GROUP_PREFERENCES);
- mStatsManager.clearPullAtomCallback(DND_MODE_RULE);
- mAppOps.stopWatchingMode(mAppOpsListener);
- mAlarmManager.cancelAll();
+ if (mStatsManager != null) {
+ mStatsManager.clearPullAtomCallback(PACKAGE_NOTIFICATION_PREFERENCES);
+ mStatsManager.clearPullAtomCallback(PACKAGE_NOTIFICATION_CHANNEL_PREFERENCES);
+ mStatsManager.clearPullAtomCallback(PACKAGE_NOTIFICATION_CHANNEL_GROUP_PREFERENCES);
+ mStatsManager.clearPullAtomCallback(DND_MODE_RULE);
+ }
+ if (mAppOps != null) {
+ mAppOps.stopWatchingMode(mAppOpsListener);
+ }
+ if (mAlarmManager != null) {
+ mAlarmManager.cancelAll();
+ }
}
protected String[] getStringArrayResource(int key) {
@@ -5809,7 +5832,16 @@
@Override
public ComponentName getEffectsSuppressor() {
- return !mEffectsSuppressors.isEmpty() ? mEffectsSuppressors.get(0) : null;
+ ComponentName suppressor = !mEffectsSuppressors.isEmpty()
+ ? mEffectsSuppressors.get(0)
+ : null;
+ if (isCallerSystemOrSystemUiOrShell() || suppressor == null
+ || mPackageManagerInternal.isSameApp(suppressor.getPackageName(),
+ Binder.getCallingUid(), UserHandle.getUserId(Binder.getCallingUid()))) {
+ return suppressor;
+ }
+
+ return null;
}
@Override
@@ -7202,7 +7234,15 @@
callingUid, userId, true, false, "cancelNotificationWithTag", pkg);
// ensure opPkg is delegate if does not match pkg
- int uid = resolveNotificationUid(opPkg, pkg, callingUid, userId);
+
+ int uid = INVALID_UID;
+
+ try {
+ uid = resolveNotificationUid(opPkg, pkg, callingUid, userId);
+ } catch (NameNotFoundException e) {
+ // package either never existed so there's no posted notification or it's being
+ // uninstalled so we'll be cleaning it up soon. log and return immediately below.
+ }
if (uid == INVALID_UID) {
Slog.w(TAG, opPkg + ":" + callingUid + " trying to cancel notification "
@@ -7296,7 +7336,13 @@
// Can throw a SecurityException if the calling uid doesn't have permission to post
// as "pkg"
- final int notificationUid = resolveNotificationUid(opPkg, pkg, callingUid, userId);
+ int notificationUid = INVALID_UID;
+
+ try {
+ notificationUid = resolveNotificationUid(opPkg, pkg, callingUid, userId);
+ } catch (NameNotFoundException e) {
+ // not great - throw immediately below
+ }
if (notificationUid == INVALID_UID) {
throw new SecurityException("Caller " + opPkg + ":" + callingUid
@@ -7722,6 +7768,9 @@
return true;
}
// Check if an app has been given system exemption
+ if (ai.uid == Process.SYSTEM_UID) {
+ return false;
+ }
return mAppOps.checkOpNoThrow(
AppOpsManager.OP_SYSTEM_EXEMPT_FROM_DISMISSIBLE_NOTIFICATIONS, ai.uid,
ai.packageName) == MODE_ALLOWED;
@@ -7850,7 +7899,8 @@
}
@VisibleForTesting
- int resolveNotificationUid(String callingPkg, String targetPkg, int callingUid, int userId) {
+ int resolveNotificationUid(String callingPkg, String targetPkg, int callingUid, int userId)
+ throws NameNotFoundException {
if (userId == USER_ALL) {
userId = USER_SYSTEM;
}
@@ -7861,12 +7911,8 @@
return callingUid;
}
- int targetUid = INVALID_UID;
- try {
- targetUid = mPackageManagerClient.getPackageUidAsUser(targetPkg, userId);
- } catch (NameNotFoundException e) {
- /* ignore, handled by caller */
- }
+ int targetUid = mPackageManagerClient.getPackageUidAsUser(targetPkg, userId);
+
// posted from app A on behalf of app B
if (isCallerAndroid(callingPkg, callingUid)
|| mPreferencesHelper.isDelegateAllowed(
@@ -8016,6 +8062,13 @@
return false;
}
+ if (Flags.rejectOldNotifications() && n.hasAppProvidedWhen() && n.getWhen() > 0
+ && (System.currentTimeMillis() - n.getWhen()) > NOTIFICATION_MAX_AGE_AT_POST) {
+ Slog.d(TAG, "Ignored enqueue for old " + n.getWhen() + " notification " + r.getKey());
+ mUsageStats.registerTooOldBlocked(r);
+ return false;
+ }
+
return true;
}
diff --git a/services/core/java/com/android/server/notification/NotificationUsageStats.java b/services/core/java/com/android/server/notification/NotificationUsageStats.java
index e960f4b..c09077e 100644
--- a/services/core/java/com/android/server/notification/NotificationUsageStats.java
+++ b/services/core/java/com/android/server/notification/NotificationUsageStats.java
@@ -257,6 +257,14 @@
}
}
+ public synchronized void registerTooOldBlocked(NotificationRecord notification) {
+ AggregatedStats[] aggregatedStatsArray = getAggregatedStatsLocked(notification);
+ for (AggregatedStats stats : aggregatedStatsArray) {
+ stats.numTooOld++;
+ }
+ releaseAggregatedStatsLocked(aggregatedStatsArray);
+ }
+
@GuardedBy("this")
private AggregatedStats[] getAggregatedStatsLocked(NotificationRecord record) {
return getAggregatedStatsLocked(record.getSbn().getPackageName());
@@ -405,6 +413,7 @@
public int numUndecoratedRemoteViews;
public long mLastAccessTime;
public int numImagesRemoved;
+ public int numTooOld;
public AggregatedStats(Context context, String key) {
this.key = key;
@@ -535,6 +544,7 @@
maybeCount("note_over_alert_rate", (numAlertViolations - previous.numAlertViolations));
maybeCount("note_over_quota", (numQuotaViolations - previous.numQuotaViolations));
maybeCount("note_images_removed", (numImagesRemoved - previous.numImagesRemoved));
+ maybeCount("not_too_old", (numTooOld - previous.numTooOld));
noisyImportance.maybeCount(previous.noisyImportance);
quietImportance.maybeCount(previous.quietImportance);
finalImportance.maybeCount(previous.finalImportance);
@@ -570,6 +580,7 @@
previous.numAlertViolations = numAlertViolations;
previous.numQuotaViolations = numQuotaViolations;
previous.numImagesRemoved = numImagesRemoved;
+ previous.numTooOld = numTooOld;
noisyImportance.update(previous.noisyImportance);
quietImportance.update(previous.quietImportance);
finalImportance.update(previous.finalImportance);
@@ -679,6 +690,8 @@
output.append("numQuotaViolations=").append(numQuotaViolations).append("\n");
output.append(indentPlusTwo);
output.append("numImagesRemoved=").append(numImagesRemoved).append("\n");
+ output.append(indentPlusTwo);
+ output.append("numTooOld=").append(numTooOld).append("\n");
output.append(indentPlusTwo).append(noisyImportance.toString()).append("\n");
output.append(indentPlusTwo).append(quietImportance.toString()).append("\n");
output.append(indentPlusTwo).append(finalImportance.toString()).append("\n");
@@ -725,6 +738,7 @@
maybePut(dump, "notificationEnqueueRate", getEnqueueRate());
maybePut(dump, "numAlertViolations", numAlertViolations);
maybePut(dump, "numImagesRemoved", numImagesRemoved);
+ maybePut(dump, "numTooOld", numTooOld);
noisyImportance.maybePut(dump, previous.noisyImportance);
quietImportance.maybePut(dump, previous.quietImportance);
finalImportance.maybePut(dump, previous.finalImportance);
diff --git a/services/core/java/com/android/server/notification/flags.aconfig b/services/core/java/com/android/server/notification/flags.aconfig
index 9dcca49..bf6b652 100644
--- a/services/core/java/com/android/server/notification/flags.aconfig
+++ b/services/core/java/com/android/server/notification/flags.aconfig
@@ -135,3 +135,10 @@
description: "This flag controls which signal is used to handle a user switch system event"
bug: "337077643"
}
+
+flag {
+ name: "reject_old_notifications"
+ namespace: "systemui"
+ description: "This flag does not allow notifications older than 2 weeks old to be posted"
+ bug: "339833083"
+}
\ No newline at end of file
diff --git a/services/core/java/com/android/server/ondeviceintelligence/BundleUtil.java b/services/core/java/com/android/server/ondeviceintelligence/BundleUtil.java
index 681dd0b..96ab2cc 100644
--- a/services/core/java/com/android/server/ondeviceintelligence/BundleUtil.java
+++ b/services/core/java/com/android/server/ondeviceintelligence/BundleUtil.java
@@ -33,6 +33,7 @@
import android.os.BadParcelableException;
import android.os.Bundle;
import android.os.ParcelFileDescriptor;
+import android.os.Parcelable;
import android.os.PersistableBundle;
import android.os.RemoteCallback;
import android.os.RemoteException;
@@ -41,7 +42,10 @@
import android.system.Os;
import android.util.Log;
+import com.android.internal.infra.AndroidFuture;
+
import java.util.concurrent.Executor;
+import java.util.concurrent.TimeoutException;
/**
* Util methods for ensuring the Bundle passed in various methods are read-only and restricted to
@@ -78,16 +82,16 @@
if (canMarshall(obj) || obj instanceof CursorWindow) {
continue;
}
-
- if (obj instanceof ParcelFileDescriptor) {
+ if (obj instanceof Bundle) {
+ sanitizeInferenceParams((Bundle) obj);
+ } else if (obj instanceof ParcelFileDescriptor) {
validatePfdReadOnly((ParcelFileDescriptor) obj);
} else if (obj instanceof SharedMemory) {
((SharedMemory) obj).setProtect(PROT_READ);
} else if (obj instanceof Bitmap) {
- if (((Bitmap) obj).isMutable()) {
- throw new BadParcelableException(
- "Encountered a mutable Bitmap in the Bundle at key : " + key);
- }
+ validateBitmap((Bitmap) obj);
+ } else if (obj instanceof Parcelable[]) {
+ validateParcelableArray((Parcelable[]) obj);
} else {
throw new BadParcelableException(
"Unsupported Parcelable type encountered in the Bundle: "
@@ -125,20 +129,20 @@
continue;
}
- if (obj instanceof ParcelFileDescriptor) {
+ if (obj instanceof Bundle) {
+ sanitizeResponseParams((Bundle) obj);
+ } else if (obj instanceof ParcelFileDescriptor) {
validatePfdReadOnly((ParcelFileDescriptor) obj);
} else if (obj instanceof Bitmap) {
- if (((Bitmap) obj).isMutable()) {
- throw new BadParcelableException(
- "Encountered a mutable Bitmap in the Bundle at key : " + key);
- }
+ validateBitmap((Bitmap) obj);
+ } else if (obj instanceof Parcelable[]) {
+ validateParcelableArray((Parcelable[]) obj);
} else {
throw new BadParcelableException(
"Unsupported Parcelable type encountered in the Bundle: "
+ obj.getClass().getSimpleName());
}
}
- Log.e(TAG, "validateResponseParams : Finished");
}
/**
@@ -183,7 +187,8 @@
public static IStreamingResponseCallback wrapWithValidation(
IStreamingResponseCallback streamingResponseCallback,
- Executor resourceClosingExecutor) {
+ Executor resourceClosingExecutor,
+ AndroidFuture future) {
return new IStreamingResponseCallback.Stub() {
@Override
public void onNewContent(Bundle processedResult) throws RemoteException {
@@ -203,6 +208,7 @@
streamingResponseCallback.onSuccess(resultBundle);
} finally {
resourceClosingExecutor.execute(() -> tryCloseResource(resultBundle));
+ future.complete(null);
}
}
@@ -210,6 +216,7 @@
public void onFailure(int errorCode, String errorMessage,
PersistableBundle errorParams) throws RemoteException {
streamingResponseCallback.onFailure(errorCode, errorMessage, errorParams);
+ future.completeExceptionally(new TimeoutException());
}
@Override
@@ -237,7 +244,8 @@
}
public static IResponseCallback wrapWithValidation(IResponseCallback responseCallback,
- Executor resourceClosingExecutor) {
+ Executor resourceClosingExecutor,
+ AndroidFuture future) {
return new IResponseCallback.Stub() {
@Override
public void onSuccess(Bundle resultBundle)
@@ -247,6 +255,7 @@
responseCallback.onSuccess(resultBundle);
} finally {
resourceClosingExecutor.execute(() -> tryCloseResource(resultBundle));
+ future.complete(null);
}
}
@@ -254,6 +263,7 @@
public void onFailure(int errorCode, String errorMessage,
PersistableBundle errorParams) throws RemoteException {
responseCallback.onFailure(errorCode, errorMessage, errorParams);
+ future.completeExceptionally(new TimeoutException());
}
@Override
@@ -280,17 +290,20 @@
}
- public static ITokenInfoCallback wrapWithValidation(ITokenInfoCallback responseCallback) {
+ public static ITokenInfoCallback wrapWithValidation(ITokenInfoCallback responseCallback,
+ AndroidFuture future) {
return new ITokenInfoCallback.Stub() {
@Override
public void onSuccess(TokenInfo tokenInfo) throws RemoteException {
responseCallback.onSuccess(tokenInfo);
+ future.complete(null);
}
@Override
public void onFailure(int errorCode, String errorMessage, PersistableBundle errorParams)
throws RemoteException {
responseCallback.onFailure(errorCode, errorMessage, errorParams);
+ future.completeExceptionally(new TimeoutException());
}
};
}
@@ -310,6 +323,26 @@
}
}
+ private static void validateParcelableArray(Parcelable[] parcelables) {
+ if (parcelables.length > 0
+ && parcelables[0] instanceof ParcelFileDescriptor) {
+ // Safe to cast
+ validatePfdsReadOnly(parcelables);
+ } else if (parcelables.length > 0
+ && parcelables[0] instanceof Bitmap) {
+ validateBitmapsImmutable(parcelables);
+ } else {
+ throw new BadParcelableException(
+ "Could not cast to any known parcelable array");
+ }
+ }
+
+ public static void validatePfdsReadOnly(Parcelable[] pfds) {
+ for (Parcelable pfd : pfds) {
+ validatePfdReadOnly((ParcelFileDescriptor) pfd);
+ }
+ }
+
public static void validatePfdReadOnly(ParcelFileDescriptor pfd) {
if (pfd == null) {
return;
@@ -326,6 +359,19 @@
}
}
+ private static void validateBitmap(Bitmap obj) {
+ if (obj.isMutable()) {
+ throw new BadParcelableException(
+ "Encountered a mutable Bitmap in the Bundle at key : " + obj);
+ }
+ }
+
+ private static void validateBitmapsImmutable(Parcelable[] bitmaps) {
+ for (Parcelable bitmap : bitmaps) {
+ validateBitmap((Bitmap) bitmap);
+ }
+ }
+
public static void tryCloseResource(Bundle bundle) {
if (bundle == null || bundle.isEmpty() || !bundle.hasFileDescriptors()) {
return;
diff --git a/services/core/java/com/android/server/ondeviceintelligence/OnDeviceIntelligenceManagerService.java b/services/core/java/com/android/server/ondeviceintelligence/OnDeviceIntelligenceManagerService.java
index 99401a1..dd76037 100644
--- a/services/core/java/com/android/server/ondeviceintelligence/OnDeviceIntelligenceManagerService.java
+++ b/services/core/java/com/android/server/ondeviceintelligence/OnDeviceIntelligenceManagerService.java
@@ -16,6 +16,11 @@
package com.android.server.ondeviceintelligence;
+import static android.service.ondeviceintelligence.OnDeviceSandboxedInferenceService.DEVICE_CONFIG_UPDATE_BUNDLE_KEY;
+import static android.service.ondeviceintelligence.OnDeviceSandboxedInferenceService.MODEL_LOADED_BUNDLE_KEY;
+import static android.service.ondeviceintelligence.OnDeviceSandboxedInferenceService.MODEL_UNLOADED_BUNDLE_KEY;
+import static android.service.ondeviceintelligence.OnDeviceSandboxedInferenceService.REGISTER_MODEL_UPDATE_CALLBACK_BUNDLE_KEY;
+
import static com.android.server.ondeviceintelligence.BundleUtil.sanitizeInferenceParams;
import static com.android.server.ondeviceintelligence.BundleUtil.validatePfdReadOnly;
import static com.android.server.ondeviceintelligence.BundleUtil.sanitizeStateParams;
@@ -29,6 +34,7 @@
import android.app.AppGlobals;
import android.app.ondeviceintelligence.DownloadCallback;
import android.app.ondeviceintelligence.Feature;
+import android.app.ondeviceintelligence.FeatureDetails;
import android.app.ondeviceintelligence.IDownloadCallback;
import android.app.ondeviceintelligence.IFeatureCallback;
import android.app.ondeviceintelligence.IFeatureDetailsCallback;
@@ -41,6 +47,7 @@
import android.app.ondeviceintelligence.OnDeviceIntelligenceException;
import android.content.ComponentName;
import android.content.Context;
+import android.content.Intent;
import android.content.pm.PackageManager;
import android.content.pm.ServiceInfo;
import android.content.res.Resources;
@@ -59,6 +66,7 @@
import android.os.ShellCallback;
import android.os.UserHandle;
import android.provider.DeviceConfig;
+import android.provider.Settings;
import android.service.ondeviceintelligence.IOnDeviceIntelligenceService;
import android.service.ondeviceintelligence.IOnDeviceSandboxedInferenceService;
import android.service.ondeviceintelligence.IProcessingUpdateStatusCallback;
@@ -77,13 +85,17 @@
import com.android.internal.os.BackgroundThread;
import com.android.server.LocalServices;
import com.android.server.SystemService;
+import com.android.server.ondeviceintelligence.callbacks.ListenableDownloadCallback;
import java.io.FileDescriptor;
import java.io.IOException;
+import java.util.List;
import java.util.Objects;
import java.util.Set;
import java.util.concurrent.Executor;
import java.util.concurrent.Executors;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.TimeoutException;
/**
* This is the system service for handling calls on the
@@ -104,13 +116,23 @@
/** Handler message to {@link #resetTemporaryServices()} */
private static final int MSG_RESET_TEMPORARY_SERVICE = 0;
+ /** Handler message to clean up temporary broadcast keys. */
+ private static final int MSG_RESET_BROADCAST_KEYS = 1;
+ /** Handler message to clean up temporary config namespace. */
+ private static final int MSG_RESET_CONFIG_NAMESPACE = 2;
/** Default value in absence of {@link DeviceConfig} override. */
private static final boolean DEFAULT_SERVICE_ENABLED = true;
private static final String NAMESPACE_ON_DEVICE_INTELLIGENCE = "ondeviceintelligence";
+ private static final String SYSTEM_PACKAGE = "android";
+
+
private final Executor resourceClosingExecutor = Executors.newCachedThreadPool();
private final Executor callbackExecutor = Executors.newCachedThreadPool();
+ private final Executor broadcastExecutor = Executors.newCachedThreadPool();
+ private final Executor mConfigExecutor = Executors.newCachedThreadPool();
+
private final Context mContext;
protected final Object mLock = new Object();
@@ -122,12 +144,23 @@
@GuardedBy("mLock")
private String[] mTemporaryServiceNames;
+ @GuardedBy("mLock")
+ private String[] mTemporaryBroadcastKeys;
+ @GuardedBy("mLock")
+ private String mBroadcastPackageName;
+ @GuardedBy("mLock")
+ private String mTemporaryConfigNamespace;
+
+ private final DeviceConfig.OnPropertiesChangedListener mOnPropertiesChangedListener =
+ this::sendUpdatedConfig;
+
/**
* Handler used to reset the temporary service names.
*/
- @GuardedBy("mLock")
private Handler mTemporaryHandler;
+ private final @NonNull Handler mMainHandler = new Handler(Looper.getMainLooper());
+
public OnDeviceIntelligenceManagerService(Context context) {
super(context);
@@ -187,8 +220,16 @@
return;
}
ensureRemoteIntelligenceServiceInitialized();
- mRemoteOnDeviceIntelligenceService.run(
- service -> service.getVersion(remoteCallback));
+ mRemoteOnDeviceIntelligenceService.postAsync(
+ service -> {
+ AndroidFuture future = new AndroidFuture();
+ service.getVersion(new RemoteCallback(
+ result -> {
+ remoteCallback.sendResult(result);
+ future.complete(null);
+ }));
+ return future.orTimeout(getIdleTimeoutMs(), TimeUnit.MILLISECONDS);
+ });
}
@Override
@@ -208,8 +249,25 @@
}
ensureRemoteIntelligenceServiceInitialized();
int callerUid = Binder.getCallingUid();
- mRemoteOnDeviceIntelligenceService.run(
- service -> service.getFeature(callerUid, id, featureCallback));
+ mRemoteOnDeviceIntelligenceService.postAsync(
+ service -> {
+ AndroidFuture future = new AndroidFuture();
+ service.getFeature(callerUid, id, new IFeatureCallback.Stub() {
+ @Override
+ public void onSuccess(Feature result) throws RemoteException {
+ featureCallback.onSuccess(result);
+ future.complete(null);
+ }
+
+ @Override
+ public void onFailure(int errorCode, String errorMessage,
+ PersistableBundle errorParams) throws RemoteException {
+ featureCallback.onFailure(errorCode, errorMessage, errorParams);
+ future.completeExceptionally(new TimeoutException());
+ }
+ });
+ return future.orTimeout(getIdleTimeoutMs(), TimeUnit.MILLISECONDS);
+ });
}
@Override
@@ -229,9 +287,29 @@
}
ensureRemoteIntelligenceServiceInitialized();
int callerUid = Binder.getCallingUid();
- mRemoteOnDeviceIntelligenceService.run(
- service -> service.listFeatures(callerUid,
- listFeaturesCallback));
+ mRemoteOnDeviceIntelligenceService.postAsync(
+ service -> {
+ AndroidFuture future = new AndroidFuture();
+ service.listFeatures(callerUid,
+ new IListFeaturesCallback.Stub() {
+ @Override
+ public void onSuccess(List<Feature> result)
+ throws RemoteException {
+ listFeaturesCallback.onSuccess(result);
+ future.complete(null);
+ }
+
+ @Override
+ public void onFailure(int errorCode, String errorMessage,
+ PersistableBundle errorParams)
+ throws RemoteException {
+ listFeaturesCallback.onFailure(errorCode, errorMessage,
+ errorParams);
+ future.completeExceptionally(new TimeoutException());
+ }
+ });
+ return future.orTimeout(getIdleTimeoutMs(), TimeUnit.MILLISECONDS);
+ });
}
@Override
@@ -253,9 +331,29 @@
}
ensureRemoteIntelligenceServiceInitialized();
int callerUid = Binder.getCallingUid();
- mRemoteOnDeviceIntelligenceService.run(
- service -> service.getFeatureDetails(callerUid, feature,
- featureDetailsCallback));
+ mRemoteOnDeviceIntelligenceService.postAsync(
+ service -> {
+ AndroidFuture future = new AndroidFuture();
+ service.getFeatureDetails(callerUid, feature,
+ new IFeatureDetailsCallback.Stub() {
+ @Override
+ public void onSuccess(FeatureDetails result)
+ throws RemoteException {
+ future.complete(null);
+ featureDetailsCallback.onSuccess(result);
+ }
+
+ @Override
+ public void onFailure(int errorCode, String errorMessage,
+ PersistableBundle errorParams)
+ throws RemoteException {
+ future.completeExceptionally(null);
+ featureDetailsCallback.onFailure(errorCode,
+ errorMessage, errorParams);
+ }
+ });
+ return future.orTimeout(getIdleTimeoutMs(), TimeUnit.MILLISECONDS);
+ });
}
@Override
@@ -276,10 +374,20 @@
}
ensureRemoteIntelligenceServiceInitialized();
int callerUid = Binder.getCallingUid();
- mRemoteOnDeviceIntelligenceService.run(
- service -> service.requestFeatureDownload(callerUid, feature,
- wrapCancellationFuture(cancellationSignalFuture),
- downloadCallback));
+ mRemoteOnDeviceIntelligenceService.postAsync(
+ service -> {
+ AndroidFuture future = new AndroidFuture();
+ ListenableDownloadCallback listenableDownloadCallback =
+ new ListenableDownloadCallback(
+ downloadCallback,
+ mMainHandler, future, getIdleTimeoutMs());
+ service.requestFeatureDownload(callerUid, feature,
+ wrapCancellationFuture(cancellationSignalFuture),
+ listenableDownloadCallback);
+ return future; // this future has no timeout because, actual download
+ // might take long, but we fail early if there is no progress callbacks.
+ }
+ );
}
@@ -306,11 +414,15 @@
}
ensureRemoteInferenceServiceInitialized();
int callerUid = Binder.getCallingUid();
- result = mRemoteInferenceService.post(
- service -> service.requestTokenInfo(callerUid, feature,
- request,
- wrapCancellationFuture(cancellationSignalFuture),
- wrapWithValidation(tokenInfoCallback)));
+ result = mRemoteInferenceService.postAsync(
+ service -> {
+ AndroidFuture future = new AndroidFuture();
+ service.requestTokenInfo(callerUid, feature,
+ request,
+ wrapCancellationFuture(cancellationSignalFuture),
+ wrapWithValidation(tokenInfoCallback, future));
+ return future.orTimeout(getIdleTimeoutMs(), TimeUnit.MILLISECONDS);
+ });
result.whenCompleteAsync((c, e) -> BundleUtil.tryCloseResource(request),
resourceClosingExecutor);
} finally {
@@ -345,13 +457,18 @@
}
ensureRemoteInferenceServiceInitialized();
int callerUid = Binder.getCallingUid();
- result = mRemoteInferenceService.post(
- service -> service.processRequest(callerUid, feature,
- request,
- requestType,
- wrapCancellationFuture(cancellationSignalFuture),
- wrapProcessingFuture(processingSignalFuture),
- wrapWithValidation(responseCallback, resourceClosingExecutor)));
+ result = mRemoteInferenceService.postAsync(
+ service -> {
+ AndroidFuture future = new AndroidFuture();
+ service.processRequest(callerUid, feature,
+ request,
+ requestType,
+ wrapCancellationFuture(cancellationSignalFuture),
+ wrapProcessingFuture(processingSignalFuture),
+ wrapWithValidation(responseCallback,
+ resourceClosingExecutor, future));
+ return future.orTimeout(getIdleTimeoutMs(), TimeUnit.MILLISECONDS);
+ });
result.whenCompleteAsync((c, e) -> BundleUtil.tryCloseResource(request),
resourceClosingExecutor);
} finally {
@@ -385,13 +502,18 @@
}
ensureRemoteInferenceServiceInitialized();
int callerUid = Binder.getCallingUid();
- result = mRemoteInferenceService.post(
- service -> service.processRequestStreaming(callerUid,
- feature,
- request, requestType,
- wrapCancellationFuture(cancellationSignalFuture),
- wrapProcessingFuture(processingSignalFuture),
- streamingCallback));
+ result = mRemoteInferenceService.postAsync(
+ service -> {
+ AndroidFuture future = new AndroidFuture();
+ service.processRequestStreaming(callerUid,
+ feature,
+ request, requestType,
+ wrapCancellationFuture(cancellationSignalFuture),
+ wrapProcessingFuture(processingSignalFuture),
+ wrapWithValidation(streamingCallback,
+ resourceClosingExecutor, future));
+ return future.orTimeout(getIdleTimeoutMs(), TimeUnit.MILLISECONDS);
+ });
result.whenCompleteAsync((c, e) -> BundleUtil.tryCloseResource(request),
resourceClosingExecutor);
} finally {
@@ -480,10 +602,14 @@
@NonNull IOnDeviceSandboxedInferenceService service) {
try {
ensureRemoteIntelligenceServiceInitialized();
- mRemoteOnDeviceIntelligenceService.run(
- IOnDeviceIntelligenceService::notifyInferenceServiceConnected);
service.registerRemoteStorageService(
getIRemoteStorageService());
+ mRemoteOnDeviceIntelligenceService.run(
+ IOnDeviceIntelligenceService::notifyInferenceServiceConnected);
+ broadcastExecutor.execute(
+ () -> registerModelLoadingBroadcasts(service));
+ mConfigExecutor.execute(
+ () -> registerDeviceConfigChangeListener());
} catch (RemoteException ex) {
Slog.w(TAG, "Failed to send connected event", ex);
}
@@ -493,6 +619,106 @@
}
}
+ private void registerModelLoadingBroadcasts(IOnDeviceSandboxedInferenceService service) {
+ String[] modelBroadcastKeys;
+ try {
+ modelBroadcastKeys = getBroadcastKeys();
+ } catch (Resources.NotFoundException e) {
+ Slog.d(TAG, "Skipping model broadcasts as broadcast intents configured.");
+ return;
+ }
+
+ Bundle bundle = new Bundle();
+ bundle.putBoolean(REGISTER_MODEL_UPDATE_CALLBACK_BUNDLE_KEY, true);
+ try {
+ service.updateProcessingState(bundle, new IProcessingUpdateStatusCallback.Stub() {
+ @Override
+ public void onSuccess(PersistableBundle statusParams) {
+ Binder.clearCallingIdentity();
+ synchronized (mLock) {
+ if (statusParams.containsKey(MODEL_LOADED_BUNDLE_KEY)) {
+ String modelLoadedBroadcastKey = modelBroadcastKeys[0];
+ if (modelLoadedBroadcastKey != null
+ && !modelLoadedBroadcastKey.isEmpty()) {
+ final Intent intent = new Intent(modelLoadedBroadcastKey);
+ intent.setPackage(mBroadcastPackageName);
+ mContext.sendBroadcast(intent,
+ Manifest.permission.USE_ON_DEVICE_INTELLIGENCE);
+ }
+ } else if (statusParams.containsKey(MODEL_UNLOADED_BUNDLE_KEY)) {
+ String modelUnloadedBroadcastKey = modelBroadcastKeys[1];
+ if (modelUnloadedBroadcastKey != null
+ && !modelUnloadedBroadcastKey.isEmpty()) {
+ final Intent intent = new Intent(modelUnloadedBroadcastKey);
+ intent.setPackage(mBroadcastPackageName);
+ mContext.sendBroadcast(intent,
+ Manifest.permission.USE_ON_DEVICE_INTELLIGENCE);
+ }
+ }
+ }
+ }
+
+ @Override
+ public void onFailure(int errorCode, String errorMessage) {
+ Slog.e(TAG, "Failed to register model loading callback with status code",
+ new OnDeviceIntelligenceException(errorCode, errorMessage));
+ }
+ });
+ } catch (RemoteException e) {
+ Slog.e(TAG, "Failed to register model loading callback with status code", e);
+ }
+ }
+
+ private void registerDeviceConfigChangeListener() {
+ Log.d(TAG, "registerDeviceConfigChangeListener");
+ String configNamespace = getConfigNamespace();
+ if (configNamespace.isEmpty()) {
+ Slog.e(TAG, "config_defaultOnDeviceIntelligenceDeviceConfigNamespace is empty");
+ return;
+ }
+ DeviceConfig.addOnPropertiesChangedListener(
+ configNamespace,
+ mConfigExecutor,
+ mOnPropertiesChangedListener);
+ }
+
+ private String getConfigNamespace() {
+ synchronized (mLock) {
+ if (mTemporaryConfigNamespace != null) {
+ return mTemporaryConfigNamespace;
+ }
+
+ return mContext.getResources().getString(
+ R.string.config_defaultOnDeviceIntelligenceDeviceConfigNamespace);
+ }
+ }
+
+ private void sendUpdatedConfig(
+ DeviceConfig.Properties props) {
+ Log.d(TAG, "sendUpdatedConfig");
+
+ PersistableBundle persistableBundle = new PersistableBundle();
+ for (String key : props.getKeyset()) {
+ persistableBundle.putString(key, props.getString(key, ""));
+ }
+ Bundle bundle = new Bundle();
+ bundle.putParcelable(DEVICE_CONFIG_UPDATE_BUNDLE_KEY, persistableBundle);
+ ensureRemoteInferenceServiceInitialized();
+ mRemoteInferenceService.run(service -> service.updateProcessingState(bundle,
+ new IProcessingUpdateStatusCallback.Stub() {
+ @Override
+ public void onSuccess(PersistableBundle result) {
+ Slog.d(TAG, "Config update successful." + result);
+ }
+
+ @Override
+ public void onFailure(int errorCode, String errorMessage) {
+ Slog.e(TAG, "Config update failed with code ["
+ + String.valueOf(errorCode) + "] and message = " + errorMessage);
+ }
+ }));
+ }
+
@NonNull
private IRemoteStorageService.Stub getIRemoteStorageService() {
return new IRemoteStorageService.Stub() {
@@ -629,6 +855,20 @@
R.string.config_defaultOnDeviceSandboxedInferenceService)};
}
+ protected String[] getBroadcastKeys() throws Resources.NotFoundException {
+ // TODO 329240495 : Consider a small class with explicit field names for the two services
+ synchronized (mLock) {
+ if (mTemporaryBroadcastKeys != null && mTemporaryBroadcastKeys.length == 2) {
+ return mTemporaryBroadcastKeys;
+ }
+ }
+
+ return new String[]{mContext.getResources().getString(
+ R.string.config_onDeviceIntelligenceModelLoadedBroadcastKey),
+ mContext.getResources().getString(
+ R.string.config_onDeviceIntelligenceModelUnloadedBroadcastKey)};
+ }
+
@RequiresPermission(Manifest.permission.USE_ON_DEVICE_INTELLIGENCE)
public void setTemporaryServices(@NonNull String[] componentNames, int durationMs) {
Objects.requireNonNull(componentNames);
@@ -645,32 +885,48 @@
mRemoteOnDeviceIntelligenceService.unbind();
mRemoteOnDeviceIntelligenceService = null;
}
- if (mTemporaryHandler == null) {
- mTemporaryHandler = new Handler(Looper.getMainLooper(), null, true) {
- @Override
- public void handleMessage(Message msg) {
- if (msg.what == MSG_RESET_TEMPORARY_SERVICE) {
- synchronized (mLock) {
- resetTemporaryServices();
- }
- } else {
- Slog.wtf(TAG, "invalid handler msg: " + msg);
- }
- }
- };
- } else {
- mTemporaryHandler.removeMessages(MSG_RESET_TEMPORARY_SERVICE);
- }
if (durationMs != -1) {
- mTemporaryHandler.sendEmptyMessageDelayed(MSG_RESET_TEMPORARY_SERVICE, durationMs);
+ getTemporaryHandler().sendEmptyMessageDelayed(MSG_RESET_TEMPORARY_SERVICE,
+ durationMs);
+ }
+ }
+ }
+
+ @RequiresPermission(Manifest.permission.USE_ON_DEVICE_INTELLIGENCE)
+ public void setModelBroadcastKeys(@NonNull String[] broadcastKeys, String receiverPackageName,
+ int durationMs) {
+ Objects.requireNonNull(broadcastKeys);
+ enforceShellOnly(Binder.getCallingUid(), "setModelBroadcastKeys");
+ mContext.enforceCallingPermission(
+ Manifest.permission.USE_ON_DEVICE_INTELLIGENCE, TAG);
+ synchronized (mLock) {
+ mTemporaryBroadcastKeys = broadcastKeys;
+ mBroadcastPackageName = receiverPackageName;
+ if (durationMs != -1) {
+ getTemporaryHandler().sendEmptyMessageDelayed(MSG_RESET_BROADCAST_KEYS, durationMs);
+ }
+ }
+ }
+
+ @RequiresPermission(Manifest.permission.USE_ON_DEVICE_INTELLIGENCE)
+ public void setTemporaryDeviceConfigNamespace(@NonNull String configNamespace,
+ int durationMs) {
+ Objects.requireNonNull(configNamespace);
+ enforceShellOnly(Binder.getCallingUid(), "setTemporaryDeviceConfigNamespace");
+ mContext.enforceCallingPermission(
+ Manifest.permission.USE_ON_DEVICE_INTELLIGENCE, TAG);
+ synchronized (mLock) {
+ mTemporaryConfigNamespace = configNamespace;
+ if (durationMs != -1) {
+ getTemporaryHandler().sendEmptyMessageDelayed(MSG_RESET_CONFIG_NAMESPACE,
+ durationMs);
}
}
}
@RequiresPermission(Manifest.permission.USE_ON_DEVICE_INTELLIGENCE)
public void resetTemporaryServices() {
- enforceShellOnly(Binder.getCallingUid(), "resetTemporaryServices");
mContext.enforceCallingPermission(
Manifest.permission.USE_ON_DEVICE_INTELLIGENCE, TAG);
synchronized (mLock) {
@@ -751,4 +1007,34 @@
}
}
}
+
+ private synchronized Handler getTemporaryHandler() {
+ if (mTemporaryHandler == null) {
+ mTemporaryHandler = new Handler(Looper.getMainLooper(), null, true) {
+ @Override
+ public void handleMessage(Message msg) {
+ synchronized (mLock) {
+ if (msg.what == MSG_RESET_TEMPORARY_SERVICE) {
+ resetTemporaryServices();
+ } else if (msg.what == MSG_RESET_BROADCAST_KEYS) {
+ mTemporaryBroadcastKeys = null;
+ mBroadcastPackageName = SYSTEM_PACKAGE;
+ } else if (msg.what == MSG_RESET_CONFIG_NAMESPACE) {
+ mTemporaryConfigNamespace = null;
+ } else {
+ Slog.wtf(TAG, "invalid handler msg: " + msg);
+ }
+ }
+ }
+ };
+ }
+
+ return mTemporaryHandler;
+ }
+
+ private long getIdleTimeoutMs() {
+ return Settings.Secure.getLongForUser(mContext.getContentResolver(),
+ Settings.Secure.ON_DEVICE_INTELLIGENCE_IDLE_TIMEOUT_MS, TimeUnit.HOURS.toMillis(1),
+ mContext.getUserId());
+ }
}
diff --git a/services/core/java/com/android/server/ondeviceintelligence/OnDeviceIntelligenceShellCommand.java b/services/core/java/com/android/server/ondeviceintelligence/OnDeviceIntelligenceShellCommand.java
index a76d8a3..d2c84fa 100644
--- a/services/core/java/com/android/server/ondeviceintelligence/OnDeviceIntelligenceShellCommand.java
+++ b/services/core/java/com/android/server/ondeviceintelligence/OnDeviceIntelligenceShellCommand.java
@@ -17,6 +17,7 @@
package com.android.server.ondeviceintelligence;
import android.annotation.NonNull;
+import android.os.Binder;
import android.os.ShellCommand;
import java.io.PrintWriter;
@@ -43,6 +44,10 @@
return setTemporaryServices();
case "get-services":
return getConfiguredServices();
+ case "set-model-broadcasts":
+ return setBroadcastKeys();
+ case "set-deviceconfig-namespace":
+ return setDeviceConfigNamespace();
default:
return handleDefaultCommands(cmd);
}
@@ -62,14 +67,26 @@
pw.println(" To reset, call without any arguments.");
pw.println(" get-services To get the names of services that are currently being used.");
+ pw.println(
+ " set-model-broadcasts [ModelLoadedBroadcastKey] [ModelUnloadedBroadcastKey] "
+ + "[ReceiverPackageName] "
+ + "[DURATION] To set the names of broadcast intent keys that are to be "
+ + "emitted for cts tests.");
+ pw.println(
+ " set-deviceconfig-namespace [DeviceConfigNamespace] "
+ + "[DURATION] To set the device config namespace "
+ + "to use for cts tests.");
}
private int setTemporaryServices() {
final PrintWriter out = getOutPrintWriter();
final String intelligenceServiceName = getNextArg();
final String inferenceServiceName = getNextArg();
+
if (getRemainingArgsCount() == 0 && intelligenceServiceName == null
&& inferenceServiceName == null) {
+ OnDeviceIntelligenceManagerService.enforceShellOnly(Binder.getCallingUid(),
+ "resetTemporaryServices");
mService.resetTemporaryServices();
out.println("OnDeviceIntelligenceManagerService temporary reset. ");
return 0;
@@ -79,7 +96,8 @@
Objects.requireNonNull(inferenceServiceName);
final int duration = Integer.parseInt(getNextArgRequired());
mService.setTemporaryServices(
- new String[]{intelligenceServiceName, inferenceServiceName}, duration);
+ new String[]{intelligenceServiceName, inferenceServiceName},
+ duration);
out.println("OnDeviceIntelligenceService temporarily set to " + intelligenceServiceName
+ " \n and \n OnDeviceTrustedInferenceService set to " + inferenceServiceName
+ " for " + duration + "ms");
@@ -93,4 +111,34 @@
+ " \n and \n OnDeviceTrustedInferenceService set to : " + services[1]);
return 0;
}
+
+ private int setBroadcastKeys() {
+ final PrintWriter out = getOutPrintWriter();
+ final String modelLoadedKey = getNextArgRequired();
+ final String modelUnloadedKey = getNextArgRequired();
+ final String receiverPackageName = getNextArg();
+
+ final int duration = Integer.parseInt(getNextArgRequired());
+ mService.setModelBroadcastKeys(
+ new String[]{modelLoadedKey, modelUnloadedKey}, receiverPackageName, duration);
+ out.println("OnDeviceIntelligence Model Loading broadcast keys temporarily set to "
+ + modelLoadedKey
+ + " \n and \n OnDeviceTrustedInferenceService set to " + modelUnloadedKey
+ + "\n and Package name set to : " + receiverPackageName
+ + " for " + duration + "ms");
+ return 0;
+ }
+
+ private int setDeviceConfigNamespace() {
+ final PrintWriter out = getOutPrintWriter();
+ final String configNamespace = getNextArg();
+
+ final int duration = Integer.parseInt(getNextArgRequired());
+ mService.setTemporaryDeviceConfigNamespace(configNamespace, duration);
+ out.println("OnDeviceIntelligence DeviceConfig Namespace temporarily set to "
+ + configNamespace
+ + " for " + duration + "ms");
+ return 0;
+ }
+
}
\ No newline at end of file
diff --git a/services/core/java/com/android/server/ondeviceintelligence/RemoteOnDeviceIntelligenceService.java b/services/core/java/com/android/server/ondeviceintelligence/RemoteOnDeviceIntelligenceService.java
index 48258d7..ac9747a 100644
--- a/services/core/java/com/android/server/ondeviceintelligence/RemoteOnDeviceIntelligenceService.java
+++ b/services/core/java/com/android/server/ondeviceintelligence/RemoteOnDeviceIntelligenceService.java
@@ -22,17 +22,21 @@
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
+import android.provider.Settings;
import android.service.ondeviceintelligence.IOnDeviceIntelligenceService;
import android.service.ondeviceintelligence.OnDeviceIntelligenceService;
import com.android.internal.infra.ServiceConnector;
+import java.util.concurrent.TimeUnit;
+
/**
* Manages the connection to the remote on-device intelligence service. Also, handles unbinding
* logic set by the service implementation via a Secure Settings flag.
*/
public class RemoteOnDeviceIntelligenceService extends
ServiceConnector.Impl<IOnDeviceIntelligenceService> {
+ private static final long LONG_TIMEOUT = TimeUnit.HOURS.toMillis(4);
private static final String TAG =
RemoteOnDeviceIntelligenceService.class.getSimpleName();
@@ -48,9 +52,15 @@
}
@Override
+ protected long getRequestTimeoutMs() {
+ return LONG_TIMEOUT;
+ }
+
+ @Override
protected long getAutoDisconnectTimeoutMs() {
- // Disable automatic unbinding.
- // TODO: add logic to fetch this flag via SecureSettings.
- return -1;
+ return Settings.Secure.getLongForUser(mContext.getContentResolver(),
+ Settings.Secure.ON_DEVICE_INTELLIGENCE_UNBIND_TIMEOUT_MS,
+ TimeUnit.SECONDS.toMillis(30),
+ mContext.getUserId());
}
}
diff --git a/services/core/java/com/android/server/ondeviceintelligence/RemoteOnDeviceSandboxedInferenceService.java b/services/core/java/com/android/server/ondeviceintelligence/RemoteOnDeviceSandboxedInferenceService.java
index 69ba1d2..18b1383 100644
--- a/services/core/java/com/android/server/ondeviceintelligence/RemoteOnDeviceSandboxedInferenceService.java
+++ b/services/core/java/com/android/server/ondeviceintelligence/RemoteOnDeviceSandboxedInferenceService.java
@@ -22,18 +22,24 @@
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
+import android.provider.Settings;
import android.service.ondeviceintelligence.IOnDeviceSandboxedInferenceService;
import android.service.ondeviceintelligence.OnDeviceSandboxedInferenceService;
import com.android.internal.infra.ServiceConnector;
+import java.util.concurrent.TimeUnit;
+
/**
- * Manages the connection to the remote on-device sand boxed inference service. Also, handles unbinding
+ * Manages the connection to the remote on-device sand boxed inference service. Also, handles
+ * unbinding
* logic set by the service implementation via a SecureSettings flag.
*/
public class RemoteOnDeviceSandboxedInferenceService extends
ServiceConnector.Impl<IOnDeviceSandboxedInferenceService> {
+ private static final long LONG_TIMEOUT = TimeUnit.HOURS.toMillis(1);
+
/**
* Creates an instance of {@link ServiceConnector}
*
@@ -54,11 +60,17 @@
connect();
}
+ @Override
+ protected long getRequestTimeoutMs() {
+ return LONG_TIMEOUT;
+ }
+
@Override
protected long getAutoDisconnectTimeoutMs() {
- // Disable automatic unbinding.
- // TODO: add logic to fetch this flag via SecureSettings.
- return -1;
+ return Settings.Secure.getLongForUser(mContext.getContentResolver(),
+ Settings.Secure.ON_DEVICE_INFERENCE_UNBIND_TIMEOUT_MS,
+ TimeUnit.SECONDS.toMillis(30),
+ mContext.getUserId());
}
}
diff --git a/services/core/java/com/android/server/ondeviceintelligence/callbacks/ListenableDownloadCallback.java b/services/core/java/com/android/server/ondeviceintelligence/callbacks/ListenableDownloadCallback.java
new file mode 100644
index 0000000..32f0698
--- /dev/null
+++ b/services/core/java/com/android/server/ondeviceintelligence/callbacks/ListenableDownloadCallback.java
@@ -0,0 +1,97 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.ondeviceintelligence.callbacks;
+
+import android.app.ondeviceintelligence.IDownloadCallback;
+import android.os.Handler;
+import android.os.PersistableBundle;
+import android.os.RemoteException;
+
+import com.android.internal.infra.AndroidFuture;
+
+import java.util.concurrent.TimeoutException;
+
+/**
+ * This class extends the {@link IDownloadCallback} and adds a timeout Runnable to the callback
+ * such that, in the case where the callback methods are not invoked, we do not have to wait for
+ * timeout based on {@link #onDownloadCompleted} which might take minutes or hours to complete in
+ * some cases. Instead, in such cases we rely on the remote service sending progress updates and if
+ * there are *no* progress callbacks in the duration of {@link #idleTimeoutMs}, we can assume the
+ * download will not complete and enabling faster cleanup.
+ */
+public class ListenableDownloadCallback extends IDownloadCallback.Stub implements Runnable {
+ private final IDownloadCallback callback;
+ private final Handler handler;
+ private final AndroidFuture future;
+ private final long idleTimeoutMs;
+
+ /**
+ * Constructor to create a ListenableDownloadCallback.
+ *
+ * @param callback callback to send download updates to caller.
+ * @param handler handler to schedule timeout runnable.
+ * @param future future to complete to signal the callback has reached a terminal state.
+ * @param idleTimeoutMs timeout within which download updates should be received.
+ */
+ public ListenableDownloadCallback(IDownloadCallback callback, Handler handler,
+ AndroidFuture future,
+ long idleTimeoutMs) {
+ this.callback = callback;
+ this.handler = handler;
+ this.future = future;
+ this.idleTimeoutMs = idleTimeoutMs;
+ handler.postDelayed(this,
+ idleTimeoutMs); // init the timeout runnable in case no callback is ever invoked
+ }
+
+ @Override
+ public void onDownloadStarted(long bytesToDownload) throws RemoteException {
+ callback.onDownloadStarted(bytesToDownload);
+ handler.removeCallbacks(this);
+ handler.postDelayed(this, idleTimeoutMs);
+ }
+
+ @Override
+ public void onDownloadProgress(long bytesDownloaded) throws RemoteException {
+ callback.onDownloadProgress(bytesDownloaded);
+ handler.removeCallbacks(this); // remove previously queued timeout tasks.
+ handler.postDelayed(this, idleTimeoutMs); // queue fresh timeout task for next update.
+ }
+
+ @Override
+ public void onDownloadFailed(int failureStatus,
+ String errorMessage, PersistableBundle errorParams) throws RemoteException {
+ callback.onDownloadFailed(failureStatus, errorMessage, errorParams);
+ handler.removeCallbacks(this);
+ future.completeExceptionally(new TimeoutException());
+ }
+
+ @Override
+ public void onDownloadCompleted(
+ android.os.PersistableBundle downloadParams) throws RemoteException {
+ callback.onDownloadCompleted(downloadParams);
+ handler.removeCallbacks(this);
+ future.complete(null);
+ }
+
+ @Override
+ public void run() {
+ future.completeExceptionally(
+ new TimeoutException()); // complete the future as we haven't received updates
+ // for download progress.
+ }
+}
\ No newline at end of file
diff --git a/services/core/java/com/android/server/pm/AppDataHelper.java b/services/core/java/com/android/server/pm/AppDataHelper.java
index 9ba88aa..fe774aa 100644
--- a/services/core/java/com/android/server/pm/AppDataHelper.java
+++ b/services/core/java/com/android/server/pm/AppDataHelper.java
@@ -504,9 +504,12 @@
} else {
storageFlags = StorageManager.FLAG_STORAGE_DE | StorageManager.FLAG_STORAGE_CE;
}
- List<String> deferPackages = reconcileAppsDataLI(StorageManager.UUID_PRIVATE_INTERNAL,
- UserHandle.USER_SYSTEM, storageFlags, true /* migrateAppData */,
- true /* onlyCoreApps */);
+ final List<String> deferPackages;
+ synchronized (mPm.mInstallLock) {
+ deferPackages = reconcileAppsDataLI(StorageManager.UUID_PRIVATE_INTERNAL,
+ UserHandle.USER_SYSTEM, storageFlags, true /* migrateAppData */,
+ true /* onlyCoreApps */);
+ }
Future<?> prepareAppDataFuture = SystemServerInitThreadPool.submit(() -> {
TimingsTraceLog traceLog = new TimingsTraceLog("SystemServerTimingAsync",
Trace.TRACE_TAG_PACKAGE_MANAGER);
diff --git a/services/core/java/com/android/server/pm/InstallPackageHelper.java b/services/core/java/com/android/server/pm/InstallPackageHelper.java
index 19a0ba7..472f228 100644
--- a/services/core/java/com/android/server/pm/InstallPackageHelper.java
+++ b/services/core/java/com/android/server/pm/InstallPackageHelper.java
@@ -37,7 +37,7 @@
import static android.content.pm.PackageManager.INSTALL_FAILED_UPDATE_INCOMPATIBLE;
import static android.content.pm.PackageManager.INSTALL_STAGED;
import static android.content.pm.PackageManager.INSTALL_SUCCEEDED;
-import static android.content.pm.PackageManager.PROPERTY_ANDROID_SAFETY_LABEL_PATH;
+import static android.content.pm.PackageManager.PROPERTY_ANDROID_SAFETY_LABEL;
import static android.content.pm.PackageManager.UNINSTALL_REASON_UNKNOWN;
import static android.content.pm.SigningDetails.SignatureSchemeVersion.SIGNING_BLOCK_V4;
import static android.content.pm.parsing.ApkLiteParseUtils.isApkFile;
@@ -505,6 +505,7 @@
// metadata file path for the new package.
if (oldPkgSetting != null) {
pkgSetting.setAppMetadataFilePath(null);
+ pkgSetting.setAppMetadataSource(APP_METADATA_SOURCE_UNKNOWN);
}
// If the app metadata file path is not null then this is a system app with a preloaded app
// metadata file on the system image. Do not reset the path and source if this is the
@@ -523,7 +524,7 @@
}
} else if (Flags.aslInApkAppMetadataSource()) {
Map<String, PackageManager.Property> properties = pkg.getProperties();
- if (properties.containsKey(PROPERTY_ANDROID_SAFETY_LABEL_PATH)) {
+ if (properties.containsKey(PROPERTY_ANDROID_SAFETY_LABEL)) {
// ASL file extraction is done in post-install
pkgSetting.setAppMetadataFilePath(appMetadataFile.getAbsolutePath());
pkgSetting.setAppMetadataSource(APP_METADATA_SOURCE_APK);
@@ -985,13 +986,13 @@
}
void installPackagesTraced(List<InstallRequest> requests) {
- synchronized (mPm.mInstallLock) {
- try {
- Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "installPackages");
- installPackagesLI(requests);
- } finally {
- Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
- }
+ mPm.mInstallLock.lock();
+ try {
+ Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "installPackages");
+ installPackagesLI(requests);
+ } finally {
+ Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
+ mPm.mInstallLock.unlock();
}
}
@@ -2590,22 +2591,30 @@
final boolean performDexopt = DexOptHelper.shouldPerformDexopt(installRequest,
dexoptOptions, mContext);
if (performDexopt) {
- Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "dexopt");
+ // dexopt can take long, and ArtService doesn't require installd, so we release
+ // the lock here and re-acquire the lock after dexopt is finished.
+ mPm.mInstallLock.unlock();
+ try {
+ Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "dexopt");
- // This mirrors logic from commitReconciledScanResultLocked, where the library files
- // needed for dexopt are assigned.
- PackageSetting realPkgSetting = installRequest.getRealPackageSetting();
+ // This mirrors logic from commitReconciledScanResultLocked, where the library
+ // files needed for dexopt are assigned.
+ PackageSetting realPkgSetting = installRequest.getRealPackageSetting();
- // Unfortunately, the updated system app flag is only tracked on this PackageSetting
- boolean isUpdatedSystemApp =
- installRequest.getScannedPackageSetting().isUpdatedSystemApp();
+ // Unfortunately, the updated system app flag is only tracked on this
+ // PackageSetting
+ boolean isUpdatedSystemApp =
+ installRequest.getScannedPackageSetting().isUpdatedSystemApp();
- realPkgSetting.getPkgState().setUpdatedSystemApp(isUpdatedSystemApp);
+ realPkgSetting.getPkgState().setUpdatedSystemApp(isUpdatedSystemApp);
- DexoptResult dexOptResult =
- DexOptHelper.dexoptPackageUsingArtService(installRequest, dexoptOptions);
- installRequest.onDexoptFinished(dexOptResult);
- Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
+ DexoptResult dexOptResult = DexOptHelper.dexoptPackageUsingArtService(
+ installRequest, dexoptOptions);
+ installRequest.onDexoptFinished(dexOptResult);
+ Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
+ } finally {
+ mPm.mInstallLock.lock();
+ }
}
}
PackageManagerServiceUtils.waitForNativeBinariesExtractionForIncremental(
diff --git a/services/core/java/com/android/server/pm/PackageFreezer.java b/services/core/java/com/android/server/pm/PackageFreezer.java
index 7c56157..0afda45 100644
--- a/services/core/java/com/android/server/pm/PackageFreezer.java
+++ b/services/core/java/com/android/server/pm/PackageFreezer.java
@@ -18,6 +18,7 @@
import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.content.pm.Flags;
import android.content.pm.PackageManager;
import dalvik.system.CloseGuard;
@@ -76,8 +77,13 @@
ps = mPm.mSettings.getPackageLPr(mPackageName);
}
if (ps != null) {
- mPm.killApplication(ps.getPackageName(), ps.getAppId(), userId, killReason,
- exitInfoReason);
+ if (Flags.waitApplicationKilled()) {
+ mPm.killApplicationSync(ps.getPackageName(), ps.getAppId(), userId, killReason,
+ exitInfoReason);
+ } else {
+ mPm.killApplication(ps.getPackageName(), ps.getAppId(), userId, killReason,
+ exitInfoReason);
+ }
}
mCloseGuard.open("close");
}
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index ae485ed..f8fceda 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -53,6 +53,7 @@
import android.annotation.UserIdInt;
import android.annotation.WorkerThread;
import android.app.ActivityManager;
+import android.app.ActivityManagerInternal;
import android.app.AppOpsManager;
import android.app.ApplicationExitInfo;
import android.app.ApplicationPackageManager;
@@ -626,7 +627,7 @@
// Lock for state used when installing and doing other long running
// operations. Methods that must be called with this lock held have
// the suffix "LI".
- final Object mInstallLock;
+ final PackageManagerTracedLock mInstallLock;
// ----------------------------------------------------------------
@@ -1692,8 +1693,8 @@
final TimingsTraceAndSlog t = new TimingsTraceAndSlog(TAG + "Timing",
Trace.TRACE_TAG_PACKAGE_MANAGER);
t.traceBegin("create package manager");
- final PackageManagerTracedLock lock = new PackageManagerTracedLock();
- final Object installLock = new Object();
+ final PackageManagerTracedLock lock = new PackageManagerTracedLock("mLock");
+ final PackageManagerTracedLock installLock = new PackageManagerTracedLock("mInstallLock");
HandlerThread backgroundThread = new ServiceThread("PackageManagerBg",
Process.THREAD_PRIORITY_BACKGROUND, true /*allowIo*/);
@@ -3132,6 +3133,20 @@
}
}
+ void killApplicationSync(String pkgName, @AppIdInt int appId,
+ @UserIdInt int userId, String reason, int exitInfoReason) {
+ ActivityManagerInternal mAmi = LocalServices.getService(ActivityManagerInternal.class);
+ if (mAmi != null) {
+ if (Thread.holdsLock(mLock)) {
+ // holds PM's lock, go back killApplication to avoid it run into watchdog reset.
+ Slog.e(TAG, "Holds PM's locker, unable kill application synchronized");
+ killApplication(pkgName, appId, userId, reason, exitInfoReason);
+ } else {
+ mAmi.killApplicationSync(pkgName, appId, userId, reason, exitInfoReason);
+ }
+ }
+ }
+
@Override
public void notifyPackageAdded(String packageName, int uid) {
mPackageObserverHelper.notifyAdded(packageName, uid);
@@ -4003,7 +4018,7 @@
final PackageMetrics.ComponentStateMetrics componentStateMetrics =
new PackageMetrics.ComponentStateMetrics(setting,
UserHandle.getUid(userId, packageSetting.getAppId()),
- packageSetting.getEnabled(userId));
+ packageSetting.getEnabled(userId), callingUid);
if (!setEnabledSettingInternalLocked(computer, packageSetting, setting, userId,
callingPackage)) {
continue;
diff --git a/services/core/java/com/android/server/pm/PackageManagerServiceInjector.java b/services/core/java/com/android/server/pm/PackageManagerServiceInjector.java
index 83f3b16..ae2eaeb 100644
--- a/services/core/java/com/android/server/pm/PackageManagerServiceInjector.java
+++ b/services/core/java/com/android/server/pm/PackageManagerServiceInjector.java
@@ -86,7 +86,7 @@
private final Context mContext;
private final PackageManagerTracedLock mLock;
private final Installer mInstaller;
- private final Object mInstallLock;
+ private final PackageManagerTracedLock mInstallLock;
private final Handler mBackgroundHandler;
private final Executor mBackgroundExecutor;
private final List<ScanPartition> mSystemPartitions;
@@ -144,7 +144,7 @@
private final Singleton<PackageMonitorCallbackHelper> mPackageMonitorCallbackHelper;
PackageManagerServiceInjector(Context context, PackageManagerTracedLock lock,
- Installer installer, Object installLock, PackageAbiHelper abiHelper,
+ Installer installer, PackageManagerTracedLock installLock, PackageAbiHelper abiHelper,
Handler backgroundHandler,
List<ScanPartition> systemPartitions,
Producer<ComponentResolver> componentResolverProducer,
@@ -254,7 +254,7 @@
return mAbiHelper;
}
- public Object getInstallLock() {
+ public PackageManagerTracedLock getInstallLock() {
return mInstallLock;
}
diff --git a/services/core/java/com/android/server/pm/PackageManagerServiceUtils.java b/services/core/java/com/android/server/pm/PackageManagerServiceUtils.java
index b369f03..6700f00 100644
--- a/services/core/java/com/android/server/pm/PackageManagerServiceUtils.java
+++ b/services/core/java/com/android/server/pm/PackageManagerServiceUtils.java
@@ -19,7 +19,7 @@
import static android.content.pm.PackageManager.INSTALL_FAILED_SHARED_USER_INCOMPATIBLE;
import static android.content.pm.PackageManager.INSTALL_FAILED_UPDATE_INCOMPATIBLE;
import static android.content.pm.PackageManager.INSTALL_FAILED_VERSION_DOWNGRADE;
-import static android.content.pm.PackageManager.PROPERTY_ANDROID_SAFETY_LABEL_PATH;
+import static android.content.pm.PackageManager.PROPERTY_ANDROID_SAFETY_LABEL;
import static android.content.pm.SigningDetails.CertCapabilities.SHARED_USER_ID;
import static android.system.OsConstants.O_CREAT;
import static android.system.OsConstants.O_RDWR;
@@ -71,8 +71,12 @@
import android.content.pm.parsing.PackageLite;
import android.content.pm.parsing.result.ParseResult;
import android.content.pm.parsing.result.ParseTypeImpl;
+import android.content.res.ApkAssets;
+import android.content.res.AssetManager;
+import android.content.res.Resources;
import android.os.Binder;
import android.os.Build;
+import android.os.CancellationSignal;
import android.os.Debug;
import android.os.Environment;
import android.os.FileUtils;
@@ -93,6 +97,7 @@
import android.util.ArraySet;
import android.util.AtomicFile;
import android.util.Base64;
+import android.util.DisplayMetrics;
import android.util.Log;
import android.util.LogPrinter;
import android.util.Printer;
@@ -147,11 +152,10 @@
import java.util.Map;
import java.util.Objects;
import java.util.Set;
+import java.util.concurrent.atomic.AtomicBoolean;
import java.util.function.Function;
import java.util.function.Predicate;
import java.util.zip.GZIPInputStream;
-import java.util.zip.ZipEntry;
-import java.util.zip.ZipFile;
/**
* Class containing helper methods for the PackageManagerService.
@@ -1248,7 +1252,7 @@
}
final ParsedMainComponent comp = componentInfoToComponent(
resolveInfo.getComponentInfo(), resolver, isReceiver);
- if (!comp.getIntents().isEmpty() && intent.getAction() == null) {
+ if (comp != null && !comp.getIntents().isEmpty() && intent.getAction() == null) {
match = false;
}
} else if (c instanceof IntentFilter) {
@@ -1668,11 +1672,11 @@
return true;
}
Map<String, Property> properties = pkg.getProperties();
- if (!properties.containsKey(PROPERTY_ANDROID_SAFETY_LABEL_PATH)) {
+ if (!properties.containsKey(PROPERTY_ANDROID_SAFETY_LABEL)) {
return false;
}
- Property fileInAPkPathProperty = properties.get(PROPERTY_ANDROID_SAFETY_LABEL_PATH);
- if (!fileInAPkPathProperty.isString()) {
+ Property fileInApkProperty = properties.get(PROPERTY_ANDROID_SAFETY_LABEL);
+ if (!fileInApkProperty.isResourceId()) {
return false;
}
if (isSystem && !appMetadataFile.getParentFile().exists()) {
@@ -1684,28 +1688,46 @@
return false;
}
}
- String fileInApkPath = fileInAPkPathProperty.getString();
List<AndroidPackageSplit> splits = pkg.getSplits();
+ AssetManager.Builder builder = new AssetManager.Builder();
for (int i = 0; i < splits.size(); i++) {
- try (ZipFile zipFile = new ZipFile(splits.get(i).getPath())) {
- ZipEntry zipEntry = zipFile.getEntry(fileInApkPath);
- if (zipEntry != null
- && (isSystem || zipEntry.getSize() <= getAppMetadataSizeLimit())) {
- try (InputStream in = zipFile.getInputStream(zipEntry)) {
- try (FileOutputStream out = new FileOutputStream(appMetadataFile)) {
- FileUtils.copy(in, out);
- Os.chmod(appMetadataFile.getAbsolutePath(),
- APP_METADATA_FILE_ACCESS_MODE);
- return true;
+ try {
+ builder.addApkAssets(ApkAssets.loadFromPath(splits.get(i).getPath()));
+ } catch (IOException e) {
+ Slog.e(TAG, "Failed to load resources from APK " + splits.get(i).getPath());
+ }
+ }
+ AssetManager assetManager = builder.build();
+ DisplayMetrics displayMetrics = new DisplayMetrics();
+ displayMetrics.setToDefaults();
+ Resources res = new Resources(assetManager, displayMetrics, null);
+ AtomicBoolean copyFailed = new AtomicBoolean(false);
+ try (InputStream in = res.openRawResource(fileInApkProperty.getResourceId())) {
+ try (FileOutputStream out = new FileOutputStream(appMetadataFile)) {
+ if (isSystem) {
+ FileUtils.copy(in, out);
+ } else {
+ long sizeLimit = getAppMetadataSizeLimit();
+ CancellationSignal signal = new CancellationSignal();
+ FileUtils.copy(in, out, signal, Runnable::run, (long progress) -> {
+ if (progress > sizeLimit) {
+ copyFailed.set(true);
+ signal.cancel();
}
- }
+ });
}
- } catch (Exception e) {
- Slog.e(TAG, e.getMessage());
+ Os.chmod(appMetadataFile.getAbsolutePath(),
+ APP_METADATA_FILE_ACCESS_MODE);
+ }
+ } catch (Exception e) {
+ Slog.e(TAG, e.getMessage());
+ copyFailed.set(true);
+ } finally {
+ if (copyFailed.get()) {
appMetadataFile.delete();
}
}
- return false;
+ return !copyFailed.get();
}
public static void linkFilesToOldDirs(@NonNull Installer installer,
diff --git a/services/core/java/com/android/server/pm/PackageManagerShellCommand.java b/services/core/java/com/android/server/pm/PackageManagerShellCommand.java
index 7a36f6d..0a8b2b2 100644
--- a/services/core/java/com/android/server/pm/PackageManagerShellCommand.java
+++ b/services/core/java/com/android/server/pm/PackageManagerShellCommand.java
@@ -328,6 +328,8 @@
return runGetPrivappDenyPermissions();
case "get-oem-permissions":
return runGetOemPermissions();
+ case "get-signature-permission-allowlist":
+ return runGetSignaturePermissionAllowlist();
case "trim-caches":
return runTrimCaches();
case "create-user":
@@ -2920,6 +2922,54 @@
return 0;
}
+ private int runGetSignaturePermissionAllowlist() {
+ final var partition = getNextArg();
+ if (partition == null) {
+ getErrPrintWriter().println("Error: no partition specified.");
+ return 1;
+ }
+ final var permissionAllowlist =
+ SystemConfig.getInstance().getPermissionAllowlist();
+ final ArrayMap<String, ArrayMap<String, Boolean>> allowlist;
+ switch (partition) {
+ case "system":
+ allowlist = permissionAllowlist.getSignatureAppAllowlist();
+ break;
+ case "vendor":
+ allowlist = permissionAllowlist.getVendorSignatureAppAllowlist();
+ break;
+ case "product":
+ allowlist = permissionAllowlist.getProductSignatureAppAllowlist();
+ break;
+ case "system-ext":
+ allowlist = permissionAllowlist.getSystemExtSignatureAppAllowlist();
+ break;
+ default:
+ getErrPrintWriter().println("Error: unknown partition: " + partition);
+ return 1;
+ }
+ final var ipw = new IndentingPrintWriter(getOutPrintWriter(), " ");
+ final var allowlistSize = allowlist.size();
+ for (var allowlistIndex = 0; allowlistIndex < allowlistSize; allowlistIndex++) {
+ final var packageName = allowlist.keyAt(allowlistIndex);
+ final var permissions = allowlist.valueAt(allowlistIndex);
+ ipw.print("Package: ");
+ ipw.println(packageName);
+ ipw.increaseIndent();
+ final var permissionsSize = permissions.size();
+ for (var permissionsIndex = 0; permissionsIndex < permissionsSize; permissionsIndex++) {
+ final var permissionName = permissions.keyAt(permissionsIndex);
+ final var granted = permissions.valueAt(permissionsIndex);
+ if (granted) {
+ ipw.print("Permission: ");
+ ipw.println(permissionName);
+ }
+ }
+ ipw.decreaseIndent();
+ }
+ return 0;
+ }
+
private int runTrimCaches() throws RemoteException {
String size = getNextArg();
if (size == null) {
@@ -4852,6 +4902,10 @@
pw.println(" get-oem-permissions TARGET-PACKAGE");
pw.println(" Prints all OEM permissions for a package.");
pw.println("");
+ pw.println(" get-signature-permission-allowlist PARTITION");
+ pw.println(" Prints the signature permission allowlist for a partition.");
+ pw.println(" PARTITION is one of system, vendor, product and system-ext");
+ pw.println("");
pw.println(" trim-caches DESIRED_FREE_SPACE [internal|UUID]");
pw.println(" Trim cache files to reach the given free space.");
pw.println("");
diff --git a/services/core/java/com/android/server/pm/PackageManagerTracedLock.java b/services/core/java/com/android/server/pm/PackageManagerTracedLock.java
index 75e1803f..303b8b9 100644
--- a/services/core/java/com/android/server/pm/PackageManagerTracedLock.java
+++ b/services/core/java/com/android/server/pm/PackageManagerTracedLock.java
@@ -16,6 +16,9 @@
package com.android.server.pm;
+import android.annotation.Nullable;
+import android.util.Slog;
+
import java.util.concurrent.locks.ReentrantLock;
/**
@@ -23,4 +26,31 @@
* injection, similar to {@link ActivityManagerGlobalLock}.
*/
public class PackageManagerTracedLock extends ReentrantLock {
+ private static final String TAG = "PackageManagerTracedLock";
+ private static final boolean DEBUG = false;
+ @Nullable private final String mLockName;
+
+ public PackageManagerTracedLock(@Nullable String lockName) {
+ mLockName = lockName;
+ }
+
+ public PackageManagerTracedLock() {
+ this(null);
+ }
+
+ @Override
+ public void lock() {
+ super.lock();
+ if (DEBUG && mLockName != null) {
+ Slog.i(TAG, "locked " + mLockName);
+ }
+ }
+
+ @Override
+ public void unlock() {
+ super.unlock();
+ if (DEBUG && mLockName != null) {
+ Slog.i(TAG, "unlocked " + mLockName);
+ }
+ }
}
diff --git a/services/core/java/com/android/server/pm/PackageMetrics.java b/services/core/java/com/android/server/pm/PackageMetrics.java
index 20598f9..2081f73 100644
--- a/services/core/java/com/android/server/pm/PackageMetrics.java
+++ b/services/core/java/com/android/server/pm/PackageMetrics.java
@@ -358,6 +358,7 @@
public static class ComponentStateMetrics {
public int mUid;
+ public int mCallingUid;
public int mComponentOldState;
public int mComponentNewState;
public boolean mIsForWholeApp;
@@ -365,13 +366,14 @@
@Nullable private String mClassName;
ComponentStateMetrics(@NonNull PackageManager.ComponentEnabledSetting setting, int uid,
- int componentOldState) {
+ int componentOldState, int callingUid) {
mUid = uid;
mComponentOldState = componentOldState;
mComponentNewState = setting.getEnabledState();
mIsForWholeApp = !setting.isComponent();
mPackageName = setting.getPackageName();
mClassName = setting.getClassName();
+ mCallingUid = callingUid;
}
public boolean isSameComponent(ActivityInfo activityInfo) {
@@ -412,14 +414,15 @@
componentStateMetrics.mComponentOldState,
componentStateMetrics.mComponentNewState,
isLauncher,
- componentStateMetrics.mIsForWholeApp);
+ componentStateMetrics.mIsForWholeApp,
+ componentStateMetrics.mCallingUid);
}
}
private static void reportComponentStateChanged(int uid, int componentOldState,
- int componentNewState, boolean isLauncher, boolean isForWholeApp) {
+ int componentNewState, boolean isLauncher, boolean isForWholeApp, int callingUid) {
FrameworkStatsLog.write(FrameworkStatsLog.COMPONENT_STATE_CHANGED_REPORTED,
- uid, componentOldState, componentNewState, isLauncher, isForWholeApp);
+ uid, componentOldState, componentNewState, isLauncher, isForWholeApp, callingUid);
}
private static List<ResolveInfo> getHomeActivitiesResolveInfoAsUser(@NonNull Computer computer,
diff --git a/services/core/java/com/android/server/pm/Settings.java b/services/core/java/com/android/server/pm/Settings.java
index 41d6288..8d6d774 100644
--- a/services/core/java/com/android/server/pm/Settings.java
+++ b/services/core/java/com/android/server/pm/Settings.java
@@ -2142,7 +2142,8 @@
ComponentName unflattenOriginalComponentName = ComponentName.unflattenFromString(
originalComponentName);
if (unflattenOriginalComponentName == null) {
- Slog.d(TAG, "Incorrect component name from the attributes");
+ Slog.wtf(TAG, "Incorrect component name: " + originalComponentName
+ + " from the attributes");
continue;
}
diff --git a/services/core/java/com/android/server/pm/UserDataPreparer.java b/services/core/java/com/android/server/pm/UserDataPreparer.java
index 1d41401..ef32485 100644
--- a/services/core/java/com/android/server/pm/UserDataPreparer.java
+++ b/services/core/java/com/android/server/pm/UserDataPreparer.java
@@ -52,11 +52,11 @@
private static final String TAG = "UserDataPreparer";
private static final String XATTR_SERIAL = "user.serial";
- private final Object mInstallLock;
+ private final PackageManagerTracedLock mInstallLock;
private final Context mContext;
private final Installer mInstaller;
- UserDataPreparer(Installer installer, Object installLock, Context context) {
+ UserDataPreparer(Installer installer, PackageManagerTracedLock installLock, Context context) {
mInstallLock = installLock;
mContext = context;
mInstaller = installer;
diff --git a/services/core/java/com/android/server/pm/UserManagerService.java b/services/core/java/com/android/server/pm/UserManagerService.java
index b1976cd..ebdca5b 100644
--- a/services/core/java/com/android/server/pm/UserManagerService.java
+++ b/services/core/java/com/android/server/pm/UserManagerService.java
@@ -600,8 +600,11 @@
public void onReceive(Context context, Intent intent) {
if (isAutoLockForPrivateSpaceEnabled()) {
if (ACTION_SCREEN_OFF.equals(intent.getAction())) {
+ Slog.d(LOG_TAG, "SCREEN_OFF broadcast received");
maybeScheduleMessageToAutoLockPrivateSpace();
} else if (ACTION_SCREEN_ON.equals(intent.getAction())) {
+ Slog.d(LOG_TAG, "SCREEN_ON broadcast received, "
+ + "removing queued message to auto-lock private space");
// Remove any queued messages since the device is interactive again
mHandler.removeCallbacksAndMessages(PRIVATE_SPACE_AUTO_LOCK_MESSAGE_TOKEN);
}
@@ -619,6 +622,8 @@
getMainUserIdUnchecked());
if (privateSpaceAutoLockPreference
!= Settings.Secure.PRIVATE_SPACE_AUTO_LOCK_AFTER_INACTIVITY) {
+ Slogf.d(LOG_TAG, "Not scheduling auto-lock on inactivity,"
+ + "preference is set to %d", privateSpaceAutoLockPreference);
return;
}
int privateProfileUserId = getPrivateProfileUserId();
@@ -632,6 +637,7 @@
@VisibleForTesting
void scheduleMessageToAutoLockPrivateSpace(int userId, Object token,
long delayInMillis) {
+ Slog.i(LOG_TAG, "Scheduling auto-lock message");
mHandler.postDelayed(() -> {
final PowerManager powerManager = mContext.getSystemService(PowerManager.class);
if (powerManager != null && !powerManager.isInteractive()) {
@@ -1060,8 +1066,6 @@
if (isAutoLockingPrivateSpaceOnRestartsEnabled()) {
autoLockPrivateSpace();
}
-
- markEphemeralUsersForRemoval();
}
private boolean isAutoLockingPrivateSpaceOnRestartsEnabled() {
@@ -1098,21 +1102,6 @@
}
}
- /** Marks all ephemeral users as slated for deletion. **/
- private void markEphemeralUsersForRemoval() {
- synchronized (mUsersLock) {
- final int userSize = mUsers.size();
- for (int i = 0; i < userSize; i++) {
- final UserInfo ui = mUsers.valueAt(i).info;
- if (ui.isEphemeral() && !ui.preCreated && ui.id != UserHandle.USER_SYSTEM) {
- addRemovingUserIdLocked(ui.id);
- ui.partial = true;
- ui.flags |= UserInfo.FLAG_DISABLED;
- }
- }
- }
- }
-
/* Prunes out any partially created or partially removed users. */
private void cleanupPartialUsers() {
ArrayList<UserInfo> partials = new ArrayList<>();
@@ -1935,16 +1924,20 @@
private void showConfirmCredentialToDisableQuietMode(
@UserIdInt int userId, @Nullable IntentSender target, @Nullable String callingPackage) {
if (android.app.admin.flags.Flags.quietModeCredentialBugFix()) {
- // TODO (b/308121702) It may be brittle to rely on user states to check profile state
- int state;
- synchronized (mUserStates) {
- state = mUserStates.get(userId, UserState.STATE_NONE);
- }
- if (state != UserState.STATE_NONE) {
- Slog.i(LOG_TAG,
- "showConfirmCredentialToDisableQuietMode() called too early, user " + userId
- + " is still alive.");
- return;
+ if (!android.multiuser.Flags.restrictQuietModeCredentialBugFixToManagedProfiles()
+ || getUserInfo(userId).isManagedProfile()) {
+ // TODO (b/308121702) It may be brittle to rely on user states to check managed
+ // profile state
+ int state;
+ synchronized (mUserStates) {
+ state = mUserStates.get(userId, UserState.STATE_NONE);
+ }
+ if (state != UserState.STATE_NONE) {
+ Slog.i(LOG_TAG,
+ "showConfirmCredentialToDisableQuietMode() called too early, managed "
+ + "user " + userId + " is still alive.");
+ return;
+ }
}
}
// otherwise, we show a profile challenge to trigger decryption of the user
@@ -4223,6 +4216,13 @@
|| mNextSerialNumber <= userData.info.id) {
mNextSerialNumber = userData.info.id + 1;
}
+ if (userData.info.isEphemeral() && !userData.info.preCreated
+ && userData.info.id != UserHandle.USER_SYSTEM) {
+ // Mark ephemeral user as slated for deletion.
+ addRemovingUserIdLocked(userData.info.id);
+ userData.info.partial = true;
+ userData.info.flags |= UserInfo.FLAG_DISABLED;
+ }
}
}
} else if (name.equals(TAG_GUEST_RESTRICTIONS)) {
diff --git a/services/core/java/com/android/server/power/Notifier.java b/services/core/java/com/android/server/power/Notifier.java
index 5fa8856..11b9e77 100644
--- a/services/core/java/com/android/server/power/Notifier.java
+++ b/services/core/java/com/android/server/power/Notifier.java
@@ -64,6 +64,7 @@
import com.android.server.input.InputManagerInternal;
import com.android.server.inputmethod.InputMethodManagerInternal;
import com.android.server.policy.WindowManagerPolicy;
+import com.android.server.power.feature.PowerManagerFlags;
import com.android.server.statusbar.StatusBarManagerInternal;
import java.io.PrintWriter;
@@ -187,11 +188,16 @@
private final AtomicBoolean mIsPlayingChargingStartedFeedback = new AtomicBoolean(false);
+ private final Injector mInjector;
+
+ private final PowerManagerFlags mFlags;
+
public Notifier(Looper looper, Context context, IBatteryStats batteryStats,
SuspendBlocker suspendBlocker, WindowManagerPolicy policy,
FaceDownDetector faceDownDetector, ScreenUndimDetector screenUndimDetector,
- Executor backgroundExecutor) {
+ Executor backgroundExecutor, PowerManagerFlags powerManagerFlags, Injector injector) {
mContext = context;
+ mFlags = powerManagerFlags;
mBatteryStats = batteryStats;
mAppOps = mContext.getSystemService(AppOpsManager.class);
mSuspendBlocker = suspendBlocker;
@@ -224,8 +230,8 @@
mShowWirelessChargingAnimationConfig = context.getResources().getBoolean(
com.android.internal.R.bool.config_showBuiltinWirelessChargingAnim);
- mWakeLockLog = new WakeLockLog(context);
-
+ mInjector = (injector == null) ? new RealInjector() : injector;
+ mWakeLockLog = mInjector.getWakeLockLog(context);
// Initialize interactive state for battery stats.
try {
mBatteryStats.noteInteractive(true);
@@ -267,7 +273,7 @@
+ ", ownerUid=" + ownerUid + ", ownerPid=" + ownerPid
+ ", workSource=" + workSource);
}
- notifyWakeLockListener(callback, tag, true);
+ notifyWakeLockListener(callback, tag, true, ownerUid, flags);
final int monitorType = getBatteryStatsWakeLockMonitorType(flags);
if (monitorType >= 0) {
try {
@@ -287,8 +293,9 @@
}
}
- mWakeLockLog.onWakeLockAcquired(tag, ownerUid, flags);
-
+ if (!mFlags.improveWakelockLatency()) {
+ mWakeLockLog.onWakeLockAcquired(tag, ownerUid, flags, /*eventTime=*/ -1);
+ }
mWakefulnessSessionObserver.onWakeLockAcquired(flags);
}
@@ -406,7 +413,7 @@
+ ", ownerUid=" + ownerUid + ", ownerPid=" + ownerPid
+ ", workSource=" + workSource);
}
- notifyWakeLockListener(callback, tag, false);
+ notifyWakeLockListener(callback, tag, false, ownerUid, flags);
final int monitorType = getBatteryStatsWakeLockMonitorType(flags);
if (monitorType >= 0) {
try {
@@ -422,8 +429,9 @@
// Ignore
}
}
- mWakeLockLog.onWakeLockReleased(tag, ownerUid);
-
+ if (!mFlags.improveWakelockLatency()) {
+ mWakeLockLog.onWakeLockReleased(tag, ownerUid, /*eventTime=*/ -1);
+ }
mWakefulnessSessionObserver.onWakeLockReleased(flags, releaseReason);
}
@@ -1040,10 +1048,19 @@
return enabled && dndOff;
}
- private void notifyWakeLockListener(IWakeLockCallback callback, String tag, boolean isEnabled) {
+ private void notifyWakeLockListener(IWakeLockCallback callback, String tag, boolean isEnabled,
+ int ownerUid, int flags) {
if (callback != null) {
+ long currentTime = mInjector.currentTimeMillis();
mHandler.post(() -> {
try {
+ if (mFlags.improveWakelockLatency()) {
+ if (isEnabled) {
+ mWakeLockLog.onWakeLockAcquired(tag, ownerUid, flags, currentTime);
+ } else {
+ mWakeLockLog.onWakeLockReleased(tag, ownerUid, currentTime);
+ }
+ }
callback.onStateChanged(isEnabled);
} catch (RemoteException e) {
Slog.e(TAG, "Wakelock.mCallback [" + tag + "] is already dead.", e);
@@ -1057,6 +1074,7 @@
public NotifierHandler(Looper looper) {
super(looper, null, true /*async*/);
}
+
@Override
public void handleMessage(Message msg) {
switch (msg.what) {
@@ -1085,4 +1103,28 @@
}
}
}
+
+ public interface Injector {
+ /**
+ * Gets the current time in millis
+ */
+ long currentTimeMillis();
+
+ /**
+ * Gets the WakeLockLog object
+ */
+ WakeLockLog getWakeLockLog(Context context);
+ }
+
+ static class RealInjector implements Injector {
+ @Override
+ public long currentTimeMillis() {
+ return System.currentTimeMillis();
+ }
+
+ @Override
+ public WakeLockLog getWakeLockLog(Context context) {
+ return new WakeLockLog(context);
+ }
+ }
}
diff --git a/services/core/java/com/android/server/power/PowerManagerService.java b/services/core/java/com/android/server/power/PowerManagerService.java
index 76cedd8..ce0120c 100644
--- a/services/core/java/com/android/server/power/PowerManagerService.java
+++ b/services/core/java/com/android/server/power/PowerManagerService.java
@@ -988,10 +988,10 @@
Notifier createNotifier(Looper looper, Context context, IBatteryStats batteryStats,
SuspendBlocker suspendBlocker, WindowManagerPolicy policy,
FaceDownDetector faceDownDetector, ScreenUndimDetector screenUndimDetector,
- Executor backgroundExecutor) {
+ Executor backgroundExecutor, PowerManagerFlags powerManagerFlags) {
return new Notifier(
looper, context, batteryStats, suspendBlocker, policy, faceDownDetector,
- screenUndimDetector, backgroundExecutor);
+ screenUndimDetector, backgroundExecutor, powerManagerFlags, /*injector=*/ null);
}
SuspendBlocker createSuspendBlocker(PowerManagerService service, String name) {
@@ -1373,7 +1373,7 @@
mNotifier = mInjector.createNotifier(Looper.getMainLooper(), mContext, mBatteryStats,
mInjector.createSuspendBlocker(this, "PowerManagerService.Broadcasts"),
mPolicy, mFaceDownDetector, mScreenUndimDetector,
- BackgroundThread.getExecutor());
+ BackgroundThread.getExecutor(), mFeatureFlags);
mPowerGroups.append(Display.DEFAULT_DISPLAY_GROUP,
new PowerGroup(WAKEFULNESS_AWAKE, mPowerGroupWakefulnessChangeListener,
diff --git a/services/core/java/com/android/server/power/WakeLockLog.java b/services/core/java/com/android/server/power/WakeLockLog.java
index b131311..968ff59 100644
--- a/services/core/java/com/android/server/power/WakeLockLog.java
+++ b/services/core/java/com/android/server/power/WakeLockLog.java
@@ -154,9 +154,10 @@
* @param tag The wake lock tag
* @param ownerUid The owner UID of the wake lock.
* @param flags Flags used for the wake lock.
+ * @param eventTime The time at which the event occurred
*/
- public void onWakeLockAcquired(String tag, int ownerUid, int flags) {
- onWakeLockEvent(TYPE_ACQUIRE, tag, ownerUid, flags);
+ public void onWakeLockAcquired(String tag, int ownerUid, int flags, long eventTime) {
+ onWakeLockEvent(TYPE_ACQUIRE, tag, ownerUid, flags, eventTime);
}
/**
@@ -164,9 +165,10 @@
*
* @param tag The wake lock tag
* @param ownerUid The owner UID of the wake lock.
+ * @param eventTime The time at which the event occurred
*/
- public void onWakeLockReleased(String tag, int ownerUid) {
- onWakeLockEvent(TYPE_RELEASE, tag, ownerUid, 0 /* flags */);
+ public void onWakeLockReleased(String tag, int ownerUid, long eventTime) {
+ onWakeLockEvent(TYPE_RELEASE, tag, ownerUid, 0 /* flags */, eventTime);
}
/**
@@ -242,9 +244,10 @@
* @param tag The wake lock's identifying tag.
* @param ownerUid The owner UID of the wake lock.
* @param flags The flags used with the wake lock.
+ * @param eventTime The time at which the event occurred
*/
private void onWakeLockEvent(int eventType, String tag, int ownerUid,
- int flags) {
+ int flags, long eventTime) {
if (tag == null) {
Slog.w(TAG, "Insufficient data to log wakelock [tag: " + tag
+ ", ownerUid: " + ownerUid
@@ -252,7 +255,8 @@
return;
}
- final long time = mInjector.currentTimeMillis();
+ final long time = (eventTime == -1) ? mInjector.currentTimeMillis() : eventTime;
+
final int translatedFlags = eventType == TYPE_ACQUIRE
? translateFlagsFromPowerManager(flags)
: 0;
diff --git a/services/core/java/com/android/server/power/feature/PowerManagerFlags.java b/services/core/java/com/android/server/power/feature/PowerManagerFlags.java
index a5a7069..ff1d2e4 100644
--- a/services/core/java/com/android/server/power/feature/PowerManagerFlags.java
+++ b/services/core/java/com/android/server/power/feature/PowerManagerFlags.java
@@ -35,18 +35,31 @@
Flags.FLAG_ENABLE_EARLY_SCREEN_TIMEOUT_DETECTOR,
Flags::enableEarlyScreenTimeoutDetector);
+ private final FlagState mImproveWakelockLatency = new FlagState(
+ Flags.FLAG_IMPROVE_WAKELOCK_LATENCY,
+ Flags::improveWakelockLatency
+ );
+
/** Returns whether early-screen-timeout-detector is enabled on not. */
public boolean isEarlyScreenTimeoutDetectorEnabled() {
return mEarlyScreenTimeoutDetectorFlagState.isEnabled();
}
/**
+ * @return Whether to improve the wakelock acquire/release latency or not
+ */
+ public boolean improveWakelockLatency() {
+ return mImproveWakelockLatency.isEnabled();
+ }
+
+ /**
* dumps all flagstates
* @param pw printWriter
*/
public void dump(PrintWriter pw) {
pw.println("PowerManagerFlags:");
pw.println(" " + mEarlyScreenTimeoutDetectorFlagState);
+ pw.println(" " + mImproveWakelockLatency);
}
private static class FlagState {
diff --git a/services/core/java/com/android/server/power/feature/power_flags.aconfig b/services/core/java/com/android/server/power/feature/power_flags.aconfig
index ca58153..3581b2f 100644
--- a/services/core/java/com/android/server/power/feature/power_flags.aconfig
+++ b/services/core/java/com/android/server/power/feature/power_flags.aconfig
@@ -10,3 +10,11 @@
bug: "309861917"
is_fixed_read_only: true
}
+
+flag {
+ name: "improve_wakelock_latency"
+ namespace: "power"
+ description: "Feature flag for tracking the optimizations to improve the latency of acquiring and releasing a wakelock."
+ bug: "339590565"
+ is_fixed_read_only: true
+}
\ No newline at end of file
diff --git a/services/core/java/com/android/server/power/stats/AggregatedPowerStatsConfig.java b/services/core/java/com/android/server/power/stats/AggregatedPowerStatsConfig.java
index 5aad570..884c26c 100644
--- a/services/core/java/com/android/server/power/stats/AggregatedPowerStatsConfig.java
+++ b/services/core/java/com/android/server/power/stats/AggregatedPowerStatsConfig.java
@@ -19,12 +19,9 @@
import android.annotation.NonNull;
import android.os.BatteryConsumer;
-import com.android.internal.os.PowerStats;
-
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.util.ArrayList;
-import java.util.Arrays;
import java.util.List;
/**
@@ -206,25 +203,9 @@
return mPowerComponents;
}
- private static final PowerStatsProcessor NO_OP_PROCESSOR =
- new PowerStatsProcessor() {
- @Override
- void finish(PowerComponentAggregatedPowerStats stats) {
- }
-
- @Override
- String deviceStatsToString(PowerStats.Descriptor descriptor, long[] stats) {
- return Arrays.toString(stats);
- }
-
- @Override
- String stateStatsToString(PowerStats.Descriptor descriptor, int key, long[] stats) {
- return descriptor.getStateLabel(key) + " " + Arrays.toString(stats);
- }
-
- @Override
- String uidStatsToString(PowerStats.Descriptor descriptor, long[] stats) {
- return Arrays.toString(stats);
- }
- };
+ private static final PowerStatsProcessor NO_OP_PROCESSOR = new PowerStatsProcessor() {
+ @Override
+ void finish(PowerComponentAggregatedPowerStats stats) {
+ }
+ };
}
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 49c4000..9a41551 100644
--- a/services/core/java/com/android/server/power/stats/BatteryStatsImpl.java
+++ b/services/core/java/com/android/server/power/stats/BatteryStatsImpl.java
@@ -463,11 +463,17 @@
public static class BatteryStatsConfig {
static final int RESET_ON_UNPLUG_HIGH_BATTERY_LEVEL_FLAG = 1 << 0;
static final int RESET_ON_UNPLUG_AFTER_SIGNIFICANT_CHARGE_FLAG = 1 << 1;
- static final long DEFAULT_POWER_STATS_COLLECTION_THROTTLE_PERIOD =
- TimeUnit.HOURS.toMillis(1);
private final int mFlags;
- private SparseLongArray mPowerStatsThrottlePeriods;
+ private final Long mDefaultPowerStatsThrottlePeriod;
+ private final Map<String, Long> mPowerStatsThrottlePeriods;
+
+ @VisibleForTesting
+ public BatteryStatsConfig() {
+ mFlags = 0;
+ mDefaultPowerStatsThrottlePeriod = 0L;
+ mPowerStatsThrottlePeriods = Map.of();
+ }
private BatteryStatsConfig(Builder builder) {
int flags = 0;
@@ -478,6 +484,7 @@
flags |= RESET_ON_UNPLUG_AFTER_SIGNIFICANT_CHARGE_FLAG;
}
mFlags = flags;
+ mDefaultPowerStatsThrottlePeriod = builder.mDefaultPowerStatsThrottlePeriod;
mPowerStatsThrottlePeriods = builder.mPowerStatsThrottlePeriods;
}
@@ -485,7 +492,7 @@
* Returns whether a BatteryStats reset should occur on unplug when the battery level is
* high.
*/
- boolean shouldResetOnUnplugHighBatteryLevel() {
+ public boolean shouldResetOnUnplugHighBatteryLevel() {
return (mFlags & RESET_ON_UNPLUG_HIGH_BATTERY_LEVEL_FLAG)
== RESET_ON_UNPLUG_HIGH_BATTERY_LEVEL_FLAG;
}
@@ -494,14 +501,18 @@
* Returns whether a BatteryStats reset should occur on unplug if the battery charge a
* significant amount since it has been plugged in.
*/
- boolean shouldResetOnUnplugAfterSignificantCharge() {
+ public boolean shouldResetOnUnplugAfterSignificantCharge() {
return (mFlags & RESET_ON_UNPLUG_AFTER_SIGNIFICANT_CHARGE_FLAG)
== RESET_ON_UNPLUG_AFTER_SIGNIFICANT_CHARGE_FLAG;
}
- long getPowerStatsThrottlePeriod(@BatteryConsumer.PowerComponent int powerComponent) {
- return mPowerStatsThrottlePeriods.get(powerComponent,
- DEFAULT_POWER_STATS_COLLECTION_THROTTLE_PERIOD);
+ /**
+ * Returns the minimum amount of time (in millis) to wait between passes
+ * of power stats collection for the specified power component.
+ */
+ public long getPowerStatsThrottlePeriod(String powerComponentName) {
+ return mPowerStatsThrottlePeriods.getOrDefault(powerComponentName,
+ mDefaultPowerStatsThrottlePeriod);
}
/**
@@ -510,18 +521,19 @@
public static class Builder {
private boolean mResetOnUnplugHighBatteryLevel;
private boolean mResetOnUnplugAfterSignificantCharge;
- private SparseLongArray mPowerStatsThrottlePeriods;
+ public static final long DEFAULT_POWER_STATS_THROTTLE_PERIOD =
+ TimeUnit.HOURS.toMillis(1);
+ public static final long DEFAULT_POWER_STATS_THROTTLE_PERIOD_CPU =
+ TimeUnit.MINUTES.toMillis(1);
+ private long mDefaultPowerStatsThrottlePeriod = DEFAULT_POWER_STATS_THROTTLE_PERIOD;
+ private final Map<String, Long> mPowerStatsThrottlePeriods = new HashMap<>();
public Builder() {
mResetOnUnplugHighBatteryLevel = true;
mResetOnUnplugAfterSignificantCharge = true;
- mPowerStatsThrottlePeriods = new SparseLongArray();
- setPowerStatsThrottlePeriodMillis(BatteryConsumer.POWER_COMPONENT_CPU,
- TimeUnit.MINUTES.toMillis(1));
- setPowerStatsThrottlePeriodMillis(BatteryConsumer.POWER_COMPONENT_MOBILE_RADIO,
- TimeUnit.HOURS.toMillis(1));
- setPowerStatsThrottlePeriodMillis(BatteryConsumer.POWER_COMPONENT_WIFI,
- TimeUnit.HOURS.toMillis(1));
+ setPowerStatsThrottlePeriodMillis(BatteryConsumer.powerComponentIdToString(
+ BatteryConsumer.POWER_COMPONENT_CPU),
+ DEFAULT_POWER_STATS_THROTTLE_PERIOD_CPU);
}
/**
@@ -553,9 +565,18 @@
* Sets the minimum amount of time (in millis) to wait between passes
* of power stats collection for the specified power component.
*/
- public Builder setPowerStatsThrottlePeriodMillis(
- @BatteryConsumer.PowerComponent int powerComponent, long periodMs) {
- mPowerStatsThrottlePeriods.put(powerComponent, periodMs);
+ public Builder setPowerStatsThrottlePeriodMillis(String powerComponentName,
+ long periodMs) {
+ mPowerStatsThrottlePeriods.put(powerComponentName, periodMs);
+ return this;
+ }
+
+ /**
+ * Sets the minimum amount of time (in millis) to wait between passes
+ * of power stats collection for any components not configured explicitly.
+ */
+ public Builder setDefaultPowerStatsThrottlePeriodMillis(long periodMs) {
+ mDefaultPowerStatsThrottlePeriod = periodMs;
return this;
}
}
@@ -1586,8 +1607,7 @@
protected final Constants mConstants;
@VisibleForTesting
- @GuardedBy("this")
- protected BatteryStatsConfig mBatteryStatsConfig;
+ protected final BatteryStatsConfig mBatteryStatsConfig;
@GuardedBy("this")
private AlarmManager mAlarmManager = null;
@@ -1933,6 +1953,11 @@
}
@Override
+ public long getPowerStatsCollectionThrottlePeriod(String powerComponentName) {
+ return mBatteryStatsConfig.getPowerStatsThrottlePeriod(powerComponentName);
+ }
+
+ @Override
public PowerStatsUidResolver getUidResolver() {
return mPowerStatsUidResolver;
}
@@ -11167,19 +11192,14 @@
mConstants.MAX_HISTORY_FILES, mConstants.MAX_HISTORY_BUFFER, mStepDetailsCalculator,
mClock, mMonotonicClock, traceDelegate, eventLogger);
- mCpuPowerStatsCollector = new CpuPowerStatsCollector(mPowerStatsCollectorInjector,
- mBatteryStatsConfig.getPowerStatsThrottlePeriod(
- BatteryConsumer.POWER_COMPONENT_CPU));
+ mCpuPowerStatsCollector = new CpuPowerStatsCollector(mPowerStatsCollectorInjector);
mCpuPowerStatsCollector.addConsumer(this::recordPowerStats);
mMobileRadioPowerStatsCollector = new MobileRadioPowerStatsCollector(
- mPowerStatsCollectorInjector, mBatteryStatsConfig.getPowerStatsThrottlePeriod(
- BatteryConsumer.POWER_COMPONENT_MOBILE_RADIO));
+ mPowerStatsCollectorInjector);
mMobileRadioPowerStatsCollector.addConsumer(this::recordPowerStats);
- mWifiPowerStatsCollector = new WifiPowerStatsCollector(
- mPowerStatsCollectorInjector, mBatteryStatsConfig.getPowerStatsThrottlePeriod(
- BatteryConsumer.POWER_COMPONENT_WIFI));
+ mWifiPowerStatsCollector = new WifiPowerStatsCollector(mPowerStatsCollectorInjector);
mWifiPowerStatsCollector.addConsumer(this::recordPowerStats);
mStartCount++;
diff --git a/services/core/java/com/android/server/power/stats/CpuPowerStatsCollector.java b/services/core/java/com/android/server/power/stats/CpuPowerStatsCollector.java
index f53a1b0..b5ef67b 100644
--- a/services/core/java/com/android/server/power/stats/CpuPowerStatsCollector.java
+++ b/services/core/java/com/android/server/power/stats/CpuPowerStatsCollector.java
@@ -59,6 +59,7 @@
KernelCpuStatsReader getKernelCpuStatsReader();
ConsumedEnergyRetriever getConsumedEnergyRetriever();
IntSupplier getVoltageSupplier();
+ long getPowerStatsCollectionThrottlePeriod(String powerComponentName);
default int getDefaultCpuPowerBrackets() {
return DEFAULT_CPU_POWER_BRACKETS;
@@ -94,9 +95,11 @@
private int mLastVoltageMv;
private long[] mLastConsumedEnergyUws;
- public CpuPowerStatsCollector(Injector injector, long throttlePeriodMs) {
- super(injector.getHandler(), throttlePeriodMs, injector.getUidResolver(),
- injector.getClock());
+ CpuPowerStatsCollector(Injector injector) {
+ super(injector.getHandler(), injector.getPowerStatsCollectionThrottlePeriod(
+ BatteryConsumer.powerComponentIdToString(
+ BatteryConsumer.POWER_COMPONENT_CPU)),
+ injector.getUidResolver(), injector.getClock());
mInjector = injector;
}
diff --git a/services/core/java/com/android/server/power/stats/CpuPowerStatsLayout.java b/services/core/java/com/android/server/power/stats/CpuPowerStatsLayout.java
index 1bcb2c4..2a02bd0 100644
--- a/services/core/java/com/android/server/power/stats/CpuPowerStatsLayout.java
+++ b/services/core/java/com/android/server/power/stats/CpuPowerStatsLayout.java
@@ -44,7 +44,7 @@
* Declare that the stats array has a section capturing CPU time per scaling step
*/
public void addDeviceSectionCpuTimeByScalingStep(int scalingStepCount) {
- mDeviceCpuTimeByScalingStepPosition = addDeviceSection(scalingStepCount);
+ mDeviceCpuTimeByScalingStepPosition = addDeviceSection(scalingStepCount, "steps");
mDeviceCpuTimeByScalingStepCount = scalingStepCount;
}
@@ -72,7 +72,7 @@
* Declare that the stats array has a section capturing CPU time in each cluster
*/
public void addDeviceSectionCpuTimeByCluster(int clusterCount) {
- mDeviceCpuTimeByClusterPosition = addDeviceSection(clusterCount);
+ mDeviceCpuTimeByClusterPosition = addDeviceSection(clusterCount, "clusters");
mDeviceCpuTimeByClusterCount = clusterCount;
}
@@ -102,7 +102,7 @@
public void addUidSectionCpuTimeByPowerBracket(int[] scalingStepToPowerBracketMap) {
mScalingStepToPowerBracketMap = scalingStepToPowerBracketMap;
updatePowerBracketCount();
- mUidPowerBracketsPosition = addUidSection(mUidPowerBracketCount);
+ mUidPowerBracketsPosition = addUidSection(mUidPowerBracketCount, "time");
}
private void updatePowerBracketCount() {
diff --git a/services/core/java/com/android/server/power/stats/CpuPowerStatsProcessor.java b/services/core/java/com/android/server/power/stats/CpuPowerStatsProcessor.java
index c34b8a8..57b7259 100644
--- a/services/core/java/com/android/server/power/stats/CpuPowerStatsProcessor.java
+++ b/services/core/java/com/android/server/power/stats/CpuPowerStatsProcessor.java
@@ -16,7 +16,6 @@
package com.android.server.power.stats;
-import android.os.BatteryStats;
import android.util.ArraySet;
import android.util.Log;
@@ -487,64 +486,4 @@
stats.setUidStats(uid, proportionalEstimate.stateValues, mTmpUidStatsArray);
}
}
-
- @Override
- public String deviceStatsToString(PowerStats.Descriptor descriptor, long[] stats) {
- unpackPowerStatsDescriptor(descriptor);
- StringBuilder sb = new StringBuilder();
- int cpuScalingStepCount = mStatsLayout.getCpuScalingStepCount();
- sb.append("steps: [");
- for (int step = 0; step < cpuScalingStepCount; step++) {
- if (step != 0) {
- sb.append(", ");
- }
- sb.append(mStatsLayout.getTimeByScalingStep(stats, step));
- }
- int clusterCount = mStatsLayout.getCpuClusterCount();
- sb.append("] clusters: [");
- for (int cluster = 0; cluster < clusterCount; cluster++) {
- if (cluster != 0) {
- sb.append(", ");
- }
- sb.append(mStatsLayout.getTimeByCluster(stats, cluster));
- }
- sb.append("] uptime: ").append(mStatsLayout.getUsageDuration(stats));
- int energyConsumerCount = mStatsLayout.getEnergyConsumerCount();
- if (energyConsumerCount > 0) {
- sb.append(" energy: [");
- for (int i = 0; i < energyConsumerCount; i++) {
- if (i != 0) {
- sb.append(", ");
- }
- sb.append(mStatsLayout.getConsumedEnergy(stats, i));
- }
- sb.append("]");
- }
- sb.append(" power: ").append(
- BatteryStats.formatCharge(mStatsLayout.getDevicePowerEstimate(stats)));
- return sb.toString();
- }
-
- @Override
- String stateStatsToString(PowerStats.Descriptor descriptor, int key, long[] stats) {
- // Unsupported for this power component
- return null;
- }
-
- @Override
- public String uidStatsToString(PowerStats.Descriptor descriptor, long[] stats) {
- unpackPowerStatsDescriptor(descriptor);
- StringBuilder sb = new StringBuilder();
- sb.append("[");
- int powerBracketCount = mStatsLayout.getCpuPowerBracketCount();
- for (int bracket = 0; bracket < powerBracketCount; bracket++) {
- if (bracket != 0) {
- sb.append(", ");
- }
- sb.append(mStatsLayout.getUidTimeByPowerBracket(stats, bracket));
- }
- sb.append("] power: ").append(
- BatteryStats.formatCharge(mStatsLayout.getUidPowerEstimate(stats)));
- return sb.toString();
- }
}
diff --git a/services/core/java/com/android/server/power/stats/MobileRadioPowerStatsCollector.java b/services/core/java/com/android/server/power/stats/MobileRadioPowerStatsCollector.java
index 7bc6817..a96e01b 100644
--- a/services/core/java/com/android/server/power/stats/MobileRadioPowerStatsCollector.java
+++ b/services/core/java/com/android/server/power/stats/MobileRadioPowerStatsCollector.java
@@ -73,6 +73,7 @@
Handler getHandler();
Clock getClock();
PowerStatsUidResolver getUidResolver();
+ long getPowerStatsCollectionThrottlePeriod(String powerComponentName);
PackageManager getPackageManager();
ConsumedEnergyRetriever getConsumedEnergyRetriever();
IntSupplier getVoltageSupplier();
@@ -104,8 +105,11 @@
private long mLastCallDuration;
private long mLastScanDuration;
- public MobileRadioPowerStatsCollector(Injector injector, long throttlePeriodMs) {
- super(injector.getHandler(), throttlePeriodMs, injector.getUidResolver(),
+ MobileRadioPowerStatsCollector(Injector injector) {
+ super(injector.getHandler(), injector.getPowerStatsCollectionThrottlePeriod(
+ BatteryConsumer.powerComponentIdToString(
+ BatteryConsumer.POWER_COMPONENT_MOBILE_RADIO)),
+ injector.getUidResolver(),
injector.getClock());
mInjector = injector;
}
diff --git a/services/core/java/com/android/server/power/stats/MobileRadioPowerStatsLayout.java b/services/core/java/com/android/server/power/stats/MobileRadioPowerStatsLayout.java
index 81d7c2f..07d78f8 100644
--- a/services/core/java/com/android/server/power/stats/MobileRadioPowerStatsLayout.java
+++ b/services/core/java/com/android/server/power/stats/MobileRadioPowerStatsLayout.java
@@ -64,29 +64,30 @@
}
void addDeviceMobileActivity() {
- mDeviceSleepTimePosition = addDeviceSection(1);
- mDeviceIdleTimePosition = addDeviceSection(1);
- mDeviceScanTimePosition = addDeviceSection(1);
- mDeviceCallTimePosition = addDeviceSection(1);
+ mDeviceSleepTimePosition = addDeviceSection(1, "sleep");
+ mDeviceIdleTimePosition = addDeviceSection(1, "idle");
+ mDeviceScanTimePosition = addDeviceSection(1, "scan");
+ mDeviceCallTimePosition = addDeviceSection(1, "call", FLAG_OPTIONAL);
}
void addStateStats() {
- mStateRxTimePosition = addStateSection(1);
+ mStateRxTimePosition = addStateSection(1, "rx");
mStateTxTimesCount = ModemActivityInfo.getNumTxPowerLevels();
- mStateTxTimesPosition = addStateSection(mStateTxTimesCount);
+ mStateTxTimesPosition = addStateSection(mStateTxTimesCount, "tx");
}
void addUidNetworkStats() {
- mUidRxBytesPosition = addUidSection(1);
- mUidTxBytesPosition = addUidSection(1);
- mUidRxPacketsPosition = addUidSection(1);
- mUidTxPacketsPosition = addUidSection(1);
+ mUidRxPacketsPosition = addUidSection(1, "rx-pkts");
+ mUidRxBytesPosition = addUidSection(1, "rx-B");
+ mUidTxPacketsPosition = addUidSection(1, "tx-pkts");
+ mUidTxBytesPosition = addUidSection(1, "tx-B");
}
@Override
public void addDeviceSectionPowerEstimate() {
super.addDeviceSectionPowerEstimate();
- mDeviceCallPowerPosition = addDeviceSection(1);
+ // Printed as part of the PhoneCallPowerStatsProcessor
+ mDeviceCallPowerPosition = addDeviceSection(1, "call-power", FLAG_HIDDEN);
}
public void setDeviceSleepTime(long[] stats, long durationMillis) {
diff --git a/services/core/java/com/android/server/power/stats/MobileRadioPowerStatsProcessor.java b/services/core/java/com/android/server/power/stats/MobileRadioPowerStatsProcessor.java
index c97c64b..eebed2f 100644
--- a/services/core/java/com/android/server/power/stats/MobileRadioPowerStatsProcessor.java
+++ b/services/core/java/com/android/server/power/stats/MobileRadioPowerStatsProcessor.java
@@ -398,37 +398,4 @@
}
}
}
-
- @Override
- String deviceStatsToString(PowerStats.Descriptor descriptor, long[] stats) {
- unpackPowerStatsDescriptor(descriptor);
- return "idle: " + mStatsLayout.getDeviceIdleTime(stats)
- + " sleep: " + mStatsLayout.getDeviceSleepTime(stats)
- + " scan: " + mStatsLayout.getDeviceScanTime(stats)
- + " power: " + mStatsLayout.getDevicePowerEstimate(stats);
- }
-
- @Override
- String stateStatsToString(PowerStats.Descriptor descriptor, int key, long[] stats) {
- unpackPowerStatsDescriptor(descriptor);
- StringBuilder sb = new StringBuilder();
- sb.append(descriptor.getStateLabel(key));
- sb.append(" rx: ").append(mStatsLayout.getStateRxTime(stats));
- sb.append(" tx: ");
- for (int txLevel = 0; txLevel < ModemActivityInfo.getNumTxPowerLevels(); txLevel++) {
- if (txLevel != 0) {
- sb.append(", ");
- }
- sb.append(mStatsLayout.getStateTxTime(stats, txLevel));
- }
- return sb.toString();
- }
-
- @Override
- String uidStatsToString(PowerStats.Descriptor descriptor, long[] stats) {
- unpackPowerStatsDescriptor(descriptor);
- return "rx: " + mStatsLayout.getUidRxPackets(stats)
- + " tx: " + mStatsLayout.getUidTxPackets(stats)
- + " power: " + mStatsLayout.getUidPowerEstimate(stats);
- }
}
diff --git a/services/core/java/com/android/server/power/stats/MultiStateStats.java b/services/core/java/com/android/server/power/stats/MultiStateStats.java
index 6c4a2b6..a822281 100644
--- a/services/core/java/com/android/server/power/stats/MultiStateStats.java
+++ b/services/core/java/com/android/server/power/stats/MultiStateStats.java
@@ -28,10 +28,8 @@
import org.xmlpull.v1.XmlPullParserException;
import java.io.IOException;
-import java.io.PrintWriter;
import java.util.Arrays;
import java.util.function.Consumer;
-import java.util.function.Function;
/**
* Maintains multidimensional multi-state stats. States could be something like on-battery (0,1),
@@ -287,6 +285,14 @@
mCounter = new LongArrayMultiStateCounter(factory.mSerialStateCount, dimensionCount);
}
+ public int getDimensionCount() {
+ return mFactory.mDimensionCount;
+ }
+
+ public States[] getStates() {
+ return mFactory.mStates;
+ }
+
/**
* Copies time-in-state and timestamps from the supplied prototype. Does not
* copy accumulated counts.
@@ -343,11 +349,6 @@
mTracking = false;
}
- @Override
- public String toString() {
- return mCounter.toString();
- }
-
/**
* Stores contents in an XML doc.
*/
@@ -451,10 +452,9 @@
return true;
}
- /**
- * Prints the accumulated stats, one line of every combination of states that has data.
- */
- public void dump(PrintWriter pw, Function<long[], String> statsFormatter) {
+ @Override
+ public String toString() {
+ StringBuilder sb = new StringBuilder();
long[] values = new long[mCounter.getArrayLength()];
States.forEachTrackedStateCombination(mFactory.mStates, states -> {
mCounter.getCounts(values, mFactory.getSerialState(states));
@@ -469,18 +469,24 @@
return;
}
- StringBuilder sb = new StringBuilder();
+ if (!sb.isEmpty()) {
+ sb.append("\n");
+ }
+
+ sb.append("(");
+ boolean first = true;
for (int i = 0; i < states.length; i++) {
if (mFactory.mStates[i].mTracked) {
- if (sb.length() != 0) {
+ if (!first) {
sb.append(" ");
}
+ first = false;
sb.append(mFactory.mStates[i].mLabels[states[i]]);
}
}
- sb.append(" ");
- sb.append(statsFormatter.apply(values));
- pw.println(sb);
+ sb.append(") ");
+ sb.append(Arrays.toString(values));
});
+ return sb.toString();
}
}
diff --git a/services/core/java/com/android/server/power/stats/PhoneCallPowerStatsProcessor.java b/services/core/java/com/android/server/power/stats/PhoneCallPowerStatsProcessor.java
index 62b653f..5c545fd 100644
--- a/services/core/java/com/android/server/power/stats/PhoneCallPowerStatsProcessor.java
+++ b/services/core/java/com/android/server/power/stats/PhoneCallPowerStatsProcessor.java
@@ -76,21 +76,4 @@
stats.setDeviceStats(states, mTmpDeviceStats);
});
}
-
- @Override
- String deviceStatsToString(PowerStats.Descriptor descriptor, long[] stats) {
- return "power: " + mStatsLayout.getDevicePowerEstimate(stats);
- }
-
- @Override
- String stateStatsToString(PowerStats.Descriptor descriptor, int key, long[] stats) {
- // Unsupported for this power component
- return null;
- }
-
- @Override
- String uidStatsToString(PowerStats.Descriptor descriptor, long[] stats) {
- // Unsupported for this power component
- return null;
- }
}
diff --git a/services/core/java/com/android/server/power/stats/PowerComponentAggregatedPowerStats.java b/services/core/java/com/android/server/power/stats/PowerComponentAggregatedPowerStats.java
index 6d58307..0528733 100644
--- a/services/core/java/com/android/server/power/stats/PowerComponentAggregatedPowerStats.java
+++ b/services/core/java/com/android/server/power/stats/PowerComponentAggregatedPowerStats.java
@@ -436,36 +436,76 @@
void dumpDevice(IndentingPrintWriter ipw) {
if (mDeviceStats != null) {
- ipw.println(mPowerStatsDescriptor.name);
- ipw.increaseIndent();
- mDeviceStats.dump(ipw, stats ->
- mConfig.getProcessor().deviceStatsToString(mPowerStatsDescriptor, stats));
- ipw.decreaseIndent();
+ dumpMultiStateStats(ipw, mDeviceStats, mPowerStatsDescriptor.name, null,
+ mPowerStatsDescriptor.getDeviceStatsFormatter());
}
if (mStateStats.size() != 0) {
ipw.increaseIndent();
- ipw.println(mPowerStatsDescriptor.name + " states");
- ipw.increaseIndent();
+ String header = mPowerStatsDescriptor.name + " states";
+ PowerStats.PowerStatsFormatter formatter =
+ mPowerStatsDescriptor.getStateStatsFormatter();
for (int i = 0; i < mStateStats.size(); i++) {
int key = mStateStats.keyAt(i);
+ String stateLabel = mPowerStatsDescriptor.getStateLabel(key);
MultiStateStats stateStats = mStateStats.valueAt(i);
- stateStats.dump(ipw, stats ->
- mConfig.getProcessor().stateStatsToString(mPowerStatsDescriptor, key,
- stats));
+ dumpMultiStateStats(ipw, stateStats, header, stateLabel, formatter);
}
ipw.decreaseIndent();
- ipw.decreaseIndent();
}
}
void dumpUid(IndentingPrintWriter ipw, int uid) {
UidStats uidStats = mUidStats.get(uid);
if (uidStats != null && uidStats.stats != null) {
- ipw.println(mPowerStatsDescriptor.name);
- ipw.increaseIndent();
- uidStats.stats.dump(ipw, stats ->
- mConfig.getProcessor().uidStatsToString(mPowerStatsDescriptor, stats));
+ dumpMultiStateStats(ipw, uidStats.stats, mPowerStatsDescriptor.name, null,
+ mPowerStatsDescriptor.getUidStatsFormatter());
+ }
+ }
+
+ private void dumpMultiStateStats(IndentingPrintWriter ipw, MultiStateStats stats,
+ String header, String additionalLabel,
+ PowerStats.PowerStatsFormatter statsFormatter) {
+ boolean[] firstLine = new boolean[]{true};
+ long[] values = new long[stats.getDimensionCount()];
+ MultiStateStats.States[] stateInfo = stats.getStates();
+ MultiStateStats.States.forEachTrackedStateCombination(stateInfo, states -> {
+ stats.getStats(values, states);
+ boolean nonZero = false;
+ for (long value : values) {
+ if (value != 0) {
+ nonZero = true;
+ break;
+ }
+ }
+ if (!nonZero) {
+ return;
+ }
+
+ if (firstLine[0]) {
+ ipw.println(header);
+ ipw.increaseIndent();
+ }
+ firstLine[0] = false;
+ StringBuilder sb = new StringBuilder();
+ sb.append("(");
+ boolean first = true;
+ for (int i = 0; i < states.length; i++) {
+ if (stateInfo[i].isTracked()) {
+ if (!first) {
+ sb.append(" ");
+ }
+ first = false;
+ sb.append(stateInfo[i].getLabels()[states[i]]);
+ }
+ }
+ if (additionalLabel != null) {
+ sb.append(" ").append(additionalLabel);
+ }
+ sb.append(") ").append(statsFormatter.format(values));
+ ipw.println(sb);
+ });
+ if (!firstLine[0]) {
ipw.decreaseIndent();
}
}
diff --git a/services/core/java/com/android/server/power/stats/PowerStatsLayout.java b/services/core/java/com/android/server/power/stats/PowerStatsLayout.java
index aa96409..58efd94 100644
--- a/services/core/java/com/android/server/power/stats/PowerStatsLayout.java
+++ b/services/core/java/com/android/server/power/stats/PowerStatsLayout.java
@@ -33,13 +33,20 @@
private static final String EXTRA_DEVICE_ENERGY_CONSUMERS_COUNT = "dec";
private static final String EXTRA_UID_POWER_POSITION = "up";
- protected static final double MILLI_TO_NANO_MULTIPLIER = 1000000.0;
protected static final int UNSUPPORTED = -1;
+ protected static final double MILLI_TO_NANO_MULTIPLIER = 1000000.0;
+ protected static final int FLAG_OPTIONAL = 1;
+ protected static final int FLAG_HIDDEN = 2;
+ protected static final int FLAG_FORMAT_AS_POWER = 4;
private int mDeviceStatsArrayLength;
private int mStateStatsArrayLength;
private int mUidStatsArrayLength;
+ private StringBuilder mDeviceFormat = new StringBuilder();
+ private StringBuilder mStateFormat = new StringBuilder();
+ private StringBuilder mUidFormat = new StringBuilder();
+
protected int mDeviceDurationPosition = UNSUPPORTED;
private int mDeviceEnergyConsumerPosition;
private int mDeviceEnergyConsumerCount;
@@ -65,29 +72,71 @@
return mUidStatsArrayLength;
}
- protected int addDeviceSection(int length) {
+ /**
+ * @param label should not contain either spaces or colons
+ */
+ private void appendFormat(StringBuilder sb, int position, int length, String label,
+ int flags) {
+ if ((flags & FLAG_HIDDEN) != 0) {
+ return;
+ }
+
+ if (!sb.isEmpty()) {
+ sb.append(' ');
+ }
+
+ sb.append(label).append(':');
+ sb.append(position);
+ if (length != 1) {
+ sb.append('[').append(length).append(']');
+ }
+ if ((flags & FLAG_FORMAT_AS_POWER) != 0) {
+ sb.append('p');
+ }
+ if ((flags & FLAG_OPTIONAL) != 0) {
+ sb.append('?');
+ }
+ }
+
+ protected int addDeviceSection(int length, String label, int flags) {
int position = mDeviceStatsArrayLength;
mDeviceStatsArrayLength += length;
+ appendFormat(mDeviceFormat, position, length, label, flags);
return position;
}
- protected int addStateSection(int length) {
+ protected int addDeviceSection(int length, String label) {
+ return addDeviceSection(length, label, 0);
+ }
+
+ protected int addStateSection(int length, String label, int flags) {
int position = mStateStatsArrayLength;
mStateStatsArrayLength += length;
+ appendFormat(mStateFormat, position, length, label, flags);
return position;
}
- protected int addUidSection(int length) {
+ protected int addStateSection(int length, String label) {
+ return addStateSection(length, label, 0);
+ }
+
+
+ protected int addUidSection(int length, String label, int flags) {
int position = mUidStatsArrayLength;
mUidStatsArrayLength += length;
+ appendFormat(mUidFormat, position, length, label, flags);
return position;
}
+ protected int addUidSection(int length, String label) {
+ return addUidSection(length, label, 0);
+ }
+
/**
* Declare that the stats array has a section capturing usage duration
*/
public void addDeviceSectionUsageDuration() {
- mDeviceDurationPosition = addDeviceSection(1);
+ mDeviceDurationPosition = addDeviceSection(1, "usage", FLAG_OPTIONAL);
}
/**
@@ -109,7 +158,7 @@
* PowerStatsService.
*/
public void addDeviceSectionEnergyConsumers(int energyConsumerCount) {
- mDeviceEnergyConsumerPosition = addDeviceSection(energyConsumerCount);
+ mDeviceEnergyConsumerPosition = addDeviceSection(energyConsumerCount, "energy");
mDeviceEnergyConsumerCount = energyConsumerCount;
}
@@ -137,7 +186,8 @@
* Declare that the stats array has a section capturing a power estimate
*/
public void addDeviceSectionPowerEstimate() {
- mDevicePowerEstimatePosition = addDeviceSection(1);
+ mDevicePowerEstimatePosition = addDeviceSection(1, "power",
+ FLAG_FORMAT_AS_POWER | FLAG_OPTIONAL);
}
/**
@@ -159,7 +209,7 @@
* Declare that the UID stats array has a section capturing a power estimate
*/
public void addUidSectionPowerEstimate() {
- mUidPowerEstimatePosition = addUidSection(1);
+ mUidPowerEstimatePosition = addUidSection(1, "power", FLAG_FORMAT_AS_POWER | FLAG_OPTIONAL);
}
/**
@@ -195,6 +245,9 @@
mDeviceEnergyConsumerCount);
extras.putInt(EXTRA_DEVICE_POWER_POSITION, mDevicePowerEstimatePosition);
extras.putInt(EXTRA_UID_POWER_POSITION, mUidPowerEstimatePosition);
+ extras.putString(PowerStats.Descriptor.EXTRA_DEVICE_STATS_FORMAT, mDeviceFormat.toString());
+ extras.putString(PowerStats.Descriptor.EXTRA_STATE_STATS_FORMAT, mStateFormat.toString());
+ extras.putString(PowerStats.Descriptor.EXTRA_UID_STATS_FORMAT, mUidFormat.toString());
}
/**
diff --git a/services/core/java/com/android/server/power/stats/PowerStatsProcessor.java b/services/core/java/com/android/server/power/stats/PowerStatsProcessor.java
index 0d5c542..2fd0b9a 100644
--- a/services/core/java/com/android/server/power/stats/PowerStatsProcessor.java
+++ b/services/core/java/com/android/server/power/stats/PowerStatsProcessor.java
@@ -19,8 +19,6 @@
import android.annotation.Nullable;
import android.util.Log;
-import com.android.internal.os.PowerStats;
-
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
@@ -47,12 +45,6 @@
abstract void finish(PowerComponentAggregatedPowerStats stats);
- abstract String deviceStatsToString(PowerStats.Descriptor descriptor, long[] stats);
-
- abstract String stateStatsToString(PowerStats.Descriptor descriptor, int key, long[] stats);
-
- abstract String uidStatsToString(PowerStats.Descriptor descriptor, long[] stats);
-
protected static class PowerEstimationPlan {
private final AggregatedPowerStatsConfig.PowerComponent mConfig;
public List<DeviceStateEstimation> deviceStateEstimations = new ArrayList<>();
diff --git a/services/core/java/com/android/server/power/stats/WifiPowerStatsCollector.java b/services/core/java/com/android/server/power/stats/WifiPowerStatsCollector.java
index 6321053..bd04199 100644
--- a/services/core/java/com/android/server/power/stats/WifiPowerStatsCollector.java
+++ b/services/core/java/com/android/server/power/stats/WifiPowerStatsCollector.java
@@ -56,6 +56,7 @@
Handler getHandler();
Clock getClock();
PowerStatsUidResolver getUidResolver();
+ long getPowerStatsCollectionThrottlePeriod(String powerComponentName);
PackageManager getPackageManager();
ConsumedEnergyRetriever getConsumedEnergyRetriever();
IntSupplier getVoltageSupplier();
@@ -92,9 +93,11 @@
private final SparseArray<WifiScanTimes> mLastScanTimes = new SparseArray<>();
private long mLastWifiActiveDuration;
- public WifiPowerStatsCollector(Injector injector, long throttlePeriodMs) {
- super(injector.getHandler(), throttlePeriodMs, injector.getUidResolver(),
- injector.getClock());
+ WifiPowerStatsCollector(Injector injector) {
+ super(injector.getHandler(), injector.getPowerStatsCollectionThrottlePeriod(
+ BatteryConsumer.powerComponentIdToString(
+ BatteryConsumer.POWER_COMPONENT_WIFI)),
+ injector.getUidResolver(), injector.getClock());
mInjector = injector;
}
diff --git a/services/core/java/com/android/server/power/stats/WifiPowerStatsLayout.java b/services/core/java/com/android/server/power/stats/WifiPowerStatsLayout.java
index 0fa6ec6..e2e8226 100644
--- a/services/core/java/com/android/server/power/stats/WifiPowerStatsLayout.java
+++ b/services/core/java/com/android/server/power/stats/WifiPowerStatsLayout.java
@@ -65,28 +65,28 @@
mPowerReportingSupported = powerReportingSupported;
if (mPowerReportingSupported) {
mDeviceActiveTimePosition = UNSPECIFIED;
- mDeviceRxTimePosition = addDeviceSection(1);
- mDeviceTxTimePosition = addDeviceSection(1);
- mDeviceIdleTimePosition = addDeviceSection(1);
- mDeviceScanTimePosition = addDeviceSection(1);
+ mDeviceRxTimePosition = addDeviceSection(1, "rx");
+ mDeviceTxTimePosition = addDeviceSection(1, "tx");
+ mDeviceIdleTimePosition = addDeviceSection(1, "idle");
+ mDeviceScanTimePosition = addDeviceSection(1, "scan");
} else {
- mDeviceActiveTimePosition = addDeviceSection(1);
+ mDeviceActiveTimePosition = addDeviceSection(1, "rx-tx");
mDeviceRxTimePosition = UNSPECIFIED;
mDeviceTxTimePosition = UNSPECIFIED;
mDeviceIdleTimePosition = UNSPECIFIED;
mDeviceScanTimePosition = UNSPECIFIED;
}
- mDeviceBasicScanTimePosition = addDeviceSection(1);
- mDeviceBatchedScanTimePosition = addDeviceSection(1);
+ mDeviceBasicScanTimePosition = addDeviceSection(1, "basic-scan", FLAG_OPTIONAL);
+ mDeviceBatchedScanTimePosition = addDeviceSection(1, "batched-scan", FLAG_OPTIONAL);
}
void addUidNetworkStats() {
- mUidRxBytesPosition = addUidSection(1);
- mUidTxBytesPosition = addUidSection(1);
- mUidRxPacketsPosition = addUidSection(1);
- mUidTxPacketsPosition = addUidSection(1);
- mUidScanTimePosition = addUidSection(1);
- mUidBatchScanTimePosition = addUidSection(1);
+ mUidRxPacketsPosition = addUidSection(1, "rx-pkts");
+ mUidRxBytesPosition = addUidSection(1, "rx-B");
+ mUidTxPacketsPosition = addUidSection(1, "tx-pkts");
+ mUidTxBytesPosition = addUidSection(1, "tx-B");
+ mUidScanTimePosition = addUidSection(1, "scan", FLAG_OPTIONAL);
+ mUidBatchScanTimePosition = addUidSection(1, "batched-scan", FLAG_OPTIONAL);
}
public boolean isPowerReportingSupported() {
diff --git a/services/core/java/com/android/server/power/stats/WifiPowerStatsProcessor.java b/services/core/java/com/android/server/power/stats/WifiPowerStatsProcessor.java
index 5e9cc40..a4a2e18 100644
--- a/services/core/java/com/android/server/power/stats/WifiPowerStatsProcessor.java
+++ b/services/core/java/com/android/server/power/stats/WifiPowerStatsProcessor.java
@@ -389,37 +389,4 @@
}
}
}
-
- @Override
- String deviceStatsToString(PowerStats.Descriptor descriptor, long[] stats) {
- unpackPowerStatsDescriptor(descriptor);
- if (mHasWifiPowerController) {
- return "rx: " + mStatsLayout.getDeviceRxTime(stats)
- + " tx: " + mStatsLayout.getDeviceTxTime(stats)
- + " scan: " + mStatsLayout.getDeviceScanTime(stats)
- + " idle: " + mStatsLayout.getDeviceIdleTime(stats)
- + " power: " + mStatsLayout.getDevicePowerEstimate(stats);
- } else {
- return "active: " + mStatsLayout.getDeviceActiveTime(stats)
- + " scan: " + mStatsLayout.getDeviceBasicScanTime(stats)
- + " batched-scan: " + mStatsLayout.getDeviceBatchedScanTime(stats)
- + " power: " + mStatsLayout.getDevicePowerEstimate(stats);
- }
- }
-
- @Override
- String stateStatsToString(PowerStats.Descriptor descriptor, int key, long[] stats) {
- // Unsupported for this power component
- return null;
- }
-
- @Override
- String uidStatsToString(PowerStats.Descriptor descriptor, long[] stats) {
- unpackPowerStatsDescriptor(descriptor);
- return "rx: " + mStatsLayout.getUidRxPackets(stats)
- + " tx: " + mStatsLayout.getUidTxPackets(stats)
- + " scan: " + mStatsLayout.getUidScanTime(stats)
- + " batched-scan: " + mStatsLayout.getUidBatchedScanTime(stats)
- + " power: " + mStatsLayout.getUidPowerEstimate(stats);
- }
}
diff --git a/services/core/java/com/android/server/stats/pull/StatsPullAtomService.java b/services/core/java/com/android/server/stats/pull/StatsPullAtomService.java
index e3e478d..c1b825b 100644
--- a/services/core/java/com/android/server/stats/pull/StatsPullAtomService.java
+++ b/services/core/java/com/android/server/stats/pull/StatsPullAtomService.java
@@ -49,10 +49,13 @@
import static android.view.Display.HdrCapabilities.HDR_TYPE_INVALID;
import static com.android.internal.util.ConcurrentUtils.DIRECT_EXECUTOR;
-import static com.android.internal.util.FrameworkStatsLog.ACCESSIBILITY_SHORTCUT_REPORTED__SHORTCUT_TYPE__A11Y_BUTTON;
-import static com.android.internal.util.FrameworkStatsLog.ACCESSIBILITY_SHORTCUT_REPORTED__SHORTCUT_TYPE__A11Y_FLOATING_MENU;
-import static com.android.internal.util.FrameworkStatsLog.ACCESSIBILITY_SHORTCUT_REPORTED__SHORTCUT_TYPE__A11Y_GESTURE;
-import static com.android.internal.util.FrameworkStatsLog.ACCESSIBILITY_SHORTCUT_REPORTED__SHORTCUT_TYPE__UNKNOWN_TYPE;
+import static com.android.internal.util.FrameworkStatsLog.ACCESSIBILITY_SHORTCUT_STATS__GESTURE_SHORTCUT_TYPE__TRIPLE_TAP;
+import static com.android.internal.util.FrameworkStatsLog.ACCESSIBILITY_SHORTCUT_STATS__HARDWARE_SHORTCUT_TYPE__VOLUME_KEY;
+import static com.android.internal.util.FrameworkStatsLog.ACCESSIBILITY_SHORTCUT_STATS__QS_SHORTCUT_TYPE__QUICK_SETTINGS;
+import static com.android.internal.util.FrameworkStatsLog.ACCESSIBILITY_SHORTCUT_STATS__SOFTWARE_SHORTCUT_TYPE__A11Y_BUTTON;
+import static com.android.internal.util.FrameworkStatsLog.ACCESSIBILITY_SHORTCUT_STATS__SOFTWARE_SHORTCUT_TYPE__A11Y_FLOATING_MENU;
+import static com.android.internal.util.FrameworkStatsLog.ACCESSIBILITY_SHORTCUT_STATS__SOFTWARE_SHORTCUT_TYPE__A11Y_GESTURE;
+import static com.android.internal.util.FrameworkStatsLog.ACCESSIBILITY_SHORTCUT_STATS__SOFTWARE_SHORTCUT_TYPE__UNKNOWN_TYPE;
import static com.android.internal.util.FrameworkStatsLog.DATA_USAGE_BYTES_TRANSFER__OPPORTUNISTIC_DATA_SUB__NOT_OPPORTUNISTIC;
import static com.android.internal.util.FrameworkStatsLog.DATA_USAGE_BYTES_TRANSFER__OPPORTUNISTIC_DATA_SUB__OPPORTUNISTIC;
import static com.android.internal.util.FrameworkStatsLog.TIME_ZONE_DETECTOR_STATE__DETECTION_MODE__GEO;
@@ -61,7 +64,6 @@
import static com.android.internal.util.FrameworkStatsLog.TIME_ZONE_DETECTOR_STATE__DETECTION_MODE__UNKNOWN;
import static com.android.server.am.MemoryStatUtil.readMemoryStatFromFilesystem;
import static com.android.server.stats.Flags.addMobileBytesTransferByProcStatePuller;
-import static com.android.server.stats.Flags.statsPullNetworkStatsManagerInitOrderFix;
import static com.android.server.stats.pull.IonMemoryUtil.readProcessSystemIonHeapSizesFromDebugfs;
import static com.android.server.stats.pull.IonMemoryUtil.readSystemIonHeapSizeFromDebugfs;
import static com.android.server.stats.pull.ProcfsMemoryUtil.getProcessCmdlines;
@@ -431,12 +433,6 @@
public static final boolean ENABLE_MOBILE_DATA_STATS_AGGREGATED_PULLER =
addMobileBytesTransferByProcStatePuller();
- /**
- * Whether or not to enable the mNetworkStatsManager initialization order fix
- */
- private static final boolean ENABLE_NETWORK_STATS_MANAGER_INIT_ORDER_FIX =
- statsPullNetworkStatsManagerInitOrderFix();
-
// Puller locks
private final Object mDataBytesTransferLock = new Object();
private final Object mBluetoothBytesTransferLock = new Object();
@@ -799,7 +795,7 @@
case FrameworkStatsLog.KEYSTORE2_CRASH_STATS:
return pullKeystoreAtoms(atomTag, data);
case FrameworkStatsLog.ACCESSIBILITY_SHORTCUT_STATS:
- return pullAccessibilityShortcutStatsLocked(atomTag, data);
+ return pullAccessibilityShortcutStatsLocked(data);
case FrameworkStatsLog.ACCESSIBILITY_FLOATING_MENU_STATS:
return pullAccessibilityFloatingMenuStatsLocked(atomTag, data);
case FrameworkStatsLog.MEDIA_CAPABILITIES:
@@ -840,9 +836,7 @@
registerEventListeners();
});
} else if (phase == PHASE_THIRD_PARTY_APPS_CAN_START) {
- if (ENABLE_NETWORK_STATS_MANAGER_INIT_ORDER_FIX) {
- initNetworkStatsManager();
- }
+ initNetworkStatsManager();
BackgroundThread.getHandler().post(() -> {
// Network stats related pullers can only be initialized after service is ready.
initAndRegisterNetworkStatsPullers();
@@ -863,9 +857,6 @@
mContext.getSystemService(Context.TELEPHONY_SUBSCRIPTION_SERVICE);
mStatsSubscriptionsListener = new StatsSubscriptionsListener(mSubscriptionManager);
mStorageManager = (StorageManager) mContext.getSystemService(StorageManager.class);
- if (!ENABLE_NETWORK_STATS_MANAGER_INIT_ORDER_FIX) {
- initNetworkStatsManager();
- }
// Initialize DiskIO
mStoragedUidIoStatsReader = new StoragedUidIoStatsReader();
@@ -1047,10 +1038,8 @@
*/
@NonNull
private NetworkStatsManager getNetworkStatsManager() {
- if (ENABLE_NETWORK_STATS_MANAGER_INIT_ORDER_FIX) {
- if (mNetworkStatsManager == null) {
- throw new IllegalStateException("NetworkStatsManager is not ready");
- }
+ if (mNetworkStatsManager == null) {
+ throw new IllegalStateException("NetworkStatsManager is not ready");
}
return mNetworkStatsManager;
}
@@ -4774,7 +4763,10 @@
}
}
- int pullAccessibilityShortcutStatsLocked(int atomTag, List<StatsEvent> pulledData) {
+ /**
+ * Pulls ACCESSIBILITY_SHORTCUT_STATS atom
+ */
+ int pullAccessibilityShortcutStatsLocked(List<StatsEvent> pulledData) {
UserManager userManager = mContext.getSystemService(UserManager.class);
if (userManager == null) {
return StatsManager.PULL_SKIP;
@@ -4782,10 +4774,6 @@
final long token = Binder.clearCallingIdentity();
try {
final ContentResolver resolver = mContext.getContentResolver();
- final int hardware_shortcut_type =
- FrameworkStatsLog.ACCESSIBILITY_SHORTCUT_REPORTED__SHORTCUT_TYPE__VOLUME_KEY;
- final int triple_tap_shortcut =
- FrameworkStatsLog.ACCESSIBILITY_SHORTCUT_REPORTED__SHORTCUT_TYPE__TRIPLE_TAP;
for (UserInfo userInfo : userManager.getUsers()) {
final int userId = userInfo.getUserHandle().getIdentifier();
@@ -4803,15 +4791,22 @@
final int hardware_shortcut_service_num = countAccessibilityServices(
hardware_shortcut_list);
+ final String qs_shortcut_list = Settings.Secure.getStringForUser(resolver,
+ Settings.Secure.ACCESSIBILITY_QS_TARGETS, userId);
+ final boolean qs_shortcut_enabled = !TextUtils.isEmpty(qs_shortcut_list);
+
// only allow magnification to use it for now
final int triple_tap_service_num = Settings.Secure.getIntForUser(resolver,
Settings.Secure.ACCESSIBILITY_DISPLAY_MAGNIFICATION_ENABLED, 0, userId);
-
- pulledData.add(
- FrameworkStatsLog.buildStatsEvent(atomTag,
- software_shortcut_type, software_shortcut_service_num,
- hardware_shortcut_type, hardware_shortcut_service_num,
- triple_tap_shortcut, triple_tap_service_num));
+ pulledData.add(FrameworkStatsLog.buildStatsEvent(
+ FrameworkStatsLog.ACCESSIBILITY_SHORTCUT_STATS,
+ software_shortcut_type, software_shortcut_service_num,
+ ACCESSIBILITY_SHORTCUT_STATS__HARDWARE_SHORTCUT_TYPE__VOLUME_KEY,
+ hardware_shortcut_service_num,
+ ACCESSIBILITY_SHORTCUT_STATS__GESTURE_SHORTCUT_TYPE__TRIPLE_TAP,
+ triple_tap_service_num,
+ ACCESSIBILITY_SHORTCUT_STATS__QS_SHORTCUT_TYPE__QUICK_SETTINGS,
+ qs_shortcut_enabled));
}
}
} catch (RuntimeException e) {
@@ -5150,16 +5145,19 @@
Settings.Secure.ACCESSIBILITY_BUTTON_TARGETS, userId);
final String hardware_shortcut_list = Settings.Secure.getStringForUser(resolver,
Settings.Secure.ACCESSIBILITY_SHORTCUT_TARGET_SERVICE, userId);
+ final String qs_shortcut_list = Settings.Secure.getStringForUser(resolver,
+ Settings.Secure.ACCESSIBILITY_QS_TARGETS, userId);
final boolean hardware_shortcut_dialog_shown = Settings.Secure.getIntForUser(resolver,
Settings.Secure.ACCESSIBILITY_SHORTCUT_DIALOG_SHOWN, 0, userId) == 1;
final boolean software_shortcut_enabled = !TextUtils.isEmpty(software_shortcut_list);
final boolean hardware_shortcut_enabled =
hardware_shortcut_dialog_shown && !TextUtils.isEmpty(hardware_shortcut_list);
+ final boolean qs_shortcut_enabled = !TextUtils.isEmpty(qs_shortcut_list);
final boolean triple_tap_shortcut_enabled = Settings.Secure.getIntForUser(resolver,
Settings.Secure.ACCESSIBILITY_DISPLAY_MAGNIFICATION_ENABLED, 0, userId) == 1;
return software_shortcut_enabled || hardware_shortcut_enabled
- || triple_tap_shortcut_enabled;
+ || triple_tap_shortcut_enabled || qs_shortcut_enabled;
}
private boolean isAccessibilityFloatingMenuUser(Context context, @UserIdInt int userId) {
@@ -5176,13 +5174,13 @@
private int convertToAccessibilityShortcutType(int shortcutType) {
switch (shortcutType) {
case Settings.Secure.ACCESSIBILITY_BUTTON_MODE_NAVIGATION_BAR:
- return ACCESSIBILITY_SHORTCUT_REPORTED__SHORTCUT_TYPE__A11Y_BUTTON;
+ return ACCESSIBILITY_SHORTCUT_STATS__SOFTWARE_SHORTCUT_TYPE__A11Y_BUTTON;
case Settings.Secure.ACCESSIBILITY_BUTTON_MODE_FLOATING_MENU:
- return ACCESSIBILITY_SHORTCUT_REPORTED__SHORTCUT_TYPE__A11Y_FLOATING_MENU;
+ return ACCESSIBILITY_SHORTCUT_STATS__SOFTWARE_SHORTCUT_TYPE__A11Y_FLOATING_MENU;
case Settings.Secure.ACCESSIBILITY_BUTTON_MODE_GESTURE:
- return ACCESSIBILITY_SHORTCUT_REPORTED__SHORTCUT_TYPE__A11Y_GESTURE;
+ return ACCESSIBILITY_SHORTCUT_STATS__SOFTWARE_SHORTCUT_TYPE__A11Y_GESTURE;
default:
- return ACCESSIBILITY_SHORTCUT_REPORTED__SHORTCUT_TYPE__UNKNOWN_TYPE;
+ return ACCESSIBILITY_SHORTCUT_STATS__SOFTWARE_SHORTCUT_TYPE__UNKNOWN_TYPE;
}
}
diff --git a/services/core/java/com/android/server/stats/stats_flags.aconfig b/services/core/java/com/android/server/stats/stats_flags.aconfig
index c479c6d..6faa273 100644
--- a/services/core/java/com/android/server/stats/stats_flags.aconfig
+++ b/services/core/java/com/android/server/stats/stats_flags.aconfig
@@ -8,11 +8,3 @@
bug: "309512867"
is_fixed_read_only: true
}
-
-flag {
- name: "stats_pull_network_stats_manager_init_order_fix"
- namespace: "statsd"
- description: "Fix the mNetworkStatsManager initialization order"
- bug: "331989853"
- is_fixed_read_only: true
-}
diff --git a/services/core/java/com/android/server/wm/AccessibilityController.java b/services/core/java/com/android/server/wm/AccessibilityController.java
index e280bdc..5be5bc5 100644
--- a/services/core/java/com/android/server/wm/AccessibilityController.java
+++ b/services/core/java/com/android/server/wm/AccessibilityController.java
@@ -984,36 +984,26 @@
Region touchableRegion = mTempRegion3;
windowState.getTouchableRegion(touchableRegion);
Region windowBounds = mTempRegion2;
- if (Flags.useWindowOriginalTouchableRegionWhenMagnificationRecomputeBounds()) {
- // For b/323366243, if using the bounds from touchableRegion.getBounds, in
- // non-magnifiable windowBounds computation, part of the non-touchableRegion
- // may be included into nonMagnifiedBounds. This will make users lose
- // the magnification control on mis-included areas.
- // Therefore, to prevent the above issue, we change to use the window exact
- // touchableRegion in magnificationRegion computation.
- // Like the original approach, the touchableRegion is in non-magnified display
- // space, so first we need to offset the region by the windowFrames bounds, then
- // apply the transform matrix to the region to get the exact region in magnified
- // display space.
- // TODO: For a long-term plan, since touchable regions provided by WindowState
- // doesn't actually reflect the real touchable regions on display, we should
- // delete the WindowState dependency and migrate to use the touchableRegion
- // from WindowInfoListener data. (b/330653961)
- touchableRegion.translate(-windowState.getFrame().left,
- -windowState.getFrame().top);
- applyMatrixToRegion(matrix, touchableRegion);
- windowBounds.set(touchableRegion);
- } else {
- Rect touchableFrame = mTempRect1;
- touchableRegion.getBounds(touchableFrame);
- RectF windowFrame = mTempRectF;
- windowFrame.set(touchableFrame);
- windowFrame.offset(-windowState.getFrame().left,
- -windowState.getFrame().top);
- matrix.mapRect(windowFrame);
- windowBounds.set((int) windowFrame.left, (int) windowFrame.top,
- (int) windowFrame.right, (int) windowFrame.bottom);
- }
+
+ // For b/323366243, if using the bounds from touchableRegion.getBounds, in
+ // non-magnifiable windowBounds computation, part of the non-touchableRegion
+ // may be included into nonMagnifiedBounds. This will make users lose
+ // the magnification control on mis-included areas.
+ // Therefore, to prevent the above issue, we change to use the window exact
+ // touchableRegion in magnificationRegion computation.
+ // Like the original approach, the touchableRegion is in non-magnified display
+ // space, so first we need to offset the region by the windowFrames bounds, then
+ // apply the transform matrix to the region to get the exact region in magnified
+ // display space.
+ // TODO: For a long-term plan, since touchable regions provided by WindowState
+ // doesn't actually reflect the real touchable regions on display, we should
+ // delete the WindowState dependency and migrate to use the touchableRegion
+ // from WindowInfoListener data. (b/330653961)
+ touchableRegion.translate(-windowState.getFrame().left,
+ -windowState.getFrame().top);
+ applyMatrixToRegion(matrix, touchableRegion);
+ windowBounds.set(touchableRegion);
+
// Only update new regions
Region portionOfWindowAlreadyAccountedFor = mTempRegion3;
portionOfWindowAlreadyAccountedFor.set(mMagnificationRegion);
diff --git a/services/core/java/com/android/server/wm/AccessibilityWindowsPopulator.java b/services/core/java/com/android/server/wm/AccessibilityWindowsPopulator.java
index f6afc52..3393d3e 100644
--- a/services/core/java/com/android/server/wm/AccessibilityWindowsPopulator.java
+++ b/services/core/java/com/android/server/wm/AccessibilityWindowsPopulator.java
@@ -150,7 +150,11 @@
@Override
public void onWindowInfosChanged(InputWindowHandle[] windowHandles,
DisplayInfo[] displayInfos) {
- mHandler.post(() -> onWindowInfosChangedInternal(windowHandles, displayInfos));
+ if (com.android.server.accessibility.Flags.removeOnWindowInfosChangedHandler()) {
+ onWindowInfosChangedInternal(windowHandles, displayInfos);
+ } else {
+ mHandler.post(() -> onWindowInfosChangedInternal(windowHandles, displayInfos));
+ }
}
private void onWindowInfosChangedInternal(InputWindowHandle[] windowHandles,
diff --git a/services/core/java/com/android/server/wm/ActivityRecord.java b/services/core/java/com/android/server/wm/ActivityRecord.java
index 21e4c96..2f6e07c 100644
--- a/services/core/java/com/android/server/wm/ActivityRecord.java
+++ b/services/core/java/com/android/server/wm/ActivityRecord.java
@@ -77,7 +77,6 @@
import static android.content.pm.ActivityInfo.FLAG_SHOW_FOR_ALL_USERS;
import static android.content.pm.ActivityInfo.FLAG_STATE_NOT_NEEDED;
import static android.content.pm.ActivityInfo.FLAG_TURN_SCREEN_ON;
-import static android.content.pm.ActivityInfo.OVERRIDE_ENABLE_INSETS_DECOUPLED_CONFIGURATION;
import static android.content.pm.ActivityInfo.INSETS_DECOUPLED_CONFIGURATION_ENFORCED;
import static android.content.pm.ActivityInfo.LAUNCH_MULTIPLE;
import static android.content.pm.ActivityInfo.LAUNCH_SINGLE_INSTANCE;
@@ -87,6 +86,7 @@
import static android.content.pm.ActivityInfo.LOCK_TASK_LAUNCH_MODE_DEFAULT;
import static android.content.pm.ActivityInfo.LOCK_TASK_LAUNCH_MODE_IF_ALLOWLISTED;
import static android.content.pm.ActivityInfo.LOCK_TASK_LAUNCH_MODE_NEVER;
+import static android.content.pm.ActivityInfo.OVERRIDE_ENABLE_INSETS_DECOUPLED_CONFIGURATION;
import static android.content.pm.ActivityInfo.OVERRIDE_MIN_ASPECT_RATIO_EXCLUDE_PORTRAIT_FULLSCREEN;
import static android.content.pm.ActivityInfo.OVERRIDE_MIN_ASPECT_RATIO_LARGE;
import static android.content.pm.ActivityInfo.OVERRIDE_MIN_ASPECT_RATIO_MEDIUM;
@@ -121,6 +121,7 @@
import static android.view.Surface.ROTATION_270;
import static android.view.Surface.ROTATION_90;
import static android.view.WindowManager.ACTIVITY_EMBEDDING_GUARD_WITH_ANDROID_15;
+import static android.view.WindowManager.ENABLE_ACTIVITY_EMBEDDING_FOR_ANDROID_15;
import static android.view.WindowManager.LayoutParams.FLAG_DISMISS_KEYGUARD;
import static android.view.WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED;
import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION;
@@ -128,7 +129,6 @@
import static android.view.WindowManager.LayoutParams.TYPE_BASE_APPLICATION;
import static android.view.WindowManager.PROPERTY_ACTIVITY_EMBEDDING_SPLITS_ENABLED;
import static android.view.WindowManager.PROPERTY_ALLOW_UNTRUSTED_ACTIVITY_EMBEDDING_STATE_SHARING;
-import static android.view.WindowManager.ENABLE_ACTIVITY_EMBEDDING_FOR_ANDROID_15;
import static android.view.WindowManager.TRANSIT_CLOSE;
import static android.view.WindowManager.TRANSIT_FLAG_OPEN_BEHIND;
import static android.view.WindowManager.TRANSIT_OLD_UNSET;
@@ -339,7 +339,6 @@
import android.service.dreams.DreamActivity;
import android.service.voice.IVoiceInteractionSession;
import android.util.ArraySet;
-import android.util.DisplayMetrics;
import android.util.EventLog;
import android.util.Log;
import android.util.MergedConfiguration;
@@ -661,6 +660,8 @@
private final TaskFragment.ConfigOverrideHint mResolveConfigHint;
+ private final boolean mOptOutEdgeToEdge;
+
private static ConstrainDisplayApisConfig sConstrainDisplayApisConfig;
boolean pendingVoiceInteractionStart; // Waiting for activity-invoked voice session
@@ -683,6 +684,12 @@
// it references to gets removed. This should also be cleared when we move out of pip.
private Task mLastParentBeforePip;
+ // The token of the previous TaskFragment parent of this embedded ActivityRecord when it is
+ // reparented to a new Task due to picture-in-picture.
+ // Note that the TaskFragment may be finished and no longer attached in WM hierarchy.
+ @Nullable
+ private IBinder mLastEmbeddedParentTfTokenBeforePip;
+
// Only set if this instance is a launch-into-pip Activity, points to the
// host Activity the launch-into-pip Activity is originated from.
private ActivityRecord mLaunchIntoPipHostActivity;
@@ -1807,6 +1814,11 @@
mLastTaskFragmentOrganizerBeforePip = organizedTf != null
? organizedTf.getTaskFragmentOrganizer()
: null;
+ if (organizedTf != null
+ // Not necessary for content pip.
+ && launchIntoPipHostActivity == null) {
+ mLastEmbeddedParentTfTokenBeforePip = organizedTf.getFragmentToken();
+ }
}
void clearLastParentBeforePip() {
@@ -1816,12 +1828,17 @@
}
mLaunchIntoPipHostActivity = null;
mLastTaskFragmentOrganizerBeforePip = null;
+ mLastEmbeddedParentTfTokenBeforePip = null;
}
@Nullable Task getLastParentBeforePip() {
return mLastParentBeforePip;
}
+ @Nullable IBinder getLastEmbeddedParentTfTokenBeforePip() {
+ return mLastEmbeddedParentTfTokenBeforePip;
+ }
+
@Nullable ActivityRecord getLaunchIntoPipHostActivity() {
return mLaunchIntoPipHostActivity;
}
@@ -2123,14 +2140,14 @@
if (mWmService.mFlags.mInsetsDecoupledConfiguration) {
// When the stable configuration is the default behavior, override for the legacy apps
// without forward override flag.
- mResolveConfigHint.mUseOverrideInsetsForStableBounds =
+ mResolveConfigHint.mUseOverrideInsetsForConfig =
!info.isChangeEnabled(INSETS_DECOUPLED_CONFIGURATION_ENFORCED)
&& !info.isChangeEnabled(
OVERRIDE_ENABLE_INSETS_DECOUPLED_CONFIGURATION);
} else {
// When the stable configuration is not the default behavior, forward overriding the
// listed apps.
- mResolveConfigHint.mUseOverrideInsetsForStableBounds =
+ mResolveConfigHint.mUseOverrideInsetsForConfig =
info.isChangeEnabled(OVERRIDE_ENABLE_INSETS_DECOUPLED_CONFIGURATION);
}
@@ -2164,9 +2181,12 @@
|| ent.array.getBoolean(R.styleable.Window_windowShowWallpaper, false);
mStyleFillsParent = mOccludesParent;
noDisplay = ent.array.getBoolean(R.styleable.Window_windowNoDisplay, false);
+ mOptOutEdgeToEdge = ent.array.getBoolean(
+ R.styleable.Window_windowOptOutEdgeToEdgeEnforcement, false);
} else {
mStyleFillsParent = mOccludesParent = true;
noDisplay = false;
+ mOptOutEdgeToEdge = false;
}
if (options != null) {
@@ -3272,8 +3292,12 @@
mOccludesParent = occludesParent;
setMainWindowOpaque(occludesParent);
- if (changed && task != null && !occludesParent) {
- getRootTask().convertActivityToTranslucent(this);
+ if (changed && task != null) {
+ if (!occludesParent) {
+ getRootTask().convertActivityToTranslucent(this);
+ } else {
+ getRootTask().convertActivityFromTranslucent(this);
+ }
}
// Always ensure visibility if this activity doesn't occlude parent, so the
// {@link #returningOptions} of the activity under this one can be applied in
@@ -4266,6 +4290,12 @@
getTaskFragment().cleanUpActivityReferences(this);
clearLastParentBeforePip();
+ // Abort and reset state if the scence transition is playing.
+ final Task rootTask = getRootTask();
+ if (rootTask != null) {
+ rootTask.abortTranslucentActivityWaiting(this);
+ }
+
// Clean up the splash screen if it was still displayed.
cleanUpSplashScreen();
@@ -5672,6 +5702,8 @@
} else if (mTransitionController.inFinishingTransition(this)) {
mTransitionChangeFlags |= FLAGS_IS_OCCLUDED_NO_ANIMATION;
}
+ } else {
+ mTransitionChangeFlags &= ~FLAG_IS_OCCLUDED;
}
return;
}
@@ -6519,8 +6551,8 @@
// and the token could be null.
return;
}
- if (r.mDisplayContent.mDisplayRotationCompatPolicy != null) {
- r.mDisplayContent.mDisplayRotationCompatPolicy.onActivityRefreshed(r);
+ if (r.mDisplayContent.mActivityRefresher != null) {
+ r.mDisplayContent.mActivityRefresher.onActivityRefreshed(r);
}
}
@@ -8480,7 +8512,7 @@
mCompatDisplayInsets =
new CompatDisplayInsets(
mDisplayContent, this, letterboxedContainerBounds,
- mResolveConfigHint.mUseOverrideInsetsForStableBounds);
+ mResolveConfigHint.mUseOverrideInsetsForConfig);
}
private void clearSizeCompatModeAttributes() {
@@ -8560,8 +8592,6 @@
final int parentWindowingMode =
newParentConfiguration.windowConfiguration.getWindowingMode();
- applySizeOverrideIfNeeded(newParentConfiguration, parentWindowingMode, resolvedConfig);
-
// Bubble activities should always fill their parent and should not be letterboxed.
final boolean isFixedOrientationLetterboxAllowed = !getLaunchedFromBubble()
&& (parentWindowingMode == WINDOWING_MODE_MULTI_WINDOW
@@ -8661,6 +8691,8 @@
resolvedConfig.windowConfiguration.setMaxBounds(mTmpBounds);
}
+ applySizeOverrideIfNeeded(newParentConfiguration, parentWindowingMode, resolvedConfig);
+
logAppCompatState();
}
@@ -8679,14 +8711,13 @@
if (mDisplayContent == null) {
return;
}
- final Rect parentBounds = newParentConfiguration.windowConfiguration.getBounds();
int rotation = newParentConfiguration.windowConfiguration.getRotation();
if (rotation == ROTATION_UNDEFINED && !isFixedRotationTransforming()) {
rotation = mDisplayContent.getRotation();
}
- if (!mResolveConfigHint.mUseOverrideInsetsForStableBounds
+ if (!mOptOutEdgeToEdge && (!mResolveConfigHint.mUseOverrideInsetsForConfig
|| getCompatDisplayInsets() != null || isFloating(parentWindowingMode)
- || rotation == ROTATION_UNDEFINED) {
+ || rotation == ROTATION_UNDEFINED)) {
// If the insets configuration decoupled logic is not enabled for the app, or the app
// already has a compat override, or the context doesn't contain enough info to
// calculate the override, skip the override.
@@ -8703,53 +8734,7 @@
}
// Override starts here.
- final boolean rotated = (rotation == ROTATION_90 || rotation == ROTATION_270);
- final int dw = rotated ? mDisplayContent.mBaseDisplayHeight
- : mDisplayContent.mBaseDisplayWidth;
- final int dh = rotated ? mDisplayContent.mBaseDisplayWidth
- : mDisplayContent.mBaseDisplayHeight;
- final Rect nonDecorInsets = mDisplayContent.getDisplayPolicy()
- .getDecorInsetsInfo(rotation, dw, dh).mOverrideNonDecorInsets;
- // This should be the only place override the configuration for ActivityRecord. Override
- // the value if not calculated yet.
- Rect outAppBounds = inOutConfig.windowConfiguration.getAppBounds();
- if (outAppBounds == null || outAppBounds.isEmpty()) {
- inOutConfig.windowConfiguration.setAppBounds(parentBounds);
- outAppBounds = inOutConfig.windowConfiguration.getAppBounds();
- outAppBounds.inset(nonDecorInsets);
- }
- float density = inOutConfig.densityDpi;
- if (density == Configuration.DENSITY_DPI_UNDEFINED) {
- density = newParentConfiguration.densityDpi;
- }
- density *= DisplayMetrics.DENSITY_DEFAULT_SCALE;
- if (inOutConfig.screenWidthDp == Configuration.SCREEN_WIDTH_DP_UNDEFINED) {
- final int overrideScreenWidthDp = (int) (outAppBounds.width() / density + 0.5f);
- inOutConfig.screenWidthDp = overrideScreenWidthDp;
- }
- if (inOutConfig.screenHeightDp == Configuration.SCREEN_HEIGHT_DP_UNDEFINED) {
- final int overrideScreenHeightDp = (int) (outAppBounds.height() / density + 0.5f);
- inOutConfig.screenHeightDp = overrideScreenHeightDp;
- }
- if (inOutConfig.smallestScreenWidthDp
- == Configuration.SMALLEST_SCREEN_WIDTH_DP_UNDEFINED
- && parentWindowingMode == WINDOWING_MODE_FULLSCREEN) {
- // For the case of PIP transition and multi-window environment, the
- // smallestScreenWidthDp is handled already. Override only if the app is in
- // fullscreen.
- final DisplayInfo info = new DisplayInfo(mDisplayContent.getDisplayInfo());
- mDisplayContent.computeSizeRanges(info, rotated, dw, dh,
- mDisplayContent.getDisplayMetrics().density,
- inOutConfig, true /* overrideConfig */);
- }
-
- // It's possible that screen size will be considered in different orientation with or
- // without considering the system bar insets. Override orientation as well.
- if (inOutConfig.orientation == ORIENTATION_UNDEFINED) {
- inOutConfig.orientation =
- (inOutConfig.screenWidthDp <= inOutConfig.screenHeightDp)
- ? ORIENTATION_PORTRAIT : ORIENTATION_LANDSCAPE;
- }
+ computeConfigByResolveHint(inOutConfig, newParentConfiguration);
}
private void computeConfigByResolveHint(@NonNull Configuration resolvedConfig,
@@ -9005,7 +8990,7 @@
if (mDisplayContent == null) {
return true;
}
- if (!mResolveConfigHint.mUseOverrideInsetsForStableBounds) {
+ if (!mResolveConfigHint.mUseOverrideInsetsForConfig) {
// No insets should be considered any more.
return true;
}
@@ -9024,7 +9009,7 @@
final Task task = getTask();
task.calculateInsetFrames(outNonDecorBounds /* outNonDecorBounds */,
outStableBounds /* outStableBounds */, parentBounds /* bounds */, di,
- mResolveConfigHint.mUseOverrideInsetsForStableBounds);
+ mResolveConfigHint.mUseOverrideInsetsForConfig);
final int orientationWithInsets = outStableBounds.height() >= outStableBounds.width()
? ORIENTATION_PORTRAIT : ORIENTATION_LANDSCAPE;
// If orientation does not match the orientation with insets applied, then a
@@ -9081,7 +9066,7 @@
getResolvedOverrideConfiguration().windowConfiguration.getBounds();
final int stableBoundsOrientation = stableBounds.width() > stableBounds.height()
? ORIENTATION_LANDSCAPE : ORIENTATION_PORTRAIT;
- final int parentOrientation = mResolveConfigHint.mUseOverrideInsetsForStableBounds
+ final int parentOrientation = mResolveConfigHint.mUseOverrideInsetsForConfig
? stableBoundsOrientation : newParentConfig.orientation;
// If the activity requires a different orientation (either by override or activityInfo),
@@ -9106,7 +9091,7 @@
return;
}
- final Rect parentAppBounds = mResolveConfigHint.mUseOverrideInsetsForStableBounds
+ final Rect parentAppBounds = mResolveConfigHint.mUseOverrideInsetsForConfig
? outNonDecorBounds : newParentConfig.windowConfiguration.getAppBounds();
// TODO(b/182268157): Explore using only one type of parentBoundsWithInsets, either app
// bounds or stable bounds to unify aspect ratio logic.
@@ -10032,7 +10017,7 @@
} else {
scheduleConfigurationChanged(newMergedOverrideConfig, newActivityWindowInfo);
}
- notifyDisplayCompatPolicyAboutConfigurationChange(
+ notifyActivityRefresherAboutConfigurationChange(
mLastReportedConfiguration.getMergedConfiguration(), mTmpConfig);
return true;
}
@@ -10099,18 +10084,18 @@
} else {
scheduleConfigurationChanged(newMergedOverrideConfig, newActivityWindowInfo);
}
- notifyDisplayCompatPolicyAboutConfigurationChange(
+ notifyActivityRefresherAboutConfigurationChange(
mLastReportedConfiguration.getMergedConfiguration(), mTmpConfig);
return true;
}
- private void notifyDisplayCompatPolicyAboutConfigurationChange(
+ private void notifyActivityRefresherAboutConfigurationChange(
Configuration newConfig, Configuration lastReportedConfig) {
- if (mDisplayContent.mDisplayRotationCompatPolicy == null
+ if (mDisplayContent.mActivityRefresher == null
|| !shouldBeResumed(/* activeActivity */ null)) {
return;
}
- mDisplayContent.mDisplayRotationCompatPolicy.onActivityConfigurationChanging(
+ mDisplayContent.mActivityRefresher.onActivityConfigurationChanging(
this, newConfig, lastReportedConfig);
}
@@ -11111,7 +11096,7 @@
* Otherwise, return the creation time of the top window.
*/
long getLastWindowCreateTime() {
- final WindowState window = getWindow(win -> true);
+ final WindowState window = getWindow(alwaysTruePredicate());
return window != null && window.mAttrs.type != TYPE_BASE_APPLICATION
? window.getCreateTime()
: createTime;
diff --git a/services/core/java/com/android/server/wm/ActivityRefresher.java b/services/core/java/com/android/server/wm/ActivityRefresher.java
new file mode 100644
index 0000000..23a9708
--- /dev/null
+++ b/services/core/java/com/android/server/wm/ActivityRefresher.java
@@ -0,0 +1,131 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.server.wm;
+
+import static android.app.servertransaction.ActivityLifecycleItem.ON_PAUSE;
+import static android.app.servertransaction.ActivityLifecycleItem.ON_STOP;
+
+import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_STATES;
+
+import android.annotation.NonNull;
+import android.app.servertransaction.RefreshCallbackItem;
+import android.app.servertransaction.ResumeActivityItem;
+import android.content.res.Configuration;
+import android.os.Handler;
+import android.os.RemoteException;
+
+import com.android.internal.protolog.common.ProtoLog;
+import com.android.internal.util.ArrayUtils;
+
+import java.util.ArrayList;
+
+/**
+ * Class that refreshes the activity (through stop/pause -> resume) based on configuration change.
+ *
+ * <p>This class queries all of its {@link Evaluator}s and restarts the activity if any of them
+ * return {@code true} in {@link Evaluator#shouldRefreshActivity}. {@link ActivityRefresher} cycles
+ * through either stop or pause and then resume, based on the global config and per-app override.
+ */
+class ActivityRefresher {
+ // Delay for ensuring that onActivityRefreshed is always called after an activity refresh. The
+ // client process may not always report the event back to the server, such as process is
+ // crashed or got killed.
+ private static final long REFRESH_CALLBACK_TIMEOUT_MS = 2000L;
+
+ @NonNull private final WindowManagerService mWmService;
+ @NonNull private final Handler mHandler;
+ @NonNull private final ArrayList<Evaluator> mEvaluators = new ArrayList<>();
+
+ ActivityRefresher(@NonNull WindowManagerService wmService, @NonNull Handler handler) {
+ mWmService = wmService;
+ mHandler = handler;
+ }
+
+ void addEvaluator(@NonNull Evaluator evaluator) {
+ mEvaluators.add(evaluator);
+ }
+
+ void removeEvaluator(@NonNull Evaluator evaluator) {
+ mEvaluators.remove(evaluator);
+ }
+
+ /**
+ * "Refreshes" activity by going through "stopped -> resumed" or "paused -> resumed" cycle.
+ * This allows to clear cached values in apps (e.g. display or camera rotation) that influence
+ * camera preview and can lead to sideways or stretching issues persisting even after force
+ * rotation.
+ */
+ void onActivityConfigurationChanging(@NonNull ActivityRecord activity,
+ @NonNull Configuration newConfig, @NonNull Configuration lastReportedConfig) {
+ if (!shouldRefreshActivity(activity, newConfig, lastReportedConfig)) {
+ return;
+ }
+
+ final boolean cycleThroughStop =
+ mWmService.mLetterboxConfiguration
+ .isCameraCompatRefreshCycleThroughStopEnabled()
+ && !activity.mLetterboxUiController
+ .shouldRefreshActivityViaPauseForCameraCompat();
+
+ activity.mLetterboxUiController.setIsRefreshRequested(true);
+ ProtoLog.v(WM_DEBUG_STATES,
+ "Refreshing activity for freeform camera compatibility treatment, "
+ + "activityRecord=%s", activity);
+ final RefreshCallbackItem refreshCallbackItem = RefreshCallbackItem.obtain(
+ activity.token, cycleThroughStop ? ON_STOP : ON_PAUSE);
+ final ResumeActivityItem resumeActivityItem = ResumeActivityItem.obtain(
+ activity.token, /* isForward */ false, /* shouldSendCompatFakeFocus */ false);
+ try {
+ activity.mAtmService.getLifecycleManager().scheduleTransactionAndLifecycleItems(
+ activity.app.getThread(), refreshCallbackItem, resumeActivityItem);
+ mHandler.postDelayed(() -> {
+ synchronized (mWmService.mGlobalLock) {
+ onActivityRefreshed(activity);
+ }
+ }, REFRESH_CALLBACK_TIMEOUT_MS);
+ } catch (RemoteException e) {
+ activity.mLetterboxUiController.setIsRefreshRequested(false);
+ }
+ }
+
+ boolean isActivityRefreshing(@NonNull ActivityRecord activity) {
+ return activity.mLetterboxUiController.isRefreshRequested();
+ }
+
+ void onActivityRefreshed(@NonNull ActivityRecord activity) {
+ // TODO(b/333060789): can we tell that refresh did not happen by observing the activity
+ // state?
+ activity.mLetterboxUiController.setIsRefreshRequested(false);
+ }
+
+ private boolean shouldRefreshActivity(@NonNull ActivityRecord activity,
+ @NonNull Configuration newConfig, @NonNull Configuration lastReportedConfig) {
+ return mWmService.mLetterboxConfiguration.isCameraCompatRefreshEnabled()
+ && activity.mLetterboxUiController.shouldRefreshActivityForCameraCompat()
+ && ArrayUtils.find(mEvaluators.toArray(), evaluator ->
+ ((Evaluator) evaluator)
+ .shouldRefreshActivity(activity, newConfig, lastReportedConfig)) != null;
+ }
+
+ /**
+ * Interface for classes that would like to refresh the recently updated activity, based on the
+ * configuration change.
+ */
+ interface Evaluator {
+ boolean shouldRefreshActivity(@NonNull ActivityRecord activity,
+ @NonNull Configuration newConfig, @NonNull Configuration lastReportedConfig);
+ }
+}
diff --git a/services/core/java/com/android/server/wm/ActivitySnapshotCache.java b/services/core/java/com/android/server/wm/ActivitySnapshotCache.java
index 3609837..ed07afd 100644
--- a/services/core/java/com/android/server/wm/ActivitySnapshotCache.java
+++ b/services/core/java/com/android/server/wm/ActivitySnapshotCache.java
@@ -30,10 +30,12 @@
@Override
void putSnapshot(ActivityRecord ar, TaskSnapshot snapshot) {
final int hasCode = System.identityHashCode(ar);
+ snapshot.addReference(TaskSnapshot.REFERENCE_CACHE);
synchronized (mLock) {
final CacheEntry entry = mRunningCache.get(hasCode);
if (entry != null) {
mAppIdMap.remove(entry.topApp);
+ entry.snapshot.removeReference(TaskSnapshot.REFERENCE_CACHE);
}
mAppIdMap.put(ar, hasCode);
mRunningCache.put(hasCode, new CacheEntry(snapshot, ar));
diff --git a/services/core/java/com/android/server/wm/ActivityStarter.java b/services/core/java/com/android/server/wm/ActivityStarter.java
index 3303367..72b854b 100644
--- a/services/core/java/com/android/server/wm/ActivityStarter.java
+++ b/services/core/java/com/android/server/wm/ActivityStarter.java
@@ -2113,7 +2113,6 @@
if (hostTask == null || targetTask != hostTask) {
return EMBEDDING_DISALLOWED_NEW_TASK;
}
-
return taskFragment.isAllowedToEmbedActivity(starting);
}
@@ -2733,7 +2732,7 @@
// If a target task is specified, try to reuse that one
if (mOptions != null && mOptions.getLaunchTaskId() != INVALID_TASK_ID) {
Task launchTask = mRootWindowContainer.anyTaskForId(mOptions.getLaunchTaskId());
- if (launchTask != null) {
+ if (launchTask != null && launchTask.isLeafTask()) {
return launchTask;
}
return null;
@@ -2961,23 +2960,9 @@
sendCanNotEmbedActivityError(mInTaskFragment, embeddingCheckResult);
}
} else {
- TaskFragment candidateTf = mAddingToTaskFragment != null ? mAddingToTaskFragment : null;
+ TaskFragment candidateTf = mAddingToTaskFragment;
if (candidateTf == null) {
- // Puts the activity on the top-most non-isolated navigation TF, unless the
- // activity is launched from the same TF.
- final TaskFragment sourceTaskFragment =
- mSourceRecord != null ? mSourceRecord.getTaskFragment() : null;
- final ActivityRecord top = task.getActivity(r -> {
- if (!r.canBeTopRunning()) {
- return false;
- }
- final TaskFragment taskFragment = r.getTaskFragment();
- return !taskFragment.isIsolatedNav() || (sourceTaskFragment != null
- && sourceTaskFragment == taskFragment);
- });
- if (top != null) {
- candidateTf = top.getTaskFragment();
- }
+ candidateTf = findCandidateTaskFragment(task);
}
if (candidateTf != null && candidateTf.isEmbedded()
&& canEmbedActivity(candidateTf, mStartActivity, task) == EMBEDDING_ALLOWED) {
@@ -2995,6 +2980,50 @@
}
/**
+ * Finds a candidate TaskFragment in {@code task} to launch activity, or returns {@code null}
+ * if there's no such a TaskFragment.
+ */
+ @Nullable
+ private TaskFragment findCandidateTaskFragment(@NonNull Task task) {
+ final TaskFragment sourceTaskFragment =
+ mSourceRecord != null ? mSourceRecord.getTaskFragment() : null;
+ for (int i = task.getChildCount() - 1; i >= 0; --i) {
+ final WindowContainer<?> wc = task.getChildAt(i);
+ final ActivityRecord activity = wc.asActivityRecord();
+ if (activity != null) {
+ if (activity.finishing) {
+ continue;
+ }
+ // Early return if the top child is an Activity.
+ return null;
+ }
+ final TaskFragment taskFragment = wc.asTaskFragment();
+ if (taskFragment == null || taskFragment.isRemovalRequested()) {
+ // Skip if the TaskFragment is going to be finished.
+ continue;
+ }
+ if (taskFragment.getActivity(ActivityRecord::canBeTopRunning) == null) {
+ // Skip if there's no activity in this TF can be top running.
+ continue;
+ }
+ if (taskFragment.isIsolatedNav()) {
+ // Stop here if we reach an isolated navigated TF.
+ return null;
+ }
+ if (sourceTaskFragment != null && sourceTaskFragment == taskFragment) {
+ // Choose the taskFragment launched from even if it's pinned.
+ return taskFragment;
+ }
+ if (taskFragment.isPinned()) {
+ // Skip the pinned TaskFragment.
+ continue;
+ }
+ return taskFragment;
+ }
+ return null;
+ }
+
+ /**
* Notifies the client side that {@link #mStartActivity} cannot be embedded to
* {@code taskFragment}.
*/
diff --git a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
index 237003a..3aa63af 100644
--- a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
+++ b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
@@ -7418,7 +7418,8 @@
FEATURE_LEANBACK);
final boolean isArc = arcFeature != null && arcFeature.version >= 0;
final boolean isTv = tvFeature != null && tvFeature.version >= 0;
- sIsPip2ExperimentEnabled = SystemProperties.getBoolean("wm_shell.pip2", false)
+ sIsPip2ExperimentEnabled = SystemProperties.getBoolean(
+ "persist.wm_shell.pip2", false)
|| (Flags.enablePip2Implementation() && !isArc && !isTv);
}
return sIsPip2ExperimentEnabled;
diff --git a/services/core/java/com/android/server/wm/BackNavigationController.java b/services/core/java/com/android/server/wm/BackNavigationController.java
index c9703d8..0e4f033 100644
--- a/services/core/java/com/android/server/wm/BackNavigationController.java
+++ b/services/core/java/com/android/server/wm/BackNavigationController.java
@@ -1732,7 +1732,10 @@
// The activity was detached from hierarchy.
return;
}
- activity.mDisplayContent.continueUpdateOrientationForDiffOrienLaunchingApp();
+
+ if (activity.mDisplayContent.isFixedRotationLaunchingApp(activity)) {
+ activity.mDisplayContent.continueUpdateOrientationForDiffOrienLaunchingApp();
+ }
// Restore the launch-behind state.
activity.mTaskSupervisor.scheduleLaunchTaskBehindComplete(activity.token);
diff --git a/services/core/java/com/android/server/wm/BackgroundActivityStartController.java b/services/core/java/com/android/server/wm/BackgroundActivityStartController.java
index eb1f3b4..f7910b0 100644
--- a/services/core/java/com/android/server/wm/BackgroundActivityStartController.java
+++ b/services/core/java/com/android/server/wm/BackgroundActivityStartController.java
@@ -20,6 +20,7 @@
import static android.app.ActivityManager.PROCESS_STATE_NONEXISTENT;
import static android.app.ActivityOptions.BackgroundActivityStartMode;
import static android.app.ActivityOptions.MODE_BACKGROUND_ACTIVITY_START_ALLOWED;
+import static android.app.ActivityOptions.MODE_BACKGROUND_ACTIVITY_START_COMPAT;
import static android.app.ActivityOptions.MODE_BACKGROUND_ACTIVITY_START_DENIED;
import static android.app.ActivityOptions.MODE_BACKGROUND_ACTIVITY_START_SYSTEM_DEFINED;
import static android.content.Intent.FLAG_ACTIVITY_NEW_TASK;
@@ -105,6 +106,7 @@
static final String AUTO_OPT_IN_NOT_PENDING_INTENT = "notPendingIntent";
static final String AUTO_OPT_IN_CALL_FOR_RESULT = "callForResult";
static final String AUTO_OPT_IN_SAME_UID = "sameUid";
+ static final String AUTO_OPT_IN_COMPAT = "compatibility";
/** If enabled the creator will not allow BAL on its behalf by default. */
@ChangeId
@@ -303,6 +305,10 @@
} else if (callingUid == realCallingUid && !balRequireOptInSameUid()) {
mAutoOptInReason = AUTO_OPT_IN_SAME_UID;
mAutoOptInCaller = false;
+ } else if (realCallerBackgroundActivityStartMode
+ == MODE_BACKGROUND_ACTIVITY_START_COMPAT) {
+ mAutoOptInReason = AUTO_OPT_IN_COMPAT;
+ mAutoOptInCaller = false;
} else {
mAutoOptInReason = null;
mAutoOptInCaller = false;
@@ -1695,6 +1701,7 @@
return false;
}
if (state.mBalAllowedByPiSender.allowsBackgroundActivityStarts()
+ && state.mResultForRealCaller != null
&& state.mResultForRealCaller.getRawCode() == BAL_ALLOW_VISIBLE_WINDOW) {
return false;
}
diff --git a/services/core/java/com/android/server/wm/DisplayContent.java b/services/core/java/com/android/server/wm/DisplayContent.java
index c9a5e71..e49cb38 100644
--- a/services/core/java/com/android/server/wm/DisplayContent.java
+++ b/services/core/java/com/android/server/wm/DisplayContent.java
@@ -478,6 +478,8 @@
final DisplayRotationCompatPolicy mDisplayRotationCompatPolicy;
@Nullable
final CameraStateMonitor mCameraStateMonitor;
+ @Nullable
+ final ActivityRefresher mActivityRefresher;
DisplayFrames mDisplayFrames;
final DisplayUpdater mDisplayUpdater;
@@ -1233,13 +1235,15 @@
mWmService.mLetterboxConfiguration.isCameraCompatTreatmentEnabledAtBuildTime();
if (shouldCreateDisplayRotationCompatPolicy) {
mCameraStateMonitor = new CameraStateMonitor(this, mWmService.mH);
+ mActivityRefresher = new ActivityRefresher(mWmService, mWmService.mH);
mDisplayRotationCompatPolicy = new DisplayRotationCompatPolicy(
- this, mWmService.mH, mCameraStateMonitor);
+ this, mCameraStateMonitor, mActivityRefresher);
mCameraStateMonitor.startListeningToCameraState();
} else {
// These are to satisfy the `final` check.
mCameraStateMonitor = null;
+ mActivityRefresher = null;
mDisplayRotationCompatPolicy = null;
}
@@ -2755,7 +2759,7 @@
@Nullable
Task getTopRootTask() {
- return getRootTask(t -> true);
+ return getRootTask(alwaysTruePredicate());
}
/**
@@ -5009,7 +5013,7 @@
// This should be called after the insets have been dispatched to clients and we have
// committed finish drawing windows.
- mInsetsStateController.getImeSourceProvider().checkShowImePostLayout();
+ mInsetsStateController.getImeSourceProvider().checkAndStartShowImePostLayout();
mLastHasContent = mTmpApplySurfaceChangesTransactionState.displayHasContent;
if (!inTransition() && !mDisplayRotation.isRotatingSeamlessly()) {
diff --git a/services/core/java/com/android/server/wm/DisplayRotationCompatPolicy.java b/services/core/java/com/android/server/wm/DisplayRotationCompatPolicy.java
index eacf9a3..e0cc064 100644
--- a/services/core/java/com/android/server/wm/DisplayRotationCompatPolicy.java
+++ b/services/core/java/com/android/server/wm/DisplayRotationCompatPolicy.java
@@ -18,8 +18,6 @@
import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
import static android.app.WindowConfiguration.WINDOWING_MODE_MULTI_WINDOW;
-import static android.app.servertransaction.ActivityLifecycleItem.ON_PAUSE;
-import static android.app.servertransaction.ActivityLifecycleItem.ON_STOP;
import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE;
import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_LOCKED;
import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_NOSENSOR;
@@ -32,19 +30,14 @@
import static android.view.Display.TYPE_INTERNAL;
import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_ORIENTATION;
-import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_STATES;
import static com.android.server.wm.DisplayRotationReversionController.REVERSION_TYPE_CAMERA_COMPAT;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.StringRes;
-import android.app.servertransaction.RefreshCallbackItem;
-import android.app.servertransaction.ResumeActivityItem;
import android.content.pm.ActivityInfo.ScreenOrientation;
import android.content.pm.PackageManager;
import android.content.res.Configuration;
-import android.os.Handler;
-import android.os.RemoteException;
import android.widget.Toast;
import com.android.internal.R;
@@ -64,48 +57,38 @@
* R.bool.config_isWindowManagerCameraCompatTreatmentEnabled} is {@code true}.
*/
// TODO(b/261444714): Consider moving Camera-specific logic outside of the WM Core path
-class DisplayRotationCompatPolicy implements CameraStateMonitor.CameraCompatStateListener {
+final class DisplayRotationCompatPolicy implements CameraStateMonitor.CameraCompatStateListener,
+ ActivityRefresher.Evaluator {
- // Delay for updating display rotation after Camera connection is closed. Needed to avoid
- // rotation flickering when an app is flipping between front and rear cameras or when size
- // compat mode is restarted.
- // TODO(b/263114289): Consider associating this delay with a specific activity so that if
- // the new non-camera activity started on top of the camer one we can rotate faster.
- private static final int CAMERA_CLOSED_ROTATION_UPDATE_DELAY_MS = 2000;
- // Delay for updating display rotation after Camera connection is opened. This delay is
- // selected to be long enough to avoid conflicts with transitions on the app's side.
- // Using a delay < CAMERA_CLOSED_ROTATION_UPDATE_DELAY_MS to avoid flickering when an app
- // is flipping between front and rear cameras (in case requested orientation changes at
- // runtime at the same time) or when size compat mode is restarted.
- private static final int CAMERA_OPENED_ROTATION_UPDATE_DELAY_MS =
- CAMERA_CLOSED_ROTATION_UPDATE_DELAY_MS / 2;
- // Delay for ensuring that onActivityRefreshed is always called after an activity refresh. The
- // client process may not always report the event back to the server, such as process is
- // crashed or got killed.
- private static final int REFRESH_CALLBACK_TIMEOUT_MS = 2000;
-
+ @NonNull
private final DisplayContent mDisplayContent;
+ @NonNull
private final WindowManagerService mWmService;
+ @NonNull
private final CameraStateMonitor mCameraStateMonitor;
- private final Handler mHandler;
+ @NonNull
+ private final ActivityRefresher mActivityRefresher;
@ScreenOrientation
private int mLastReportedOrientation = SCREEN_ORIENTATION_UNSET;
- DisplayRotationCompatPolicy(@NonNull DisplayContent displayContent, Handler handler,
- @NonNull CameraStateMonitor cameraStateMonitor) {
+ DisplayRotationCompatPolicy(@NonNull DisplayContent displayContent,
+ @NonNull CameraStateMonitor cameraStateMonitor,
+ @NonNull ActivityRefresher activityRefresher) {
// This constructor is called from DisplayContent constructor. Don't use any fields in
// DisplayContent here since they aren't guaranteed to be set.
- mHandler = handler;
mDisplayContent = displayContent;
mWmService = displayContent.mWmService;
mCameraStateMonitor = cameraStateMonitor;
mCameraStateMonitor.addCameraStateListener(this);
+ mActivityRefresher = activityRefresher;
+ mActivityRefresher.addEvaluator(this);
}
/** Releases camera state listener. */
void dispose() {
mCameraStateMonitor.removeCameraStateListener(this);
+ mActivityRefresher.removeEvaluator(this);
}
/**
@@ -169,47 +152,6 @@
}
/**
- * "Refreshes" activity by going through "stopped -> resumed" or "paused -> resumed" cycle.
- * This allows to clear cached values in apps (e.g. display or camera rotation) that influence
- * camera preview and can lead to sideways or stretching issues persisting even after force
- * rotation.
- */
- void onActivityConfigurationChanging(ActivityRecord activity, Configuration newConfig,
- Configuration lastReportedConfig) {
- if (!isTreatmentEnabledForDisplay()
- || !mWmService.mLetterboxConfiguration.isCameraCompatRefreshEnabled()
- || !shouldRefreshActivity(activity, newConfig, lastReportedConfig)) {
- return;
- }
- boolean cycleThroughStop =
- mWmService.mLetterboxConfiguration
- .isCameraCompatRefreshCycleThroughStopEnabled()
- && !activity.mLetterboxUiController
- .shouldRefreshActivityViaPauseForCameraCompat();
- try {
- activity.mLetterboxUiController.setIsRefreshAfterRotationRequested(true);
- ProtoLog.v(WM_DEBUG_STATES,
- "Refreshing activity for camera compatibility treatment, "
- + "activityRecord=%s", activity);
- final RefreshCallbackItem refreshCallbackItem = RefreshCallbackItem.obtain(
- activity.token, cycleThroughStop ? ON_STOP : ON_PAUSE);
- final ResumeActivityItem resumeActivityItem = ResumeActivityItem.obtain(
- activity.token, /* isForward */ false, /* shouldSendCompatFakeFocus */ false);
- activity.mAtmService.getLifecycleManager().scheduleTransactionAndLifecycleItems(
- activity.app.getThread(), refreshCallbackItem, resumeActivityItem);
- mHandler.postDelayed(
- () -> onActivityRefreshed(activity),
- REFRESH_CALLBACK_TIMEOUT_MS);
- } catch (RemoteException e) {
- activity.mLetterboxUiController.setIsRefreshAfterRotationRequested(false);
- }
- }
-
- void onActivityRefreshed(@NonNull ActivityRecord activity) {
- activity.mLetterboxUiController.setIsRefreshAfterRotationRequested(false);
- }
-
- /**
* Notifies that animation in {@link ScreenRotationAnimation} has finished.
*
* <p>This class uses this signal as a trigger for notifying the user about forced rotation
@@ -276,14 +218,16 @@
// Refreshing only when configuration changes after rotation or camera split screen aspect ratio
// treatment is enabled
- private boolean shouldRefreshActivity(ActivityRecord activity, Configuration newConfig,
- Configuration lastReportedConfig) {
+ @Override
+ public boolean shouldRefreshActivity(@NonNull ActivityRecord activity,
+ @NonNull Configuration newConfig, @NonNull Configuration lastReportedConfig) {
final boolean displayRotationChanged = (newConfig.windowConfiguration.getDisplayRotation()
!= lastReportedConfig.windowConfiguration.getDisplayRotation());
- return (displayRotationChanged
- || activity.mLetterboxUiController.isCameraCompatSplitScreenAspectRatioAllowed())
+ return isTreatmentEnabledForDisplay()
&& isTreatmentEnabledForActivity(activity)
- && activity.mLetterboxUiController.shouldRefreshActivityForCameraCompat();
+ && activity.mLetterboxUiController.shouldRefreshActivityForCameraCompat()
+ && (displayRotationChanged
+ || activity.mLetterboxUiController.isCameraCompatSplitScreenAspectRatioAllowed());
}
/**
@@ -310,7 +254,6 @@
&& activity.mLetterboxUiController.shouldForceRotateForCameraCompat();
}
-
/**
* Whether camera compat treatment is applicable for the given activity.
*
@@ -429,6 +372,6 @@
|| !mCameraStateMonitor.isCameraWithIdRunningForActivity(topActivity, cameraId)) {
return false;
}
- return topActivity.mLetterboxUiController.isRefreshAfterRotationRequested();
+ return mActivityRefresher.isActivityRefreshing(topActivity);
}
}
diff --git a/services/core/java/com/android/server/wm/ImeInsetsSourceProvider.java b/services/core/java/com/android/server/wm/ImeInsetsSourceProvider.java
index 092ff3d..e03ff688 100644
--- a/services/core/java/com/android/server/wm/ImeInsetsSourceProvider.java
+++ b/services/core/java/com/android/server/wm/ImeInsetsSourceProvider.java
@@ -24,7 +24,6 @@
import static com.android.server.wm.DisplayContent.IME_TARGET_LAYERING;
import static com.android.server.wm.ImeInsetsSourceProviderProto.IME_TARGET_FROM_IME;
import static com.android.server.wm.ImeInsetsSourceProviderProto.INSETS_SOURCE_PROVIDER;
-import static com.android.server.wm.ImeInsetsSourceProviderProto.IS_IME_LAYOUT_DRAWN;
import static com.android.server.wm.WindowManagerService.H.UPDATE_MULTI_WINDOW_STACKS;
import android.annotation.NonNull;
@@ -52,19 +51,26 @@
private static final String TAG = ImeInsetsSourceProvider.class.getSimpleName();
- /** The token tracking the current IME request or {@code null} otherwise. */
+ /** The token tracking the show IME request, non-null only while a show request is pending. */
@Nullable
- private ImeTracker.Token mImeRequesterStatsToken;
+ private ImeTracker.Token mStatsToken;
+ /** The target that requested to show the IME, non-null only while a show request is pending. */
+ @Nullable
private InsetsControlTarget mImeRequester;
- private Runnable mShowImeRunner;
- private boolean mIsImeLayoutDrawn;
+ /** @see #isImeShowing() */
private boolean mImeShowing;
+ /** The latest received insets source. */
private final InsetsSource mLastSource = new InsetsSource(ID_IME, WindowInsets.Type.ime());
/** @see #setFrozen(boolean) */
private boolean mFrozen;
- /** @see #setServerVisible(boolean) */
+ /**
+ * The server visibility of the source provider's window container. This is out of sync with
+ * {@link InsetsSourceProvider#mServerVisible} while {@link #mFrozen} is {@code true}.
+ *
+ * @see #setServerVisible
+ */
private boolean mServerVisible;
ImeInsetsSourceProvider(@NonNull InsetsSource source,
@@ -73,6 +79,7 @@
super(source, stateController, displayContent);
}
+ @Nullable
@Override
InsetsSourceControl getControl(InsetsControlTarget target) {
final InsetsSourceControl control = super.getControl(target);
@@ -124,9 +131,9 @@
/**
* Freeze IME insets source state when required.
*
- * When setting {@param frozen} as {@code true}, the IME insets provider will freeze the
+ * <p>When setting {@param frozen} as {@code true}, the IME insets provider will freeze the
* current IME insets state and pending the IME insets state update until setting
- * {@param frozen} as {@code false}.
+ * {@param frozen} as {@code false}.</p>
*/
void setFrozen(boolean frozen) {
if (mFrozen == frozen) {
@@ -223,27 +230,29 @@
/**
* Called from {@link WindowManagerInternal#showImePostLayout}
* when {@link android.inputmethodservice.InputMethodService} requests to show IME
- * on {@param imeTarget}.
+ * on the given control target.
*
- * @param imeTarget imeTarget on which IME request is coming from.
+ * @param imeTarget the control target on which the IME request is coming from.
* @param statsToken the token tracking the current IME request.
*/
- void scheduleShowImePostLayout(InsetsControlTarget imeTarget,
+ void scheduleShowImePostLayout(@NonNull InsetsControlTarget imeTarget,
@NonNull ImeTracker.Token statsToken) {
- if (mImeRequesterStatsToken != null) {
- // Cancel the pre-existing stats token, if any.
- // Log state on pre-existing request cancel.
- logShowImePostLayoutState(false /* aborted */);
- ImeTracker.forLogging().onCancelled(
- mImeRequesterStatsToken, ImeTracker.PHASE_WM_SHOW_IME_RUNNER);
+ if (mImeRequester == null) {
+ // Start tracing only on initial scheduled show IME request, to record end-to-end time.
+ Trace.asyncTraceBegin(TRACE_TAG_WINDOW_MANAGER, "WMS.showImePostLayout", 0);
+ } else {
+ // We already have a scheduled show IME request, cancel the previous statsToken and
+ // continue with the new one.
+ logIsScheduledAndReadyToShowIme(false /* aborted */);
+ ImeTracker.forLogging().onCancelled(mStatsToken, ImeTracker.PHASE_WM_SHOW_IME_RUNNER);
}
- mImeRequesterStatsToken = statsToken;
- boolean targetChanged = isTargetChangedWithinActivity(imeTarget);
+ final boolean targetChanged = isTargetChangedWithinActivity(imeTarget);
mImeRequester = imeTarget;
+ mStatsToken = statsToken;
if (targetChanged) {
// target changed, check if new target can show IME.
ProtoLog.d(WM_DEBUG_IME, "IME target changed within ActivityRecord");
- checkShowImePostLayout();
+ checkAndStartShowImePostLayout();
// if IME cannot be shown at this time, it is scheduled to be shown.
// once window that called IMM.showSoftInput() and DisplayContent's ImeTarget match,
// it will be shown.
@@ -252,79 +261,58 @@
ProtoLog.d(WM_DEBUG_IME, "Schedule IME show for %s", mImeRequester.getWindow() == null
? mImeRequester : mImeRequester.getWindow().getName());
- mShowImeRunner = () -> {
- ImeTracker.forLogging().onProgress(mImeRequesterStatsToken,
- ImeTracker.PHASE_WM_SHOW_IME_RUNNER);
- ProtoLog.d(WM_DEBUG_IME, "Run showImeRunner");
- // Target should still be the same.
- if (isReadyToShowIme()) {
- ImeTracker.forLogging().onProgress(mImeRequesterStatsToken,
- ImeTracker.PHASE_WM_SHOW_IME_READY);
- final InsetsControlTarget target = mDisplayContent.getImeTarget(IME_TARGET_CONTROL);
-
- ProtoLog.i(WM_DEBUG_IME, "call showInsets(ime) on %s",
- target.getWindow() != null ? target.getWindow().getName() : "");
- setImeShowing(true);
- target.showInsets(WindowInsets.Type.ime(), true /* fromIme */,
- mImeRequesterStatsToken);
- Trace.asyncTraceEnd(TRACE_TAG_WINDOW_MANAGER, "WMS.showImePostLayout", 0);
- if (target != mImeRequester && mImeRequester != null) {
- ProtoLog.w(WM_DEBUG_IME,
- "showInsets(ime) was requested by different window: %s ",
- (mImeRequester.getWindow() != null
- ? mImeRequester.getWindow().getName() : ""));
- }
- } else {
- ImeTracker.forLogging().onFailed(mImeRequesterStatsToken,
- ImeTracker.PHASE_WM_SHOW_IME_READY);
- }
- // Clear token here so we don't report an error in abortShowImePostLayout().
- mImeRequesterStatsToken = null;
- abortShowImePostLayout();
- };
mDisplayContent.mWmService.requestTraversal();
}
- void checkShowImePostLayout() {
- if (mWindowContainer == null) {
+ /**
+ * Checks whether there is a previously scheduled show IME request and we are ready to show,
+ * in which case also start handling the request.
+ */
+ void checkAndStartShowImePostLayout() {
+ if (!isScheduledAndReadyToShowIme()) {
+ // This can later become ready, so we don't want to cancel the pending request here.
return;
}
- WindowState windowState = mWindowContainer.asWindowState();
- if (windowState == null) {
- throw new IllegalArgumentException("IME insets must be provided by a window.");
+
+ ImeTracker.forLogging().onProgress(mStatsToken, ImeTracker.PHASE_WM_SHOW_IME_RUNNER);
+ ProtoLog.d(WM_DEBUG_IME, "Run showImeRunner");
+
+ final InsetsControlTarget target = getControlTarget();
+
+ ProtoLog.i(WM_DEBUG_IME, "call showInsets(ime) on %s",
+ target.getWindow() != null ? target.getWindow().getName() : "");
+ setImeShowing(true);
+ target.showInsets(WindowInsets.Type.ime(), true /* fromIme */, mStatsToken);
+ Trace.asyncTraceEnd(TRACE_TAG_WINDOW_MANAGER, "WMS.showImePostLayout", 0);
+ if (target != mImeRequester) {
+ ProtoLog.w(WM_DEBUG_IME, "showInsets(ime) was requested by different window: %s ",
+ (mImeRequester.getWindow() != null ? mImeRequester.getWindow().getName() : ""));
}
- // check if IME is drawn
- if (mIsImeLayoutDrawn
- || (isReadyToShowIme()
- && windowState.isDrawn()
- && !windowState.mGivenInsetsPending)) {
- mIsImeLayoutDrawn = true;
- // show IME if InputMethodService requested it to be shown.
- if (mShowImeRunner != null) {
- mShowImeRunner.run();
- }
- }
+ resetShowImePostLayout();
}
- /**
- * Abort any pending request to show IME post layout.
- */
+ /** Aborts the previously scheduled show IME request. */
void abortShowImePostLayout() {
- ProtoLog.d(WM_DEBUG_IME, "abortShowImePostLayout");
- if (mImeRequesterStatsToken != null) {
- // Log state on abort.
- logShowImePostLayoutState(true /* aborted */);
- ImeTracker.forLogging().onFailed(
- mImeRequesterStatsToken, ImeTracker.PHASE_WM_ABORT_SHOW_IME_POST_LAYOUT);
- mImeRequesterStatsToken = null;
+ if (mImeRequester == null) {
+ return;
}
- mImeRequester = null;
- mIsImeLayoutDrawn = false;
- mShowImeRunner = null;
+ ProtoLog.d(WM_DEBUG_IME, "abortShowImePostLayout");
+ Trace.asyncTraceEnd(TRACE_TAG_WINDOW_MANAGER, "WMS.showImePostLayout", 0);
+ logIsScheduledAndReadyToShowIme(true /* aborted */);
+ ImeTracker.forLogging().onFailed(
+ mStatsToken, ImeTracker.PHASE_WM_ABORT_SHOW_IME_POST_LAYOUT);
+ resetShowImePostLayout();
}
+ /** Resets the state of the previously scheduled show IME request. */
+ private void resetShowImePostLayout() {
+ mImeRequester = null;
+ mStatsToken = null;
+ }
+
+ /** Checks whether there is a previously scheduled show IME request and we are ready to show. */
@VisibleForTesting
- boolean isReadyToShowIme() {
+ boolean isScheduledAndReadyToShowIme() {
// IMMS#mLastImeTargetWindow always considers focused window as
// IME target, however DisplayContent#computeImeTarget() can compute
// a different IME target.
@@ -334,32 +322,47 @@
// Also, if imeTarget is closing, it would be considered as outdated target.
// TODO(b/139861270): Remove the child & sublayer check once IMMS is aware of
// actual IME target.
+ if (mImeRequester == null) {
+ // No show IME request previously scheduled.
+ return false;
+ }
+ if (!mServerVisible || mFrozen) {
+ // The window container is not available and considered visible.
+ // If frozen, the server visibility is not set until unfrozen.
+ return false;
+ }
+ if (mWindowContainer == null) {
+ // No window container set.
+ return false;
+ }
+ final WindowState windowState = mWindowContainer.asWindowState();
+ if (windowState == null) {
+ throw new IllegalArgumentException("IME insets must be provided by a window.");
+ }
+ if (!windowState.isDrawn() || windowState.mGivenInsetsPending) {
+ // The window is not drawn, or it has pending insets.
+ return false;
+ }
final InsetsControlTarget dcTarget = mDisplayContent.getImeTarget(IME_TARGET_LAYERING);
- if (dcTarget == null || mImeRequester == null) {
- // Not ready to show if there is no IME layering target, or no IME requester.
+ if (dcTarget == null) {
+ // No IME layering target.
return false;
}
final InsetsControlTarget controlTarget = getControlTarget();
if (controlTarget == null) {
- // Not ready to show if there is no IME control target.
+ // No IME control target.
return false;
}
if (controlTarget != mDisplayContent.getImeTarget(IME_TARGET_CONTROL)) {
- // Not ready to show if control target does not match the one in DisplayContent.
- return false;
- }
- if (!mServerVisible || mFrozen) {
- // Not ready to show if the window container is not available and considered visible.
- // If frozen, the server visibility is not set until unfrozen.
+ // The control target does not match the one in DisplayContent.
return false;
}
if (mStateController.hasPendingControls(controlTarget)) {
- // Not ready to show if control target has pending controls.
+ // The control target has pending controls.
return false;
}
if (getLeash(controlTarget) == null) {
- // Not ready to show if control target has no source control leash (or leash is not
- // ready for dispatching).
+ // The control target has no source control leash (or it is not ready for dispatching).
return false;
}
@@ -371,51 +374,44 @@
|| isAboveImeLayeringTarget(mImeRequester, dcTarget)
|| isImeFallbackTarget(mImeRequester)
|| isImeInputTarget(mImeRequester)
- || sameAsImeControlTarget();
+ || sameAsImeControlTarget(mImeRequester);
}
/**
- * Logs the current state required for showImePostLayout to be triggered.
+ * Logs the current state that can be checked by {@link #isScheduledAndReadyToShowIme}.
*
- * @param aborted whether the showImePostLayout was aborted or cancelled.
+ * @param aborted whether the scheduled show IME request was aborted or cancelled.
*/
- private void logShowImePostLayoutState(boolean aborted) {
+ private void logIsScheduledAndReadyToShowIme(boolean aborted) {
final var windowState = mWindowContainer != null ? mWindowContainer.asWindowState() : null;
final var dcTarget = mDisplayContent.getImeTarget(IME_TARGET_LAYERING);
final var controlTarget = getControlTarget();
final var sb = new StringBuilder();
sb.append("showImePostLayout ").append(aborted ? "aborted" : "cancelled");
- sb.append(", mWindowContainer is: ");
- sb.append(mWindowContainer != null ? "non-null" : "null");
+ sb.append(", isScheduledAndReadyToShowIme: ").append(isScheduledAndReadyToShowIme());
+ sb.append(", mImeRequester: ").append(mImeRequester);
+ sb.append(", serverVisible: ").append(mServerVisible);
+ sb.append(", frozen: ").append(mFrozen);
+ sb.append(", mWindowContainer is: ").append(mWindowContainer != null ? "non-null" : "null");
sb.append(", windowState: ").append(windowState);
if (windowState != null) {
- sb.append(", windowState.isDrawn(): ");
- sb.append(windowState.isDrawn());
- sb.append(", windowState.mGivenInsetsPending: ");
- sb.append(windowState.mGivenInsetsPending);
+ sb.append(", isDrawn: ").append(windowState.isDrawn());
+ sb.append(", mGivenInsetsPending: ").append(windowState.mGivenInsetsPending);
}
- sb.append(", mIsImeLayoutDrawn: ").append(mIsImeLayoutDrawn);
- sb.append(", mShowImeRunner: ").append(mShowImeRunner);
- sb.append(", mImeRequester: ").append(mImeRequester);
sb.append(", dcTarget: ").append(dcTarget);
sb.append(", controlTarget: ").append(controlTarget);
- sb.append("\n");
- sb.append("isReadyToShowIme(): ").append(isReadyToShowIme());
if (mImeRequester != null && dcTarget != null && controlTarget != null) {
- sb.append(", controlTarget == DisplayContent.controlTarget: ");
+ sb.append("\n");
+ sb.append("controlTarget == DisplayContent.controlTarget: ");
sb.append(controlTarget == mDisplayContent.getImeTarget(IME_TARGET_CONTROL));
sb.append(", hasPendingControls: ");
sb.append(mStateController.hasPendingControls(controlTarget));
- sb.append(", serverVisible: ");
- sb.append(mServerVisible);
- sb.append(", frozen: ");
- sb.append(mFrozen);
- sb.append(", leash is: ");
- sb.append(getLeash(controlTarget) != null ? "non-null" : "null");
- sb.append(", control is: ");
- sb.append(mControl != null ? "non-null" : "null");
- sb.append(", mIsLeashReadyForDispatching: ");
- sb.append(mIsLeashReadyForDispatching);
+ final boolean hasLeash = getLeash(controlTarget) != null;
+ sb.append(", leash is: ").append(hasLeash ? "non-null" : "null");
+ if (!hasLeash) {
+ sb.append(", control is: ").append(mControl != null ? "non-null" : "null");
+ sb.append(", mIsLeashReadyForDispatching: ").append(mIsLeashReadyForDispatching);
+ }
sb.append(", isImeLayeringTarget: ");
sb.append(isImeLayeringTarget(mImeRequester, dcTarget));
sb.append(", isAboveImeLayeringTarget: ");
@@ -425,7 +421,7 @@
sb.append(", isImeInputTarget: ");
sb.append(isImeInputTarget(mImeRequester));
sb.append(", sameAsImeControlTarget: ");
- sb.append(sameAsImeControlTarget());
+ sb.append(sameAsImeControlTarget(mImeRequester));
}
Slog.d(TAG, sb.toString());
}
@@ -445,19 +441,18 @@
&& dcTarget.getWindow().mSubLayer > target.getWindow().mSubLayer;
}
- private boolean isImeFallbackTarget(InsetsControlTarget target) {
+ private boolean isImeFallbackTarget(@NonNull InsetsControlTarget target) {
return target == mDisplayContent.getImeFallback();
}
- private boolean isImeInputTarget(InsetsControlTarget target) {
+ private boolean isImeInputTarget(@NonNull InsetsControlTarget target) {
return target == mDisplayContent.getImeInputTarget();
}
- private boolean sameAsImeControlTarget() {
- final InsetsControlTarget target = mDisplayContent.getImeTarget(IME_TARGET_CONTROL);
- return target == mImeRequester
- && (mImeRequester.getWindow() == null
- || !isImeTargetWindowClosing(mImeRequester.getWindow()));
+ private boolean sameAsImeControlTarget(@NonNull InsetsControlTarget target) {
+ final InsetsControlTarget controlTarget = getControlTarget();
+ return controlTarget == target
+ && (target.getWindow() == null || !isImeTargetWindowClosing(target.getWindow()));
}
private static boolean isImeTargetWindowClosing(@NonNull WindowState win) {
@@ -467,16 +462,15 @@
|| win.mActivityRecord.willCloseOrEnterPip());
}
- private boolean isTargetChangedWithinActivity(InsetsControlTarget target) {
+ private boolean isTargetChangedWithinActivity(@NonNull InsetsControlTarget target) {
// We don't consider the target out of the activity.
- if (target == null || target.getWindow() == null) {
+ if (target.getWindow() == null) {
return false;
}
return mImeRequester != target
- && mImeRequester != null && mShowImeRunner != null
+ && mImeRequester != null
&& mImeRequester.getWindow() != null
- && mImeRequester.getWindow().mActivityRecord
- == target.getWindow().mActivityRecord;
+ && mImeRequester.getWindow().mActivityRecord == target.getWindow().mActivityRecord;
}
// ---------------------------------------------------------------------------------------
@@ -509,7 +503,6 @@
if (imeRequesterWindow != null) {
imeRequesterWindow.dumpDebug(proto, IME_TARGET_FROM_IME, logLevel);
}
- proto.write(IS_IME_LAYOUT_DRAWN, mIsImeLayoutDrawn);
proto.end(token);
}
diff --git a/services/core/java/com/android/server/wm/InsetsSourceProvider.java b/services/core/java/com/android/server/wm/InsetsSourceProvider.java
index 4400ed2..2288fe9 100644
--- a/services/core/java/com/android/server/wm/InsetsSourceProvider.java
+++ b/services/core/java/com/android/server/wm/InsetsSourceProvider.java
@@ -198,7 +198,7 @@
if (mControllable) {
mWindowContainer.setControllableInsetProvider(this);
if (mPendingControlTarget != mControlTarget) {
- updateControlForTarget(mPendingControlTarget, true /* force */);
+ mStateController.notifyControlTargetChanged(mPendingControlTarget, this);
}
}
}
diff --git a/services/core/java/com/android/server/wm/InsetsStateController.java b/services/core/java/com/android/server/wm/InsetsStateController.java
index dfee164..7a1f57b 100644
--- a/services/core/java/com/android/server/wm/InsetsStateController.java
+++ b/services/core/java/com/android/server/wm/InsetsStateController.java
@@ -389,7 +389,7 @@
newControlTargets.clear();
// Check for and try to run the scheduled show IME request (if it exists), as we
// now applied the surface transaction and notified the target of the new control.
- getImeSourceProvider().checkShowImePostLayout();
+ getImeSourceProvider().checkAndStartShowImePostLayout();
});
}
diff --git a/services/core/java/com/android/server/wm/LetterboxUiController.java b/services/core/java/com/android/server/wm/LetterboxUiController.java
index 9e16b8a..6e11e08 100644
--- a/services/core/java/com/android/server/wm/LetterboxUiController.java
+++ b/services/core/java/com/android/server/wm/LetterboxUiController.java
@@ -260,7 +260,7 @@
// Whether activity "refresh" was requested but not finished in
// ActivityRecord#activityResumedLocked following the camera compat force rotation in
// DisplayRotationCompatPolicy.
- private boolean mIsRefreshAfterRotationRequested;
+ private boolean mIsRefreshRequested;
@NonNull
private final OptProp mIgnoreRequestedOrientationOptProp;
@@ -571,15 +571,14 @@
}
/**
- * Whether activity "refresh" was requested but not finished in {@link #activityResumedLocked}
- * following the camera compat force rotation in {@link DisplayRotationCompatPolicy}.
+ * Whether activity "refresh" was requested but not finished in {@link #activityResumedLocked}.
*/
- boolean isRefreshAfterRotationRequested() {
- return mIsRefreshAfterRotationRequested;
+ boolean isRefreshRequested() {
+ return mIsRefreshRequested;
}
- void setIsRefreshAfterRotationRequested(boolean isRequested) {
- mIsRefreshAfterRotationRequested = isRequested;
+ void setIsRefreshRequested(boolean isRequested) {
+ mIsRefreshRequested = isRequested;
}
boolean isOverrideRespectRequestedOrientationEnabled() {
@@ -989,6 +988,10 @@
}
}
+ boolean isLetterboxEducationEnabled() {
+ return mLetterboxConfiguration.getIsEducationEnabled();
+ }
+
/**
* Whether we use split screen aspect ratio for the activity when camera compat treatment
* is active because the corresponding config is enabled and activity supports resizing.
@@ -1064,7 +1067,7 @@
* thin letteboxing
*/
boolean allowVerticalReachabilityForThinLetterbox() {
- if (!Flags.disableThinLetterboxingReachability()) {
+ if (!Flags.disableThinLetterboxingPolicy()) {
return true;
}
// When the flag is enabled we allow vertical reachability only if the
@@ -1077,7 +1080,7 @@
* thin letteboxing
*/
boolean allowHorizontalReachabilityForThinLetterbox() {
- if (!Flags.disableThinLetterboxingReachability()) {
+ if (!Flags.disableThinLetterboxingPolicy()) {
return true;
}
// When the flag is enabled we allow horizontal reachability only if the
@@ -1146,6 +1149,17 @@
}
boolean shouldApplyUserFullscreenOverride() {
+ // Do not override orientation to fullscreen for camera activities.
+ // Fixed-orientation activities are rarely tested in other orientations, and it often
+ // results in sideways or stretched previews. As the camera compat treatment targets
+ // fixed-orientation activities, overriding the orientation disables the treatment.
+ final DisplayContent displayContent = mActivityRecord.mDisplayContent;
+ if (displayContent != null && displayContent.mDisplayRotationCompatPolicy != null
+ && displayContent.mDisplayRotationCompatPolicy
+ .isCameraActive(mActivityRecord, /* mustBeFullscreen= */ true)) {
+ return false;
+ }
+
if (isUserFullscreenOverrideEnabled()) {
mUserAspectRatio = getUserMinAspectRatioOverrideCode();
diff --git a/services/core/java/com/android/server/wm/PerfettoTransitionTracer.java b/services/core/java/com/android/server/wm/PerfettoTransitionTracer.java
index 498182d..3606a34 100644
--- a/services/core/java/com/android/server/wm/PerfettoTransitionTracer.java
+++ b/services/core/java/com/android/server/wm/PerfettoTransitionTracer.java
@@ -41,8 +41,12 @@
PerfettoTransitionTracer() {
Producer.init(InitArguments.DEFAULTS);
- mDataSource.register(
- new DataSourceParams(PERFETTO_DS_BUFFER_EXHAUSTED_POLICY_STALL_AND_ABORT));
+ DataSourceParams params =
+ new DataSourceParams.Builder()
+ .setBufferExhaustedPolicy(
+ PERFETTO_DS_BUFFER_EXHAUSTED_POLICY_STALL_AND_ABORT)
+ .build();
+ mDataSource.register(params);
}
/**
diff --git a/services/core/java/com/android/server/wm/SnapshotCache.java b/services/core/java/com/android/server/wm/SnapshotCache.java
index 8680436..1e6ee7d 100644
--- a/services/core/java/com/android/server/wm/SnapshotCache.java
+++ b/services/core/java/com/android/server/wm/SnapshotCache.java
@@ -92,6 +92,7 @@
if (entry != null) {
mAppIdMap.remove(entry.topApp);
mRunningCache.remove(id);
+ entry.snapshot.removeReference(TaskSnapshot.REFERENCE_CACHE);
}
}
}
diff --git a/services/core/java/com/android/server/wm/SnapshotPersistQueue.java b/services/core/java/com/android/server/wm/SnapshotPersistQueue.java
index 3578971..42ca7b4 100644
--- a/services/core/java/com/android/server/wm/SnapshotPersistQueue.java
+++ b/services/core/java/com/android/server/wm/SnapshotPersistQueue.java
@@ -253,6 +253,7 @@
PersistInfoProvider provider) {
super(provider, userId);
mId = id;
+ snapshot.addReference(TaskSnapshot.REFERENCE_PERSIST);
mSnapshot = snapshot;
}
@@ -289,6 +290,7 @@
if (failed) {
deleteSnapshot(mId, mUserId, mPersistInfoProvider);
}
+ mSnapshot.removeReference(TaskSnapshot.REFERENCE_PERSIST);
Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER);
}
diff --git a/services/core/java/com/android/server/wm/Task.java b/services/core/java/com/android/server/wm/Task.java
index 8bd7b5f..a555388 100644
--- a/services/core/java/com/android/server/wm/Task.java
+++ b/services/core/java/com/android/server/wm/Task.java
@@ -297,6 +297,10 @@
ActivityRecord mTranslucentActivityWaiting = null;
ArrayList<ActivityRecord> mUndrawnActivitiesBelowTopTranslucent = new ArrayList<>();
+ // The topmost Activity that was converted to translucent for scene transition, which should
+ // be converted from translucent once the transition is completed, or the app died.
+ private ActivityRecord mPendingConvertFromTranslucentActivity = null;
+
/**
* Set when we know we are going to be calling updateConfiguration()
* soon, so want to skip intermediate config checks.
@@ -3448,6 +3452,8 @@
// Whether the direct top activity is eligible for letterbox education.
appCompatTaskInfo.topActivityEligibleForLetterboxEducation = isTopActivityResumed
&& top.isEligibleForLetterboxEducation();
+ appCompatTaskInfo.isLetterboxEducationEnabled = top != null
+ && top.mLetterboxUiController.isLetterboxEducationEnabled();
// Whether the direct top activity requested showing camera compat control.
appCompatTaskInfo.cameraCompatTaskInfo.cameraCompatControlState = isTopActivityResumed
? top.getCameraCompatControlState()
@@ -4986,6 +4992,27 @@
}
}
+ void abortTranslucentActivityWaiting(@NonNull ActivityRecord r) {
+ if (r != mTranslucentActivityWaiting && r != mPendingConvertFromTranslucentActivity) {
+ return;
+ }
+
+ if (mTranslucentActivityWaiting != null) {
+ if (!mTranslucentActivityWaiting.finishing) {
+ mTranslucentActivityWaiting.setOccludesParent(true);
+ }
+ mTranslucentActivityWaiting = null;
+ }
+ if (mPendingConvertFromTranslucentActivity != null) {
+ if (!mPendingConvertFromTranslucentActivity.finishing) {
+ mPendingConvertFromTranslucentActivity.setOccludesParent(true);
+ }
+ mPendingConvertFromTranslucentActivity = null;
+ }
+ mUndrawnActivitiesBelowTopTranslucent.clear();
+ mHandler.removeMessages(TRANSLUCENT_TIMEOUT_MSG);
+ }
+
void checkTranslucentActivityWaiting(ActivityRecord top) {
if (mTranslucentActivityWaiting != top) {
mUndrawnActivitiesBelowTopTranslucent.clear();
@@ -5000,10 +5027,19 @@
void convertActivityToTranslucent(ActivityRecord r) {
mTranslucentActivityWaiting = r;
+ mPendingConvertFromTranslucentActivity = r;
mUndrawnActivitiesBelowTopTranslucent.clear();
mHandler.sendEmptyMessageDelayed(TRANSLUCENT_TIMEOUT_MSG, TRANSLUCENT_CONVERSION_TIMEOUT);
}
+ void convertActivityFromTranslucent(ActivityRecord r) {
+ if (r != mPendingConvertFromTranslucentActivity) {
+ Slog.e(TAG, "convertFromTranslucent expects " + mPendingConvertFromTranslucentActivity
+ + " but is " + r);
+ }
+ mPendingConvertFromTranslucentActivity = null;
+ }
+
/**
* Called as activities below the top translucent activity are redrawn. When the last one is
* redrawn notify the top activity by calling
diff --git a/services/core/java/com/android/server/wm/TaskChangeNotificationController.java b/services/core/java/com/android/server/wm/TaskChangeNotificationController.java
index 21e7a8d..586f3c3 100644
--- a/services/core/java/com/android/server/wm/TaskChangeNotificationController.java
+++ b/services/core/java/com/android/server/wm/TaskChangeNotificationController.java
@@ -247,6 +247,7 @@
break;
case NOTIFY_TASK_SNAPSHOT_CHANGED_LISTENERS_MSG:
forAllRemoteListeners(mNotifyTaskSnapshotChanged, msg);
+ ((TaskSnapshot) msg.obj).removeReference(TaskSnapshot.REFERENCE_BROADCAST);
break;
case NOTIFY_BACK_PRESSED_ON_TASK_ROOT:
forAllRemoteListeners(mNotifyBackPressedOnTaskRoot, msg);
@@ -485,6 +486,7 @@
* Notify listeners that the snapshot of a task has changed.
*/
void notifyTaskSnapshotChanged(int taskId, TaskSnapshot snapshot) {
+ snapshot.addReference(TaskSnapshot.REFERENCE_BROADCAST);
final Message msg = mHandler.obtainMessage(NOTIFY_TASK_SNAPSHOT_CHANGED_LISTENERS_MSG,
taskId, 0, snapshot);
forAllLocalListeners(mNotifyTaskSnapshotChanged, msg);
diff --git a/services/core/java/com/android/server/wm/TaskDisplayArea.java b/services/core/java/com/android/server/wm/TaskDisplayArea.java
index 2c27b98..eff8315 100644
--- a/services/core/java/com/android/server/wm/TaskDisplayArea.java
+++ b/services/core/java/com/android/server/wm/TaskDisplayArea.java
@@ -223,7 +223,7 @@
@VisibleForTesting
Task getTopRootTask() {
- return getRootTask(t -> true);
+ return getRootTask(alwaysTruePredicate());
}
@Nullable
diff --git a/services/core/java/com/android/server/wm/TaskFragment.java b/services/core/java/com/android/server/wm/TaskFragment.java
index 6a7f60b..a3f1503 100644
--- a/services/core/java/com/android/server/wm/TaskFragment.java
+++ b/services/core/java/com/android/server/wm/TaskFragment.java
@@ -40,6 +40,8 @@
import static android.os.Process.SYSTEM_UID;
import static android.os.UserHandle.USER_NULL;
import static android.view.Display.INVALID_DISPLAY;
+import static android.view.Surface.ROTATION_270;
+import static android.view.Surface.ROTATION_90;
import static android.view.WindowManager.LayoutParams.FLAG_DIM_BEHIND;
import static android.view.WindowManager.TRANSIT_CLOSE;
import static android.view.WindowManager.TRANSIT_FLAG_OPEN_BEHIND;
@@ -354,14 +356,21 @@
/**
* Whether the activity navigation should be isolated. That is, Activities cannot be launched
- * on an isolated TaskFragment, unless the activity is launched from an Activity in the same
- * isolated TaskFragment, or explicitly requested to be launched to.
- * <p>
- * Note that only an embedded TaskFragment can be isolated.
+ * on an isolated TaskFragment unless explicitly requested to be launched to.
*/
private boolean mIsolatedNav;
/**
+ * Whether the TaskFragment to be pinned.
+ * <p>
+ * If a TaskFragment is pinned, the TaskFragment should be the top-most TaskFragment among other
+ * sibling TaskFragments. Any newly launched and embeddable activity should not be placed in the
+ * pinned TaskFragment, unless the activity is launched from the pinned TaskFragment or
+ * explicitly requested to. Non-embeddable activities are not restricted to.
+ */
+ private boolean mPinned;
+
+ /**
* Whether the TaskFragment should move to bottom of task when any activity below it is
* launched in clear top mode.
*/
@@ -515,6 +524,18 @@
}
/**
+ * Sets whether this TaskFragment {@link #isPinned()}.
+ * <p>
+ * Note that this is no-op if the TaskFragment is not {@link #isEmbedded() embedded}.
+ */
+ void setPinned(boolean pinned) {
+ if (!isEmbedded()) {
+ return;
+ }
+ mPinned = pinned;
+ }
+
+ /**
* Sets whether transitions are allowed when the TaskFragment is empty. If {@code true},
* transitions are allowed when the TaskFragment is empty. If {@code false}, transitions
* will wait until the TaskFragment becomes non-empty or other conditions are met. Default
@@ -532,6 +553,15 @@
return isEmbedded() && mIsolatedNav;
}
+ /**
+ * Indicates whether this TaskFragment is pinned.
+ *
+ * @see android.window.TaskFragmentOperation#OP_TYPE_SET_PINNED
+ */
+ boolean isPinned() {
+ return isEmbedded() && mPinned;
+ }
+
TaskFragment getAdjacentTaskFragment() {
return mAdjacentTaskFragment;
}
@@ -564,7 +594,6 @@
}
void setResumedActivity(ActivityRecord r, String reason) {
- warnForNonLeafTaskFragment("setResumedActivity");
if (mResumedActivity == r) {
return;
}
@@ -850,15 +879,6 @@
return parentTaskFragment != null ? parentTaskFragment.getOrganizedTaskFragment() : null;
}
- /**
- * Simply check and give warning logs if this is not operated on leaf {@link TaskFragment}.
- */
- private void warnForNonLeafTaskFragment(String func) {
- if (!isLeafTaskFragment()) {
- Slog.w(TAG, func + " on non-leaf task fragment " + this);
- }
- }
-
boolean hasDirectChildActivities() {
for (int i = mChildren.size() - 1; i >= 0; --i) {
if (mChildren.get(i).asActivityRecord() != null) {
@@ -935,7 +955,6 @@
*/
void onActivityStateChanged(ActivityRecord record, ActivityRecord.State state,
String reason) {
- warnForNonLeafTaskFragment("onActivityStateChanged");
if (record == mResumedActivity && state != RESUMED) {
setResumedActivity(null, reason + " - onActivityStateChanged");
}
@@ -965,7 +984,6 @@
* @return {@code true} if the process of the pausing activity is died.
*/
boolean handleAppDied(WindowProcessController app) {
- warnForNonLeafTaskFragment("handleAppDied");
boolean isPausingDied = false;
if (mPausingActivity != null && mPausingActivity.app == app) {
ProtoLog.v(WM_DEBUG_STATES, "App died while pausing: %s",
@@ -1223,7 +1241,7 @@
// have any running activities, not starting one and not home stack.
shouldBeVisible = hasRunningActivities
|| (starting != null && starting.isDescendantOf(this))
- || isActivityTypeHome();
+ || (isActivityTypeHome() && !isEmbedded());
break;
}
@@ -2206,7 +2224,7 @@
static class ConfigOverrideHint {
@Nullable DisplayInfo mTmpOverrideDisplayInfo;
@Nullable ActivityRecord.CompatDisplayInsets mTmpCompatInsets;
- boolean mUseOverrideInsetsForStableBounds;
+ boolean mUseOverrideInsetsForConfig;
}
void computeConfigResourceOverrides(@NonNull Configuration inOutConfig,
@@ -2239,11 +2257,11 @@
@NonNull Configuration parentConfig, @Nullable ConfigOverrideHint overrideHint) {
DisplayInfo overrideDisplayInfo = null;
ActivityRecord.CompatDisplayInsets compatInsets = null;
- boolean useOverrideInsetsForStableBounds = false;
+ boolean useOverrideInsetsForConfig = false;
if (overrideHint != null) {
overrideDisplayInfo = overrideHint.mTmpOverrideDisplayInfo;
compatInsets = overrideHint.mTmpCompatInsets;
- useOverrideInsetsForStableBounds = overrideHint.mUseOverrideInsetsForStableBounds;
+ useOverrideInsetsForConfig = overrideHint.mUseOverrideInsetsForConfig;
if (overrideDisplayInfo != null) {
// Make sure the screen related configs can be computed by the provided
// display info.
@@ -2307,6 +2325,7 @@
}
}
+ boolean insetsOverrideApplied = false;
if (inOutConfig.screenWidthDp == Configuration.SCREEN_WIDTH_DP_UNDEFINED
|| inOutConfig.screenHeightDp == Configuration.SCREEN_HEIGHT_DP_UNDEFINED) {
if (!customContainerPolicy && WindowConfiguration.isFloating(windowingMode)) {
@@ -2323,7 +2342,7 @@
// The non decor inset are areas that could never be removed in Honeycomb. See
// {@link WindowManagerPolicy#getNonDecorInsetsLw}.
calculateInsetFrames(mTmpNonDecorBounds, mTmpStableBounds, mTmpFullBounds, di,
- useOverrideInsetsForStableBounds);
+ useOverrideInsetsForConfig);
} else {
// Apply the given non-decor and stable insets to calculate the corresponding bounds
// for screen size of configuration.
@@ -2340,8 +2359,21 @@
intersectWithInsetsIfFits(mTmpStableBounds, mTmpBounds,
compatInsets.mStableInsets[rotation]);
outAppBounds.set(mTmpNonDecorBounds);
+ } else if (useOverrideInsetsForConfig) {
+ final boolean rotated = (rotation == ROTATION_90 || rotation == ROTATION_270);
+ final int dw = rotated ? mDisplayContent.mBaseDisplayHeight
+ : mDisplayContent.mBaseDisplayWidth;
+ final int dh = rotated ? mDisplayContent.mBaseDisplayWidth
+ : mDisplayContent.mBaseDisplayHeight;
+ final DisplayPolicy.DecorInsets.Info decorInsets = mDisplayContent
+ .getDisplayPolicy().getDecorInsetsInfo(rotation, dw, dh);
+ mTmpStableBounds.set(outAppBounds);
+ mTmpStableBounds.inset(decorInsets.mOverrideConfigInsets);
+ outAppBounds.inset(decorInsets.mOverrideNonDecorInsets);
+ mTmpNonDecorBounds.set(outAppBounds);
+ // Record the override apply to avoid duplicated check.
+ insetsOverrideApplied = true;
} else {
- // Set to app bounds because it excludes decor insets.
mTmpNonDecorBounds.set(outAppBounds);
mTmpStableBounds.set(outAppBounds);
}
@@ -2383,6 +2415,11 @@
// from the parent task would result in applications loaded wrong resource.
inOutConfig.smallestScreenWidthDp =
Math.min(inOutConfig.screenWidthDp, inOutConfig.screenHeightDp);
+ } else if (insetsOverrideApplied) {
+ // The smallest width should also consider insets. If the insets are overridden,
+ // use the overridden value.
+ inOutConfig.smallestScreenWidthDp =
+ Math.min(inOutConfig.screenWidthDp, inOutConfig.screenHeightDp);
}
// otherwise, it will just inherit
}
@@ -2895,6 +2932,13 @@
return !mCreatedByOrganizer || mIsRemovalRequested;
}
+ /**
+ * Returns whether this TaskFragment is going to be removed.
+ */
+ boolean isRemovalRequested() {
+ return mIsRemovalRequested;
+ }
+
@Override
void removeChild(WindowContainer child) {
removeChild(child, true /* removeSelfIfPossible */);
diff --git a/services/core/java/com/android/server/wm/TaskFragmentOrganizerController.java b/services/core/java/com/android/server/wm/TaskFragmentOrganizerController.java
index 24b533a..c4e932a 100644
--- a/services/core/java/com/android/server/wm/TaskFragmentOrganizerController.java
+++ b/services/core/java/com/android/server/wm/TaskFragmentOrganizerController.java
@@ -365,7 +365,8 @@
@Nullable
TaskFragmentTransaction.Change prepareActivityReparentedToTask(
- @NonNull ActivityRecord activity) {
+ @NonNull ActivityRecord activity, @Nullable ActivityRecord nextFillTaskActivity,
+ @Nullable IBinder lastParentTfToken) {
if (activity.finishing) {
Slog.d(TAG, "Reparent activity=" + activity.token + " is finishing");
return null;
@@ -408,10 +409,21 @@
}
ProtoLog.v(WM_DEBUG_WINDOW_ORGANIZER, "Activity=%s reparent to taskId=%d",
activity.token, task.mTaskId);
- return new TaskFragmentTransaction.Change(TYPE_ACTIVITY_REPARENTED_TO_TASK)
- .setTaskId(task.mTaskId)
- .setActivityIntent(trimIntent(activity.intent))
- .setActivityToken(activityToken);
+
+ final TaskFragmentTransaction.Change change =
+ new TaskFragmentTransaction.Change(TYPE_ACTIVITY_REPARENTED_TO_TASK)
+ .setTaskId(task.mTaskId)
+ .setActivityIntent(trimIntent(activity.intent))
+ .setActivityToken(activityToken);
+ if (lastParentTfToken != null) {
+ change.setTaskFragmentToken(lastParentTfToken);
+ }
+ // Only pass the activity token to the client if it belongs to the same process.
+ if (Flags.fixPipRestoreToOverlay() && nextFillTaskActivity != null
+ && nextFillTaskActivity.getPid() == mOrganizerPid) {
+ change.setOtherActivityToken(nextFillTaskActivity.token);
+ }
+ return change;
}
void dispatchTransaction(@NonNull TaskFragmentTransaction transaction) {
@@ -733,13 +745,13 @@
}
void onActivityReparentedToTask(@NonNull ActivityRecord activity) {
+ final Task task = activity.getTask();
final ITaskFragmentOrganizer organizer;
if (activity.mLastTaskFragmentOrganizerBeforePip != null) {
// If the activity is previously embedded in an organized TaskFragment.
organizer = activity.mLastTaskFragmentOrganizerBeforePip;
} else {
// Find the topmost TaskFragmentOrganizer.
- final Task task = activity.getTask();
final TaskFragment[] organizedTf = new TaskFragment[1];
task.forAllLeafTaskFragments(tf -> {
if (tf.isOrganizedTaskFragment()) {
@@ -757,10 +769,24 @@
Slog.w(TAG, "The last TaskFragmentOrganizer no longer exists");
return;
}
- addPendingEvent(new PendingTaskFragmentEvent.Builder(
+
+ final IBinder parentTfTokenBeforePip = activity.getLastEmbeddedParentTfTokenBeforePip();
+ final PendingTaskFragmentEvent.Builder builder = new PendingTaskFragmentEvent.Builder(
PendingTaskFragmentEvent.EVENT_ACTIVITY_REPARENTED_TO_TASK, organizer)
.setActivity(activity)
- .build());
+ .setTaskFragmentToken(activity.getLastEmbeddedParentTfTokenBeforePip());
+
+ // Sets the next activity behinds the reparented Activity that's also not in the last
+ // embedded parent TF.
+ final ActivityRecord candidateAssociatedActivity = task.getActivity(
+ ar -> ar != activity && !ar.finishing
+ && ar.getTaskFragment().getFragmentToken() != parentTfTokenBeforePip);
+ if (candidateAssociatedActivity != null && (!candidateAssociatedActivity.isEmbedded()
+ || candidateAssociatedActivity.getTaskFragment().fillsParent())) {
+ builder.setOtherActivity(candidateAssociatedActivity);
+ }
+
+ addPendingEvent(builder.build());
}
void onTaskFragmentParentInfoChanged(@NonNull ITaskFragmentOrganizer organizer,
@@ -889,11 +915,16 @@
@Nullable
private final TaskFragment mTaskFragment;
@Nullable
+ private final IBinder mTaskFragmentToken;
+ @Nullable
private final IBinder mErrorCallbackToken;
@Nullable
private final Throwable mException;
@Nullable
private final ActivityRecord mActivity;
+ // An additional Activity that's needed to send back to the client other than the mActivity.
+ @Nullable
+ private final ActivityRecord mOtherActivity;
@Nullable
private final Task mTask;
// Set when the event is deferred due to the host task is invisible. The defer time will
@@ -905,17 +936,21 @@
private PendingTaskFragmentEvent(@EventType int eventType,
ITaskFragmentOrganizer taskFragmentOrg,
@Nullable TaskFragment taskFragment,
+ @Nullable IBinder taskFragmentToken,
@Nullable IBinder errorCallbackToken,
@Nullable Throwable exception,
@Nullable ActivityRecord activity,
+ @Nullable ActivityRecord otherActivity,
@Nullable Task task,
@TaskFragmentOperation.OperationType int opType) {
mEventType = eventType;
mTaskFragmentOrg = taskFragmentOrg;
mTaskFragment = taskFragment;
+ mTaskFragmentToken = taskFragmentToken;
mErrorCallbackToken = errorCallbackToken;
mException = exception;
mActivity = activity;
+ mOtherActivity = otherActivity;
mTask = task;
mOpType = opType;
}
@@ -943,12 +978,16 @@
@Nullable
private TaskFragment mTaskFragment;
@Nullable
+ private IBinder mTaskFragmentToken;
+ @Nullable
private IBinder mErrorCallbackToken;
@Nullable
private Throwable mException;
@Nullable
private ActivityRecord mActivity;
@Nullable
+ private ActivityRecord mOtherActivity;
+ @Nullable
private Task mTask;
@TaskFragmentOperation.OperationType
private int mOpType;
@@ -963,6 +1002,11 @@
return this;
}
+ Builder setTaskFragmentToken(@Nullable IBinder fragmentToken) {
+ mTaskFragmentToken = fragmentToken;
+ return this;
+ }
+
Builder setErrorCallbackToken(@Nullable IBinder errorCallbackToken) {
mErrorCallbackToken = errorCallbackToken;
return this;
@@ -978,6 +1022,11 @@
return this;
}
+ Builder setOtherActivity(@NonNull ActivityRecord otherActivity) {
+ mOtherActivity = otherActivity;
+ return this;
+ }
+
Builder setTask(@NonNull Task task) {
mTask = requireNonNull(task);
return this;
@@ -990,7 +1039,8 @@
PendingTaskFragmentEvent build() {
return new PendingTaskFragmentEvent(mEventType, mTaskFragmentOrg, mTaskFragment,
- mErrorCallbackToken, mException, mActivity, mTask, mOpType);
+ mTaskFragmentToken, mErrorCallbackToken, mException, mActivity,
+ mOtherActivity, mTask, mOpType);
}
}
}
@@ -1191,7 +1241,8 @@
return state.prepareTaskFragmentError(event.mErrorCallbackToken, taskFragment,
event.mOpType, event.mException);
case PendingTaskFragmentEvent.EVENT_ACTIVITY_REPARENTED_TO_TASK:
- return state.prepareActivityReparentedToTask(event.mActivity);
+ return state.prepareActivityReparentedToTask(event.mActivity, event.mOtherActivity,
+ event.mTaskFragmentToken);
default:
throw new IllegalArgumentException("Unknown TaskFragmentEvent=" + event.mEventType);
}
diff --git a/services/core/java/com/android/server/wm/TaskSnapshotCache.java b/services/core/java/com/android/server/wm/TaskSnapshotCache.java
index b69ac1b..64b9df5 100644
--- a/services/core/java/com/android/server/wm/TaskSnapshotCache.java
+++ b/services/core/java/com/android/server/wm/TaskSnapshotCache.java
@@ -35,9 +35,11 @@
void putSnapshot(Task task, TaskSnapshot snapshot) {
synchronized (mLock) {
+ snapshot.addReference(TaskSnapshot.REFERENCE_CACHE);
final CacheEntry entry = mRunningCache.get(task.mTaskId);
if (entry != null) {
mAppIdMap.remove(entry.topApp);
+ entry.snapshot.removeReference(TaskSnapshot.REFERENCE_CACHE);
}
final ActivityRecord top = task.getTopMostActivity();
mAppIdMap.put(top, task.mTaskId);
diff --git a/services/core/java/com/android/server/wm/WindowContainer.java b/services/core/java/com/android/server/wm/WindowContainer.java
index d70ca02..edbba92 100644
--- a/services/core/java/com/android/server/wm/WindowContainer.java
+++ b/services/core/java/com/android/server/wm/WindowContainer.java
@@ -116,6 +116,7 @@
import com.android.server.wm.SurfaceAnimator.Animatable;
import com.android.server.wm.SurfaceAnimator.AnimationType;
import com.android.server.wm.SurfaceAnimator.OnAnimationFinishedCallback;
+import com.android.server.wm.utils.AlwaysTruePredicate;
import java.io.PrintWriter;
import java.lang.ref.WeakReference;
@@ -2019,29 +2020,34 @@
callback, boundary, includeBoundary, traverseTopToBottom, boundaryFound);
}
+ @SuppressWarnings("unchecked")
+ static <T> Predicate<T> alwaysTruePredicate() {
+ return (Predicate<T>) AlwaysTruePredicate.INSTANCE;
+ }
+
ActivityRecord getActivityAbove(ActivityRecord r) {
- return getActivity((above) -> true, r,
+ return getActivity(alwaysTruePredicate(), r /* boundary */,
false /*includeBoundary*/, false /*traverseTopToBottom*/);
}
ActivityRecord getActivityBelow(ActivityRecord r) {
- return getActivity((below) -> true, r,
+ return getActivity(alwaysTruePredicate(), r /* boundary */,
false /*includeBoundary*/, true /*traverseTopToBottom*/);
}
ActivityRecord getBottomMostActivity() {
- return getActivity((r) -> true, false /*traverseTopToBottom*/);
+ return getActivity(alwaysTruePredicate(), false /* traverseTopToBottom */);
}
ActivityRecord getTopMostActivity() {
- return getActivity((r) -> true, true /*traverseTopToBottom*/);
+ return getActivity(alwaysTruePredicate(), true /* traverseTopToBottom */);
}
ActivityRecord getTopActivity(boolean includeFinishing, boolean includeOverlays) {
// Break down into 4 calls to avoid object creation due to capturing input params.
if (includeFinishing) {
if (includeOverlays) {
- return getActivity((r) -> true);
+ return getActivity(alwaysTruePredicate());
}
return getActivity((r) -> !r.isTaskOverlay());
} else if (includeOverlays) {
@@ -2220,21 +2226,17 @@
}
}
- Task getTaskAbove(Task t) {
- return getTask(
- (above) -> true, t, false /*includeBoundary*/, false /*traverseTopToBottom*/);
- }
-
Task getTaskBelow(Task t) {
- return getTask((below) -> true, t, false /*includeBoundary*/, true /*traverseTopToBottom*/);
+ return getTask(alwaysTruePredicate(), t /* boundary */,
+ false /* includeBoundary */, true /* traverseTopToBottom */);
}
Task getBottomMostTask() {
- return getTask((t) -> true, false /*traverseTopToBottom*/);
+ return getTask(alwaysTruePredicate(), false /* traverseTopToBottom */);
}
Task getTopMostTask() {
- return getTask((t) -> true, true /*traverseTopToBottom*/);
+ return getTask(alwaysTruePredicate(), true /* traverseTopToBottom */);
}
Task getTask(Predicate<Task> callback) {
diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java
index e02e5be..b603551 100644
--- a/services/core/java/com/android/server/wm/WindowManagerService.java
+++ b/services/core/java/com/android/server/wm/WindowManagerService.java
@@ -8302,7 +8302,6 @@
ImeTracker.forLogging().onProgress(statsToken,
ImeTracker.PHASE_WM_HAS_IME_INSETS_CONTROL_TARGET);
- Trace.asyncTraceBegin(TRACE_TAG_WINDOW_MANAGER, "WMS.showImePostLayout", 0);
final InsetsControlTarget controlTarget = imeTarget.getImeControlTarget();
imeTarget = controlTarget.getWindow();
// If InsetsControlTarget doesn't have a window, it's using remoteControlTarget
diff --git a/services/core/java/com/android/server/wm/WindowOrganizerController.java b/services/core/java/com/android/server/wm/WindowOrganizerController.java
index 90e7bd7..99c4736 100644
--- a/services/core/java/com/android/server/wm/WindowOrganizerController.java
+++ b/services/core/java/com/android/server/wm/WindowOrganizerController.java
@@ -39,6 +39,7 @@
import static android.window.TaskFragmentOperation.OP_TYPE_SET_DIM_ON_TASK;
import static android.window.TaskFragmentOperation.OP_TYPE_SET_ISOLATED_NAVIGATION;
import static android.window.TaskFragmentOperation.OP_TYPE_SET_MOVE_TO_BOTTOM_IF_CLEAR_WHEN_LAUNCH;
+import static android.window.TaskFragmentOperation.OP_TYPE_SET_PINNED;
import static android.window.TaskFragmentOperation.OP_TYPE_SET_RELATIVE_BOUNDS;
import static android.window.TaskFragmentOperation.OP_TYPE_START_ACTIVITY_IN_TASK_FRAGMENT;
import static android.window.TaskFragmentOperation.OP_TYPE_UNKNOWN;
@@ -1627,6 +1628,11 @@
}
break;
}
+ case OP_TYPE_SET_PINNED: {
+ final boolean pinned = operation.getBooleanValue();
+ taskFragment.setPinned(pinned);
+ break;
+ }
}
return effects;
}
diff --git a/services/core/java/com/android/server/wm/utils/AlwaysTruePredicate.java b/services/core/java/com/android/server/wm/utils/AlwaysTruePredicate.java
new file mode 100644
index 0000000..49dcb6c
--- /dev/null
+++ b/services/core/java/com/android/server/wm/utils/AlwaysTruePredicate.java
@@ -0,0 +1,33 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.wm.utils;
+
+import java.util.function.Predicate;
+
+/** A simple Predicate to avoid synthetic allocation of lambda expression "o -> true". */
+public class AlwaysTruePredicate implements Predicate<Object> {
+
+ public static final AlwaysTruePredicate INSTANCE = new AlwaysTruePredicate();
+
+ private AlwaysTruePredicate() {
+ }
+
+ @Override
+ public boolean test(Object o) {
+ return true;
+ }
+}
diff --git a/services/core/jni/com_android_server_input_InputManagerService.cpp b/services/core/jni/com_android_server_input_InputManagerService.cpp
index a01c123..4c746a9 100644
--- a/services/core/jni/com_android_server_input_InputManagerService.cpp
+++ b/services/core/jni/com_android_server_input_InputManagerService.cpp
@@ -122,7 +122,6 @@
jmethodID interceptMotionBeforeQueueingNonInteractive;
jmethodID interceptKeyBeforeDispatching;
jmethodID dispatchUnhandledKey;
- jmethodID onPointerDisplayIdChanged;
jmethodID onPointerDownOutsideFocus;
jmethodID getVirtualKeyQuietTimeMillis;
jmethodID getExcludedDeviceNames;
@@ -423,7 +422,7 @@
std::set<int32_t> disabledInputDevices{};
// Associated Pointer controller display.
- ui::LogicalDisplayId pointerDisplayId{ui::ADISPLAY_ID_DEFAULT};
+ ui::LogicalDisplayId pointerDisplayId{ui::LogicalDisplayId::DEFAULT};
// True if stylus button reporting through motion events is enabled.
bool stylusButtonMotionEventsEnabled{true};
@@ -786,12 +785,6 @@
} // 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);
- checkAndClearExceptionFromCallback(env, "onPointerDisplayIdChanged");
}
void NativeInputManager::notifyStickyModifierStateChanged(uint32_t modifierState,
@@ -1886,7 +1879,7 @@
jstring nameObj, jint pid) {
NativeInputManager* im = getNativeInputManager(env, nativeImplObj);
- if (displayId == ui::ADISPLAY_ID_NONE.val()) {
+ if (ui::LogicalDisplayId{displayId} == ui::LogicalDisplayId::INVALID) {
std::string message = "InputChannel used as a monitor must be associated with a display";
jniThrowRuntimeException(env, message.c_str());
return nullptr;
@@ -2458,12 +2451,6 @@
im->getInputManager()->getDispatcher().monitor();
}
-static jboolean nativeIsInputDeviceEnabled(JNIEnv* env, jobject nativeImplObj, jint deviceId) {
- NativeInputManager* im = getNativeInputManager(env, nativeImplObj);
-
- return im->getInputManager()->getReader().isInputDeviceEnabled(deviceId);
-}
-
static void nativeEnableInputDevice(JNIEnv* env, jobject nativeImplObj, jint deviceId) {
NativeInputManager* im = getNativeInputManager(env, nativeImplObj);
@@ -2727,6 +2714,11 @@
im->setInputMethodConnectionIsActive(isActive);
}
+static jint nativeGetLastUsedInputDeviceId(JNIEnv* env, jobject nativeImplObj) {
+ NativeInputManager* im = getNativeInputManager(env, nativeImplObj);
+ return static_cast<jint>(im->getInputManager()->getReader().getLastUsedInputDeviceId());
+}
+
// ----------------------------------------------------------------------------
static const JNINativeMethod gInputManagerMethods[] = {
@@ -2800,7 +2792,6 @@
{"sysfsNodeChanged", "(Ljava/lang/String;)V", (void*)nativeSysfsNodeChanged},
{"dump", "()Ljava/lang/String;", (void*)nativeDump},
{"monitor", "()V", (void*)nativeMonitor},
- {"isInputDeviceEnabled", "(I)Z", (void*)nativeIsInputDeviceEnabled},
{"enableInputDevice", "(I)V", (void*)nativeEnableInputDevice},
{"disableInputDevice", "(I)V", (void*)nativeDisableInputDevice},
{"reloadPointerIcons", "()V", (void*)nativeReloadPointerIcons},
@@ -2835,6 +2826,7 @@
{"setAccessibilityStickyKeysEnabled", "(Z)V",
(void*)nativeSetAccessibilityStickyKeysEnabled},
{"setInputMethodConnectionIsActive", "(Z)V", (void*)nativeSetInputMethodConnectionIsActive},
+ {"getLastUsedInputDeviceId", "()I", (void*)nativeGetLastUsedInputDeviceId},
};
#define FIND_CLASS(var, className) \
@@ -2927,9 +2919,6 @@
"dispatchUnhandledKey",
"(Landroid/os/IBinder;Landroid/view/KeyEvent;I)Landroid/view/KeyEvent;");
- GET_METHOD_ID(gServiceClassInfo.onPointerDisplayIdChanged, clazz, "onPointerDisplayIdChanged",
- "(IFF)V");
-
GET_METHOD_ID(gServiceClassInfo.notifyStickyModifierStateChanged, clazz,
"notifyStickyModifierStateChanged", "(II)V");
diff --git a/services/core/xsd/display-device-config/display-device-config.xsd b/services/core/xsd/display-device-config/display-device-config.xsd
index 6143f1d..610b502 100644
--- a/services/core/xsd/display-device-config/display-device-config.xsd
+++ b/services/core/xsd/display-device-config/display-device-config.xsd
@@ -746,6 +746,20 @@
minOccurs="0" maxOccurs="1">
<xs:annotation name="final"/>
</xs:element>
+ <!-- list of supported modes for low power. Each point corresponds to one mode.
+ Mode format is : first = refreshRate, second = vsyncRate. E.g. :
+ <lowPowerSupportedModes>
+ <point>
+ <first>60</first> // refreshRate
+ <second>60</second> //vsyncRate
+ </point>
+ ....
+ </lowPowerSupportedModes>
+ -->
+ <xs:element type="nonNegativeFloatToFloatMap" name="lowPowerSupportedModes" minOccurs="0">
+ <xs:annotation name="nullable"/>
+ <xs:annotation name="final"/>
+ </xs:element>
</xs:complexType>
<xs:complexType name="refreshRateZoneProfiles">
diff --git a/services/core/xsd/display-device-config/schema/current.txt b/services/core/xsd/display-device-config/schema/current.txt
index 45ec8f2..203a6d9 100644
--- a/services/core/xsd/display-device-config/schema/current.txt
+++ b/services/core/xsd/display-device-config/schema/current.txt
@@ -360,6 +360,7 @@
method public final java.math.BigInteger getDefaultRefreshRateInHbmHdr();
method public final java.math.BigInteger getDefaultRefreshRateInHbmSunlight();
method public final com.android.server.display.config.BlockingZoneConfig getHigherBlockingZoneConfigs();
+ method @Nullable public final com.android.server.display.config.NonNegativeFloatToFloatMap getLowPowerSupportedModes();
method public final com.android.server.display.config.BlockingZoneConfig getLowerBlockingZoneConfigs();
method public final com.android.server.display.config.RefreshRateZoneProfiles getRefreshRateZoneProfiles();
method public final void setDefaultPeakRefreshRate(java.math.BigInteger);
@@ -367,6 +368,7 @@
method public final void setDefaultRefreshRateInHbmHdr(java.math.BigInteger);
method public final void setDefaultRefreshRateInHbmSunlight(java.math.BigInteger);
method public final void setHigherBlockingZoneConfigs(com.android.server.display.config.BlockingZoneConfig);
+ method public final void setLowPowerSupportedModes(@Nullable com.android.server.display.config.NonNegativeFloatToFloatMap);
method public final void setLowerBlockingZoneConfigs(com.android.server.display.config.BlockingZoneConfig);
method public final void setRefreshRateZoneProfiles(com.android.server.display.config.RefreshRateZoneProfiles);
}
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyEngine.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyEngine.java
index d114337..d733762 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyEngine.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyEngine.java
@@ -217,7 +217,7 @@
<V> void setLocalPolicy(
@NonNull PolicyDefinition<V> policyDefinition,
@NonNull EnforcingAdmin enforcingAdmin,
- @Nullable PolicyValue<V> value,
+ @NonNull PolicyValue<V> value,
int userId,
boolean skipEnforcePolicy) {
Objects.requireNonNull(policyDefinition);
@@ -313,6 +313,7 @@
}
updateDeviceAdminServiceOnPolicyAddLocked(enforcingAdmin);
write();
+ applyToInheritableProfiles(policyDefinition, enforcingAdmin, value, userId);
}
// TODO: add more documentation on broadcasts/callbacks to use to get current enforced values
@@ -400,7 +401,7 @@
* else remove the policy from child.
*/
private <V> void applyToInheritableProfiles(PolicyDefinition<V> policyDefinition,
- EnforcingAdmin enforcingAdmin, PolicyValue<V> value, int userId) {
+ EnforcingAdmin enforcingAdmin, @Nullable PolicyValue<V> value, int userId) {
if (policyDefinition.isInheritable()) {
Binder.withCleanCallingIdentity(() -> {
List<UserInfo> userInfos = mUserManager.getProfiles(userId);
@@ -1742,14 +1743,17 @@
}
}
- <V> void reapplyAllPoliciesLocked() {
+ <V> void reapplyAllPoliciesOnBootLocked() {
for (PolicyKey policy : mGlobalPolicies.keySet()) {
PolicyState<?> policyState = mGlobalPolicies.get(policy);
// Policy definition and value will always be of the same type
PolicyDefinition<V> policyDefinition =
(PolicyDefinition<V>) policyState.getPolicyDefinition();
- PolicyValue<V> policyValue = (PolicyValue<V>) policyState.getCurrentResolvedPolicy();
- enforcePolicy(policyDefinition, policyValue, UserHandle.USER_ALL);
+ if (!policyDefinition.shouldSkipEnforcementIfNotChanged()) {
+ PolicyValue<V> policyValue =
+ (PolicyValue<V>) policyState.getCurrentResolvedPolicy();
+ enforcePolicy(policyDefinition, policyValue, UserHandle.USER_ALL);
+ }
}
for (int i = 0; i < mLocalPolicies.size(); i++) {
int userId = mLocalPolicies.keyAt(i);
@@ -1758,10 +1762,11 @@
// Policy definition and value will always be of the same type
PolicyDefinition<V> policyDefinition =
(PolicyDefinition<V>) policyState.getPolicyDefinition();
- PolicyValue<V> policyValue =
- (PolicyValue<V>) policyState.getCurrentResolvedPolicy();
- enforcePolicy(policyDefinition, policyValue, userId);
-
+ if (!policyDefinition.shouldSkipEnforcementIfNotChanged()) {
+ PolicyValue<V> policyValue =
+ (PolicyValue<V>) policyState.getCurrentResolvedPolicy();
+ enforcePolicy(policyDefinition, policyValue, userId);
+ }
}
}
}
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
index f75803f..85d2a0d 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
@@ -339,6 +339,7 @@
import android.app.admin.ManagedSubscriptionsPolicy;
import android.app.admin.NetworkEvent;
import android.app.admin.PackagePolicy;
+import android.app.admin.PackageSetPolicyValue;
import android.app.admin.ParcelableGranteeMap;
import android.app.admin.ParcelableResource;
import android.app.admin.PasswordMetrics;
@@ -349,7 +350,6 @@
import android.app.admin.PreferentialNetworkServiceConfig;
import android.app.admin.SecurityLog;
import android.app.admin.SecurityLog.SecurityEvent;
-import android.app.admin.PackageSetPolicyValue;
import android.app.admin.StartInstallingUpdateCallback;
import android.app.admin.SystemUpdateInfo;
import android.app.admin.SystemUpdatePolicy;
@@ -2718,6 +2718,7 @@
mDevicePolicyEngine.getResolvedPolicy(
PolicyDefinition.SECURITY_LOGGING, UserHandle.USER_ALL));
setLoggingConfiguration(securityLoggingEnabled, auditLoggingEnabled);
+ mInjector.runCryptoSelfTest();
} else {
synchronized (getLockObject()) {
mSecurityLogMonitor.start(getSecurityLoggingEnabledUser());
@@ -3350,7 +3351,7 @@
break;
case SystemService.PHASE_SYSTEM_SERVICES_READY:
synchronized (getLockObject()) {
- mDevicePolicyEngine.reapplyAllPoliciesLocked();
+ mDevicePolicyEngine.reapplyAllPoliciesOnBootLocked();
}
break;
case SystemService.PHASE_ACTIVITY_MANAGER_READY:
@@ -11442,7 +11443,7 @@
}
setBackwardsCompatibleAppRestrictions(
caller, packageName, restrictions, caller.getUserHandle());
- } else if (Flags.dmrhCanSetAppRestriction()) {
+ } else if (Flags.dmrhSetAppRestrictions()) {
final boolean isRoleHolder;
if (who != null) {
// DO or PO
@@ -11483,10 +11484,6 @@
new BundlePolicyValue(restrictions),
affectedUserId);
}
- Intent changeIntent = new Intent(Intent.ACTION_APPLICATION_RESTRICTIONS_CHANGED);
- changeIntent.setPackage(packageName);
- changeIntent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY);
- mContext.sendBroadcastAsUser(changeIntent, UserHandle.of(affectedUserId));
} else {
mInjector.binderWithCleanCallingIdentity(() -> {
mUserManager.setApplicationRestrictions(packageName, restrictions,
@@ -12844,7 +12841,7 @@
return Bundle.EMPTY;
}
return policies.get(enforcingAdmin).getValue();
- } else if (Flags.dmrhCanSetAppRestriction()) {
+ } else if (Flags.dmrhSetAppRestrictions()) {
final boolean isRoleHolder;
if (who != null) {
// Caller is DO or PO. They cannot call this on parent
@@ -15769,8 +15766,13 @@
PolicyDefinition.APPLICATION_RESTRICTIONS(packageName),
userId);
List<Bundle> restrictions = new ArrayList<>();
- for (EnforcingAdmin admin : policies.keySet()) {
- restrictions.add(policies.get(admin).getValue());
+ for (PolicyValue<Bundle> policyValue: policies.values()) {
+ Bundle value = policyValue.getValue();
+ // Probably not necessary since setApplicationRestrictions only sets non-empty
+ // Bundle, but just in case.
+ if (value != null && !value.isEmpty()) {
+ restrictions.add(value);
+ }
}
return mInjector.binderWithCleanCallingIdentity(() -> {
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/EnterpriseSpecificIdCalculator.java b/services/devicepolicy/java/com/android/server/devicepolicy/EnterpriseSpecificIdCalculator.java
index 1000bfa..cbd2847 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/EnterpriseSpecificIdCalculator.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/EnterpriseSpecificIdCalculator.java
@@ -52,7 +52,18 @@
EnterpriseSpecificIdCalculator(Context context) {
TelephonyManager telephonyService = context.getSystemService(TelephonyManager.class);
Preconditions.checkState(telephonyService != null, "Unable to access telephony service");
- mImei = telephonyService.getImei(0);
+
+ String imei;
+ try {
+ imei = telephonyService.getImei(0);
+ } catch (UnsupportedOperationException doesNotSupportGms) {
+ // Instead of catching the exception, we could check for FEATURE_TELEPHONY_GSM.
+ // However that runs the risk of changing a device's existing ESID if on these devices
+ // telephonyService.getImei() actually returns non-null even when the device does not
+ // declare FEATURE_TELEPHONY_GSM.
+ imei = null;
+ }
+ mImei = imei;
String meid;
try {
meid = telephonyService.getMeid(0);
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/PolicyDefinition.java b/services/devicepolicy/java/com/android/server/devicepolicy/PolicyDefinition.java
index 8d980b5..8bec384 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/PolicyDefinition.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/PolicyDefinition.java
@@ -51,6 +51,7 @@
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
+import java.util.Objects;
import java.util.Set;
final class PolicyDefinition<V> {
@@ -82,6 +83,10 @@
// them.
private static final int POLICY_FLAG_USER_RESTRICTION_POLICY = 1 << 4;
+ // Only invoke the policy enforcer callback when the policy value changes, and do not invoke the
+ // callback in other cases such as device reboots.
+ private static final int POLICY_FLAG_SKIP_ENFORCEMENT_IF_UNCHANGED = 1 << 5;
+
private static final MostRestrictive<Boolean> FALSE_MORE_RESTRICTIVE = new MostRestrictive<>(
List.of(new BooleanPolicyValue(false), new BooleanPolicyValue(true)));
@@ -231,11 +236,11 @@
// Don't need to take in a resolution mechanism since its never used, but might
// need some refactoring to not always assume a non-null mechanism.
new MostRecent<>(),
- POLICY_FLAG_LOCAL_ONLY_POLICY | POLICY_FLAG_NON_COEXISTABLE_POLICY,
- // Application restrictions are now stored and retrieved from DPMS, so no
- // enforcing is required, however DPMS calls into UM to set restrictions for
- // backwards compatibility.
- (Bundle value, Context context, Integer userId, PolicyKey policyKey) -> true,
+ // Only invoke the enforcement callback during policy change and not other state
+ POLICY_FLAG_LOCAL_ONLY_POLICY | POLICY_FLAG_INHERITABLE
+ | POLICY_FLAG_NON_COEXISTABLE_POLICY
+ | POLICY_FLAG_SKIP_ENFORCEMENT_IF_UNCHANGED,
+ PolicyEnforcerCallbacks::setApplicationRestrictions,
new BundlePolicySerializer());
/**
@@ -581,6 +586,10 @@
return (mPolicyFlags & POLICY_FLAG_USER_RESTRICTION_POLICY) != 0;
}
+ boolean shouldSkipEnforcementIfNotChanged() {
+ return (mPolicyFlags & POLICY_FLAG_SKIP_ENFORCEMENT_IF_UNCHANGED) != 0;
+ }
+
@Nullable
PolicyValue<V> resolvePolicy(LinkedHashMap<EnforcingAdmin, PolicyValue<V>> adminsPolicy) {
return mResolutionMechanism.resolve(adminsPolicy);
@@ -610,7 +619,7 @@
* {@link Object#equals} implementation.
*/
private PolicyDefinition(
- PolicyKey key,
+ @NonNull PolicyKey key,
ResolutionMechanism<V> resolutionMechanism,
QuadFunction<V, Context, Integer, PolicyKey, Boolean> policyEnforcerCallback,
PolicySerializer<V> policySerializer) {
@@ -622,11 +631,12 @@
* {@link Object#equals} and {@link Object#hashCode()} implementation.
*/
private PolicyDefinition(
- PolicyKey policyKey,
+ @NonNull PolicyKey policyKey,
ResolutionMechanism<V> resolutionMechanism,
int policyFlags,
QuadFunction<V, Context, Integer, PolicyKey, Boolean> policyEnforcerCallback,
PolicySerializer<V> policySerializer) {
+ Objects.requireNonNull(policyKey);
mPolicyKey = policyKey;
mResolutionMechanism = resolutionMechanism;
mPolicyFlags = policyFlags;
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/PolicyEnforcerCallbacks.java b/services/devicepolicy/java/com/android/server/devicepolicy/PolicyEnforcerCallbacks.java
index 09eef45..04d277e 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/PolicyEnforcerCallbacks.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/PolicyEnforcerCallbacks.java
@@ -37,11 +37,13 @@
import android.app.usage.UsageStatsManagerInternal;
import android.content.ComponentName;
import android.content.Context;
+import android.content.Intent;
import android.content.IntentFilter;
import android.content.pm.IPackageManager;
import android.content.pm.PackageManager;
import android.content.pm.PackageManagerInternal;
import android.os.Binder;
+import android.os.Bundle;
import android.os.Process;
import android.os.RemoteException;
import android.os.ServiceManager;
@@ -172,6 +174,29 @@
return true;
}
+
+ /**
+ * Application restrictions are stored and retrieved from DPMS, so no enforcing (aka pushing
+ * it to UMS) is required. Only need to send broadcast to the target user here as we rely on
+ * the inheritable policy propagation logic in PolicyEngine to apply this policy to multiple
+ * profiles. The broadcast should only be sent when an application restriction is set, so we
+ * rely on the POLICY_FLAG_SKIP_ENFORCEMENT_IF_UNCHANGED flag so DPE only invokes this callback
+ * when the policy is set, and not during system boot or other situations.
+ */
+ static boolean setApplicationRestrictions(Bundle bundle, Context context, Integer userId,
+ PolicyKey policyKey) {
+ Binder.withCleanCallingIdentity(() -> {
+ PackagePolicyKey key = (PackagePolicyKey) policyKey;
+ String packageName = key.getPackageName();
+ Objects.requireNonNull(packageName);
+ Intent changeIntent = new Intent(Intent.ACTION_APPLICATION_RESTRICTIONS_CHANGED);
+ changeIntent.setPackage(packageName);
+ changeIntent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY);
+ context.sendBroadcastAsUser(changeIntent, UserHandle.of(userId));
+ });
+ return true;
+ }
+
private static class BlockingCallback {
private final CountDownLatch mLatch = new CountDownLatch(1);
private final AtomicReference<Boolean> mValue = new AtomicReference<>();
diff --git a/services/people/java/com/android/server/people/TEST_MAPPING b/services/people/java/com/android/server/people/TEST_MAPPING
new file mode 100644
index 0000000..55b355c
--- /dev/null
+++ b/services/people/java/com/android/server/people/TEST_MAPPING
@@ -0,0 +1,12 @@
+{
+ "presubmit": [
+ {
+ "name": "FrameworksServicesTests",
+ "options": [
+ {
+ "include-filter": "com.android.server.people.data"
+ }
+ ]
+ }
+ ]
+}
\ No newline at end of file
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 9e4f821..d307200 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
@@ -1276,7 +1276,23 @@
packageName,
permissionName
)
- else -> permissionAllowlist.getSignatureAppAllowlistState(packageName, permissionName)
+ else ->
+ permissionAllowlist.getProductSignatureAppAllowlistState(
+ packageName,
+ permissionName
+ )
+ ?: permissionAllowlist.getVendorSignatureAppAllowlistState(
+ packageName,
+ permissionName
+ )
+ ?: permissionAllowlist.getSystemExtSignatureAppAllowlistState(
+ packageName,
+ permissionName
+ )
+ ?: permissionAllowlist.getSignatureAppAllowlistState(
+ packageName,
+ permissionName
+ )
}
}
diff --git a/services/robotests/backup/src/com/android/server/backup/transport/TransportConnectionTest.java b/services/robotests/backup/src/com/android/server/backup/transport/TransportConnectionTest.java
index 6a82f16..3e87c6f 100644
--- a/services/robotests/backup/src/com/android/server/backup/transport/TransportConnectionTest.java
+++ b/services/robotests/backup/src/com/android/server/backup/transport/TransportConnectionTest.java
@@ -28,6 +28,7 @@
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.ArgumentMatchers.isNull;
import static org.mockito.Mockito.doAnswer;
+import static org.mockito.Mockito.doThrow;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
@@ -543,6 +544,18 @@
return future.get();
}
+ @Test
+ public void onBindingDied_referenceLost_doesNotThrow() {
+ TransportConnection.TransportConnectionMonitor transportConnectionMonitor =
+ new TransportConnection.TransportConnectionMonitor(
+ mContext, /* transportConnection= */ null);
+ doThrow(new IllegalArgumentException("Service not registered")).when(
+ mContext).unbindService(any());
+
+ // Test no exception is thrown
+ transportConnectionMonitor.onBindingDied(mTransportComponent);
+ }
+
private ServiceConnection verifyBindServiceAsUserAndCaptureServiceConnection(Context context) {
ArgumentCaptor<ServiceConnection> connectionCaptor =
ArgumentCaptor.forClass(ServiceConnection.class);
diff --git a/services/tests/InputMethodSystemServerTests/src/com/android/server/inputmethod/DefaultImeVisibilityApplierTest.java b/services/tests/InputMethodSystemServerTests/src/com/android/server/inputmethod/DefaultImeVisibilityApplierTest.java
index 1d225ba..221a991 100644
--- a/services/tests/InputMethodSystemServerTests/src/com/android/server/inputmethod/DefaultImeVisibilityApplierTest.java
+++ b/services/tests/InputMethodSystemServerTests/src/com/android/server/inputmethod/DefaultImeVisibilityApplierTest.java
@@ -36,9 +36,10 @@
import static org.mockito.ArgumentMatchers.notNull;
import static org.mockito.Mockito.anyInt;
import static org.mockito.Mockito.eq;
-import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.verify;
+import static java.util.Objects.requireNonNull;
+
import android.os.Binder;
import android.os.IBinder;
import android.os.RemoteException;
@@ -72,7 +73,10 @@
super.setUp();
mVisibilityApplier =
(DefaultImeVisibilityApplier) mInputMethodManagerService.getVisibilityApplier();
- mInputMethodManagerService.setAttachedClientForTesting(mock(ClientState.class));
+ synchronized (ImfLock.class) {
+ mInputMethodManagerService.setAttachedClientForTesting(requireNonNull(
+ mInputMethodManagerService.getClientStateLocked(mMockInputMethodClient)));
+ }
}
@Test
diff --git a/services/tests/InputMethodSystemServerTests/src/com/android/server/inputmethod/InputMethodInfoUtilsTest.java b/services/tests/InputMethodSystemServerTests/src/com/android/server/inputmethod/InputMethodInfoUtilsTest.java
new file mode 100644
index 0000000..50804da
--- /dev/null
+++ b/services/tests/InputMethodSystemServerTests/src/com/android/server/inputmethod/InputMethodInfoUtilsTest.java
@@ -0,0 +1,84 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.inputmethod;
+
+import static com.android.server.inputmethod.TestUtils.TEST_IME_ID1;
+import static com.android.server.inputmethod.TestUtils.TEST_IME_ID2;
+import static com.android.server.inputmethod.TestUtils.createFakeInputMethodInfo;
+import static com.android.server.inputmethod.TestUtils.createFakeSubtypes;
+
+import static org.junit.Assert.assertArrayEquals;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.view.inputmethod.InputMethodInfo;
+
+import androidx.annotation.NonNull;
+
+import org.junit.Test;
+
+import java.util.Arrays;
+import java.util.Objects;
+
+public final class InputMethodInfoUtilsTest {
+
+ @Test
+ public void testMarshalSameObject() {
+ final var imi = createFakeInputMethodInfo(TEST_IME_ID1, createFakeSubtypes(3));
+ final byte[] buf = InputMethodInfoUtils.marshal(imi);
+
+ assertArrayEquals("The same value must be returned when called multiple times",
+ buf, InputMethodInfoUtils.marshal(imi));
+ assertArrayEquals("The same value must be returned when called multiple times",
+ buf, InputMethodInfoUtils.marshal(imi));
+ }
+
+ @Test
+ public void testMarshalDifferentObjects() {
+ final var imi1 = createFakeInputMethodInfo(TEST_IME_ID1, createFakeSubtypes(3));
+ final var imi2 = createFakeInputMethodInfo(TEST_IME_ID2, createFakeSubtypes(0));
+
+ assertFalse("Different inputs must yield different byte patterns", Arrays.equals(
+ InputMethodInfoUtils.marshal(imi1), InputMethodInfoUtils.marshal(imi2)));
+ }
+
+ @NonNull
+ private static <T> T readTypedObject(byte[] data, @NonNull Parcelable.Creator<T> creator) {
+ Parcel parcel = null;
+ try {
+ parcel = Parcel.obtain();
+ parcel.unmarshall(data, 0, data.length);
+ parcel.setDataPosition(0);
+ return Objects.requireNonNull(parcel.readTypedObject(creator));
+ } finally {
+ if (parcel != null) {
+ parcel.recycle();
+ }
+ }
+ }
+
+ @Test
+ public void testUnmarshalSameObject() {
+ final var imi = createFakeInputMethodInfo(TEST_IME_ID1, createFakeSubtypes(3));
+ final var cloned = readTypedObject(InputMethodInfoUtils.marshal(imi),
+ InputMethodInfo.CREATOR);
+ assertEquals(imi.getPackageName(), cloned.getPackageName());
+ assertEquals(imi.getSubtypeCount(), cloned.getSubtypeCount());
+ }
+}
diff --git a/services/tests/InputMethodSystemServerTests/src/com/android/server/inputmethod/InputMethodManagerServiceTestBase.java b/services/tests/InputMethodSystemServerTests/src/com/android/server/inputmethod/InputMethodManagerServiceTestBase.java
index cff2265..3b25cb1 100644
--- a/services/tests/InputMethodSystemServerTests/src/com/android/server/inputmethod/InputMethodManagerServiceTestBase.java
+++ b/services/tests/InputMethodSystemServerTests/src/com/android/server/inputmethod/InputMethodManagerServiceTestBase.java
@@ -29,6 +29,7 @@
import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.ArgumentMatchers.notNull;
import static org.mockito.Mockito.eq;
+import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
@@ -45,6 +46,7 @@
import android.os.RemoteException;
import android.os.ServiceManager;
import android.os.UserHandle;
+import android.view.InputChannel;
import android.view.inputmethod.EditorInfo;
import android.window.ImeOnBackInvokedDispatcher;
@@ -53,6 +55,7 @@
import com.android.internal.compat.IPlatformCompat;
import com.android.internal.inputmethod.IInputMethod;
import com.android.internal.inputmethod.IInputMethodClient;
+import com.android.internal.inputmethod.IInputMethodSession;
import com.android.internal.inputmethod.IRemoteAccessibilityInputConnection;
import com.android.internal.inputmethod.IRemoteInputConnection;
import com.android.internal.inputmethod.InputBindResult;
@@ -104,6 +107,7 @@
@Mock protected UserManagerInternal mMockUserManagerInternal;
@Mock protected InputMethodBindingController mMockInputMethodBindingController;
@Mock protected IInputMethodClient mMockInputMethodClient;
+ @Mock protected IInputMethodSession mMockInputMethodSession;
@Mock protected IBinder mWindowToken;
@Mock protected IRemoteInputConnection mMockRemoteInputConnection;
@Mock protected IRemoteAccessibilityInputConnection mMockRemoteAccessibilityInputConnection;
@@ -123,6 +127,7 @@
protected IInputMethodInvoker mMockInputMethodInvoker;
protected InputMethodManagerService mInputMethodManagerService;
protected ServiceThread mServiceThread;
+ protected ServiceThread mPackageMonitorThread;
protected boolean mIsLargeScreen;
private InputManagerGlobal.TestSession mInputManagerGlobalSession;
@@ -218,10 +223,17 @@
mServiceThread =
new ServiceThread(
- "TestServiceThread",
- Process.THREAD_PRIORITY_FOREGROUND, /* allowIo */
- false);
- mInputMethodManagerService = new InputMethodManagerService(mContext, mServiceThread,
+ "immstest1",
+ Process.THREAD_PRIORITY_FOREGROUND,
+ true /* allowIo */);
+ mPackageMonitorThread =
+ new ServiceThread(
+ "immstest2",
+ Process.THREAD_PRIORITY_FOREGROUND,
+ true /* allowIo */);
+ mInputMethodManagerService = new InputMethodManagerService(mContext,
+ InputMethodManagerService.shouldEnableExperimentalConcurrentMultiUserMode(mContext),
+ mServiceThread, mPackageMonitorThread,
unusedUserId -> mMockInputMethodBindingController);
spyOn(mInputMethodManagerService);
@@ -246,6 +258,7 @@
// Call InputMethodManagerService#addClient() as a preparation to start interacting with it.
mInputMethodManagerService.addClient(mMockInputMethodClient, mMockRemoteInputConnection, 0);
+ createSessionForClient(mMockInputMethodClient);
}
@After
@@ -254,6 +267,10 @@
mInputMethodManagerService.mInputMethodDeviceConfigs.destroy();
}
+ if (mPackageMonitorThread != null) {
+ mPackageMonitorThread.quitSafely();
+ }
+
if (mServiceThread != null) {
mServiceThread.quitSafely();
}
@@ -295,4 +312,13 @@
.hideSoftInput(any() /* hideInputToken */, notNull() /* statsToken */,
anyInt() /* flags */, any() /* resultReceiver */);
}
+
+ protected void createSessionForClient(IInputMethodClient client) {
+ synchronized (ImfLock.class) {
+ ClientState cs = mInputMethodManagerService.getClientStateLocked(client);
+ cs.mCurSession = new InputMethodManagerService.SessionState(cs,
+ mMockInputMethodInvoker, mMockInputMethodSession, mock(
+ InputChannel.class));
+ }
+ }
}
diff --git a/services/tests/InputMethodSystemServerTests/src/com/android/server/inputmethod/InputMethodMapTest.java b/services/tests/InputMethodSystemServerTests/src/com/android/server/inputmethod/InputMethodMapTest.java
new file mode 100644
index 0000000..be70421
--- /dev/null
+++ b/services/tests/InputMethodSystemServerTests/src/com/android/server/inputmethod/InputMethodMapTest.java
@@ -0,0 +1,93 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.inputmethod;
+
+import static com.android.server.inputmethod.TestUtils.TEST_IME_ID1;
+import static com.android.server.inputmethod.TestUtils.TEST_IME_ID2;
+import static com.android.server.inputmethod.TestUtils.TEST_IME_ID3;
+import static com.android.server.inputmethod.TestUtils.createFakeInputMethodInfo;
+import static com.android.server.inputmethod.TestUtils.createFakeSubtypes;
+
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+
+import android.util.ArrayMap;
+import android.view.inputmethod.InputMethodInfo;
+
+import androidx.annotation.NonNull;
+
+import org.junit.Test;
+
+public final class InputMethodMapTest {
+
+ @NonNull
+ private static InputMethodMap toMap(InputMethodInfo... list) {
+ final ArrayMap<String, InputMethodInfo> map = new ArrayMap<>();
+ for (var imi : list) {
+ map.put(imi.getId(), imi);
+ }
+ return InputMethodMap.of(map);
+ }
+
+ @Test
+ public void testAreSameSameObject() {
+ final var imi1 = createFakeInputMethodInfo(TEST_IME_ID1, createFakeSubtypes(0));
+ final var imi2 = createFakeInputMethodInfo(TEST_IME_ID2, createFakeSubtypes(3));
+ final var map = toMap(imi1, imi2);
+ assertTrue("Must return true for the same instance",
+ InputMethodMap.areSame(map, map));
+ }
+
+ @Test
+ public void testAreSameEquivalentObject() {
+ final var imi1 = createFakeInputMethodInfo(TEST_IME_ID1, createFakeSubtypes(0));
+ final var imi2 = createFakeInputMethodInfo(TEST_IME_ID2, createFakeSubtypes(3));
+ assertTrue("Must return true for the equivalent instances",
+ InputMethodMap.areSame(toMap(imi1, imi2), toMap(imi1, imi2)));
+
+ assertTrue("Must return true for the equivalent instances",
+ InputMethodMap.areSame(toMap(imi1, imi2), toMap(imi2, imi1)));
+ }
+
+ @Test
+ public void testAreSameDifferentKeys() {
+ final var imi1 = createFakeInputMethodInfo(TEST_IME_ID1, createFakeSubtypes(0));
+ final var imi2 = createFakeInputMethodInfo(TEST_IME_ID2, createFakeSubtypes(3));
+ final var imi3 = createFakeInputMethodInfo(TEST_IME_ID3, createFakeSubtypes(3));
+ assertFalse("Must return false if keys are different",
+ InputMethodMap.areSame(toMap(imi1), toMap(imi1, imi2)));
+ assertFalse("Must return false if keys are different",
+ InputMethodMap.areSame(toMap(imi1, imi2), toMap(imi1)));
+ assertFalse("Must return false if keys are different",
+ InputMethodMap.areSame(toMap(imi1, imi2), toMap(imi1, imi3)));
+ }
+
+ @Test
+ public void testAreSameDifferentValues() {
+ final var imi1_without_subtypes =
+ createFakeInputMethodInfo(TEST_IME_ID1, createFakeSubtypes(0));
+ final var imi1_with_subtypes =
+ createFakeInputMethodInfo(TEST_IME_ID1, createFakeSubtypes(3));
+ final var imi2 = createFakeInputMethodInfo(TEST_IME_ID2, createFakeSubtypes(3));
+ assertFalse("Must return false if values are different",
+ InputMethodMap.areSame(toMap(imi1_without_subtypes), toMap(imi1_with_subtypes)));
+ assertFalse("Must return false if values are different",
+ InputMethodMap.areSame(
+ toMap(imi1_without_subtypes, imi2),
+ toMap(imi1_with_subtypes, imi2)));
+ }
+}
diff --git a/services/tests/InputMethodSystemServerTests/src/com/android/server/inputmethod/TestUtils.java b/services/tests/InputMethodSystemServerTests/src/com/android/server/inputmethod/TestUtils.java
new file mode 100644
index 0000000..c51ff87f
--- /dev/null
+++ b/services/tests/InputMethodSystemServerTests/src/com/android/server/inputmethod/TestUtils.java
@@ -0,0 +1,101 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.inputmethod;
+
+import android.content.ComponentName;
+import android.content.pm.ApplicationInfo;
+import android.content.pm.ResolveInfo;
+import android.content.pm.ServiceInfo;
+import android.view.inputmethod.InputMethodInfo;
+import android.view.inputmethod.InputMethodSubtype;
+
+import androidx.annotation.NonNull;
+
+import java.util.ArrayList;
+import java.util.Objects;
+
+public final class TestUtils {
+ /**
+ * {@link ComponentName} for fake {@link InputMethodInfo}.
+ */
+ @NonNull
+ public static final ComponentName TEST_IME_ID1 = Objects.requireNonNull(
+ ComponentName.unflattenFromString("com.android.test.testime1/.InputMethod"));
+
+ /**
+ * {@link ComponentName} for fake {@link InputMethodInfo}.
+ */
+ @NonNull
+ public static final ComponentName TEST_IME_ID2 = Objects.requireNonNull(
+ ComponentName.unflattenFromString("com.android.test.testime2/.InputMethod"));
+
+ /**
+ * {@link ComponentName} for fake {@link InputMethodInfo}.
+ */
+ @NonNull
+ public static final ComponentName TEST_IME_ID3 = Objects.requireNonNull(
+ ComponentName.unflattenFromString("com.android.test.testime3/.InputMethod"));
+
+ /**
+ * Creates a list of fake {@link InputMethodSubtype} for unit testing for the given number.
+ *
+ * @param count The number of fake {@link InputMethodSubtype} objects
+ * @return The list of fake {@link InputMethodSubtype} objects
+ */
+ @NonNull
+ public static ArrayList<InputMethodSubtype> createFakeSubtypes(int count) {
+ final ArrayList<InputMethodSubtype> subtypes = new ArrayList<>(count);
+ for (int i = 0; i < count; ++i) {
+ subtypes.add(
+ new InputMethodSubtype.InputMethodSubtypeBuilder()
+ .setSubtypeId(i + 0x100)
+ .setLanguageTag("en-US")
+ .setSubtypeNameOverride("TestSubtype" + i)
+ .build());
+ }
+ return subtypes;
+ }
+
+ /**
+ * Creates a fake {@link InputMethodInfo} for unit testing.
+ *
+ * @param componentName {@link ComponentName} of the fake {@link InputMethodInfo}
+ * @param subtypes A list of (fake) {@link InputMethodSubtype}
+ * @return a fake {@link InputMethodInfo} object
+ */
+ @NonNull
+ public static InputMethodInfo createFakeInputMethodInfo(
+ @NonNull ComponentName componentName, @NonNull ArrayList<InputMethodSubtype> subtypes) {
+ final ApplicationInfo ai = new ApplicationInfo();
+ ai.packageName = componentName.getPackageName();
+ ai.enabled = true;
+
+ final ServiceInfo si = new ServiceInfo();
+ si.applicationInfo = ai;
+ si.enabled = true;
+ si.packageName = componentName.getPackageName();
+ si.name = componentName.getClassName();
+ si.exported = true;
+ si.nonLocalizedLabel = "Fake Label";
+
+ final ResolveInfo ri = new ResolveInfo();
+ ri.serviceInfo = si;
+
+ return new InputMethodInfo(ri, false /* isAuxIme */, null /* settingsActivity */,
+ subtypes, 0 /* isDefaultResId */, false /* forceDefault */);
+ }
+}
diff --git a/services/tests/PackageManagerServiceTests/server/src/com/android/server/pm/UserDataPreparerTest.java b/services/tests/PackageManagerServiceTests/server/src/com/android/server/pm/UserDataPreparerTest.java
index 9e11fa2..e545a49 100644
--- a/services/tests/PackageManagerServiceTests/server/src/com/android/server/pm/UserDataPreparerTest.java
+++ b/services/tests/PackageManagerServiceTests/server/src/com/android/server/pm/UserDataPreparerTest.java
@@ -71,7 +71,7 @@
@Mock
private Installer mInstaller;
- private Object mInstallLock;
+ private PackageManagerTracedLock mInstallLock;
@Before
public void setup() {
@@ -79,7 +79,7 @@
TEST_USER.serialNumber = TEST_USER_SERIAL;
Context ctx = InstrumentationRegistry.getContext();
FileUtils.deleteContents(ctx.getCacheDir());
- mInstallLock = new Object();
+ mInstallLock = new PackageManagerTracedLock();
MockitoAnnotations.initMocks(this);
mUserDataPreparer = new TestUserDataPreparer(mInstaller, mInstallLock, mContextMock,
ctx.getCacheDir());
@@ -238,8 +238,8 @@
private static class TestUserDataPreparer extends UserDataPreparer {
File testDir;
- TestUserDataPreparer(Installer installer, Object installLock, Context context,
- File testDir) {
+ TestUserDataPreparer(Installer installer, PackageManagerTracedLock installLock,
+ Context context, File testDir) {
super(installer, installLock, context);
this.testDir = testDir;
}
diff --git a/services/tests/apexsystemservices/OWNERS b/services/tests/apexsystemservices/OWNERS
index 0295b9e..8b6675a 100644
--- a/services/tests/apexsystemservices/OWNERS
+++ b/services/tests/apexsystemservices/OWNERS
@@ -1,4 +1 @@
-omakoto@google.com
-satayev@google.com
-
include platform/packages/modules/common:/OWNERS
diff --git a/services/tests/displayservicetests/src/com/android/server/display/DisplayDeviceConfigTest.java b/services/tests/displayservicetests/src/com/android/server/display/DisplayDeviceConfigTest.java
index a0a611f..46d08b0 100644
--- a/services/tests/displayservicetests/src/com/android/server/display/DisplayDeviceConfigTest.java
+++ b/services/tests/displayservicetests/src/com/android/server/display/DisplayDeviceConfigTest.java
@@ -21,7 +21,6 @@
import static com.android.server.display.AutomaticBrightnessController.AUTO_BRIGHTNESS_MODE_DEFAULT;
import static com.android.server.display.AutomaticBrightnessController.AUTO_BRIGHTNESS_MODE_DOZE;
import static com.android.server.display.config.SensorData.TEMPERATURE_TYPE_SKIN;
-import static com.android.server.display.config.SensorData.SupportedMode;
import static com.android.server.display.utils.DeviceConfigParsingUtils.ambientBrightnessThresholdsIntToFloat;
import static com.android.server.display.utils.DeviceConfigParsingUtils.displayBrightnessThresholdsIntToFloat;
@@ -58,6 +57,7 @@
import com.android.server.display.config.HysteresisLevels;
import com.android.server.display.config.IdleScreenRefreshRateTimeoutLuxThresholdPoint;
import com.android.server.display.config.RefreshRateData;
+import com.android.server.display.config.SupportedModeData;
import com.android.server.display.config.ThermalStatus;
import com.android.server.display.feature.DisplayManagerFlags;
import com.android.server.display.feature.flags.Flags;
@@ -613,7 +613,7 @@
assertEquals(mDisplayDeviceConfig.getProximitySensor().minRefreshRate, 60, SMALL_DELTA);
assertEquals(mDisplayDeviceConfig.getProximitySensor().maxRefreshRate, 90, SMALL_DELTA);
assertThat(mDisplayDeviceConfig.getProximitySensor().supportedModes).hasSize(2);
- SupportedMode mode = mDisplayDeviceConfig.getProximitySensor().supportedModes.get(0);
+ SupportedModeData mode = mDisplayDeviceConfig.getProximitySensor().supportedModes.get(0);
assertEquals(mode.refreshRate, 60, SMALL_DELTA);
assertEquals(mode.vsyncRate, 65, SMALL_DELTA);
mode = mDisplayDeviceConfig.getProximitySensor().supportedModes.get(1);
@@ -933,6 +933,21 @@
assertEquals(0.2f, mDisplayDeviceConfig.getNitsFromBacklight(0.0f), ZERO_DELTA);
}
+ @Test
+ public void testLowPowerSupportedModesFromConfigFile() throws IOException {
+ setupDisplayDeviceConfigFromDisplayConfigFile();
+
+ RefreshRateData refreshRateData = mDisplayDeviceConfig.getRefreshRateData();
+ assertNotNull(refreshRateData);
+ assertThat(refreshRateData.lowPowerSupportedModes).hasSize(2);
+ SupportedModeData supportedModeData = refreshRateData.lowPowerSupportedModes.get(0);
+ assertThat(supportedModeData.refreshRate).isEqualTo(60);
+ assertThat(supportedModeData.vsyncRate).isEqualTo(60);
+ supportedModeData = refreshRateData.lowPowerSupportedModes.get(1);
+ assertThat(supportedModeData.refreshRate).isEqualTo(60);
+ assertThat(supportedModeData.vsyncRate).isEqualTo(120);
+ }
+
private String getValidLuxThrottling() {
return "<luxThrottling>\n"
+ " <brightnessLimitMap>\n"
@@ -1089,6 +1104,19 @@
+ "</proxSensor>\n";
}
+ private String getLowPowerConfig() {
+ return "<lowPowerSupportedModes>\n"
+ + " <point>\n"
+ + " <first>60</first>\n"
+ + " <second>60</second>\n"
+ + " </point>\n"
+ + " <point>\n"
+ + " <first>60</first>\n"
+ + " <second>120</second>\n"
+ + " </point>\n"
+ + "</lowPowerSupportedModes>\n";
+ }
+
private String getHdrBrightnessConfig() {
return "<hdrBrightnessConfig>\n"
+ " <brightnessMap>\n"
@@ -1620,6 +1648,7 @@
+ "</displayBrightnessPoint>\n"
+ "</blockingZoneThreshold>\n"
+ "</higherBlockingZoneConfigs>\n"
+ + getLowPowerConfig()
+ "</refreshRate>\n"
+ "<screenOffBrightnessSensorValueToLux>\n"
+ "<item>-1</item>\n"
diff --git a/services/tests/displayservicetests/src/com/android/server/display/DisplayManagerServiceTest.java b/services/tests/displayservicetests/src/com/android/server/display/DisplayManagerServiceTest.java
index 54b2d4d..8844e6c 100644
--- a/services/tests/displayservicetests/src/com/android/server/display/DisplayManagerServiceTest.java
+++ b/services/tests/displayservicetests/src/com/android/server/display/DisplayManagerServiceTest.java
@@ -709,6 +709,64 @@
assertTrue((ddi.flags & DisplayDeviceInfo.FLAG_OWN_FOCUS) == 0);
}
+ @Test
+ public void testCreateVirtualDisplayOwnFocus_checkDisplayDeviceInfo() throws RemoteException {
+ DisplayManagerService displayManager =
+ new DisplayManagerService(mContext, mBasicInjector);
+ registerDefaultDisplays(displayManager);
+
+ // This is effectively the DisplayManager service published to ServiceManager.
+ DisplayManagerService.BinderService bs = displayManager.new BinderService();
+
+ final String uniqueId = "uniqueId --- Own Focus Test -- checkDisplayDeviceInfo";
+ float refreshRate = 60.0f;
+ int width = 600;
+ int height = 800;
+ int dpi = 320;
+ int flags = DisplayManager.VIRTUAL_DISPLAY_FLAG_OWN_FOCUS;
+
+ when(mContext.checkCallingPermission(ADD_TRUSTED_DISPLAY)).thenReturn(
+ PackageManager.PERMISSION_GRANTED);
+ when(mMockAppToken.asBinder()).thenReturn(mMockAppToken);
+ final VirtualDisplayConfig.Builder builder = new VirtualDisplayConfig.Builder(
+ VIRTUAL_DISPLAY_NAME, width, height, dpi);
+ builder.setFlags(flags);
+ builder.setUniqueId(uniqueId);
+ builder.setRequestedRefreshRate(refreshRate);
+
+ // Create a virtual display in its own display group.
+ final VirtualDisplayConfig ownerDisplayConfig = builder.build();
+ int displayId = bs.createVirtualDisplay(ownerDisplayConfig, /* callback= */ mMockAppToken,
+ /* projection= */ null, PACKAGE_NAME);
+ verify(mMockProjectionService, never()).setContentRecordingSession(any(),
+ nullable(IMediaProjection.class));
+
+ DisplayInfo displayInfo = bs.getDisplayInfo(displayId);
+ assertNotNull(displayInfo);
+ assertTrue((displayInfo.flags & DisplayDeviceInfo.FLAG_OWN_FOCUS) == 0);
+ final String displayUniqueId = VirtualDisplayAdapter.generateDisplayUniqueId(
+ PACKAGE_NAME, Process.myUid(), ownerDisplayConfig);
+ assertEquals(displayInfo.uniqueId, displayUniqueId);
+ assertEquals(displayInfo.name, VIRTUAL_DISPLAY_NAME);
+ assertEquals(displayInfo.ownerPackageName, PACKAGE_NAME);
+ assertEquals(displayInfo.getRefreshRate(), refreshRate, 0.1f);
+
+ performTraversalInternal(displayManager);
+
+ // Flush the handler.
+ displayManager.getDisplayHandler().runWithScissors(() -> {}, /* now= */ 0);
+
+ DisplayDeviceInfo ddi = displayManager.getDisplayDeviceInfoInternal(displayId);
+ assertNotNull(ddi);
+ assertTrue((ddi.flags & DisplayDeviceInfo.FLAG_OWN_FOCUS) == 0);
+ assertEquals(ddi.width, width);
+ assertEquals(ddi.height, height);
+ assertEquals(ddi.name, displayInfo.name);
+ assertEquals(ddi.ownerPackageName, displayInfo.ownerPackageName);
+ assertEquals(ddi.uniqueId, displayInfo.uniqueId);
+ assertEquals(ddi.renderFrameRate, displayInfo.getRefreshRate(), 0.1f);
+ }
+
/**
* Tests that the virtual display is created along-side the default display.
*/
diff --git a/services/tests/displayservicetests/src/com/android/server/display/VirtualDisplayAdapterTest.java b/services/tests/displayservicetests/src/com/android/server/display/VirtualDisplayAdapterTest.java
index c01b15c..81e6cc3 100644
--- a/services/tests/displayservicetests/src/com/android/server/display/VirtualDisplayAdapterTest.java
+++ b/services/tests/displayservicetests/src/com/android/server/display/VirtualDisplayAdapterTest.java
@@ -18,12 +18,14 @@
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertTrue;
import static org.mockito.Mockito.when;
import android.content.Context;
import android.hardware.display.IVirtualDisplayCallback;
import android.hardware.display.VirtualDisplayConfig;
import android.os.IBinder;
+import android.os.Process;
import androidx.test.ext.junit.runners.AndroidJUnit4;
import androidx.test.filters.SmallTest;
@@ -88,6 +90,25 @@
}
@Test
+ public void testCreatesVirtualDisplay_checkGeneratedDisplayUniqueIdPrefix() {
+ VirtualDisplayConfig config = new VirtualDisplayConfig.Builder("test", /* width= */ 1,
+ /* height= */ 1, /* densityDpi= */ 1).build();
+
+ final String packageName = "testpackage";
+ final String displayUniqueId = VirtualDisplayAdapter.generateDisplayUniqueId(
+ packageName, Process.myUid(), config);
+
+ DisplayDevice result = mVirtualDisplayAdapter.createVirtualDisplayLocked(
+ mMockCallback, /* projection= */ null, /* ownerUid= */ 10,
+ packageName, displayUniqueId, /* surface= */ null, /* flags= */ 0, config);
+
+ assertNotNull(result);
+
+ final String uniqueId = result.getUniqueId();
+ assertTrue(uniqueId.startsWith(VirtualDisplayAdapter.UNIQUE_ID_PREFIX + packageName));
+ }
+
+ @Test
public void testDoesNotCreateVirtualDisplayForSameCallback() {
VirtualDisplayConfig config1 = new VirtualDisplayConfig.Builder("test", /* width= */ 1,
/* height= */ 1, /* densityDpi= */ 1).build();
diff --git a/services/tests/displayservicetests/src/com/android/server/display/brightness/DisplayBrightnessStrategySelectorTest.java b/services/tests/displayservicetests/src/com/android/server/display/brightness/DisplayBrightnessStrategySelectorTest.java
index ae6361b..df96712 100644
--- a/services/tests/displayservicetests/src/com/android/server/display/brightness/DisplayBrightnessStrategySelectorTest.java
+++ b/services/tests/displayservicetests/src/com/android/server/display/brightness/DisplayBrightnessStrategySelectorTest.java
@@ -46,6 +46,7 @@
import com.android.server.display.brightness.strategy.BoostBrightnessStrategy;
import com.android.server.display.brightness.strategy.DisplayBrightnessStrategy;
import com.android.server.display.brightness.strategy.DozeBrightnessStrategy;
+import com.android.server.display.brightness.strategy.FallbackBrightnessStrategy;
import com.android.server.display.brightness.strategy.FollowerBrightnessStrategy;
import com.android.server.display.brightness.strategy.InvalidBrightnessStrategy;
import com.android.server.display.brightness.strategy.OffloadBrightnessStrategy;
@@ -90,6 +91,8 @@
@Mock
private AutoBrightnessFallbackStrategy mAutoBrightnessFallbackStrategy;
@Mock
+ private FallbackBrightnessStrategy mFallbackBrightnessStrategy;
+ @Mock
private Resources mResources;
@Mock
private DisplayManagerFlags mDisplayManagerFlags;
@@ -135,7 +138,7 @@
@Override
AutomaticBrightnessStrategy getAutomaticBrightnessStrategy1(Context context,
- int displayId) {
+ int displayId, DisplayManagerFlags displayManagerFlags) {
return mAutomaticBrightnessStrategy;
}
@@ -155,6 +158,11 @@
AutoBrightnessFallbackStrategy getAutoBrightnessFallbackStrategy() {
return mAutoBrightnessFallbackStrategy;
}
+
+ @Override
+ FallbackBrightnessStrategy getFallbackBrightnessStrategy() {
+ return mFallbackBrightnessStrategy;
+ }
};
@Rule
@@ -355,6 +363,25 @@
}
@Test
+ public void selectStrategy_selectsFallbackStrategyAsAnUltimateFallback() {
+ when(mDisplayManagerFlags.isRefactorDisplayPowerControllerEnabled()).thenReturn(true);
+ mDisplayBrightnessStrategySelector = new DisplayBrightnessStrategySelector(mContext,
+ mInjector, DISPLAY_ID, mDisplayManagerFlags);
+ DisplayManagerInternal.DisplayPowerRequest displayPowerRequest = mock(
+ DisplayManagerInternal.DisplayPowerRequest.class);
+ displayPowerRequest.policy = DisplayManagerInternal.DisplayPowerRequest.POLICY_BRIGHT;
+ displayPowerRequest.screenBrightnessOverride = Float.NaN;
+ when(mFollowerBrightnessStrategy.getBrightnessToFollow()).thenReturn(Float.NaN);
+ when(mTemporaryBrightnessStrategy.getTemporaryScreenBrightness()).thenReturn(Float.NaN);
+ when(mAutomaticBrightnessStrategy.shouldUseAutoBrightness()).thenReturn(false);
+ when(mAutomaticBrightnessStrategy.isAutoBrightnessValid()).thenReturn(false);
+ assertEquals(mDisplayBrightnessStrategySelector.selectStrategy(
+ new StrategySelectionRequest(displayPowerRequest, Display.STATE_ON,
+ 0.1f, false)),
+ mFallbackBrightnessStrategy);
+ }
+
+ @Test
public void selectStrategyCallsPostProcessorForAllStrategies() {
when(mDisplayManagerFlags.isRefactorDisplayPowerControllerEnabled()).thenReturn(true);
mDisplayBrightnessStrategySelector = new DisplayBrightnessStrategySelector(mContext,
diff --git a/services/tests/displayservicetests/src/com/android/server/display/brightness/strategy/AutomaticBrightnessStrategyTest.java b/services/tests/displayservicetests/src/com/android/server/display/brightness/strategy/AutomaticBrightnessStrategyTest.java
index 3e78118..d19f479 100644
--- a/services/tests/displayservicetests/src/com/android/server/display/brightness/strategy/AutomaticBrightnessStrategyTest.java
+++ b/services/tests/displayservicetests/src/com/android/server/display/brightness/strategy/AutomaticBrightnessStrategyTest.java
@@ -18,8 +18,10 @@
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
+import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.Mockito.any;
import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.never;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
@@ -45,6 +47,7 @@
import com.android.server.display.brightness.BrightnessEvent;
import com.android.server.display.brightness.BrightnessReason;
import com.android.server.display.brightness.StrategyExecutionRequest;
+import com.android.server.display.feature.DisplayManagerFlags;
import org.junit.After;
import org.junit.Before;
@@ -64,6 +67,9 @@
@Mock
private AutomaticBrightnessController mAutomaticBrightnessController;
+ @Mock
+ private DisplayManagerFlags mDisplayManagerFlags;
+
private BrightnessConfiguration mBrightnessConfiguration;
private float mDefaultScreenAutoBrightnessAdjustment;
private Context mContext;
@@ -80,7 +86,8 @@
Settings.System.SCREEN_AUTO_BRIGHTNESS_ADJ, Float.NaN);
Settings.System.putFloat(mContext.getContentResolver(),
Settings.System.SCREEN_AUTO_BRIGHTNESS_ADJ, 0.5f);
- mAutomaticBrightnessStrategy = new AutomaticBrightnessStrategy(mContext, DISPLAY_ID);
+ mAutomaticBrightnessStrategy = new AutomaticBrightnessStrategy(mContext, DISPLAY_ID,
+ mDisplayManagerFlags);
mBrightnessConfiguration = new BrightnessConfiguration.Builder(
new float[]{0f, 1f}, new float[]{0, PowerManager.BRIGHTNESS_ON}).build();
@@ -247,6 +254,46 @@
}
@Test
+ public void testAutoBrightnessState_modeSwitch() {
+ // Setup the test
+ when(mDisplayManagerFlags.areAutoBrightnessModesEnabled()).thenReturn(true);
+ mAutomaticBrightnessStrategy.setUseAutoBrightness(true);
+ boolean allowAutoBrightnessWhileDozing = false;
+ int brightnessReason = BrightnessReason.REASON_UNKNOWN;
+ float lastUserSetBrightness = 0.2f;
+ boolean userSetBrightnessChanged = true;
+ int policy = DisplayManagerInternal.DisplayPowerRequest.POLICY_BRIGHT;
+ float pendingBrightnessAdjustment = 0.1f;
+ Settings.System.putFloat(mContext.getContentResolver(),
+ Settings.System.SCREEN_AUTO_BRIGHTNESS_ADJ, pendingBrightnessAdjustment);
+ mAutomaticBrightnessStrategy.updatePendingAutoBrightnessAdjustments();
+
+ // Validate no interaction when automaticBrightnessController is in idle mode
+ when(mAutomaticBrightnessController.isInIdleMode()).thenReturn(true);
+ mAutomaticBrightnessStrategy.setAutoBrightnessState(Display.STATE_ON,
+ allowAutoBrightnessWhileDozing, brightnessReason, policy, lastUserSetBrightness,
+ userSetBrightnessChanged);
+ verify(mAutomaticBrightnessController, never()).switchMode(anyInt());
+
+ // Validate interaction when automaticBrightnessController is in non-idle mode, and display
+ // state is ON
+ when(mAutomaticBrightnessController.isInIdleMode()).thenReturn(false);
+ mAutomaticBrightnessStrategy.setAutoBrightnessState(Display.STATE_ON,
+ allowAutoBrightnessWhileDozing, brightnessReason, policy, lastUserSetBrightness,
+ userSetBrightnessChanged);
+ verify(mAutomaticBrightnessController).switchMode(
+ AutomaticBrightnessController.AUTO_BRIGHTNESS_MODE_DEFAULT);
+
+ // Validate interaction when automaticBrightnessController is in non-idle mode, and display
+ // state is DOZE
+ mAutomaticBrightnessStrategy.setAutoBrightnessState(Display.STATE_DOZE,
+ allowAutoBrightnessWhileDozing, brightnessReason, policy, lastUserSetBrightness,
+ userSetBrightnessChanged);
+ verify(mAutomaticBrightnessController).switchMode(
+ AutomaticBrightnessController.AUTO_BRIGHTNESS_MODE_DOZE);
+ }
+
+ @Test
public void accommodateUserBrightnessChangesWorksAsExpected() {
// Verify the state if automaticBrightnessController is configured.
assertFalse(mAutomaticBrightnessStrategy.isShortTermModelActive());
@@ -348,7 +395,7 @@
automaticBrightnessController);
assertEquals(automaticScreenBrightness,
mAutomaticBrightnessStrategy.getAutomaticScreenBrightness(
- new BrightnessEvent(DISPLAY_ID)), 0.0f);
+ new BrightnessEvent(DISPLAY_ID), false), 0.0f);
assertEquals(automaticScreenBrightness,
mAutomaticBrightnessStrategy.getAutomaticScreenBrightnessBasedOnLastUsedLux(
new BrightnessEvent(DISPLAY_ID)), 0.0f);
@@ -390,7 +437,8 @@
@Test
public void testVerifyNoAutoBrightnessAdjustmentsArePopulatedForNonDefaultDisplay() {
int newDisplayId = 1;
- mAutomaticBrightnessStrategy = new AutomaticBrightnessStrategy(mContext, newDisplayId);
+ mAutomaticBrightnessStrategy = new AutomaticBrightnessStrategy(mContext, newDisplayId,
+ mDisplayManagerFlags);
mAutomaticBrightnessStrategy.putAutoBrightnessAdjustmentSetting(0.3f);
assertEquals(0.5f, mAutomaticBrightnessStrategy.getAutoBrightnessAdjustment(),
0.0f);
@@ -413,8 +461,12 @@
}
@Test
- public void isAutoBrightnessValid_returnsTrueWhenBrightnessIsValid() {
+ public void isAutoBrightnessValid_returnsTrueWhenBrightnessIsValid_adjustsAutoBrightness()
+ throws Settings.SettingNotFoundException {
+ float adjustment = 0.1f;
mAutomaticBrightnessStrategy.setUseAutoBrightness(true);
+ when(mAutomaticBrightnessController.getAutomaticScreenBrightnessAdjustment())
+ .thenReturn(0.1f);
mAutomaticBrightnessStrategy.setAutoBrightnessState(Display.STATE_ON, true,
BrightnessReason.REASON_UNKNOWN,
DisplayManagerInternal.DisplayPowerRequest.POLICY_BRIGHT, 0.1f,
@@ -422,6 +474,11 @@
when(mAutomaticBrightnessController.getAutomaticScreenBrightness(null))
.thenReturn(0.2f);
assertTrue(mAutomaticBrightnessStrategy.isAutoBrightnessValid());
+ assertEquals(adjustment, mAutomaticBrightnessStrategy.getAutoBrightnessAdjustment(), 0.0f);
+ assertEquals(adjustment, Settings.System.getFloatForUser(
+ mContext.getContentResolver(),
+ Settings.System.SCREEN_AUTO_BRIGHTNESS_ADJ,
+ UserHandle.USER_CURRENT), 0.0f);
}
@Test
@@ -429,8 +486,7 @@
updateBrightness_constructsDisplayBrightnessState_withAdjustmentAutoAdjustmentFlag() {
BrightnessEvent brightnessEvent = new BrightnessEvent(DISPLAY_ID);
mAutomaticBrightnessStrategy = new AutomaticBrightnessStrategy(
- mContext, DISPLAY_ID, displayId -> brightnessEvent);
- new AutomaticBrightnessStrategy(mContext, DISPLAY_ID);
+ mContext, DISPLAY_ID, displayId -> brightnessEvent, mDisplayManagerFlags);
mAutomaticBrightnessStrategy.setAutomaticBrightnessController(
mAutomaticBrightnessController);
float brightness = 0.4f;
@@ -439,6 +495,15 @@
when(mAutomaticBrightnessController.getAutomaticScreenBrightness(brightnessEvent))
.thenReturn(brightness);
+
+ // We do this to apply the automatic brightness adjustments
+ when(mAutomaticBrightnessController.getAutomaticScreenBrightnessAdjustment()).thenReturn(
+ 0.25f);
+ when(mAutomaticBrightnessController.getAutomaticScreenBrightness(null))
+ .thenReturn(brightness);
+ assertEquals(brightness, mAutomaticBrightnessStrategy
+ .getAutomaticScreenBrightness(null, false), 0.0f);
+
DisplayManagerInternal.DisplayPowerRequest displayPowerRequest =
mock(DisplayManagerInternal.DisplayPowerRequest.class);
DisplayBrightnessState expectedDisplayBrightnessState = new DisplayBrightnessState.Builder()
@@ -461,8 +526,7 @@
updateBrightness_constructsDisplayBrightnessState_withAdjustmentTempAdjustmentFlag() {
BrightnessEvent brightnessEvent = new BrightnessEvent(DISPLAY_ID);
mAutomaticBrightnessStrategy = new AutomaticBrightnessStrategy(
- mContext, DISPLAY_ID, displayId -> brightnessEvent);
- new AutomaticBrightnessStrategy(mContext, DISPLAY_ID);
+ mContext, DISPLAY_ID, displayId -> brightnessEvent, mDisplayManagerFlags);
mAutomaticBrightnessStrategy.setAutomaticBrightnessController(
mAutomaticBrightnessController);
float brightness = 0.4f;
@@ -483,6 +547,12 @@
when(mAutomaticBrightnessController.getAutomaticScreenBrightnessAdjustment()).thenReturn(
autoBrightnessAdjustment);
+ // We do this to apply the automatic brightness adjustments
+ when(mAutomaticBrightnessController.getAutomaticScreenBrightness(null))
+ .thenReturn(brightness);
+ assertEquals(brightness, mAutomaticBrightnessStrategy
+ .getAutomaticScreenBrightness(null, false), 0.0f);
+
DisplayBrightnessState expectedDisplayBrightnessState = new DisplayBrightnessState.Builder()
.setBrightness(brightness)
.setSdrBrightness(brightness)
diff --git a/services/tests/displayservicetests/src/com/android/server/display/brightness/strategy/FallbackBrightnessStrategyTest.java b/services/tests/displayservicetests/src/com/android/server/display/brightness/strategy/FallbackBrightnessStrategyTest.java
new file mode 100644
index 0000000..c4767ae
--- /dev/null
+++ b/services/tests/displayservicetests/src/com/android/server/display/brightness/strategy/FallbackBrightnessStrategyTest.java
@@ -0,0 +1,66 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.display.brightness.strategy;
+
+
+import static org.junit.Assert.assertEquals;
+
+import android.hardware.display.DisplayManagerInternal;
+
+import androidx.test.filters.SmallTest;
+import androidx.test.runner.AndroidJUnit4;
+
+import com.android.server.display.DisplayBrightnessState;
+import com.android.server.display.brightness.BrightnessReason;
+import com.android.server.display.brightness.StrategyExecutionRequest;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+@SmallTest
+@RunWith(AndroidJUnit4.class)
+
+public class FallbackBrightnessStrategyTest {
+ private FallbackBrightnessStrategy mFallbackBrightnessStrategy;
+
+ @Before
+ public void before() {
+ mFallbackBrightnessStrategy = new FallbackBrightnessStrategy();
+ }
+
+ @Test
+ public void updateBrightness_currentBrightnessIsSet() {
+ DisplayManagerInternal.DisplayPowerRequest
+ displayPowerRequest = new DisplayManagerInternal.DisplayPowerRequest();
+ float currentBrightness = 0.2f;
+ BrightnessReason brightnessReason = new BrightnessReason();
+ brightnessReason.setReason(BrightnessReason.REASON_MANUAL);
+ DisplayBrightnessState expectedDisplayBrightnessState =
+ new DisplayBrightnessState.Builder()
+ .setBrightness(currentBrightness)
+ .setBrightnessReason(brightnessReason)
+ .setSdrBrightness(currentBrightness)
+ .setDisplayBrightnessStrategyName(mFallbackBrightnessStrategy.getName())
+ .setShouldUpdateScreenBrightnessSetting(true)
+ .build();
+ DisplayBrightnessState updatedDisplayBrightnessState =
+ mFallbackBrightnessStrategy.updateBrightness(
+ new StrategyExecutionRequest(displayPowerRequest, currentBrightness));
+ assertEquals(updatedDisplayBrightnessState, expectedDisplayBrightnessState);
+ }
+}
diff --git a/services/tests/displayservicetests/src/com/android/server/display/color/DisplayTransformManagerTest.java b/services/tests/displayservicetests/src/com/android/server/display/color/DisplayTransformManagerTest.java
index a785300..27f87aa 100644
--- a/services/tests/displayservicetests/src/com/android/server/display/color/DisplayTransformManagerTest.java
+++ b/services/tests/displayservicetests/src/com/android/server/display/color/DisplayTransformManagerTest.java
@@ -161,4 +161,20 @@
.isEqualTo(Integer.toString(testPropertyValue));
}
+ @Test
+ public void daltonizer_defaultValues() {
+ synchronized (mDtm.mDaltonizerModeLock) {
+ assertThat(mDtm.mDaltonizerMode).isEqualTo(-1);
+ assertThat(mDtm.mDaltonizerLevel).isEqualTo(-1);
+ }
+ }
+
+ @Test
+ public void setDaltonizerMode_newValues_valuesUpdated() {
+ mDtm.setDaltonizerMode(0, 0);
+ synchronized (mDtm.mDaltonizerModeLock) {
+ assertThat(mDtm.mDaltonizerMode).isEqualTo(0);
+ assertThat(mDtm.mDaltonizerLevel).isEqualTo(0);
+ }
+ }
}
diff --git a/services/tests/displayservicetests/src/com/android/server/display/mode/DisplayModeDirectorTest.java b/services/tests/displayservicetests/src/com/android/server/display/mode/DisplayModeDirectorTest.java
index cd1e9e8..714b423 100644
--- a/services/tests/displayservicetests/src/com/android/server/display/mode/DisplayModeDirectorTest.java
+++ b/services/tests/displayservicetests/src/com/android/server/display/mode/DisplayModeDirectorTest.java
@@ -131,7 +131,8 @@
/* defaultRefreshRate= */ 0,
/* defaultPeakRefreshRate= */ 0,
/* defaultRefreshRateInHbmHdr= */ 0,
- /* defaultRefreshRateInHbmSunlight= */ 0);
+ /* defaultRefreshRateInHbmSunlight= */ 0,
+ /* lowPowerSupportedModes =*/ List.of());
public static Collection<Object[]> getAppRequestedSizeTestCases() {
var appRequestedSizeTestCases = Arrays.asList(new Object[][] {
@@ -157,7 +158,7 @@
APP_MODE_HIGH_90.getPhysicalHeight()),
Vote.PRIORITY_APP_REQUEST_BASE_MODE_REFRESH_RATE,
Vote.forBaseModeRefreshRate(APP_MODE_HIGH_90.getRefreshRate()),
- Vote.PRIORITY_LOW_POWER_MODE,
+ Vote.PRIORITY_LOW_POWER_MODE_RENDER_RATE,
Vote.forSize(LIMIT_MODE_70.getPhysicalWidth(),
LIMIT_MODE_70.getPhysicalHeight()))},
{/*expectedBaseModeId*/ LIMIT_MODE_70.getModeId(),
@@ -169,7 +170,7 @@
APP_MODE_65.getPhysicalHeight()),
Vote.PRIORITY_APP_REQUEST_BASE_MODE_REFRESH_RATE,
Vote.forBaseModeRefreshRate(APP_MODE_65.getRefreshRate()),
- Vote.PRIORITY_LOW_POWER_MODE,
+ Vote.PRIORITY_LOW_POWER_MODE_RENDER_RATE,
Vote.forSize(LIMIT_MODE_70.getPhysicalWidth(),
LIMIT_MODE_70.getPhysicalHeight()))},
{/*expectedBaseModeId*/ LIMIT_MODE_70.getModeId(),
@@ -181,7 +182,7 @@
APP_MODE_65.getPhysicalHeight()),
Vote.PRIORITY_APP_REQUEST_BASE_MODE_REFRESH_RATE,
Vote.forBaseModeRefreshRate(APP_MODE_65.getRefreshRate()),
- Vote.PRIORITY_LOW_POWER_MODE,
+ Vote.PRIORITY_LOW_POWER_MODE_RENDER_RATE,
Vote.forSizeAndPhysicalRefreshRatesRange(
0, 0,
LIMIT_MODE_70.getPhysicalWidth(),
@@ -197,7 +198,7 @@
APP_MODE_65.getPhysicalHeight()),
Vote.PRIORITY_APP_REQUEST_BASE_MODE_REFRESH_RATE,
Vote.forBaseModeRefreshRate(APP_MODE_65.getRefreshRate()),
- Vote.PRIORITY_LOW_POWER_MODE,
+ Vote.PRIORITY_LOW_POWER_MODE_RENDER_RATE,
Vote.forSizeAndPhysicalRefreshRatesRange(
0, 0,
LIMIT_MODE_70.getPhysicalWidth(),
@@ -213,7 +214,7 @@
APP_MODE_HIGH_90.getPhysicalHeight()),
Vote.PRIORITY_APP_REQUEST_BASE_MODE_REFRESH_RATE,
Vote.forBaseModeRefreshRate(APP_MODE_HIGH_90.getRefreshRate()),
- Vote.PRIORITY_LOW_POWER_MODE,
+ Vote.PRIORITY_LOW_POWER_MODE_RENDER_RATE,
Vote.forSizeAndPhysicalRefreshRatesRange(
0, 0,
LIMIT_MODE_70.getPhysicalWidth(),
@@ -229,7 +230,7 @@
APP_MODE_HIGH_90.getPhysicalHeight()),
Vote.PRIORITY_APP_REQUEST_BASE_MODE_REFRESH_RATE,
Vote.forBaseModeRefreshRate(APP_MODE_HIGH_90.getRefreshRate()),
- Vote.PRIORITY_LOW_POWER_MODE,
+ Vote.PRIORITY_LOW_POWER_MODE_RENDER_RATE,
Vote.forSizeAndPhysicalRefreshRatesRange(
0, 0,
LIMIT_MODE_70.getPhysicalWidth(),
@@ -245,7 +246,7 @@
Vote.PRIORITY_APP_REQUEST_SIZE,
Vote.forSize(APP_MODE_65.getPhysicalWidth(),
APP_MODE_65.getPhysicalHeight()),
- Vote.PRIORITY_LOW_POWER_MODE,
+ Vote.PRIORITY_LOW_POWER_MODE_RENDER_RATE,
Vote.forPhysicalRefreshRates(
0, 64.99f))}});
@@ -598,7 +599,7 @@
< Vote.PRIORITY_APP_REQUEST_SIZE);
assertTrue(Vote.PRIORITY_FLICKER_REFRESH_RATE_SWITCH
- > Vote.PRIORITY_LOW_POWER_MODE);
+ > Vote.PRIORITY_LOW_POWER_MODE_RENDER_RATE);
Display.Mode[] modes = new Display.Mode[4];
modes[0] = new Display.Mode(
@@ -676,9 +677,9 @@
@Test
public void testLPMHasHigherPriorityThanUser() {
- assertTrue(Vote.PRIORITY_LOW_POWER_MODE
+ assertTrue(Vote.PRIORITY_LOW_POWER_MODE_RENDER_RATE
> Vote.PRIORITY_APP_REQUEST_BASE_MODE_REFRESH_RATE);
- assertTrue(Vote.PRIORITY_LOW_POWER_MODE
+ assertTrue(Vote.PRIORITY_LOW_POWER_MODE_RENDER_RATE
> Vote.PRIORITY_APP_REQUEST_SIZE);
Display.Mode[] modes = new Display.Mode[4];
@@ -700,7 +701,7 @@
Vote.forBaseModeRefreshRate(appRequestedMode.getRefreshRate()));
votes.put(Vote.PRIORITY_APP_REQUEST_SIZE, Vote.forSize(appRequestedMode.getPhysicalWidth(),
appRequestedMode.getPhysicalHeight()));
- votes.put(Vote.PRIORITY_LOW_POWER_MODE, Vote.forRenderFrameRates(60, 60));
+ votes.put(Vote.PRIORITY_LOW_POWER_MODE_RENDER_RATE, Vote.forRenderFrameRates(60, 60));
director.injectVotesByDisplay(votesByDisplay);
DesiredDisplayModeSpecs desiredSpecs = director.getDesiredDisplayModeSpecs(DISPLAY_ID);
assertThat(desiredSpecs.baseModeId).isEqualTo(2);
@@ -715,7 +716,7 @@
Vote.forBaseModeRefreshRate(appRequestedMode.getRefreshRate()));
votes.put(Vote.PRIORITY_APP_REQUEST_SIZE, Vote.forSize(appRequestedMode.getPhysicalWidth(),
appRequestedMode.getPhysicalHeight()));
- votes.put(Vote.PRIORITY_LOW_POWER_MODE, Vote.forRenderFrameRates(90, 90));
+ votes.put(Vote.PRIORITY_LOW_POWER_MODE_RENDER_RATE, Vote.forRenderFrameRates(90, 90));
director.injectVotesByDisplay(votesByDisplay);
desiredSpecs = director.getDesiredDisplayModeSpecs(DISPLAY_ID);
assertThat(desiredSpecs.baseModeId).isEqualTo(4);
@@ -730,7 +731,7 @@
Vote.forBaseModeRefreshRate(appRequestedMode.getRefreshRate()));
votes.put(Vote.PRIORITY_APP_REQUEST_SIZE, Vote.forSize(appRequestedMode.getPhysicalWidth(),
appRequestedMode.getPhysicalHeight()));
- votes.put(Vote.PRIORITY_LOW_POWER_MODE, Vote.forRenderFrameRates(60, 60));
+ votes.put(Vote.PRIORITY_LOW_POWER_MODE_RENDER_RATE, Vote.forRenderFrameRates(60, 60));
director.injectVotesByDisplay(votesByDisplay);
desiredSpecs = director.getDesiredDisplayModeSpecs(DISPLAY_ID);
assertThat(desiredSpecs.baseModeId).isEqualTo(2);
@@ -745,7 +746,7 @@
Vote.forBaseModeRefreshRate(appRequestedMode.getRefreshRate()));
votes.put(Vote.PRIORITY_APP_REQUEST_SIZE, Vote.forSize(appRequestedMode.getPhysicalWidth(),
appRequestedMode.getPhysicalHeight()));
- votes.put(Vote.PRIORITY_LOW_POWER_MODE, Vote.forRenderFrameRates(90, 90));
+ votes.put(Vote.PRIORITY_LOW_POWER_MODE_RENDER_RATE, Vote.forRenderFrameRates(90, 90));
director.injectVotesByDisplay(votesByDisplay);
desiredSpecs = director.getDesiredDisplayModeSpecs(DISPLAY_ID);
assertThat(desiredSpecs.baseModeId).isEqualTo(4);
@@ -906,7 +907,7 @@
Vote.forBaseModeRefreshRate(appRequestedMode.getRefreshRate()));
votes.put(Vote.PRIORITY_USER_SETTING_PEAK_RENDER_FRAME_RATE,
Vote.forRenderFrameRates(60, 60));
- votes.put(Vote.PRIORITY_LOW_POWER_MODE, Vote.forRenderFrameRates(0, 60));
+ votes.put(Vote.PRIORITY_LOW_POWER_MODE_RENDER_RATE, Vote.forRenderFrameRates(0, 60));
director.injectVotesByDisplay(votesByDisplay);
assertThat(director.shouldAlwaysRespectAppRequestedMode()).isFalse();
@@ -946,7 +947,7 @@
votesByDisplay.put(DISPLAY_ID, votes);
votes.put(Vote.PRIORITY_USER_SETTING_MIN_RENDER_FRAME_RATE,
Vote.forRenderFrameRates(30, 90));
- votes.put(Vote.PRIORITY_LOW_POWER_MODE, Vote.forRenderFrameRates(0, 60));
+ votes.put(Vote.PRIORITY_LOW_POWER_MODE_RENDER_RATE, Vote.forRenderFrameRates(0, 60));
director.injectVotesByDisplay(votesByDisplay);
assertThat(director.getModeSwitchingType())
@@ -987,7 +988,7 @@
votesByDisplay.put(DISPLAY_ID, votes);
votes.put(Vote.PRIORITY_USER_SETTING_MIN_RENDER_FRAME_RATE,
Vote.forRenderFrameRates(30, 90));
- votes.put(Vote.PRIORITY_LOW_POWER_MODE, Vote.forRenderFrameRates(0, 60));
+ votes.put(Vote.PRIORITY_LOW_POWER_MODE_RENDER_RATE, Vote.forRenderFrameRates(0, 60));
director.injectVotesByDisplay(votesByDisplay);
assertThat(director.getModeSwitchingType())
@@ -1029,7 +1030,7 @@
votesByDisplay.put(DISPLAY_ID, votes);
votes.put(Vote.PRIORITY_USER_SETTING_MIN_RENDER_FRAME_RATE,
Vote.forRenderFrameRates(30, 90));
- votes.put(Vote.PRIORITY_LOW_POWER_MODE, Vote.forRenderFrameRates(0, 60));
+ votes.put(Vote.PRIORITY_LOW_POWER_MODE_RENDER_RATE, Vote.forRenderFrameRates(0, 60));
director.injectVotesByDisplay(votesByDisplay);
assertThat(director.getModeSwitchingType())
@@ -1900,7 +1901,7 @@
director.start(createMockSensorManager());
SparseArray<Vote> votes = new SparseArray<>();
- votes.put(Vote.PRIORITY_LOW_POWER_MODE, Vote.forRenderFrameRates(0, 50f));
+ votes.put(Vote.PRIORITY_LOW_POWER_MODE_RENDER_RATE, Vote.forRenderFrameRates(0, 50f));
SparseArray<SparseArray<Vote>> votesByDisplay = new SparseArray<>();
votesByDisplay.put(DISPLAY_ID_2, votes);
@@ -2298,7 +2299,7 @@
votes.put(Vote.PRIORITY_USER_SETTING_MIN_RENDER_FRAME_RATE,
Vote.forRenderFrameRates(90, Float.POSITIVE_INFINITY));
votes.put(Vote.PRIORITY_FLICKER_REFRESH_RATE_SWITCH, Vote.forDisableRefreshRateSwitching());
- votes.put(Vote.PRIORITY_LOW_POWER_MODE, Vote.forRenderFrameRates(0, 60));
+ votes.put(Vote.PRIORITY_LOW_POWER_MODE_RENDER_RATE, Vote.forRenderFrameRates(0, 60));
director.injectVotesByDisplay(votesByDisplay);
DesiredDisplayModeSpecs desiredSpecs = director.getDesiredDisplayModeSpecs(DISPLAY_ID);
assertThat(desiredSpecs.primary.physical.min).isWithin(FLOAT_TOLERANCE).of(50);
@@ -2311,7 +2312,7 @@
votes.put(Vote.PRIORITY_USER_SETTING_MIN_RENDER_FRAME_RATE,
Vote.forRenderFrameRates(80, Float.POSITIVE_INFINITY));
votes.put(Vote.PRIORITY_FLICKER_REFRESH_RATE_SWITCH, Vote.forDisableRefreshRateSwitching());
- votes.put(Vote.PRIORITY_LOW_POWER_MODE, Vote.forRenderFrameRates(0, 90));
+ votes.put(Vote.PRIORITY_LOW_POWER_MODE_RENDER_RATE, Vote.forRenderFrameRates(0, 90));
desiredSpecs = director.getDesiredDisplayModeSpecs(DISPLAY_ID);
assertThat(desiredSpecs.primary.physical.min).isWithin(FLOAT_TOLERANCE).of(80);
assertThat(desiredSpecs.primary.physical.max).isWithin(FLOAT_TOLERANCE).of(80);
@@ -2323,7 +2324,7 @@
votes.put(Vote.PRIORITY_USER_SETTING_MIN_RENDER_FRAME_RATE,
Vote.forRenderFrameRates(80, Float.POSITIVE_INFINITY));
votes.put(Vote.PRIORITY_FLICKER_REFRESH_RATE_SWITCH, Vote.forDisableRefreshRateSwitching());
- votes.put(Vote.PRIORITY_LOW_POWER_MODE, Vote.forRenderFrameRates(0, 90));
+ votes.put(Vote.PRIORITY_LOW_POWER_MODE_RENDER_RATE, Vote.forRenderFrameRates(0, 90));
desiredSpecs = director.getDesiredDisplayModeSpecs(DISPLAY_ID);
assertThat(desiredSpecs.primary.physical.min).isWithin(FLOAT_TOLERANCE).of(90);
assertThat(desiredSpecs.primary.physical.max).isWithin(FLOAT_TOLERANCE).of(90);
@@ -2343,7 +2344,7 @@
votesByDisplay.put(DISPLAY_ID, votes);
votes.put(Vote.PRIORITY_APP_REQUEST_BASE_MODE_REFRESH_RATE,
Vote.forBaseModeRefreshRate(70));
- votes.put(Vote.PRIORITY_LOW_POWER_MODE, Vote.forRenderFrameRates(0, 60));
+ votes.put(Vote.PRIORITY_LOW_POWER_MODE_RENDER_RATE, Vote.forRenderFrameRates(0, 60));
director.injectVotesByDisplay(votesByDisplay);
DesiredDisplayModeSpecs desiredSpecs = director.getDesiredDisplayModeSpecs(DISPLAY_ID);
assertThat(desiredSpecs.primary.physical.min).isWithin(FLOAT_TOLERANCE).of(0);
@@ -2360,7 +2361,7 @@
votes.clear();
votes.put(Vote.PRIORITY_APP_REQUEST_BASE_MODE_REFRESH_RATE,
Vote.forBaseModeRefreshRate(55));
- votes.put(Vote.PRIORITY_LOW_POWER_MODE, Vote.forRenderFrameRates(0, 60));
+ votes.put(Vote.PRIORITY_LOW_POWER_MODE_RENDER_RATE, Vote.forRenderFrameRates(0, 60));
director.injectVotesByDisplay(votesByDisplay);
desiredSpecs = director.getDesiredDisplayModeSpecs(DISPLAY_ID);
assertThat(desiredSpecs.primary.physical.min).isWithin(FLOAT_TOLERANCE).of(0);
@@ -2374,7 +2375,7 @@
Vote.forRenderFrameRates(0, 52));
votes.put(Vote.PRIORITY_APP_REQUEST_BASE_MODE_REFRESH_RATE,
Vote.forBaseModeRefreshRate(55));
- votes.put(Vote.PRIORITY_LOW_POWER_MODE, Vote.forRenderFrameRates(0, 60));
+ votes.put(Vote.PRIORITY_LOW_POWER_MODE_RENDER_RATE, Vote.forRenderFrameRates(0, 60));
director.injectVotesByDisplay(votesByDisplay);
desiredSpecs = director.getDesiredDisplayModeSpecs(DISPLAY_ID);
assertThat(desiredSpecs.primary.physical.min).isWithin(FLOAT_TOLERANCE).of(0);
@@ -2392,7 +2393,7 @@
Vote.forRenderFrameRates(0, 58));
votes.put(Vote.PRIORITY_APP_REQUEST_BASE_MODE_REFRESH_RATE,
Vote.forBaseModeRefreshRate(55));
- votes.put(Vote.PRIORITY_LOW_POWER_MODE, Vote.forRenderFrameRates(0, 60));
+ votes.put(Vote.PRIORITY_LOW_POWER_MODE_RENDER_RATE, Vote.forRenderFrameRates(0, 60));
director.injectVotesByDisplay(votesByDisplay);
desiredSpecs = director.getDesiredDisplayModeSpecs(DISPLAY_ID);
assertThat(desiredSpecs.primary.physical.min).isWithin(FLOAT_TOLERANCE).of(0);
@@ -2521,7 +2522,7 @@
votes.put(Vote.PRIORITY_UDFPS, Vote.forPhysicalRefreshRates(120, 120));
- votes.put(Vote.PRIORITY_LOW_POWER_MODE, Vote.forRenderFrameRates(90, 90));
+ votes.put(Vote.PRIORITY_LOW_POWER_MODE_RENDER_RATE, Vote.forRenderFrameRates(90, 90));
director.injectVotesByDisplay(votesByDisplay);
DesiredDisplayModeSpecs desiredSpecs = director.getDesiredDisplayModeSpecs(DISPLAY_ID);
assertThat(desiredSpecs.appRequest.physical.min).isWithin(FLOAT_TOLERANCE).of(120);
@@ -2542,7 +2543,7 @@
SparseArray<SparseArray<Vote>> votesByDisplay = new SparseArray<>();
votesByDisplay.put(DISPLAY_ID, votes);
- votes.put(Vote.PRIORITY_LOW_POWER_MODE, Vote.forRenderFrameRates(0, 60));
+ votes.put(Vote.PRIORITY_LOW_POWER_MODE_RENDER_RATE, Vote.forRenderFrameRates(0, 60));
votes.put(Vote.PRIORITY_USER_SETTING_PEAK_RENDER_FRAME_RATE,
Vote.forRenderFrameRates(0, 30));
director.injectVotesByDisplay(votesByDisplay);
@@ -3168,7 +3169,8 @@
/* defaultRefreshRate= */ 60,
/* defaultPeakRefreshRate= */ 65,
/* defaultRefreshRateInHbmHdr= */ 65,
- /* defaultRefreshRateInHbmSunlight= */ 75);
+ /* defaultRefreshRateInHbmSunlight= */ 75,
+ /* lowPowerSupportedModes= */ List.of());
when(displayDeviceConfig.getRefreshRateData()).thenReturn(refreshRateData);
when(displayDeviceConfig.getDefaultLowBlockingZoneRefreshRate()).thenReturn(50);
when(displayDeviceConfig.getDefaultHighBlockingZoneRefreshRate()).thenReturn(55);
@@ -3390,9 +3392,10 @@
ArgumentCaptor<DisplayListener> displayListenerCaptor =
ArgumentCaptor.forClass(DisplayListener.class);
- verify(mInjector).registerDisplayListener(displayListenerCaptor.capture(),
+ verify(mInjector, atLeastOnce()).registerDisplayListener(displayListenerCaptor.capture(),
any(Handler.class));
- DisplayListener displayListener = displayListenerCaptor.getValue();
+ // DisplayObserver should register first
+ DisplayListener displayListener = displayListenerCaptor.getAllValues().get(0);
float refreshRate = 60;
mInjector.mDisplayInfo.layoutLimitedRefreshRate =
@@ -3417,9 +3420,10 @@
ArgumentCaptor<DisplayListener> displayListenerCaptor =
ArgumentCaptor.forClass(DisplayListener.class);
- verify(mInjector).registerDisplayListener(displayListenerCaptor.capture(),
+ verify(mInjector, atLeastOnce()).registerDisplayListener(displayListenerCaptor.capture(),
any(Handler.class));
- DisplayListener displayListener = displayListenerCaptor.getValue();
+ // DisplayObserver should register first
+ DisplayListener displayListener = displayListenerCaptor.getAllValues().get(0);
mInjector.mDisplayInfo.layoutLimitedRefreshRate = new RefreshRateRange(10, 10);
mInjector.mDisplayInfoValid = false;
diff --git a/services/tests/displayservicetests/src/com/android/server/display/mode/DisplayObserverTest.java b/services/tests/displayservicetests/src/com/android/server/display/mode/DisplayObserverTest.java
index 2d317af..ee79d19 100644
--- a/services/tests/displayservicetests/src/com/android/server/display/mode/DisplayObserverTest.java
+++ b/services/tests/displayservicetests/src/com/android/server/display/mode/DisplayObserverTest.java
@@ -407,7 +407,8 @@
assertThat(mObserver).isNull();
mObserver = invocation.getArgument(0);
return null;
- }).when(mInjector).registerDisplayListener(any(), any());
+ }).when(mInjector).registerDisplayListener(
+ any(DisplayModeDirector.DisplayObserver.class), any());
doAnswer(c -> {
DisplayInfo info = c.getArgument(1);
diff --git a/services/tests/displayservicetests/src/com/android/server/display/mode/SettingsObserverTest.kt b/services/tests/displayservicetests/src/com/android/server/display/mode/SettingsObserverTest.kt
index 4d910ce..e431c8c 100644
--- a/services/tests/displayservicetests/src/com/android/server/display/mode/SettingsObserverTest.kt
+++ b/services/tests/displayservicetests/src/com/android/server/display/mode/SettingsObserverTest.kt
@@ -27,8 +27,11 @@
import androidx.test.filters.SmallTest
import com.android.internal.util.test.FakeSettingsProvider
import com.android.server.display.DisplayDeviceConfig
+import com.android.server.display.config.RefreshRateData
+import com.android.server.display.config.SupportedModeData
import com.android.server.display.feature.DisplayManagerFlags
import com.android.server.display.mode.DisplayModeDirector.DisplayDeviceConfigProvider
+import com.android.server.display.mode.SupportedRefreshRatesVote.RefreshRates
import com.android.server.testutils.TestHandler
import com.google.common.truth.Truth.assertThat
import com.google.common.truth.Truth.assertWithMessage
@@ -69,6 +72,13 @@
private val RANGES_MIN60_60TO90 = RefreshRateRanges(RANGE_60_INF, RANGE_60_90)
private val RANGES_MIN90_90TO90 = RefreshRateRanges(RANGE_90_INF, RANGE_90_90)
+private val LOW_POWER_GLOBAL_VOTE = Vote.forRenderFrameRates(0f, 60f)
+private val LOW_POWER_REFRESH_RATE_DATA = createRefreshRateData(
+ lowPowerSupportedModes = listOf(SupportedModeData(60f, 60f), SupportedModeData(60f, 240f)))
+private val LOW_POWER_EMPTY_REFRESH_RATE_DATA = createRefreshRateData()
+private val EXPECTED_SUPPORTED_MODES_VOTE = SupportedRefreshRatesVote(
+ listOf(RefreshRates(60f, 60f), RefreshRates(60f, 240f)))
+
@SmallTest
@RunWith(TestParameterInjector::class)
class SettingsObserverTest {
@@ -103,7 +113,7 @@
val displayModeDirector = DisplayModeDirector(
spyContext, testHandler, mockInjector, mockFlags, mockDisplayDeviceConfigProvider)
val ddcByDisplay = SparseArray<DisplayDeviceConfig>()
- whenever(mockDeviceConfig.isVrrSupportEnabled).thenReturn(testCase.vrrSupported)
+ whenever(mockDeviceConfig.refreshRateData).thenReturn(testCase.refreshRateData)
ddcByDisplay.put(Display.DEFAULT_DISPLAY, mockDeviceConfig)
displayModeDirector.injectDisplayDeviceConfigByDisplay(ddcByDisplay)
val settingsObserver = displayModeDirector.SettingsObserver(
@@ -113,27 +123,30 @@
false, Settings.Global.getUriFor(Settings.Global.LOW_POWER_MODE), 1)
assertThat(displayModeDirector.getVote(VotesStorage.GLOBAL_ID,
- Vote.PRIORITY_LOW_POWER_MODE)).isEqualTo(testCase.expectedVote)
+ Vote.PRIORITY_LOW_POWER_MODE_RENDER_RATE)).isEqualTo(testCase.globalVote)
+ assertThat(displayModeDirector.getVote(Display.DEFAULT_DISPLAY,
+ Vote.PRIORITY_LOW_POWER_MODE_MODES)).isEqualTo(testCase.displayVote)
}
enum class LowPowerTestCase(
- val vrrSupported: Boolean,
+ val refreshRateData: RefreshRateData,
val vsyncLowPowerVoteEnabled: Boolean,
val lowPowerModeEnabled: Boolean,
- internal val expectedVote: Vote?
+ internal val globalVote: Vote?,
+ internal val displayVote: Vote?
) {
- ALL_ENABLED(true, true, true,
- SupportedRefreshRatesVote(listOf(
- SupportedRefreshRatesVote.RefreshRates(60f, 240f),
- SupportedRefreshRatesVote.RefreshRates(60f, 60f)
- ))),
- LOW_POWER_OFF(true, true, false, null),
- DVRR_NOT_SUPPORTED_LOW_POWER_ON(false, true, true,
- RefreshRateVote.RenderVote(0f, 60f)),
- DVRR_NOT_SUPPORTED_LOW_POWER_OFF(false, true, false, null),
- VSYNC_VOTE_DISABLED_SUPPORTED_LOW_POWER_ON(true, false, true,
- RefreshRateVote.RenderVote(0f, 60f)),
- VSYNC_VOTE_DISABLED_LOW_POWER_OFF(true, false, false, null),
+ ALL_ENABLED(LOW_POWER_REFRESH_RATE_DATA, true, true,
+ LOW_POWER_GLOBAL_VOTE, EXPECTED_SUPPORTED_MODES_VOTE),
+ LOW_POWER_OFF(LOW_POWER_REFRESH_RATE_DATA, true, false,
+ null, null),
+ EMPTY_REFRESH_LOW_POWER_ON(LOW_POWER_EMPTY_REFRESH_RATE_DATA, true, true,
+ LOW_POWER_GLOBAL_VOTE, null),
+ EMPTY_REFRESH__LOW_POWER_OFF(LOW_POWER_EMPTY_REFRESH_RATE_DATA, true, false,
+ null, null),
+ VSYNC_VOTE_DISABLED_SUPPORTED_LOW_POWER_ON(LOW_POWER_REFRESH_RATE_DATA, false, true,
+ LOW_POWER_GLOBAL_VOTE, null),
+ VSYNC_VOTE_DISABLED_LOW_POWER_OFF(LOW_POWER_REFRESH_RATE_DATA, false, false,
+ null, null),
}
@Test
diff --git a/services/tests/displayservicetests/src/com/android/server/display/mode/TestUtils.kt b/services/tests/displayservicetests/src/com/android/server/display/mode/TestUtils.kt
index 6b90bde..1206e30 100644
--- a/services/tests/displayservicetests/src/com/android/server/display/mode/TestUtils.kt
+++ b/services/tests/displayservicetests/src/com/android/server/display/mode/TestUtils.kt
@@ -16,6 +16,9 @@
package com.android.server.display.mode
+import com.android.server.display.config.RefreshRateData
+import com.android.server.display.config.SupportedModeData
+
internal fun createVotesSummary(
isDisplayResolutionRangeVotingEnabled: Boolean = true,
supportedModesVoteEnabled: Boolean = true,
@@ -24,4 +27,16 @@
): VoteSummary {
return VoteSummary(isDisplayResolutionRangeVotingEnabled, supportedModesVoteEnabled,
loggingEnabled, supportsFrameRateOverride)
-}
\ No newline at end of file
+}
+
+fun createRefreshRateData(
+ defaultRefreshRate: Int = 60,
+ defaultPeakRefreshRate: Int = 60,
+ defaultRefreshRateInHbmHdr: Int = 60,
+ defaultRefreshRateInHbmSunlight: Int = 60,
+ lowPowerSupportedModes: List<SupportedModeData> = emptyList()
+): RefreshRateData {
+ return RefreshRateData(defaultRefreshRate, defaultPeakRefreshRate,
+ defaultRefreshRateInHbmHdr, defaultRefreshRateInHbmSunlight,
+ lowPowerSupportedModes)
+}
diff --git a/services/tests/dreamservicetests/src/com/android/server/dreams/DreamControllerTest.java b/services/tests/dreamservicetests/src/com/android/server/dreams/DreamControllerTest.java
index 88ab871..874e991 100644
--- a/services/tests/dreamservicetests/src/com/android/server/dreams/DreamControllerTest.java
+++ b/services/tests/dreamservicetests/src/com/android/server/dreams/DreamControllerTest.java
@@ -273,28 +273,36 @@
}
@Test
- public void setDreamHasFocus_true_dreamHasFocus() {
+ public void setDreamIsObscured_true_dreamIsNotFrontmost() {
mDreamController.startDream(mToken, mDreamName, false /*isPreview*/, false /*doze*/,
0 /*userId*/, null /*wakeLock*/, mOverlayName, "test" /*reason*/);
- mDreamController.setDreamHasFocus(true);
- assertTrue(mDreamController.dreamHasFocus());
+ mDreamController.setDreamIsObscured(true);
+ assertFalse(mDreamController.dreamIsFrontmost());
}
@Test
- public void setDreamHasFocus_false_dreamDoesNotHaveFocus() {
+ public void setDreamIsObscured_false_dreamIsFrontmost() {
mDreamController.startDream(mToken, mDreamName, false /*isPreview*/, false /*doze*/,
0 /*userId*/, null /*wakeLock*/, mOverlayName, "test" /*reason*/);
- mDreamController.setDreamHasFocus(false);
- assertFalse(mDreamController.dreamHasFocus());
+ mDreamController.setDreamIsObscured(false);
+ assertTrue(mDreamController.dreamIsFrontmost());
}
@Test
- public void setDreamHasFocus_notDreaming_dreamDoesNotHaveFocus() {
- mDreamController.setDreamHasFocus(true);
- // Dream still doesn't have focus because it was never started.
- assertFalse(mDreamController.dreamHasFocus());
+ public void setDreamIsObscured_notDreaming_dreamIsNotFrontmost() {
+ mDreamController.setDreamIsObscured(true);
+ // Dream still isn't frontmost because it was never started.
+ assertFalse(mDreamController.dreamIsFrontmost());
+ }
+
+ @Test
+ public void startDream_dreamIsFrontmost() {
+ mDreamController.startDream(mToken, mDreamName, false /*isPreview*/, false /*doze*/,
+ 0 /*userId*/, null /*wakeLock*/, mOverlayName, "test" /*reason*/);
+
+ assertTrue(mDreamController.dreamIsFrontmost());
}
private ServiceConnection captureServiceConnection() {
diff --git a/services/tests/mockingservicestests/src/com/android/server/alarm/AlarmManagerServiceTest.java b/services/tests/mockingservicestests/src/com/android/server/alarm/AlarmManagerServiceTest.java
index c359412..cb15d6f 100644
--- a/services/tests/mockingservicestests/src/com/android/server/alarm/AlarmManagerServiceTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/alarm/AlarmManagerServiceTest.java
@@ -3094,13 +3094,14 @@
ArgumentCaptor<Bundle> bundleCaptor = ArgumentCaptor.forClass(Bundle.class);
verify(alarmPi).send(eq(mMockContext), eq(0), any(Intent.class),
any(), any(Handler.class), isNull(), bundleCaptor.capture());
+ Bundle bundle = bundleCaptor.getValue();
if (idleOptions != null) {
- assertEquals(idleOptions, bundleCaptor.getValue());
+ assertEquals(idleOptions, bundle);
} else {
- assertFalse("BAL flag needs to be false in alarm manager",
- bundleCaptor.getValue().getBoolean(
- ActivityOptions.KEY_PENDING_INTENT_BACKGROUND_ACTIVITY_ALLOWED,
- true));
+ ActivityOptions options = ActivityOptions.fromBundle(bundle);
+ assertEquals("BAL should not be allowed in alarm manager",
+ ActivityOptions.MODE_BACKGROUND_ACTIVITY_START_DENIED,
+ options.getPendingIntentBackgroundActivityStartMode());
}
}
diff --git a/services/tests/mockingservicestests/src/com/android/server/am/ApplicationStartInfoTest.java b/services/tests/mockingservicestests/src/com/android/server/am/ApplicationStartInfoTest.java
index c1f4fee..e88e28b 100644
--- a/services/tests/mockingservicestests/src/com/android/server/am/ApplicationStartInfoTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/am/ApplicationStartInfoTest.java
@@ -142,6 +142,7 @@
final String app1PackageName = "com.android.test.stub1";
final long appStartTimestampIntentStarted = 1000000;
final long appStartTimestampActivityLaunchFinished = 2000000;
+ final long appStartTimestampFirstFrameDrawn = 2500000;
final long appStartTimestampReportFullyDrawn = 3000000;
final long appStartTimestampService = 4000000;
final long appStartTimestampBroadcast = 5000000;
@@ -272,6 +273,8 @@
mAppStartInfoTracker.onActivityLaunchFinished(appStartTimestampIntentStarted, COMPONENT,
appStartTimestampActivityLaunchFinished, ApplicationStartInfo.LAUNCH_MODE_STANDARD);
+ mAppStartInfoTracker.addTimestampToStart(app1PackageName, app1Uid,
+ appStartTimestampFirstFrameDrawn, ApplicationStartInfo.START_TIMESTAMP_FIRST_FRAME);
list.clear();
mAppStartInfoTracker.getStartInfo(app1PackageName, app1Uid, app1Pid1, 0, list);
verifyInProgressRecordsSize(1);
diff --git a/services/tests/mockingservicestests/src/com/android/server/am/OWNERS b/services/tests/mockingservicestests/src/com/android/server/am/OWNERS
index 72c0a9e..2cbc226 100644
--- a/services/tests/mockingservicestests/src/com/android/server/am/OWNERS
+++ b/services/tests/mockingservicestests/src/com/android/server/am/OWNERS
@@ -1 +1,3 @@
include /services/core/java/com/android/server/am/OWNERS
+
+per-file ApplicationStartInfoTest.java = yforta@google.com, carmenjackson@google.com, jji@google.com
diff --git a/services/tests/mockingservicestests/src/com/android/server/pm/MockSystem.kt b/services/tests/mockingservicestests/src/com/android/server/pm/MockSystem.kt
index c9aab53..396edae 100644
--- a/services/tests/mockingservicestests/src/com/android/server/pm/MockSystem.kt
+++ b/services/tests/mockingservicestests/src/com/android/server/pm/MockSystem.kt
@@ -186,7 +186,7 @@
class Mocks {
val lock = PackageManagerTracedLock()
- val installLock = Any()
+ val installLock = PackageManagerTracedLock()
val injector: PackageManagerServiceInjector = mock()
val systemWrapper: PackageManagerServiceInjector.SystemWrapper = mock()
val context: Context = mock()
diff --git a/services/tests/powerservicetests/src/com/android/server/power/NotifierTest.java b/services/tests/powerservicetests/src/com/android/server/power/NotifierTest.java
index a9ff3a1..4460c6a 100644
--- a/services/tests/powerservicetests/src/com/android/server/power/NotifierTest.java
+++ b/services/tests/powerservicetests/src/com/android/server/power/NotifierTest.java
@@ -22,10 +22,12 @@
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.clearInvocations;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.verifyNoMoreInteractions;
import static org.mockito.Mockito.verifyZeroInteractions;
import static org.mockito.Mockito.when;
@@ -52,6 +54,7 @@
import com.android.server.LocalServices;
import com.android.server.policy.WindowManagerPolicy;
import com.android.server.power.batterysaver.BatterySaverStateMachine;
+import com.android.server.power.feature.PowerManagerFlags;
import com.android.server.statusbar.StatusBarManagerInternal;
import org.junit.Before;
@@ -77,6 +80,9 @@
@Mock private InattentiveSleepWarningController mInattentiveSleepWarningControllerMock;
@Mock private Vibrator mVibrator;
@Mock private StatusBarManagerInternal mStatusBarManagerInternal;
+ @Mock private WakeLockLog mWakeLockLog;
+
+ @Mock private PowerManagerFlags mPowerManagerFlags;
private PowerManagerService mService;
private Context mContextSpy;
@@ -222,6 +228,7 @@
@Test
public void testOnWakeLockListener_RemoteException_NoRethrow() {
+ when(mPowerManagerFlags.improveWakelockLatency()).thenReturn(true);
createNotifier();
IWakeLockCallback exceptingCallback = new IWakeLockCallback.Stub() {
@@ -235,6 +242,9 @@
mNotifier.onWakeLockReleased(PowerManager.PARTIAL_WAKE_LOCK, "wakelockTag",
"my.package.name", uid, pid, /* workSource= */ null, /* historyTag= */ null,
exceptingCallback);
+ verifyZeroInteractions(mWakeLockLog);
+ mTestLooper.dispatchAll();
+ verify(mWakeLockLog).onWakeLockReleased("wakelockTag", uid, 1);
mNotifier.onWakeLockAcquired(PowerManager.PARTIAL_WAKE_LOCK, "wakelockTag",
"my.package.name", uid, pid, /* workSource= */ null, /* historyTag= */ null,
exceptingCallback);
@@ -244,8 +254,27 @@
PowerManager.SCREEN_BRIGHT_WAKE_LOCK, "wakelockTag",
"my.package.name", uid, pid, /* newWorkSource= */ null, /* newHistoryTag= */ null,
exceptingCallback);
+ verifyNoMoreInteractions(mWakeLockLog);
mTestLooper.dispatchAll();
+ verify(mWakeLockLog).onWakeLockAcquired("wakelockTag", uid,
+ PowerManager.PARTIAL_WAKE_LOCK, 1);
// If we didn't throw, we're good!
+
+ // Test with improveWakelockLatency flag false, hence the wakelock log will run on the same
+ // thread
+ clearInvocations(mWakeLockLog);
+ when(mPowerManagerFlags.improveWakelockLatency()).thenReturn(false);
+
+ mNotifier.onWakeLockAcquired(PowerManager.PARTIAL_WAKE_LOCK, "wakelockTag",
+ "my.package.name", uid, pid, /* workSource= */ null, /* historyTag= */ null,
+ exceptingCallback);
+ verify(mWakeLockLog).onWakeLockAcquired("wakelockTag", uid,
+ PowerManager.PARTIAL_WAKE_LOCK, -1);
+
+ mNotifier.onWakeLockReleased(PowerManager.PARTIAL_WAKE_LOCK, "wakelockTag",
+ "my.package.name", uid, pid, /* workSource= */ null, /* historyTag= */ null,
+ exceptingCallback);
+ verify(mWakeLockLog).onWakeLockReleased("wakelockTag", uid, -1);
}
private final PowerManagerService.Injector mInjector = new PowerManagerService.Injector() {
@@ -253,7 +282,7 @@
Notifier createNotifier(Looper looper, Context context, IBatteryStats batteryStats,
SuspendBlocker suspendBlocker, WindowManagerPolicy policy,
FaceDownDetector faceDownDetector, ScreenUndimDetector screenUndimDetector,
- Executor backgroundExecutor) {
+ Executor backgroundExecutor, PowerManagerFlags powerManagerFlags) {
return mNotifierMock;
}
@@ -326,6 +355,18 @@
}
private void createNotifier() {
+ Notifier.Injector injector = new Notifier.Injector() {
+ @Override
+ public long currentTimeMillis() {
+ return 1;
+ }
+
+ @Override
+ public WakeLockLog getWakeLockLog(Context context) {
+ return mWakeLockLog;
+ }
+ };
+
mNotifier = new Notifier(
mTestLooper.getLooper(),
mContextSpy,
@@ -335,7 +376,7 @@
null,
null,
null,
- mTestExecutor);
+ mTestExecutor, mPowerManagerFlags, injector);
}
private static class FakeExecutor implements Executor {
diff --git a/services/tests/powerservicetests/src/com/android/server/power/PowerManagerServiceTest.java b/services/tests/powerservicetests/src/com/android/server/power/PowerManagerServiceTest.java
index 7f165e0..b737e0f 100644
--- a/services/tests/powerservicetests/src/com/android/server/power/PowerManagerServiceTest.java
+++ b/services/tests/powerservicetests/src/com/android/server/power/PowerManagerServiceTest.java
@@ -114,6 +114,7 @@
import com.android.server.power.batterysaver.BatterySaverController;
import com.android.server.power.batterysaver.BatterySaverPolicy;
import com.android.server.power.batterysaver.BatterySaverStateMachine;
+import com.android.server.power.feature.PowerManagerFlags;
import com.android.server.testutils.OffsettableClock;
import com.google.testing.junit.testparameterinjector.TestParameter;
@@ -275,7 +276,7 @@
Notifier createNotifier(Looper looper, Context context, IBatteryStats batteryStats,
SuspendBlocker suspendBlocker, WindowManagerPolicy policy,
FaceDownDetector faceDownDetector, ScreenUndimDetector screenUndimDetector,
- Executor executor) {
+ Executor executor, PowerManagerFlags powerManagerFlags) {
return mNotifierMock;
}
diff --git a/services/tests/powerservicetests/src/com/android/server/power/WakeLockLogTest.java b/services/tests/powerservicetests/src/com/android/server/power/WakeLockLogTest.java
index 0fad25d..1c4db6a 100644
--- a/services/tests/powerservicetests/src/com/android/server/power/WakeLockLogTest.java
+++ b/services/tests/powerservicetests/src/com/android/server/power/WakeLockLogTest.java
@@ -57,19 +57,42 @@
}
@Test
+ public void testAddTwoItems_withNoEventTimeSupplied() {
+ final int tagDatabaseSize = 128;
+ final int logSize = 20;
+ TestInjector injectorSpy = spy(new TestInjector(tagDatabaseSize, logSize));
+ WakeLockLog log = new WakeLockLog(injectorSpy, mContext);
+ when(injectorSpy.currentTimeMillis()).thenReturn(1000L);
+ log.onWakeLockAcquired("TagPartial", 101,
+ PowerManager.PARTIAL_WAKE_LOCK | PowerManager.ON_AFTER_RELEASE, -1);
+
+ when(injectorSpy.currentTimeMillis()).thenReturn(1150L);
+ log.onWakeLockAcquired("TagFull", 102,
+ PowerManager.FULL_WAKE_LOCK | PowerManager.ACQUIRE_CAUSES_WAKEUP, -1);
+
+ assertEquals("Wake Lock Log\n"
+ + " 01-01 00:00:01.000 - 101 (some.package1) - ACQ TagPartial "
+ + "(partial,on-after-release)\n"
+ + " 01-01 00:00:01.150 - 102 (some.package2) - ACQ TagFull "
+ + "(full,acq-causes-wake)\n"
+ + " -\n"
+ + " Events: 2, Time-Resets: 0\n"
+ + " Buffer, Bytes used: 6\n",
+ dumpLog(log, false));
+ }
+
+ @Test
public void testAddTwoItems() {
final int tagDatabaseSize = 128;
final int logSize = 20;
TestInjector injectorSpy = spy(new TestInjector(tagDatabaseSize, logSize));
WakeLockLog log = new WakeLockLog(injectorSpy, mContext);
- when(injectorSpy.currentTimeMillis()).thenReturn(1000L);
log.onWakeLockAcquired("TagPartial", 101,
- PowerManager.PARTIAL_WAKE_LOCK | PowerManager.ON_AFTER_RELEASE);
+ PowerManager.PARTIAL_WAKE_LOCK | PowerManager.ON_AFTER_RELEASE, 1000L);
- when(injectorSpy.currentTimeMillis()).thenReturn(1150L);
log.onWakeLockAcquired("TagFull", 102,
- PowerManager.FULL_WAKE_LOCK | PowerManager.ACQUIRE_CAUSES_WAKEUP);
+ PowerManager.FULL_WAKE_LOCK | PowerManager.ACQUIRE_CAUSES_WAKEUP, 1150L);
assertEquals("Wake Lock Log\n"
+ " 01-01 00:00:01.000 - 101 (some.package1) - ACQ TagPartial "
@@ -89,11 +112,9 @@
TestInjector injectorSpy = spy(new TestInjector(tagDatabaseSize, logSize));
WakeLockLog log = new WakeLockLog(injectorSpy, mContext);
- when(injectorSpy.currentTimeMillis()).thenReturn(1000L);
- log.onWakeLockAcquired("TagPartial", 101, PowerManager.PARTIAL_WAKE_LOCK);
+ log.onWakeLockAcquired("TagPartial", 101, PowerManager.PARTIAL_WAKE_LOCK, 1000L);
- when(injectorSpy.currentTimeMillis()).thenReturn(1350L);
- log.onWakeLockAcquired("TagFull", 102, PowerManager.FULL_WAKE_LOCK);
+ log.onWakeLockAcquired("TagFull", 102, PowerManager.FULL_WAKE_LOCK, 1350L);
assertEquals("Wake Lock Log\n"
+ " 01-01 00:00:01.000 - 101 (some.package1) - ACQ TagPartial (partial)\n"
@@ -111,11 +132,9 @@
TestInjector injectorSpy = spy(new TestInjector(tagDatabaseSize, logSize));
WakeLockLog log = new WakeLockLog(injectorSpy, mContext);
- when(injectorSpy.currentTimeMillis()).thenReturn(1000L);
- log.onWakeLockAcquired("TagPartial", 101, PowerManager.PARTIAL_WAKE_LOCK);
+ log.onWakeLockAcquired("TagPartial", 101, PowerManager.PARTIAL_WAKE_LOCK, 1000L);
- when(injectorSpy.currentTimeMillis()).thenReturn(1150L);
- log.onWakeLockAcquired("TagFull", 102, PowerManager.FULL_WAKE_LOCK);
+ log.onWakeLockAcquired("TagFull", 102, PowerManager.FULL_WAKE_LOCK, 1150L);
assertEquals("Wake Lock Log\n"
+ " 01-01 00:00:01.000 - --- - ACQ UNKNOWN (partial)\n"
@@ -134,41 +153,33 @@
WakeLockLog log = new WakeLockLog(injectorSpy, mContext);
// Wake lock 1 acquired - log size = 3
- when(injectorSpy.currentTimeMillis()).thenReturn(1000L);
- log.onWakeLockAcquired("TagPartial", 101, PowerManager.PARTIAL_WAKE_LOCK);
+ log.onWakeLockAcquired("TagPartial", 101, PowerManager.PARTIAL_WAKE_LOCK, 1000L);
// Wake lock 2 acquired - log size = 3 + 3 = 6
- when(injectorSpy.currentTimeMillis()).thenReturn(1150L);
- log.onWakeLockAcquired("TagFull", 102, PowerManager.FULL_WAKE_LOCK);
+ log.onWakeLockAcquired("TagFull", 102, PowerManager.FULL_WAKE_LOCK, 1150L);
// Wake lock 3 acquired - log size = 6 + 3 = 9
- when(injectorSpy.currentTimeMillis()).thenReturn(1151L);
- log.onWakeLockAcquired("TagThree", 101, PowerManager.PARTIAL_WAKE_LOCK);
+ log.onWakeLockAcquired("TagThree", 101, PowerManager.PARTIAL_WAKE_LOCK, 1151L);
// We need more space - wake lock 1 acquisition is removed from the log and saved in the
// list. Log size = 9 - 3 + 2 = 8
- when(injectorSpy.currentTimeMillis()).thenReturn(1152L);
- log.onWakeLockReleased("TagThree", 101);
+ log.onWakeLockReleased("TagThree", 101, 1152L);
// We need more space - wake lock 2 acquisition is removed from the log and saved in the
// list. Log size = 8 - 3 + 2 = 7
- when(injectorSpy.currentTimeMillis()).thenReturn(1153L);
- log.onWakeLockReleased("TagPartial", 101);
+ log.onWakeLockReleased("TagPartial", 101, 1153L);
// We need more space - wake lock 3 acquisition is removed from the log and saved in the
// list. Log size = 7 - 3 + 3 = 7
- when(injectorSpy.currentTimeMillis()).thenReturn(1154L);
- log.onWakeLockAcquired("TagFour", 101, PowerManager.PARTIAL_WAKE_LOCK);
+ log.onWakeLockAcquired("TagFour", 101, PowerManager.PARTIAL_WAKE_LOCK, 1154L);
// We need more space - wake lock 3 release is removed from the log and wake lock 3
// acquisition is removed from the list. Log size = 7 - 2 + 3 = 8
- when(injectorSpy.currentTimeMillis()).thenReturn(1155L);
- log.onWakeLockAcquired("TagFive", 101, PowerManager.PARTIAL_WAKE_LOCK);
+ log.onWakeLockAcquired("TagFive", 101, PowerManager.PARTIAL_WAKE_LOCK, 1155L);
// We need more space - wake lock 1 release is removed from the log and wake lock 1
// acquisition is removed from the list. Log size = 8 - 2 + 2 = 8
- when(injectorSpy.currentTimeMillis()).thenReturn(1156L);
- log.onWakeLockReleased("TagFull", 102);
+ log.onWakeLockReleased("TagFull", 102, 1156L);
// Wake lock 2 acquisition is still printed because its release have not rolled off the log
// yet.
@@ -191,8 +202,8 @@
WakeLockLog log = new WakeLockLog(injectorSpy, mContext);
// Bad tag means it wont get written
- when(injectorSpy.currentTimeMillis()).thenReturn(1000L);
- log.onWakeLockAcquired(null /* tag */, 0 /* ownerUid */, PowerManager.PARTIAL_WAKE_LOCK);
+ log.onWakeLockAcquired(
+ null /* tag */, 0 /* ownerUid */, PowerManager.PARTIAL_WAKE_LOCK, 1000L);
assertEquals("Wake Lock Log\n"
+ " -\n"
@@ -208,9 +219,8 @@
TestInjector injectorSpy = spy(new TestInjector(tagDatabaseSize, logSize));
WakeLockLog log = new WakeLockLog(injectorSpy, mContext);
- when(injectorSpy.currentTimeMillis()).thenReturn(1000L);
log.onWakeLockAcquired("*job*/com.one.two.3hree/.one..Last", 101,
- PowerManager.PARTIAL_WAKE_LOCK);
+ PowerManager.PARTIAL_WAKE_LOCK, 1000L);
assertEquals("Wake Lock Log\n"
+ " 01-01 00:00:01.000 - 101 (some.package1) - ACQ "
@@ -228,10 +238,8 @@
TestInjector injectorSpy = spy(new TestInjector(tagDatabaseSize, logSize));
WakeLockLog log = new WakeLockLog(injectorSpy, mContext);
- when(injectorSpy.currentTimeMillis()).thenReturn(1000L);
- log.onWakeLockAcquired("HowdyTag", 101, PowerManager.PARTIAL_WAKE_LOCK);
- when(injectorSpy.currentTimeMillis()).thenReturn(1001L);
- log.onWakeLockReleased("HowdyTag", 101);
+ log.onWakeLockAcquired("HowdyTag", 101, PowerManager.PARTIAL_WAKE_LOCK, 1000L);
+ log.onWakeLockReleased("HowdyTag", 101, 1001L);
assertEquals("Wake Lock Log\n"
+ " 01-01 00:00:01.000 - 101 (some.package1) - ACQ HowdyTag (partial)\n"
@@ -250,12 +258,10 @@
TestInjector injectorSpy = spy(new TestInjector(tagDatabaseSize, logSize));
WakeLockLog log = new WakeLockLog(injectorSpy, mContext);
- when(injectorSpy.currentTimeMillis()).thenReturn(1100L);
- log.onWakeLockAcquired("HowdyTag", 101, PowerManager.PARTIAL_WAKE_LOCK);
+ log.onWakeLockAcquired("HowdyTag", 101, PowerManager.PARTIAL_WAKE_LOCK, 1100L);
// New element goes back in time...should not be written to log.
- when(injectorSpy.currentTimeMillis()).thenReturn(1000L);
- log.onWakeLockReleased("HowdyTag", 101);
+ log.onWakeLockReleased("HowdyTag", 101, 1000L);
assertEquals("Wake Lock Log\n"
+ " 01-01 00:00:01.100 - 101 (some.package1) - ACQ HowdyTag (partial)\n"
@@ -272,9 +278,8 @@
TestInjector injectorSpy = spy(new TestInjector(tagDatabaseSize, logSize));
WakeLockLog log = new WakeLockLog(injectorSpy, mContext);
- when(injectorSpy.currentTimeMillis()).thenReturn(1000L);
log.onWakeLockAcquired("TagPartial", 101,
- PowerManager.PARTIAL_WAKE_LOCK | PowerManager.SYSTEM_WAKELOCK);
+ PowerManager.PARTIAL_WAKE_LOCK | PowerManager.SYSTEM_WAKELOCK, 1000L);
assertEquals("Wake Lock Log\n"
+ " 01-01 00:00:01.000 - 101 (some.package1) - ACQ TagPartial "
@@ -293,9 +298,8 @@
WakeLockLog log = new WakeLockLog(injectorSpy, mContext);
when(mPackageManager.getPackagesForUid(101)).thenReturn(null);
- when(injectorSpy.currentTimeMillis()).thenReturn(1000L);
log.onWakeLockAcquired("TagPartial", 101,
- PowerManager.PARTIAL_WAKE_LOCK | PowerManager.ON_AFTER_RELEASE);
+ PowerManager.PARTIAL_WAKE_LOCK | PowerManager.ON_AFTER_RELEASE, 1000L);
assertEquals("Wake Lock Log\n"
+ " 01-01 00:00:01.000 - 101 - ACQ TagPartial "
@@ -316,9 +320,8 @@
when(mPackageManager.getPackagesForUid(101)).thenReturn(
new String[]{ "some.package1", "some.package2", "some.package3" });
- when(injectorSpy.currentTimeMillis()).thenReturn(1000L);
log.onWakeLockAcquired("TagPartial", 101,
- PowerManager.PARTIAL_WAKE_LOCK | PowerManager.ON_AFTER_RELEASE);
+ PowerManager.PARTIAL_WAKE_LOCK | PowerManager.ON_AFTER_RELEASE, 1000L);
assertEquals("Wake Lock Log\n"
+ " 01-01 00:00:01.000 - 101 (some.package1,...) - ACQ TagPartial "
@@ -336,17 +339,14 @@
TestInjector injectorSpy = spy(new TestInjector(tagDatabaseSize, logSize));
WakeLockLog log = new WakeLockLog(injectorSpy, mContext);
- when(injectorSpy.currentTimeMillis()).thenReturn(1000L);
log.onWakeLockAcquired("TagPartial", 101,
- PowerManager.PARTIAL_WAKE_LOCK | PowerManager.ON_AFTER_RELEASE);
+ PowerManager.PARTIAL_WAKE_LOCK | PowerManager.ON_AFTER_RELEASE, 1000L);
- when(injectorSpy.currentTimeMillis()).thenReturn(1150L);
log.onWakeLockAcquired("TagFull", 101,
- PowerManager.FULL_WAKE_LOCK | PowerManager.ACQUIRE_CAUSES_WAKEUP);
+ PowerManager.FULL_WAKE_LOCK | PowerManager.ACQUIRE_CAUSES_WAKEUP, 1150L);
- when(injectorSpy.currentTimeMillis()).thenReturn(1151L);
log.onWakeLockAcquired("TagFull2", 101,
- PowerManager.FULL_WAKE_LOCK | PowerManager.ACQUIRE_CAUSES_WAKEUP);
+ PowerManager.FULL_WAKE_LOCK | PowerManager.ACQUIRE_CAUSES_WAKEUP, 1151L);
assertEquals("Wake Lock Log\n"
+ " 01-01 00:00:01.000 - 101 (some.package1) - ACQ TagPartial "
@@ -370,29 +370,23 @@
TestInjector injectorSpy = spy(new TestInjector(tagDatabaseSize, logSize));
WakeLockLog log = new WakeLockLog(injectorSpy, mContext);
- when(injectorSpy.currentTimeMillis()).thenReturn(1000L);
log.onWakeLockAcquired("TagPartial", 101,
- PowerManager.PARTIAL_WAKE_LOCK | PowerManager.ON_AFTER_RELEASE);
+ PowerManager.PARTIAL_WAKE_LOCK | PowerManager.ON_AFTER_RELEASE, 1000L);
- when(injectorSpy.currentTimeMillis()).thenReturn(1150L);
log.onWakeLockAcquired("TagFull", 101,
- PowerManager.FULL_WAKE_LOCK | PowerManager.ACQUIRE_CAUSES_WAKEUP);
+ PowerManager.FULL_WAKE_LOCK | PowerManager.ACQUIRE_CAUSES_WAKEUP, 1150L);
- when(injectorSpy.currentTimeMillis()).thenReturn(1151L);
log.onWakeLockAcquired("TagFull2", 101,
- PowerManager.FULL_WAKE_LOCK | PowerManager.ACQUIRE_CAUSES_WAKEUP);
+ PowerManager.FULL_WAKE_LOCK | PowerManager.ACQUIRE_CAUSES_WAKEUP, 1151L);
- when(injectorSpy.currentTimeMillis()).thenReturn(1152L);
log.onWakeLockAcquired("TagFull3", 101,
- PowerManager.FULL_WAKE_LOCK | PowerManager.ACQUIRE_CAUSES_WAKEUP);
+ PowerManager.FULL_WAKE_LOCK | PowerManager.ACQUIRE_CAUSES_WAKEUP, 1152L);
- when(injectorSpy.currentTimeMillis()).thenReturn(1153L);
log.onWakeLockAcquired("TagFull4", 101,
- PowerManager.FULL_WAKE_LOCK | PowerManager.ACQUIRE_CAUSES_WAKEUP);
+ PowerManager.FULL_WAKE_LOCK | PowerManager.ACQUIRE_CAUSES_WAKEUP, 1153L);
- when(injectorSpy.currentTimeMillis()).thenReturn(1154L);
log.onWakeLockAcquired("TagFull5", 101,
- PowerManager.FULL_WAKE_LOCK | PowerManager.ACQUIRE_CAUSES_WAKEUP);
+ PowerManager.FULL_WAKE_LOCK | PowerManager.ACQUIRE_CAUSES_WAKEUP, 1154L);
// The first 3 events have been removed from the log and they exist in the saved
// acquisitions list. They should also use the cache when fetching the package names.
diff --git a/services/tests/powerstatstests/src/com/android/server/power/stats/BatteryStatsResetTest.java b/services/tests/powerstatstests/src/com/android/server/power/stats/BatteryStatsResetTest.java
index d29bf1a..3635e9a 100644
--- a/services/tests/powerstatstests/src/com/android/server/power/stats/BatteryStatsResetTest.java
+++ b/services/tests/powerstatstests/src/com/android/server/power/stats/BatteryStatsResetTest.java
@@ -19,6 +19,7 @@
import static com.google.common.truth.Truth.assertThat;
import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
import android.content.Context;
import android.os.BatteryManager;
@@ -49,9 +50,9 @@
private static final int BATTERY_CHARGE_RATE_SECONDS_PER_LEVEL = 100;
private MockClock mMockClock;
+ private BatteryStatsImpl.BatteryStatsConfig mConfig;
private MockBatteryStatsImpl mBatteryStatsImpl;
-
/**
* Battery status. Must be one of the following:
* {@link BatteryManager#BATTERY_STATUS_UNKNOWN}
@@ -91,8 +92,9 @@
@Before
public void setUp() throws IOException {
+ mConfig = mock(BatteryStatsImpl.BatteryStatsConfig.class);
mMockClock = new MockClock();
- mBatteryStatsImpl = new MockBatteryStatsImpl(mMockClock,
+ mBatteryStatsImpl = new MockBatteryStatsImpl(mConfig, mMockClock,
Files.createTempDirectory("BatteryStatsResetTest").toFile());
mBatteryStatsImpl.onSystemReady(mock(Context.class));
@@ -110,10 +112,7 @@
@Test
public void testResetOnUnplug_highBatteryLevel() {
- mBatteryStatsImpl.setBatteryStatsConfig(
- new BatteryStatsImpl.BatteryStatsConfig.Builder()
- .setResetOnUnplugHighBatteryLevel(true)
- .build());
+ when(mConfig.shouldResetOnUnplugHighBatteryLevel()).thenReturn(true);
long expectedResetTimeUs = 0;
@@ -137,10 +136,7 @@
assertThat(mBatteryStatsImpl.getStatsStartRealtime()).isEqualTo(expectedResetTimeUs);
// disable high battery level reset on unplug.
- mBatteryStatsImpl.setBatteryStatsConfig(
- new BatteryStatsImpl.BatteryStatsConfig.Builder()
- .setResetOnUnplugHighBatteryLevel(false)
- .build());
+ when(mConfig.shouldResetOnUnplugHighBatteryLevel()).thenReturn(false);
dischargeToLevel(60);
@@ -153,10 +149,7 @@
@Test
public void testResetOnUnplug_significantCharge() {
- mBatteryStatsImpl.setBatteryStatsConfig(
- new BatteryStatsImpl.BatteryStatsConfig.Builder()
- .setResetOnUnplugAfterSignificantCharge(true)
- .build());
+ when(mConfig.shouldResetOnUnplugAfterSignificantCharge()).thenReturn(true);
long expectedResetTimeUs = 0;
unplugBattery();
@@ -186,10 +179,7 @@
assertThat(mBatteryStatsImpl.getStatsStartRealtime()).isEqualTo(expectedResetTimeUs);
// disable reset on unplug after significant charge.
- mBatteryStatsImpl.setBatteryStatsConfig(
- new BatteryStatsImpl.BatteryStatsConfig.Builder()
- .setResetOnUnplugAfterSignificantCharge(false)
- .build());
+ when(mConfig.shouldResetOnUnplugAfterSignificantCharge()).thenReturn(false);
// Battery level dropped below 20%.
dischargeToLevel(15);
@@ -256,11 +246,9 @@
@Test
public void testResetWhilePluggedIn_longPlugIn() {
// disable high battery level reset on unplug.
- mBatteryStatsImpl.setBatteryStatsConfig(
- new BatteryStatsImpl.BatteryStatsConfig.Builder()
- .setResetOnUnplugHighBatteryLevel(false)
- .setResetOnUnplugAfterSignificantCharge(false)
- .build());
+ when(mConfig.shouldResetOnUnplugHighBatteryLevel()).thenReturn(false);
+ when(mConfig.shouldResetOnUnplugAfterSignificantCharge()).thenReturn(false);
+
long expectedResetTimeUs = 0;
plugBattery(BatteryManager.BATTERY_PLUGGED_USB);
diff --git a/services/tests/powerstatstests/src/com/android/server/power/stats/BatteryUsageStatsRule.java b/services/tests/powerstatstests/src/com/android/server/power/stats/BatteryUsageStatsRule.java
index 2d7cb22..6edfede 100644
--- a/services/tests/powerstatstests/src/com/android/server/power/stats/BatteryUsageStatsRule.java
+++ b/services/tests/powerstatstests/src/com/android/server/power/stats/BatteryUsageStatsRule.java
@@ -98,10 +98,12 @@
mFreqsByPolicy.put(0, new int[]{300000, 1000000, 2000000});
mFreqsByPolicy.put(4, new int[]{300000, 1000000, 2500000, 3000000});
mBatteryStatsConfigBuilder = new BatteryStatsImpl.BatteryStatsConfig.Builder()
- .setPowerStatsThrottlePeriodMillis(BatteryConsumer.POWER_COMPONENT_CPU,
- 10000)
- .setPowerStatsThrottlePeriodMillis(BatteryConsumer.POWER_COMPONENT_MOBILE_RADIO,
- 10000);
+ .setPowerStatsThrottlePeriodMillis(
+ BatteryConsumer.powerComponentIdToString(
+ BatteryConsumer.POWER_COMPONENT_CPU), 10000)
+ .setPowerStatsThrottlePeriodMillis(
+ BatteryConsumer.powerComponentIdToString(
+ BatteryConsumer.POWER_COMPONENT_MOBILE_RADIO), 10000);
}
private void initBatteryStats() {
@@ -290,7 +292,8 @@
public BatteryUsageStatsRule setPowerStatsThrottlePeriodMillis(int powerComponent,
long throttleMs) {
- mBatteryStatsConfigBuilder.setPowerStatsThrottlePeriodMillis(powerComponent, throttleMs);
+ mBatteryStatsConfigBuilder.setPowerStatsThrottlePeriodMillis(
+ BatteryConsumer.powerComponentIdToString(powerComponent), throttleMs);
return this;
}
diff --git a/services/tests/powerstatstests/src/com/android/server/power/stats/CpuPowerStatsCollectorTest.java b/services/tests/powerstatstests/src/com/android/server/power/stats/CpuPowerStatsCollectorTest.java
index 4e3e80f..d1105a4 100644
--- a/services/tests/powerstatstests/src/com/android/server/power/stats/CpuPowerStatsCollectorTest.java
+++ b/services/tests/powerstatstests/src/com/android/server/power/stats/CpuPowerStatsCollectorTest.java
@@ -32,6 +32,7 @@
import android.os.Handler;
import android.os.HandlerThread;
import android.platform.test.ravenwood.RavenwoodRule;
+import android.util.IndentingPrintWriter;
import android.util.SparseArray;
import androidx.test.filters.SmallTest;
@@ -51,6 +52,7 @@
import org.xmlpull.v1.XmlPullParserException;
import java.io.IOException;
+import java.io.StringWriter;
import java.util.function.IntSupplier;
@RunWith(AndroidJUnit4.class)
@@ -127,6 +129,11 @@
}
@Override
+ public long getPowerStatsCollectionThrottlePeriod(String powerComponentName) {
+ return 0;
+ }
+
+ @Override
public int getDefaultCpuPowerBrackets() {
return mDefaultCpuPowerBrackets;
}
@@ -363,6 +370,36 @@
.isEqualTo(528);
}
+ @Test
+ public void dump() {
+ mockCpuScalingPolicies(1);
+ mockPowerProfile();
+ mockEnergyConsumers();
+
+ CpuPowerStatsCollector collector = createCollector(8, 0);
+ collector.collectStats(); // Establish baseline
+
+ mockKernelCpuStats(new long[]{1111, 2222, 3333},
+ new SparseArray<>() {{
+ put(UID_1, new long[]{100, 200});
+ put(UID_2, new long[]{100, 150});
+ put(ISOLATED_UID, new long[]{200, 450});
+ }}, 0, 1234);
+
+ PowerStats powerStats = collector.collectStats();
+
+ StringWriter sw = new StringWriter();
+ IndentingPrintWriter pw = new IndentingPrintWriter(sw);
+ powerStats.dump(pw);
+ pw.flush();
+ String dump = sw.toString();
+
+ assertThat(dump).contains("duration=1234");
+ assertThat(dump).contains("steps: [1111, 2222, 3333]");
+ assertThat(dump).contains("UID 42: time: [100, 200]");
+ assertThat(dump).contains("UID 99: time: [300, 600]");
+ }
+
private void mockCpuScalingPolicies(int clusterCount) {
SparseArray<int[]> cpus = new SparseArray<>();
SparseArray<int[]> freqs = new SparseArray<>();
@@ -386,8 +423,8 @@
private CpuPowerStatsCollector createCollector(int defaultCpuPowerBrackets,
int defaultCpuPowerBracketsPerEnergyConsumer) {
CpuPowerStatsCollector collector = new CpuPowerStatsCollector(
- new TestInjector(defaultCpuPowerBrackets, defaultCpuPowerBracketsPerEnergyConsumer),
- 0);
+ new TestInjector(defaultCpuPowerBrackets, defaultCpuPowerBracketsPerEnergyConsumer)
+ );
collector.addConsumer(stats -> mCollectedStats = stats);
collector.setEnabled(true);
return collector;
diff --git a/services/tests/powerstatstests/src/com/android/server/power/stats/CpuPowerStatsCollectorValidationTest.java b/services/tests/powerstatstests/src/com/android/server/power/stats/CpuPowerStatsCollectorValidationTest.java
index 70c40f5..644ae47 100644
--- a/services/tests/powerstatstests/src/com/android/server/power/stats/CpuPowerStatsCollectorValidationTest.java
+++ b/services/tests/powerstatstests/src/com/android/server/power/stats/CpuPowerStatsCollectorValidationTest.java
@@ -25,6 +25,7 @@
import android.content.Intent;
import android.os.Bundle;
import android.os.IBinder;
+import android.os.UserHandle;
import android.platform.test.annotations.RequiresFlagsEnabled;
import android.platform.test.flag.junit.CheckFlagsRule;
import android.platform.test.flag.junit.DeviceFlagsValueProvider;
@@ -46,7 +47,9 @@
import org.junit.Test;
import org.junit.runner.RunWith;
+import java.util.ArrayList;
import java.util.Arrays;
+import java.util.List;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
import java.util.regex.Matcher;
@@ -92,9 +95,10 @@
long duration = 0;
long[] stats = null;
- String[] cpuStatsDump = dumpCpuStats();
+ List<String> cpuStatsDump = dumpCpuStats();
Pattern durationPattern = Pattern.compile("duration=([0-9]*)");
- Pattern uidPattern = Pattern.compile("UID " + mTestPkgUid + ": \\[([0-9,\\s]*)]");
+ Pattern uidPattern = Pattern.compile(
+ "UID " + UserHandle.formatUid(mTestPkgUid) + ": time: [\\[]?([0-9,\\s]*)[]]?");
for (String line : cpuStatsDump) {
Matcher durationMatcher = durationPattern.matcher(line);
if (durationMatcher.find()) {
@@ -119,15 +123,23 @@
assertThat(total).isAtLeast((long) (WORK_DURATION_MS * 0.8));
}
- private String[] dumpCpuStats() throws Exception {
+ private List<String> dumpCpuStats() throws Exception {
+ ArrayList<String> cpuStats = new ArrayList<>();
String dump = executeCmdSilent("dumpsys batterystats --sample");
String[] lines = dump.split("\n");
+ boolean inCpuSection = false;
for (int i = 0; i < lines.length; i++) {
- if (lines[i].startsWith("CpuPowerStatsCollector")) {
- return Arrays.copyOfRange(lines, i + 1, lines.length);
+ if (!inCpuSection) {
+ if (lines[i].startsWith("CpuPowerStatsCollector")) {
+ inCpuSection = true;
+ }
+ } else if (lines[i].startsWith(" ")) {
+ cpuStats.add(lines[i]);
+ } else {
+ break;
}
}
- return new String[0];
+ return cpuStats;
}
private void doSomeWork() throws Exception {
diff --git a/services/tests/powerstatstests/src/com/android/server/power/stats/MobileRadioPowerStatsCollectorTest.java b/services/tests/powerstatstests/src/com/android/server/power/stats/MobileRadioPowerStatsCollectorTest.java
index f93c4da..0275319 100644
--- a/services/tests/powerstatstests/src/com/android/server/power/stats/MobileRadioPowerStatsCollectorTest.java
+++ b/services/tests/powerstatstests/src/com/android/server/power/stats/MobileRadioPowerStatsCollectorTest.java
@@ -123,6 +123,11 @@
}
@Override
+ public long getPowerStatsCollectionThrottlePeriod(String powerComponentName) {
+ return 0;
+ }
+
+ @Override
public PackageManager getPackageManager() {
return mPackageManager;
}
@@ -337,16 +342,18 @@
pw.flush();
String dump = sw.toString();
assertThat(dump).contains("duration=100");
+ assertThat(dump).contains("sleep: 200 idle: 300 scan: 60000 call: 40000 energy: "
+ + ((64321 - 10000) * 1000 / 3500));
+ assertThat(dump).contains("(LTE) rx: 7000 tx: [8000, 9000, 1000, 2000, 3000]");
+ assertThat(dump).contains("(NR MMWAVE) rx: 6000 tx: [1000, 2000, 3000, 4000, 5000]");
assertThat(dump).contains(
- "stats=[200, 300, 60000, 40000, " + ((64321 - 10000) * 1000 / 3500) + ", 0, 0, 0]");
- assertThat(dump).contains("state LTE: [7000, 8000, 9000, 1000, 2000, 3000]");
- assertThat(dump).contains("state NR MMWAVE: [6000, 1000, 2000, 3000, 4000, 5000]");
- assertThat(dump).contains("UID 24: [6000, 3000, 60, 30, 0]");
- assertThat(dump).contains("UID 42: [1000, 2000, 100, 200, 0]");
+ "UID 24: rx-pkts: 60 rx-B: 6000 tx-pkts: 30 tx-B: 3000");
+ assertThat(dump).contains(
+ "UID 42: rx-pkts: 100 rx-B: 1000 tx-pkts: 200 tx-B: 2000");
}
private PowerStats collectPowerStats(boolean perNetworkTypeData) throws Throwable {
- MobileRadioPowerStatsCollector collector = new MobileRadioPowerStatsCollector(mInjector, 0);
+ MobileRadioPowerStatsCollector collector = new MobileRadioPowerStatsCollector(mInjector);
collector.setEnabled(true);
when(mConsumedEnergyRetriever.getEnergyConsumerIds(
diff --git a/services/tests/powerstatstests/src/com/android/server/power/stats/MobileRadioPowerStatsProcessorTest.java b/services/tests/powerstatstests/src/com/android/server/power/stats/MobileRadioPowerStatsProcessorTest.java
index 4ac7ad8..29ef3b6 100644
--- a/services/tests/powerstatstests/src/com/android/server/power/stats/MobileRadioPowerStatsProcessorTest.java
+++ b/services/tests/powerstatstests/src/com/android/server/power/stats/MobileRadioPowerStatsProcessorTest.java
@@ -114,6 +114,11 @@
}
@Override
+ public long getPowerStatsCollectionThrottlePeriod(String powerComponentName) {
+ return 0;
+ }
+
+ @Override
public PackageManager getPackageManager() {
return mPackageManager;
}
@@ -186,7 +191,7 @@
aggregatedStats.setUidState(APP_UID, STATE_PROCESS_STATE, PROCESS_STATE_FOREGROUND, 0);
aggregatedStats.setUidState(APP_UID2, STATE_PROCESS_STATE, PROCESS_STATE_CACHED, 0);
- MobileRadioPowerStatsCollector collector = new MobileRadioPowerStatsCollector(mInjector, 0);
+ MobileRadioPowerStatsCollector collector = new MobileRadioPowerStatsCollector(mInjector);
collector.setEnabled(true);
// Initial empty ModemActivityInfo.
@@ -305,79 +310,9 @@
}
@Test
- public void measuredEnergyModel() {
- // PowerStats hardware is available
- when(mConsumedEnergyRetriever.getEnergyConsumerIds(EnergyConsumerType.MOBILE_RADIO))
- .thenReturn(new int[] {MOBILE_RADIO_ENERGY_CONSUMER_ID});
-
- mStatsRule.setTestPowerProfile("power_profile_test_legacy_modem")
- .initMeasuredEnergyStatsLocked();
-
- MobileRadioPowerStatsProcessor processor =
- new MobileRadioPowerStatsProcessor(mStatsRule.getPowerProfile());
-
- AggregatedPowerStatsConfig.PowerComponent config =
- new AggregatedPowerStatsConfig.PowerComponent(
- BatteryConsumer.POWER_COMPONENT_MOBILE_RADIO)
- .trackDeviceStates(STATE_POWER, STATE_SCREEN)
- .trackUidStates(STATE_POWER, STATE_SCREEN, STATE_PROCESS_STATE)
- .setProcessor(processor);
-
+ public void energyConsumerModel() {
PowerComponentAggregatedPowerStats aggregatedStats =
- new PowerComponentAggregatedPowerStats(
- new AggregatedPowerStats(mock(AggregatedPowerStatsConfig.class)), config);
-
- aggregatedStats.setState(STATE_POWER, POWER_STATE_OTHER, 0);
- aggregatedStats.setState(STATE_SCREEN, SCREEN_STATE_ON, 0);
- aggregatedStats.setUidState(APP_UID, STATE_PROCESS_STATE, PROCESS_STATE_FOREGROUND, 0);
- aggregatedStats.setUidState(APP_UID2, STATE_PROCESS_STATE, PROCESS_STATE_CACHED, 0);
-
- MobileRadioPowerStatsCollector collector = new MobileRadioPowerStatsCollector(mInjector, 0);
- collector.setEnabled(true);
-
- // Initial empty ModemActivityInfo.
- mockModemActivityInfo(new ModemActivityInfo(0L, 0L, 0L, new int[5], 0L));
-
- when(mConsumedEnergyRetriever.getConsumedEnergyUws(
- new int[]{MOBILE_RADIO_ENERGY_CONSUMER_ID}))
- .thenReturn(new long[]{0});
-
- // Establish a baseline
- aggregatedStats.addPowerStats(collector.collectStats(), 0);
-
- // Turn the screen off after 2.5 seconds
- aggregatedStats.setState(STATE_SCREEN, SCREEN_STATE_OTHER, 2500);
- aggregatedStats.setUidState(APP_UID, STATE_PROCESS_STATE, PROCESS_STATE_BACKGROUND, 2500);
- aggregatedStats.setUidState(APP_UID, STATE_PROCESS_STATE, PROCESS_STATE_FOREGROUND_SERVICE,
- 5000);
-
- // Note application network activity
- NetworkStats networkStats = mockNetworkStats(10000, 1,
- mockNetworkStatsEntry("cellular", APP_UID, 0, 0,
- METERED_NO, ROAMING_NO, DEFAULT_NETWORK_NO, 10000, 1500, 20000, 300, 100),
- mockNetworkStatsEntry("cellular", APP_UID2, 0, 0,
- METERED_NO, ROAMING_NO, DEFAULT_NETWORK_NO, 5000, 500, 3000, 100, 111));
-
- when(mNetworkStatsSupplier.get()).thenReturn(networkStats);
-
- ModemActivityInfo mai = new ModemActivityInfo(10000, 2000, 3000,
- new int[]{100, 200, 300, 400, 500}, 600);
- mockModemActivityInfo(mai);
-
- mStatsRule.setTime(10_000, 10_000);
-
- long energyUws = 10_000_000L * VOLTAGE_MV / 1000L;
- when(mConsumedEnergyRetriever.getConsumedEnergyUws(
- new int[]{MOBILE_RADIO_ENERGY_CONSUMER_ID})).thenReturn(new long[]{energyUws});
-
- when(mCallDurationSupplier.getAsLong()).thenReturn(200L);
- when(mScanDurationSupplier.getAsLong()).thenReturn(5555L);
-
- PowerStats powerStats = collector.collectStats();
-
- aggregatedStats.addPowerStats(powerStats, 10_000);
-
- processor.finish(aggregatedStats);
+ prepareAggregatedStats_energyConsumerModel();
MobileRadioPowerStatsLayout statsLayout =
new MobileRadioPowerStatsLayout(
@@ -448,6 +383,102 @@
.isWithin(PRECISION).of(0.75);
}
+ @Test
+ public void test_toString() {
+ PowerComponentAggregatedPowerStats stats = prepareAggregatedStats_energyConsumerModel();
+ String string = stats.toString();
+ assertThat(string).contains("(pwr-other scr-on)"
+ + " sleep: 500 idle: 750 scan: 1388 call: 50 energy: 2500000 power: 0.672");
+ assertThat(string).contains("(pwr-other scr-other)"
+ + " sleep: 1500 idle: 2250 scan: 4166 call: 150 energy: 7500000 power: 2.02");
+ assertThat(string).contains("(pwr-other scr-on other)"
+ + " rx: 150 tx: [25, 50, 75, 100, 125]");
+ assertThat(string).contains("(pwr-other scr-other other)"
+ + " rx: 450 tx: [75, 150, 225, 300, 375]");
+ assertThat(string).contains("(pwr-other scr-on fg)"
+ + " rx-pkts: 375 rx-B: 2500 tx-pkts: 75 tx-B: 5000 power: 0.198");
+ assertThat(string).contains("(pwr-other scr-other bg)"
+ + " rx-pkts: 375 rx-B: 2500 tx-pkts: 75 tx-B: 5000 power: 0.198");
+ assertThat(string).contains("(pwr-other scr-other fgs)"
+ + " rx-pkts: 750 rx-B: 5000 tx-pkts: 150 tx-B: 10000 power: 0.396");
+ }
+
+ private PowerComponentAggregatedPowerStats prepareAggregatedStats_energyConsumerModel() {
+ // PowerStats hardware is available
+ when(mConsumedEnergyRetriever.getEnergyConsumerIds(EnergyConsumerType.MOBILE_RADIO))
+ .thenReturn(new int[] {MOBILE_RADIO_ENERGY_CONSUMER_ID});
+
+ mStatsRule.setTestPowerProfile("power_profile_test_legacy_modem")
+ .initMeasuredEnergyStatsLocked();
+
+ MobileRadioPowerStatsProcessor processor =
+ new MobileRadioPowerStatsProcessor(mStatsRule.getPowerProfile());
+
+ AggregatedPowerStatsConfig.PowerComponent config =
+ new AggregatedPowerStatsConfig.PowerComponent(
+ BatteryConsumer.POWER_COMPONENT_MOBILE_RADIO)
+ .trackDeviceStates(STATE_POWER, STATE_SCREEN)
+ .trackUidStates(STATE_POWER, STATE_SCREEN, STATE_PROCESS_STATE)
+ .setProcessor(processor);
+
+ PowerComponentAggregatedPowerStats aggregatedStats =
+ new PowerComponentAggregatedPowerStats(
+ new AggregatedPowerStats(mock(AggregatedPowerStatsConfig.class)), config);
+
+ aggregatedStats.setState(STATE_POWER, POWER_STATE_OTHER, 0);
+ aggregatedStats.setState(STATE_SCREEN, SCREEN_STATE_ON, 0);
+ aggregatedStats.setUidState(APP_UID, STATE_PROCESS_STATE, PROCESS_STATE_FOREGROUND, 0);
+ aggregatedStats.setUidState(APP_UID2, STATE_PROCESS_STATE, PROCESS_STATE_CACHED, 0);
+
+ MobileRadioPowerStatsCollector collector = new MobileRadioPowerStatsCollector(mInjector);
+ collector.setEnabled(true);
+
+ // Initial empty ModemActivityInfo.
+ mockModemActivityInfo(new ModemActivityInfo(0L, 0L, 0L, new int[5], 0L));
+
+ when(mConsumedEnergyRetriever.getConsumedEnergyUws(
+ new int[]{MOBILE_RADIO_ENERGY_CONSUMER_ID}))
+ .thenReturn(new long[]{0});
+
+ // Establish a baseline
+ aggregatedStats.addPowerStats(collector.collectStats(), 0);
+
+ // Turn the screen off after 2.5 seconds
+ aggregatedStats.setState(STATE_SCREEN, SCREEN_STATE_OTHER, 2500);
+ aggregatedStats.setUidState(APP_UID, STATE_PROCESS_STATE, PROCESS_STATE_BACKGROUND, 2500);
+ aggregatedStats.setUidState(APP_UID, STATE_PROCESS_STATE, PROCESS_STATE_FOREGROUND_SERVICE,
+ 5000);
+
+ // Note application network activity
+ NetworkStats networkStats = mockNetworkStats(10000, 1,
+ mockNetworkStatsEntry("cellular", APP_UID, 0, 0,
+ METERED_NO, ROAMING_NO, DEFAULT_NETWORK_NO, 10000, 1500, 20000, 300, 100),
+ mockNetworkStatsEntry("cellular", APP_UID2, 0, 0,
+ METERED_NO, ROAMING_NO, DEFAULT_NETWORK_NO, 5000, 500, 3000, 100, 111));
+
+ when(mNetworkStatsSupplier.get()).thenReturn(networkStats);
+
+ ModemActivityInfo mai = new ModemActivityInfo(10000, 2000, 3000,
+ new int[]{100, 200, 300, 400, 500}, 600);
+ mockModemActivityInfo(mai);
+
+ mStatsRule.setTime(10_000, 10_000);
+
+ long energyUws = 10_000_000L * VOLTAGE_MV / 1000L;
+ when(mConsumedEnergyRetriever.getConsumedEnergyUws(
+ new int[]{MOBILE_RADIO_ENERGY_CONSUMER_ID})).thenReturn(new long[]{energyUws});
+
+ when(mCallDurationSupplier.getAsLong()).thenReturn(200L);
+ when(mScanDurationSupplier.getAsLong()).thenReturn(5555L);
+
+ PowerStats powerStats = collector.collectStats();
+
+ aggregatedStats.addPowerStats(powerStats, 10_000);
+
+ processor.finish(aggregatedStats);
+ return aggregatedStats;
+ }
+
private int[] states(int... states) {
return states;
}
diff --git a/services/tests/powerstatstests/src/com/android/server/power/stats/MockBatteryStatsImpl.java b/services/tests/powerstatstests/src/com/android/server/power/stats/MockBatteryStatsImpl.java
index 1d48975..2c03f9d 100644
--- a/services/tests/powerstatstests/src/com/android/server/power/stats/MockBatteryStatsImpl.java
+++ b/services/tests/powerstatstests/src/com/android/server/power/stats/MockBatteryStatsImpl.java
@@ -72,6 +72,11 @@
this(DEFAULT_CONFIG, clock, historyDirectory, handler, new PowerStatsUidResolver());
}
+ MockBatteryStatsImpl(BatteryStatsConfig config, Clock clock, File historyDirectory) {
+ this(config, clock, historyDirectory, new Handler(Looper.getMainLooper()),
+ new PowerStatsUidResolver());
+ }
+
MockBatteryStatsImpl(BatteryStatsConfig config, Clock clock, File historyDirectory,
Handler handler, PowerStatsUidResolver powerStatsUidResolver) {
super(config, clock, new MonotonicClock(0, clock), historyDirectory, handler,
@@ -137,13 +142,6 @@
return MOBILE_RADIO_POWER_STATE_UPDATE_FREQ_MS;
}
- public MockBatteryStatsImpl setBatteryStatsConfig(BatteryStatsConfig config) {
- synchronized (this) {
- mBatteryStatsConfig = config;
- }
- return this;
- }
-
public MockBatteryStatsImpl setNetworkStats(NetworkStats networkStats) {
mNetworkStats = networkStats;
return this;
diff --git a/services/tests/powerstatstests/src/com/android/server/power/stats/MultiStateStatsTest.java b/services/tests/powerstatstests/src/com/android/server/power/stats/MultiStateStatsTest.java
index 1b045c5..ae258cd3 100644
--- a/services/tests/powerstatstests/src/com/android/server/power/stats/MultiStateStatsTest.java
+++ b/services/tests/powerstatstests/src/com/android/server/power/stats/MultiStateStatsTest.java
@@ -29,8 +29,6 @@
import org.junit.Test;
import org.junit.runner.RunWith;
-import java.io.PrintWriter;
-import java.io.StringWriter;
import java.util.Arrays;
@RunWith(AndroidJUnit4.class)
@@ -165,7 +163,7 @@
}
@Test
- public void dump() {
+ public void test_toString() {
MultiStateStats.Factory factory = makeFactory(true, true, false);
MultiStateStats multiStateStats = factory.create();
multiStateStats.setState(0 /* batteryState */, 0 /* off */, 1000);
@@ -175,13 +173,10 @@
multiStateStats.setState(1 /* procState */, BatteryConsumer.PROCESS_STATE_BACKGROUND, 3000);
multiStateStats.increment(new long[]{100, 200}, 5000);
- StringWriter sw = new StringWriter();
- PrintWriter pw = new PrintWriter(sw, true);
- multiStateStats.dump(pw, Arrays::toString);
- assertThat(sw.toString()).isEqualTo(
- "plugged-in fg [25, 50]\n"
- + "on-battery fg [25, 50]\n"
- + "on-battery bg [50, 100]\n"
+ assertThat(multiStateStats.toString()).isEqualTo(
+ "(plugged-in fg) [25, 50]\n"
+ + "(on-battery fg) [25, 50]\n"
+ + "(on-battery bg) [50, 100]"
);
}
diff --git a/services/tests/powerstatstests/src/com/android/server/power/stats/PhoneCallPowerStatsProcessorTest.java b/services/tests/powerstatstests/src/com/android/server/power/stats/PhoneCallPowerStatsProcessorTest.java
index dadcf3f..69d655b 100644
--- a/services/tests/powerstatstests/src/com/android/server/power/stats/PhoneCallPowerStatsProcessorTest.java
+++ b/services/tests/powerstatstests/src/com/android/server/power/stats/PhoneCallPowerStatsProcessorTest.java
@@ -98,6 +98,11 @@
}
@Override
+ public long getPowerStatsCollectionThrottlePeriod(String powerComponentName) {
+ return 0;
+ }
+
+ @Override
public PackageManager getPackageManager() {
return mPackageManager;
}
@@ -175,7 +180,7 @@
aggregatedPowerStats.setDeviceState(STATE_POWER, POWER_STATE_OTHER, 0);
aggregatedPowerStats.setDeviceState(STATE_SCREEN, SCREEN_STATE_ON, 0);
- MobileRadioPowerStatsCollector collector = new MobileRadioPowerStatsCollector(mInjector, 0);
+ MobileRadioPowerStatsCollector collector = new MobileRadioPowerStatsCollector(mInjector);
collector.setEnabled(true);
// Initial empty ModemActivityInfo.
diff --git a/services/tests/powerstatstests/src/com/android/server/power/stats/WifiPowerStatsCollectorTest.java b/services/tests/powerstatstests/src/com/android/server/power/stats/WifiPowerStatsCollectorTest.java
index 8b1d423..a280cfe 100644
--- a/services/tests/powerstatstests/src/com/android/server/power/stats/WifiPowerStatsCollectorTest.java
+++ b/services/tests/powerstatstests/src/com/android/server/power/stats/WifiPowerStatsCollectorTest.java
@@ -138,6 +138,11 @@
}
@Override
+ public long getPowerStatsCollectionThrottlePeriod(String powerComponentName) {
+ return 0;
+ }
+
+ @Override
public PackageManager getPackageManager() {
return mPackageManager;
}
@@ -304,16 +309,20 @@
String dump = sw.toString();
assertThat(dump).contains("duration=7500");
assertThat(dump).contains(
- "stats=[6000, 1000, 300, 200, 634, 945, " + ((64321 - 10000) * 1000 / 3500)
- + ", 0, 0]");
- assertThat(dump).contains("UID 24: [6000, 3000, 60, 30, 400, 600, 0]");
- assertThat(dump).contains("UID 42: [1000, 2000, 100, 200, 234, 345, 0]");
+ "rx: 6000 tx: 1000 idle: 300 scan: 200 basic-scan: 634 batched-scan: 945"
+ + " energy: " + ((64321 - 10000) * 1000 / 3500));
+ assertThat(dump).contains(
+ "UID 24: rx-pkts: 60 rx-B: 6000 tx-pkts: 30 tx-B: 3000"
+ + " scan: 400 batched-scan: 600");
+ assertThat(dump).contains(
+ "UID 42: rx-pkts: 100 rx-B: 1000 tx-pkts: 200 tx-B: 2000"
+ + " scan: 234 batched-scan: 345");
}
private PowerStats collectPowerStats(boolean hasPowerReporting) {
when(mWifiManager.isEnhancedPowerReportingSupported()).thenReturn(hasPowerReporting);
- WifiPowerStatsCollector collector = new WifiPowerStatsCollector(mInjector, 0);
+ WifiPowerStatsCollector collector = new WifiPowerStatsCollector(mInjector);
collector.setEnabled(true);
when(mConsumedEnergyRetriever.getEnergyConsumerIds(EnergyConsumerType.WIFI))
diff --git a/services/tests/powerstatstests/src/com/android/server/power/stats/WifiPowerStatsProcessorTest.java b/services/tests/powerstatstests/src/com/android/server/power/stats/WifiPowerStatsProcessorTest.java
index 257a1a6..3ceaf35 100644
--- a/services/tests/powerstatstests/src/com/android/server/power/stats/WifiPowerStatsProcessorTest.java
+++ b/services/tests/powerstatstests/src/com/android/server/power/stats/WifiPowerStatsProcessorTest.java
@@ -142,6 +142,11 @@
}
@Override
+ public long getPowerStatsCollectionThrottlePeriod(String powerComponentName) {
+ return 0;
+ }
+
+ @Override
public PackageManager getPackageManager() {
return mPackageManager;
}
@@ -195,7 +200,7 @@
PowerComponentAggregatedPowerStats aggregatedStats = createAggregatedPowerStats(processor);
- WifiPowerStatsCollector collector = new WifiPowerStatsCollector(mInjector, 0);
+ WifiPowerStatsCollector collector = new WifiPowerStatsCollector(mInjector);
collector.setEnabled(true);
// Initial empty WifiActivityEnergyInfo.
@@ -307,7 +312,7 @@
PowerComponentAggregatedPowerStats aggregatedStats = createAggregatedPowerStats(processor);
- WifiPowerStatsCollector collector = new WifiPowerStatsCollector(mInjector, 0);
+ WifiPowerStatsCollector collector = new WifiPowerStatsCollector(mInjector);
collector.setEnabled(true);
// Initial empty WifiActivityEnergyInfo.
@@ -420,7 +425,7 @@
PowerComponentAggregatedPowerStats aggregatedStats = createAggregatedPowerStats(processor);
- WifiPowerStatsCollector collector = new WifiPowerStatsCollector(mInjector, 0);
+ WifiPowerStatsCollector collector = new WifiPowerStatsCollector(mInjector);
collector.setEnabled(true);
// Establish a baseline
diff --git a/services/tests/servicestests/AndroidTest.xml b/services/tests/servicestests/AndroidTest.xml
index 27c522d..b56af87 100644
--- a/services/tests/servicestests/AndroidTest.xml
+++ b/services/tests/servicestests/AndroidTest.xml
@@ -25,6 +25,13 @@
value="/data/local/tmp/cts/content/broken_shortcut.xml" />
</target_preparer>
+ <target_preparer class="com.android.tradefed.targetprep.DeviceSetup">
+ <option name="force-skip-system-props" value="true" />
+ <option name="set-global-setting" key="verifier_engprod" value="1" />
+ <option name="set-global-setting" key="verifier_verify_adb_installs" value="0" />
+ <option name="restore-settings" value="true" />
+ </target_preparer>
+
<target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller">
<option name="cleanup-apks" value="true" />
<option name="install-arg" value="-t" />
diff --git a/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityManagerServiceTest.java
index cb4fc75..6152326 100644
--- a/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityManagerServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityManagerServiceTest.java
@@ -30,7 +30,9 @@
import static com.google.common.truth.Truth.assertThat;
+import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertThrows;
+import static org.junit.Assert.assertTrue;
import static org.junit.Assume.assumeTrue;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyBoolean;
@@ -42,6 +44,7 @@
import static org.mockito.Mockito.doThrow;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.timeout;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
@@ -102,6 +105,7 @@
import com.android.internal.accessibility.util.AccessibilityUtils;
import com.android.internal.accessibility.util.ShortcutUtils;
import com.android.internal.compat.IPlatformCompat;
+import com.android.internal.content.PackageMonitor;
import com.android.server.LocalServices;
import com.android.server.accessibility.AccessibilityManagerService.AccessibilityDisplayListener;
import com.android.server.accessibility.magnification.FullScreenMagnificationController;
@@ -910,6 +914,38 @@
}
@Test
+ public void onPackageChanged_disableComponent_updateInstalledServices() {
+ // Sets up two accessibility services as installed services
+ setupShortcutTargetServices();
+ assertThat(mA11yms.getCurrentUserState().mInstalledServices).hasSize(2);
+ AccessibilityServiceInfo installedService1 =
+ mA11yms.getCurrentUserState().mInstalledServices.getFirst();
+ ResolveInfo resolveInfo1 = installedService1.getResolveInfo();
+ AccessibilityServiceInfo installedService2 =
+ mA11yms.getCurrentUserState().mInstalledServices.getLast();
+
+ // Disables `installedService2`
+ when(mMockPackageManager.queryIntentServicesAsUser(any(), anyInt(), anyInt()))
+ .thenReturn(List.of(resolveInfo1));
+ when(mMockSecurityPolicy.canRegisterService(any())).thenReturn(true);
+ final Intent packageIntent = new Intent(Intent.ACTION_PACKAGE_CHANGED);
+ packageIntent.setData(
+ Uri.parse("package:" + installedService2.getResolveInfo().serviceInfo.packageName));
+ packageIntent.putExtra(Intent.EXTRA_UID, UserHandle.myUserId());
+ packageIntent.putExtra(Intent.EXTRA_USER_HANDLE, mA11yms.getCurrentUserIdLocked());
+ packageIntent.putExtra(Intent.EXTRA_CHANGED_COMPONENT_NAME_LIST,
+ new String[]{
+ installedService2.getComponentName().flattenToString()});
+ mA11yms.getPackageMonitor().doHandlePackageEvent(packageIntent);
+
+ assertThat(mA11yms.getCurrentUserState().mInstalledServices).hasSize(1);
+ ComponentName installedService =
+ mA11yms.getCurrentUserState().mInstalledServices.getFirst().getComponentName();
+ assertThat(installedService)
+ .isEqualTo(installedService1.getComponentName());
+ }
+
+ @Test
public void testSwitchUserScanPackages_scansWithoutHoldingLock() {
setupAccessibilityServiceConnection(0);
final AtomicReference<Set<Boolean>> lockState = collectLockStateWhilePackageScanning();
@@ -1620,6 +1656,67 @@
.containsExactlyElementsIn(Set.of(daltonizerTile));
}
+ @Test
+ @EnableFlags(Flags.FLAG_MANAGER_PACKAGE_MONITOR_LOGIC_FIX)
+ public void onHandleForceStop_dontDoIt_packageEnabled_returnsTrue() {
+ setupShortcutTargetServices();
+ AccessibilityUserState userState = mA11yms.getCurrentUserState();
+ userState.mEnabledServices.addAll(
+ userState.mInstalledServices.stream().map(
+ (AccessibilityServiceInfo::getComponentName)).toList());
+ String[] packages = userState.mEnabledServices.stream().map(
+ ComponentName::getPackageName).toList().toArray(new String[0]);
+
+ PackageMonitor monitor = spy(mA11yms.getPackageMonitor());
+ when(monitor.getChangingUserId()).thenReturn(UserHandle.USER_SYSTEM);
+ mA11yms.setPackageMonitor(monitor);
+
+ assertTrue(mA11yms.getPackageMonitor().onHandleForceStop(
+ new Intent(),
+ packages,
+ UserHandle.USER_SYSTEM,
+ false
+ ));
+ }
+
+ @Test
+ @EnableFlags(Flags.FLAG_MANAGER_PACKAGE_MONITOR_LOGIC_FIX)
+ public void onHandleForceStop_doIt_packageEnabled_returnsFalse() {
+ setupShortcutTargetServices();
+ AccessibilityUserState userState = mA11yms.getCurrentUserState();
+ userState.mEnabledServices.addAll(
+ userState.mInstalledServices.stream().map(
+ (AccessibilityServiceInfo::getComponentName)).toList());
+ String[] packages = userState.mEnabledServices.stream().map(
+ ComponentName::getPackageName).toList().toArray(new String[0]);
+
+ PackageMonitor monitor = spy(mA11yms.getPackageMonitor());
+ when(monitor.getChangingUserId()).thenReturn(UserHandle.USER_SYSTEM);
+ mA11yms.setPackageMonitor(monitor);
+
+ assertFalse(mA11yms.getPackageMonitor().onHandleForceStop(
+ new Intent(),
+ packages,
+ UserHandle.USER_SYSTEM,
+ true
+ ));
+ }
+
+ @Test
+ @EnableFlags(Flags.FLAG_MANAGER_PACKAGE_MONITOR_LOGIC_FIX)
+ public void onHandleForceStop_dontDoIt_packageNotEnabled_returnsFalse() {
+ PackageMonitor monitor = spy(mA11yms.getPackageMonitor());
+ when(monitor.getChangingUserId()).thenReturn(UserHandle.USER_SYSTEM);
+ mA11yms.setPackageMonitor(monitor);
+
+ assertFalse(mA11yms.getPackageMonitor().onHandleForceStop(
+ new Intent(),
+ new String[]{ "FOO", "BAR"},
+ UserHandle.USER_SYSTEM,
+ false
+ ));
+ }
+
private static AccessibilityServiceInfo mockAccessibilityServiceInfo(
ComponentName componentName) {
return mockAccessibilityServiceInfo(
@@ -1630,7 +1727,7 @@
ComponentName componentName,
boolean isSystemApp, boolean isAlwaysOnService) {
AccessibilityServiceInfo accessibilityServiceInfo =
- Mockito.spy(new AccessibilityServiceInfo());
+ spy(new AccessibilityServiceInfo());
accessibilityServiceInfo.setComponentName(componentName);
ResolveInfo mockResolveInfo = Mockito.mock(ResolveInfo.class);
when(accessibilityServiceInfo.getResolveInfo()).thenReturn(mockResolveInfo);
diff --git a/services/tests/servicestests/src/com/android/server/hdmi/ActiveSourceActionTest.java b/services/tests/servicestests/src/com/android/server/hdmi/ActiveSourceActionTest.java
index be5e262..c1ae852 100644
--- a/services/tests/servicestests/src/com/android/server/hdmi/ActiveSourceActionTest.java
+++ b/services/tests/servicestests/src/com/android/server/hdmi/ActiveSourceActionTest.java
@@ -24,8 +24,10 @@
import static org.mockito.Mockito.spy;
+import android.annotation.RequiresPermission;
import android.content.Context;
import android.content.ContextWrapper;
+import android.content.Intent;
import android.os.Looper;
import android.os.test.TestLooper;
import android.platform.test.annotations.Presubmit;
@@ -72,6 +74,11 @@
protected void writeStringSystemProperty(String key, String value) {
// do nothing
}
+
+ @Override
+ protected void sendBroadcastAsUser(@RequiresPermission Intent intent) {
+ // do nothing
+ }
};
Looper looper = mTestLooper.getLooper();
diff --git a/services/tests/servicestests/src/com/android/server/hdmi/ArcInitiationActionFromAvrTest.java b/services/tests/servicestests/src/com/android/server/hdmi/ArcInitiationActionFromAvrTest.java
index 5be3c8e..a5f7bb1 100644
--- a/services/tests/servicestests/src/com/android/server/hdmi/ArcInitiationActionFromAvrTest.java
+++ b/services/tests/servicestests/src/com/android/server/hdmi/ArcInitiationActionFromAvrTest.java
@@ -22,8 +22,10 @@
import static org.mockito.Mockito.spy;
+import android.annotation.RequiresPermission;
import android.content.Context;
import android.content.ContextWrapper;
+import android.content.Intent;
import android.hardware.tv.cec.V1_0.SendMessageResult;
import android.os.Looper;
import android.os.test.TestLooper;
@@ -84,6 +86,11 @@
protected Looper getServiceLooper() {
return mTestLooper.getLooper();
}
+
+ @Override
+ protected void sendBroadcastAsUser(@RequiresPermission Intent intent) {
+ // do nothing
+ }
};
mHdmiCecLocalDeviceAudioSystem = new HdmiCecLocalDeviceAudioSystem(hdmiControlService) {
diff --git a/services/tests/servicestests/src/com/android/server/hdmi/ArcTerminationActionFromAvrTest.java b/services/tests/servicestests/src/com/android/server/hdmi/ArcTerminationActionFromAvrTest.java
index 7845c30..857ee1a 100644
--- a/services/tests/servicestests/src/com/android/server/hdmi/ArcTerminationActionFromAvrTest.java
+++ b/services/tests/servicestests/src/com/android/server/hdmi/ArcTerminationActionFromAvrTest.java
@@ -22,8 +22,10 @@
import static org.mockito.Mockito.spy;
+import android.annotation.RequiresPermission;
import android.content.Context;
import android.content.ContextWrapper;
+import android.content.Intent;
import android.hardware.hdmi.HdmiControlManager;
import android.hardware.hdmi.IHdmiControlCallback;
import android.hardware.tv.cec.V1_0.SendMessageResult;
@@ -90,6 +92,11 @@
protected Looper getServiceLooper() {
return mTestLooper.getLooper();
}
+
+ @Override
+ protected void sendBroadcastAsUser(@RequiresPermission Intent intent) {
+ // do nothing
+ }
};
Looper looper = mTestLooper.getLooper();
diff --git a/services/tests/servicestests/src/com/android/server/hdmi/BaseAbsoluteVolumeBehaviorTest.java b/services/tests/servicestests/src/com/android/server/hdmi/BaseAbsoluteVolumeBehaviorTest.java
index 98789ac..6ace9f1 100644
--- a/services/tests/servicestests/src/com/android/server/hdmi/BaseAbsoluteVolumeBehaviorTest.java
+++ b/services/tests/servicestests/src/com/android/server/hdmi/BaseAbsoluteVolumeBehaviorTest.java
@@ -33,8 +33,10 @@
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.verify;
+import android.annotation.RequiresPermission;
import android.content.Context;
import android.content.ContextWrapper;
+import android.content.Intent;
import android.hardware.hdmi.DeviceFeatures;
import android.hardware.hdmi.HdmiControlManager;
import android.hardware.hdmi.HdmiDeviceInfo;
@@ -140,17 +142,8 @@
// do nothing
}
- /**
- * Override displayOsd to prevent it from broadcasting an intent, which
- * can trigger a SecurityException.
- */
@Override
- void displayOsd(int messageId) {
- // do nothing
- }
-
- @Override
- void displayOsd(int messageId, int extra) {
+ protected void sendBroadcastAsUser(@RequiresPermission Intent intent) {
// do nothing
}
};
diff --git a/services/tests/servicestests/src/com/android/server/hdmi/DetectTvSystemAudioModeSupportActionTest.java b/services/tests/servicestests/src/com/android/server/hdmi/DetectTvSystemAudioModeSupportActionTest.java
index 9b65762..2dd593c 100644
--- a/services/tests/servicestests/src/com/android/server/hdmi/DetectTvSystemAudioModeSupportActionTest.java
+++ b/services/tests/servicestests/src/com/android/server/hdmi/DetectTvSystemAudioModeSupportActionTest.java
@@ -18,6 +18,8 @@
import static org.junit.Assert.assertEquals;
import android.annotation.Nullable;
+import android.annotation.RequiresPermission;
+import android.content.Intent;
import android.hardware.hdmi.HdmiDeviceInfo;
import android.hardware.tv.cec.V1_0.SendMessageResult;
import android.os.Looper;
@@ -103,6 +105,11 @@
protected Looper getServiceLooper() {
return mTestLooper.getLooper();
}
+
+ @Override
+ protected void sendBroadcastAsUser(@RequiresPermission Intent intent) {
+ // do nothing
+ }
};
mHdmiCecLocalDeviceAudioSystem =
new HdmiCecLocalDeviceAudioSystem(hdmiControlService) {
diff --git a/services/tests/servicestests/src/com/android/server/hdmi/DevicePowerStatusActionTest.java b/services/tests/servicestests/src/com/android/server/hdmi/DevicePowerStatusActionTest.java
index 922706e..e669e7c 100644
--- a/services/tests/servicestests/src/com/android/server/hdmi/DevicePowerStatusActionTest.java
+++ b/services/tests/servicestests/src/com/android/server/hdmi/DevicePowerStatusActionTest.java
@@ -25,8 +25,10 @@
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.verify;
+import android.annotation.RequiresPermission;
import android.content.Context;
import android.content.ContextWrapper;
+import android.content.Intent;
import android.hardware.hdmi.HdmiControlManager;
import android.hardware.hdmi.HdmiDeviceInfo;
import android.hardware.hdmi.IHdmiControlCallback;
@@ -90,6 +92,11 @@
protected void writeStringSystemProperty(String key, String value) {
// do nothing
}
+
+ @Override
+ protected void sendBroadcastAsUser(@RequiresPermission Intent intent) {
+ // do nothing
+ }
};
Looper looper = mTestLooper.getLooper();
diff --git a/services/tests/servicestests/src/com/android/server/hdmi/DeviceSelectActionFromPlaybackTest.java b/services/tests/servicestests/src/com/android/server/hdmi/DeviceSelectActionFromPlaybackTest.java
index 68ef80f..29d20a6 100644
--- a/services/tests/servicestests/src/com/android/server/hdmi/DeviceSelectActionFromPlaybackTest.java
+++ b/services/tests/servicestests/src/com/android/server/hdmi/DeviceSelectActionFromPlaybackTest.java
@@ -30,7 +30,9 @@
import static com.google.common.truth.Truth.assertThat;
+import android.annotation.RequiresPermission;
import android.content.Context;
+import android.content.Intent;
import android.hardware.hdmi.HdmiControlManager;
import android.hardware.hdmi.HdmiDeviceInfo;
import android.hardware.hdmi.IHdmiControlCallback;
@@ -123,6 +125,11 @@
boolean isPowerStandby() {
return false;
}
+
+ @Override
+ protected void sendBroadcastAsUser(@RequiresPermission Intent intent) {
+ // do nothing
+ }
};
diff --git a/services/tests/servicestests/src/com/android/server/hdmi/DeviceSelectActionFromTvTest.java b/services/tests/servicestests/src/com/android/server/hdmi/DeviceSelectActionFromTvTest.java
index 26b448a..d32b75b 100644
--- a/services/tests/servicestests/src/com/android/server/hdmi/DeviceSelectActionFromTvTest.java
+++ b/services/tests/servicestests/src/com/android/server/hdmi/DeviceSelectActionFromTvTest.java
@@ -29,7 +29,9 @@
import static com.google.common.truth.Truth.assertThat;
+import android.annotation.RequiresPermission;
import android.content.Context;
+import android.content.Intent;
import android.hardware.hdmi.HdmiControlManager;
import android.hardware.hdmi.HdmiDeviceInfo;
import android.hardware.hdmi.HdmiPortInfo;
@@ -132,6 +134,11 @@
boolean isPowerStandby() {
return false;
}
+
+ @Override
+ protected void sendBroadcastAsUser(@RequiresPermission Intent intent) {
+ // do nothing
+ }
};
diff --git a/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecAtomLoggingTest.java b/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecAtomLoggingTest.java
index a621055..c7574bd 100644
--- a/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecAtomLoggingTest.java
+++ b/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecAtomLoggingTest.java
@@ -36,6 +36,7 @@
import android.content.Context;
import android.content.ContextWrapper;
+import android.content.Intent;
import android.hardware.hdmi.HdmiControlManager;
import android.hardware.hdmi.HdmiDeviceInfo;
import android.hardware.hdmi.HdmiPortInfo;
@@ -98,6 +99,8 @@
audioFramework.getAudioManager(), audioFramework.getAudioDeviceVolumeManager()));
doNothing().when(mHdmiControlServiceSpy)
.writeStringSystemProperty(anyString(), anyString());
+ doNothing().when(mHdmiControlServiceSpy)
+ .sendBroadcastAsUser(any(Intent.class));
doReturn(mHdmiCecAtomWriterSpy).when(mHdmiControlServiceSpy).getAtomWriter();
HdmiCecConfig hdmiCecConfig = new FakeHdmiCecConfig(mContextSpy);
doReturn(hdmiCecConfig).when(mHdmiControlServiceSpy).getHdmiCecConfig();
diff --git a/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecAtomLoggingTvTest.java b/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecAtomLoggingTvTest.java
index 30ce961..e1e101f 100644
--- a/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecAtomLoggingTvTest.java
+++ b/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecAtomLoggingTvTest.java
@@ -19,6 +19,7 @@
import static com.android.server.hdmi.Constants.HDMI_EARC_STATUS_UNKNOWN;
import static com.android.server.hdmi.HdmiControlService.WAKE_UP_SCREEN_ON;
+import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.ArgumentMatchers.eq;
@@ -30,6 +31,7 @@
import android.content.Context;
import android.content.ContextWrapper;
+import android.content.Intent;
import android.hardware.hdmi.HdmiControlManager;
import android.hardware.hdmi.HdmiDeviceInfo;
import android.hardware.hdmi.HdmiPortInfo;
@@ -90,6 +92,8 @@
audioFramework.getAudioManager(), audioFramework.getAudioDeviceVolumeManager()));
doNothing().when(mHdmiControlServiceSpy)
.writeStringSystemProperty(anyString(), anyString());
+ doNothing().when(mHdmiControlServiceSpy)
+ .sendBroadcastAsUser(any(Intent.class));
doReturn(mHdmiCecAtomWriterSpy).when(mHdmiControlServiceSpy).getAtomWriter();
HdmiCecConfig hdmiCecConfig = new FakeHdmiCecConfig(mContextSpy);
diff --git a/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecControllerTest.java b/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecControllerTest.java
index 0870bac..7ed596e 100644
--- a/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecControllerTest.java
+++ b/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecControllerTest.java
@@ -48,6 +48,7 @@
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.spy;
+import android.content.Intent;
import android.hardware.hdmi.HdmiControlManager;
import android.hardware.tv.cec.V1_0.SendMessageResult;
import android.os.Binder;
@@ -110,6 +111,8 @@
doAnswer(__ -> mCecVersion).when(mHdmiControlServiceSpy).getCecVersion();
doNothing().when(mHdmiControlServiceSpy)
.writeStringSystemProperty(anyString(), anyString());
+ doNothing().when(mHdmiControlServiceSpy)
+ .sendBroadcastAsUser(any(Intent.class));
mHdmiControlServiceSpy.setDeviceConfig(new FakeDeviceConfigWrapper());
mNativeWrapper = new FakeNativeWrapper();
diff --git a/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDeviceAudioSystemTest.java b/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDeviceAudioSystemTest.java
index 5520897..5502de8 100644
--- a/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDeviceAudioSystemTest.java
+++ b/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDeviceAudioSystemTest.java
@@ -26,7 +26,9 @@
import static com.google.common.truth.Truth.assertThat;
+import android.annotation.RequiresPermission;
import android.content.Context;
+import android.content.Intent;
import android.hardware.hdmi.HdmiControlManager;
import android.hardware.hdmi.HdmiDeviceInfo;
import android.hardware.hdmi.HdmiPortInfo;
@@ -114,6 +116,11 @@
return defVal;
}
}
+
+ @Override
+ protected void sendBroadcastAsUser(@RequiresPermission Intent intent) {
+ // do nothing
+ }
};
mHdmiControlService.getHdmiCecConfig().setIntValue(
diff --git a/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDevicePlaybackTest.java b/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDevicePlaybackTest.java
index 28da97c..8df7d54 100644
--- a/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDevicePlaybackTest.java
+++ b/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDevicePlaybackTest.java
@@ -28,7 +28,9 @@
import static com.google.common.truth.Truth.assertThat;
+import android.annotation.RequiresPermission;
import android.content.Context;
+import android.content.Intent;
import android.hardware.hdmi.HdmiControlManager;
import android.hardware.hdmi.HdmiDeviceInfo;
import android.hardware.hdmi.HdmiPortInfo;
@@ -138,6 +140,11 @@
boolean canGoToStandby() {
return true;
}
+
+ @Override
+ protected void sendBroadcastAsUser(@RequiresPermission Intent intent) {
+ // do nothing
+ }
};
mHdmiControlService.setHdmiCecConfig(new FakeHdmiCecConfig(context));
diff --git a/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDeviceTest.java b/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDeviceTest.java
index 3dd8312..192be2a 100644
--- a/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDeviceTest.java
+++ b/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDeviceTest.java
@@ -37,7 +37,9 @@
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
+import android.annotation.RequiresPermission;
import android.content.Context;
+import android.content.Intent;
import android.hardware.hdmi.HdmiControlManager;
import android.hardware.hdmi.HdmiPortInfo;
import android.hardware.tv.cec.V1_0.Result;
@@ -169,6 +171,11 @@
void wakeUp() {
mWakeupMessageReceived = true;
}
+
+ @Override
+ protected void sendBroadcastAsUser(@RequiresPermission Intent intent) {
+ // do nothing
+ }
};
mHdmiControlService.setIoLooper(mTestLooper.getLooper());
mHdmiControlService.setHdmiCecConfig(new FakeHdmiCecConfig(context));
diff --git a/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDeviceTvTest.java b/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDeviceTvTest.java
index 4faeea5..b50684b 100644
--- a/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDeviceTvTest.java
+++ b/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDeviceTvTest.java
@@ -26,6 +26,8 @@
import static com.android.server.hdmi.HdmiCecLocalDevice.ActiveSource;
import static com.android.server.hdmi.HdmiControlService.INITIATED_BY_ENABLE_CEC;
import static com.android.server.hdmi.HdmiControlService.INITIATED_BY_WAKE_UP_MESSAGE;
+import static com.android.server.hdmi.HdmiControlService.STANDBY_SCREEN_OFF;
+import static com.android.server.hdmi.HdmiControlService.WAKE_UP_SCREEN_ON;
import static com.google.common.truth.Truth.assertThat;
@@ -41,7 +43,9 @@
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
+import android.annotation.RequiresPermission;
import android.content.Context;
+import android.content.Intent;
import android.hardware.hdmi.HdmiControlManager;
import android.hardware.hdmi.HdmiDeviceInfo;
import android.hardware.hdmi.HdmiPortInfo;
@@ -184,17 +188,8 @@
return mEarcBlocksArc;
}
- /**
- * Override displayOsd to prevent it from broadcasting an intent, which
- * can trigger a SecurityException.
- */
@Override
- void displayOsd(int messageId) {
- // do nothing
- }
-
- @Override
- void displayOsd(int messageId, int extra) {
+ protected void sendBroadcastAsUser(@RequiresPermission Intent intent) {
// do nothing
}
};
@@ -1787,9 +1782,17 @@
HdmiCecMessage activeSourceFromTv =
HdmiCecMessageBuilder.buildActiveSource(ADDR_TV, 0x0000);
- mHdmiControlService.getHdmiCecNetwork().clearLocalDevices();
+ // Go to standby to invalidate the active source on the local device s.t. the
+ // RequestActiveSourceAction will start.
+ mHdmiControlService.onStandby(STANDBY_SCREEN_OFF);
+ mTestLooper.dispatchAll();
+
mNativeWrapper.setPollAddressResponse(ADDR_PLAYBACK_1, SendMessageResult.SUCCESS);
- mHdmiControlService.allocateLogicalAddress(mLocalDevices, INITIATED_BY_ENABLE_CEC);
+ mHdmiControlService.onWakeUp(WAKE_UP_SCREEN_ON);
+ mTestLooper.dispatchAll();
+
+ // Skip the LauncherX API timeout.
+ mTestLooper.moveTimeForward(HdmiConfig.TIMEOUT_MS * 2);
mTestLooper.dispatchAll();
assertThat(mNativeWrapper.getResultMessages()).contains(requestActiveSource);
@@ -1798,6 +1801,10 @@
mTestLooper.moveTimeForward(HdmiConfig.TIMEOUT_MS);
mTestLooper.dispatchAll();
+ // Assume there was a retry and the action did not finish earlier.
+ mTestLooper.moveTimeForward(HdmiConfig.TIMEOUT_MS);
+ mTestLooper.dispatchAll();
+
assertThat(mNativeWrapper.getResultMessages()).doesNotContain(activeSourceFromTv);
}
@@ -1807,9 +1814,18 @@
HdmiCecMessageBuilder.buildRequestActiveSource(ADDR_TV);
HdmiCecMessage activeSourceFromTv =
HdmiCecMessageBuilder.buildActiveSource(ADDR_TV, 0x0000);
- mHdmiControlService.getHdmiCecNetwork().clearLocalDevices();
+
+ // Go to standby to invalidate the active source on the local device s.t. the
+ // RequestActiveSourceAction will start.
+ mHdmiControlService.onStandby(STANDBY_SCREEN_OFF);
+ mTestLooper.dispatchAll();
+
mNativeWrapper.setPollAddressResponse(ADDR_PLAYBACK_1, SendMessageResult.SUCCESS);
- mHdmiControlService.allocateLogicalAddress(mLocalDevices, INITIATED_BY_ENABLE_CEC);
+ mHdmiControlService.onWakeUp(WAKE_UP_SCREEN_ON);
+ mTestLooper.dispatchAll();
+
+ // Skip the LauncherX API timeout.
+ mTestLooper.moveTimeForward(HdmiConfig.TIMEOUT_MS * 2);
mTestLooper.dispatchAll();
assertThat(mNativeWrapper.getResultMessages()).contains(requestActiveSource);
@@ -1834,8 +1850,18 @@
HdmiCecMessageBuilder.buildRequestActiveSource(ADDR_TV);
HdmiCecMessage activeSourceFromTv =
HdmiCecMessageBuilder.buildActiveSource(ADDR_TV, 0x0000);
- mHdmiControlService.getHdmiCecNetwork().clearLocalDevices();
- mHdmiControlService.allocateLogicalAddress(mLocalDevices, INITIATED_BY_ENABLE_CEC);
+
+ // Go to standby to invalidate the active source on the local device s.t. the
+ // RequestActiveSourceAction will start.
+ mHdmiControlService.onStandby(STANDBY_SCREEN_OFF);
+ mTestLooper.dispatchAll();
+
+ mNativeWrapper.setPollAddressResponse(ADDR_PLAYBACK_1, SendMessageResult.SUCCESS);
+ mHdmiControlService.onWakeUp(WAKE_UP_SCREEN_ON);
+ mTestLooper.dispatchAll();
+
+ // Skip the LauncherX API timeout.
+ mTestLooper.moveTimeForward(HdmiConfig.TIMEOUT_MS * 2);
mTestLooper.dispatchAll();
assertThat(mNativeWrapper.getResultMessages()).contains(requestActiveSource);
@@ -1854,8 +1880,16 @@
HdmiCecMessageBuilder.buildRequestActiveSource(ADDR_TV);
HdmiCecMessage activeSourceFromTv =
HdmiCecMessageBuilder.buildActiveSource(ADDR_TV, 0x0000);
- mHdmiControlService.getHdmiCecNetwork().clearLocalDevices();
- mHdmiControlService.allocateLogicalAddress(mLocalDevices, INITIATED_BY_ENABLE_CEC);
+
+ // Go to standby to invalidate the active source on the local device s.t. the
+ // RequestActiveSourceAction will start.
+ mHdmiControlService.onStandby(STANDBY_SCREEN_OFF);
+ mTestLooper.dispatchAll();
+
+ mNativeWrapper.setPollAddressResponse(ADDR_PLAYBACK_1, SendMessageResult.SUCCESS);
+ mHdmiControlService.onWakeUp(WAKE_UP_SCREEN_ON);
+ mTestLooper.dispatchAll();
+
HdmiDeviceInfo playbackDevice = HdmiDeviceInfo.cecDeviceBuilder()
.setLogicalAddress(ADDR_PLAYBACK_1)
.setPhysicalAddress(0x1000)
@@ -1869,6 +1903,10 @@
mHdmiControlService.getHdmiCecNetwork().addCecDevice(playbackDevice);
mTestLooper.dispatchAll();
+ // Skip the LauncherX API timeout.
+ mTestLooper.moveTimeForward(HdmiConfig.TIMEOUT_MS * 2);
+ mTestLooper.dispatchAll();
+
assertThat(mNativeWrapper.getResultMessages()).contains(requestActiveSource);
mNativeWrapper.clearResultMessages();
mHdmiCecLocalDeviceTv.deviceSelect(playbackDevice.getId(), null);
@@ -1881,6 +1919,41 @@
assertThat(mNativeWrapper.getResultMessages()).doesNotContain(activeSourceFromTv);
}
+ @Test
+ public void onAddressAllocated_sendSourceChangingMessage_noRequestActiveSourceMessage() {
+ HdmiCecMessage requestActiveSource =
+ HdmiCecMessageBuilder.buildRequestActiveSource(ADDR_TV);
+ HdmiCecMessage activeSourceFromTv =
+ HdmiCecMessageBuilder.buildActiveSource(ADDR_TV, 0x0000);
+ HdmiCecMessage setStreamPathFromTv =
+ HdmiCecMessageBuilder.buildSetStreamPath(ADDR_TV, 0x2000);
+
+ // Go to standby to invalidate the active source on the local device s.t. the
+ // RequestActiveSourceAction will start.
+ mHdmiControlService.onStandby(STANDBY_SCREEN_OFF);
+ mTestLooper.dispatchAll();
+
+ mHdmiControlService.onWakeUp(WAKE_UP_SCREEN_ON);
+ mTestLooper.dispatchAll();
+
+ // Even if the device at the end of this path doesn't answer to this message, TV should not
+ // continue the RequestActiveSourceAction.
+ mHdmiControlService.sendCecCommand(setStreamPathFromTv);
+
+ // Skip the LauncherX API timeout.
+ mTestLooper.moveTimeForward(HdmiConfig.TIMEOUT_MS * 2);
+ mTestLooper.dispatchAll();
+
+ assertThat(mNativeWrapper.getResultMessages()).doesNotContain(requestActiveSource);
+ mTestLooper.moveTimeForward(HdmiConfig.TIMEOUT_MS);
+ mTestLooper.dispatchAll();
+
+ // Assume there was a retry and the action did not finish earlier.
+ mTestLooper.moveTimeForward(HdmiConfig.TIMEOUT_MS);
+ mTestLooper.dispatchAll();
+
+ assertThat(mNativeWrapper.getResultMessages()).doesNotContain(activeSourceFromTv);
+ }
@Test
public void newDeviceConnectedIfOnlyOneGiveOsdNameSent() {
@@ -1907,7 +1980,12 @@
HdmiCecMessage activeSourceFromTv =
HdmiCecMessageBuilder.buildActiveSource(ADDR_TV, 0x0000);
- mHdmiControlService.allocateLogicalAddress(mLocalDevices, INITIATED_BY_WAKE_UP_MESSAGE);
+ // Go to standby to invalidate the active source on the local device s.t. the
+ // TV will send <Active Source> when it selects its internal source.
+ mHdmiControlService.onStandby(STANDBY_SCREEN_OFF);
+ mTestLooper.dispatchAll();
+
+ mHdmiControlService.onWakeUp(WAKE_UP_SCREEN_ON);
mTestLooper.dispatchAll();
mTestLooper.moveTimeForward(HdmiConfig.TIMEOUT_MS);
@@ -1937,6 +2015,8 @@
public void handleStandby_fromActiveSource_standby() {
mPowerManager.setInteractive(true);
mHdmiControlService.allocateLogicalAddress(mLocalDevices, INITIATED_BY_ENABLE_CEC);
+ mTestLooper.dispatchAll();
+
mHdmiControlService.setActiveSource(ADDR_PLAYBACK_1, 0x1000,
"HdmiCecLocalDeviceTvTest");
mTestLooper.dispatchAll();
diff --git a/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecPowerStatusControllerTest.java b/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecPowerStatusControllerTest.java
index c002067..9412ee0 100644
--- a/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecPowerStatusControllerTest.java
+++ b/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecPowerStatusControllerTest.java
@@ -21,8 +21,10 @@
import static org.mockito.Mockito.spy;
+import android.annotation.RequiresPermission;
import android.content.Context;
import android.content.ContextWrapper;
+import android.content.Intent;
import android.hardware.hdmi.HdmiControlManager;
import android.hardware.hdmi.HdmiDeviceInfo;
import android.hardware.hdmi.HdmiPortInfo;
@@ -88,6 +90,11 @@
boolean isPowerStandby() {
return false;
}
+
+ @Override
+ protected void sendBroadcastAsUser(@RequiresPermission Intent intent) {
+ // do nothing
+ }
};
mHdmiControlService.onBootPhase(SystemService.PHASE_SYSTEM_SERVICES_READY);
diff --git a/services/tests/servicestests/src/com/android/server/hdmi/HdmiControlServiceTest.java b/services/tests/servicestests/src/com/android/server/hdmi/HdmiControlServiceTest.java
index e1b66b5..126a658 100644
--- a/services/tests/servicestests/src/com/android/server/hdmi/HdmiControlServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/hdmi/HdmiControlServiceTest.java
@@ -52,6 +52,7 @@
import android.content.Context;
import android.content.ContextWrapper;
+import android.content.Intent;
import android.hardware.hdmi.HdmiControlManager;
import android.hardware.hdmi.HdmiDeviceInfo;
import android.hardware.hdmi.HdmiPortInfo;
@@ -121,6 +122,8 @@
audioFramework.getAudioManager(), audioFramework.getAudioDeviceVolumeManager()));
doNothing().when(mHdmiControlServiceSpy)
.writeStringSystemProperty(anyString(), anyString());
+ doNothing().when(mHdmiControlServiceSpy)
+ .sendBroadcastAsUser(any(Intent.class));
mMyLooper = mTestLooper.getLooper();
diff --git a/services/tests/servicestests/src/com/android/server/hdmi/OneTouchPlayActionTest.java b/services/tests/servicestests/src/com/android/server/hdmi/OneTouchPlayActionTest.java
index 4641802..298ff46 100644
--- a/services/tests/servicestests/src/com/android/server/hdmi/OneTouchPlayActionTest.java
+++ b/services/tests/servicestests/src/com/android/server/hdmi/OneTouchPlayActionTest.java
@@ -26,8 +26,10 @@
import static org.mockito.Mockito.spy;
+import android.annotation.RequiresPermission;
import android.content.Context;
import android.content.ContextWrapper;
+import android.content.Intent;
import android.hardware.hdmi.HdmiControlManager;
import android.hardware.hdmi.HdmiDeviceInfo;
import android.hardware.hdmi.IHdmiControlCallback;
@@ -104,6 +106,11 @@
protected void writeStringSystemProperty(String key, String value) {
// do nothing
}
+
+ @Override
+ protected void sendBroadcastAsUser(@RequiresPermission Intent intent) {
+ // do nothing
+ }
};
Looper looper = mTestLooper.getLooper();
diff --git a/services/tests/servicestests/src/com/android/server/hdmi/PowerStatusMonitorActionTest.java b/services/tests/servicestests/src/com/android/server/hdmi/PowerStatusMonitorActionTest.java
index 9f0a44c..1d4a72f 100644
--- a/services/tests/servicestests/src/com/android/server/hdmi/PowerStatusMonitorActionTest.java
+++ b/services/tests/servicestests/src/com/android/server/hdmi/PowerStatusMonitorActionTest.java
@@ -25,8 +25,10 @@
import static org.mockito.Mockito.spy;
+import android.annotation.RequiresPermission;
import android.content.Context;
import android.content.ContextWrapper;
+import android.content.Intent;
import android.hardware.hdmi.HdmiControlManager;
import android.hardware.hdmi.HdmiDeviceInfo;
import android.hardware.hdmi.HdmiPortInfo;
@@ -78,6 +80,11 @@
protected void writeStringSystemProperty(String key, String value) {
// do nothing
}
+
+ @Override
+ protected void sendBroadcastAsUser(@RequiresPermission Intent intent) {
+ // do nothing
+ }
};
Looper looper = mTestLooper.getLooper();
diff --git a/services/tests/servicestests/src/com/android/server/hdmi/RequestSadActionTest.java b/services/tests/servicestests/src/com/android/server/hdmi/RequestSadActionTest.java
index 043db1e..cafe1e7 100644
--- a/services/tests/servicestests/src/com/android/server/hdmi/RequestSadActionTest.java
+++ b/services/tests/servicestests/src/com/android/server/hdmi/RequestSadActionTest.java
@@ -21,7 +21,9 @@
import static com.google.common.truth.Truth.assertThat;
+import android.annotation.RequiresPermission;
import android.content.Context;
+import android.content.Intent;
import android.hardware.hdmi.HdmiControlManager;
import android.hardware.hdmi.HdmiDeviceInfo;
import android.os.Looper;
@@ -115,6 +117,11 @@
boolean isPowerStandbyOrTransient() {
return false;
}
+
+ @Override
+ protected void sendBroadcastAsUser(@RequiresPermission Intent intent) {
+ // do nothing
+ }
};
mHdmiControlService.setIoLooper(mMyLooper);
diff --git a/services/tests/servicestests/src/com/android/server/hdmi/ResendCecCommandActionPlaybackTest.java b/services/tests/servicestests/src/com/android/server/hdmi/ResendCecCommandActionPlaybackTest.java
index 061e1f9..864a182 100644
--- a/services/tests/servicestests/src/com/android/server/hdmi/ResendCecCommandActionPlaybackTest.java
+++ b/services/tests/servicestests/src/com/android/server/hdmi/ResendCecCommandActionPlaybackTest.java
@@ -21,7 +21,9 @@
import static com.google.common.truth.Truth.assertThat;
+import android.annotation.RequiresPermission;
import android.content.Context;
+import android.content.Intent;
import android.hardware.hdmi.HdmiControlManager;
import android.hardware.hdmi.HdmiDeviceInfo;
import android.hardware.tv.cec.V1_0.SendMessageResult;
@@ -72,6 +74,11 @@
protected void writeStringSystemProperty(String key, String value) {
// do nothing
}
+
+ @Override
+ protected void sendBroadcastAsUser(@RequiresPermission Intent intent) {
+ // do nothing
+ }
};
mIsPowerStandby = false;
diff --git a/services/tests/servicestests/src/com/android/server/hdmi/ResendCecCommandActionTvTest.java b/services/tests/servicestests/src/com/android/server/hdmi/ResendCecCommandActionTvTest.java
index b25ea2c..06709cd 100644
--- a/services/tests/servicestests/src/com/android/server/hdmi/ResendCecCommandActionTvTest.java
+++ b/services/tests/servicestests/src/com/android/server/hdmi/ResendCecCommandActionTvTest.java
@@ -21,7 +21,9 @@
import static com.google.common.truth.Truth.assertThat;
+import android.annotation.RequiresPermission;
import android.content.Context;
+import android.content.Intent;
import android.hardware.hdmi.HdmiDeviceInfo;
import android.hardware.tv.cec.V1_0.SendMessageResult;
import android.os.Looper;
@@ -75,6 +77,11 @@
boolean verifyPhysicalAddresses(HdmiCecMessage message) {
return true;
}
+
+ @Override
+ protected void sendBroadcastAsUser(@RequiresPermission Intent intent) {
+ // do nothing
+ }
};
mMyLooper = mTestLooper.getLooper();
diff --git a/services/tests/servicestests/src/com/android/server/hdmi/RoutingControlActionTest.java b/services/tests/servicestests/src/com/android/server/hdmi/RoutingControlActionTest.java
index f608c235..5163e29 100644
--- a/services/tests/servicestests/src/com/android/server/hdmi/RoutingControlActionTest.java
+++ b/services/tests/servicestests/src/com/android/server/hdmi/RoutingControlActionTest.java
@@ -29,7 +29,9 @@
import static com.google.common.truth.Truth.assertThat;
+import android.annotation.RequiresPermission;
import android.content.Context;
+import android.content.Intent;
import android.hardware.hdmi.HdmiDeviceInfo;
import android.hardware.hdmi.HdmiPortInfo;
import android.hardware.hdmi.IHdmiControlCallback;
@@ -177,6 +179,11 @@
protected HdmiCecConfig getHdmiCecConfig() {
return hdmiCecConfig;
}
+
+ @Override
+ protected void sendBroadcastAsUser(@RequiresPermission Intent intent) {
+ // do nothing
+ }
};
mHdmiControlService.setIoLooper(mMyLooper);
diff --git a/services/tests/servicestests/src/com/android/server/hdmi/SetAudioVolumeLevelDiscoveryActionTest.java b/services/tests/servicestests/src/com/android/server/hdmi/SetAudioVolumeLevelDiscoveryActionTest.java
index a73f4aa..e4297ef 100644
--- a/services/tests/servicestests/src/com/android/server/hdmi/SetAudioVolumeLevelDiscoveryActionTest.java
+++ b/services/tests/servicestests/src/com/android/server/hdmi/SetAudioVolumeLevelDiscoveryActionTest.java
@@ -24,12 +24,14 @@
import static com.google.common.truth.Truth.assertThat;
+import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.Mockito.doNothing;
import static org.mockito.Mockito.spy;
import android.content.Context;
import android.content.ContextWrapper;
+import android.content.Intent;
import android.hardware.hdmi.DeviceFeatures;
import android.hardware.hdmi.HdmiControlManager;
import android.hardware.hdmi.HdmiDeviceInfo;
@@ -87,6 +89,8 @@
audioFramework.getAudioManager(), audioFramework.getAudioDeviceVolumeManager()));
doNothing().when(mHdmiControlServiceSpy)
.writeStringSystemProperty(anyString(), anyString());
+ doNothing().when(mHdmiControlServiceSpy)
+ .sendBroadcastAsUser(any(Intent.class));
mLooper = mTestLooper.getLooper();
mHdmiControlServiceSpy.setIoLooper(mLooper);
diff --git a/services/tests/servicestests/src/com/android/server/hdmi/SystemAudioAutoInitiationActionTest.java b/services/tests/servicestests/src/com/android/server/hdmi/SystemAudioAutoInitiationActionTest.java
index 02bed22..4dcc6a4 100644
--- a/services/tests/servicestests/src/com/android/server/hdmi/SystemAudioAutoInitiationActionTest.java
+++ b/services/tests/servicestests/src/com/android/server/hdmi/SystemAudioAutoInitiationActionTest.java
@@ -25,8 +25,10 @@
import static org.mockito.Mockito.spy;
+import android.annotation.RequiresPermission;
import android.content.Context;
import android.content.ContextWrapper;
+import android.content.Intent;
import android.hardware.hdmi.HdmiDeviceInfo;
import android.hardware.hdmi.HdmiPortInfo;
import android.os.Looper;
@@ -82,17 +84,8 @@
// do nothing
}
- /**
- * Override displayOsd to prevent it from broadcasting an intent, which
- * can trigger a SecurityException.
- */
@Override
- void displayOsd(int messageId) {
- // do nothing
- }
-
- @Override
- void displayOsd(int messageId, int extra) {
+ protected void sendBroadcastAsUser(@RequiresPermission Intent intent) {
// do nothing
}
};
diff --git a/services/tests/servicestests/src/com/android/server/hdmi/SystemAudioInitiationActionFromAvrTest.java b/services/tests/servicestests/src/com/android/server/hdmi/SystemAudioInitiationActionFromAvrTest.java
index df27e78..4aa074b 100644
--- a/services/tests/servicestests/src/com/android/server/hdmi/SystemAudioInitiationActionFromAvrTest.java
+++ b/services/tests/servicestests/src/com/android/server/hdmi/SystemAudioInitiationActionFromAvrTest.java
@@ -23,7 +23,9 @@
import static org.junit.Assert.assertTrue;
import android.annotation.Nullable;
+import android.annotation.RequiresPermission;
import android.content.Context;
+import android.content.Intent;
import android.hardware.hdmi.HdmiDeviceInfo;
import android.hardware.tv.cec.V1_0.SendMessageResult;
import android.os.Looper;
@@ -143,6 +145,11 @@
protected boolean isStandbyMessageReceived() {
return mStandbyMessageReceived;
}
+
+ @Override
+ protected void sendBroadcastAsUser(@RequiresPermission Intent intent) {
+ // do nothing
+ }
};
Looper looper = mTestLooper.getLooper();
diff --git a/services/tests/servicestests/src/com/android/server/net/NetworkManagementServiceTest.java b/services/tests/servicestests/src/com/android/server/net/NetworkManagementServiceTest.java
index 07fb9fc..570256b 100644
--- a/services/tests/servicestests/src/com/android/server/net/NetworkManagementServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/net/NetworkManagementServiceTest.java
@@ -19,9 +19,16 @@
import static android.net.ConnectivityManager.FIREWALL_CHAIN_BACKGROUND;
import static android.net.ConnectivityManager.FIREWALL_CHAIN_DOZABLE;
import static android.net.ConnectivityManager.FIREWALL_CHAIN_LOW_POWER_STANDBY;
+import static android.net.ConnectivityManager.FIREWALL_CHAIN_METERED_ALLOW;
+import static android.net.ConnectivityManager.FIREWALL_CHAIN_METERED_DENY_ADMIN;
+import static android.net.ConnectivityManager.FIREWALL_CHAIN_METERED_DENY_USER;
import static android.net.ConnectivityManager.FIREWALL_CHAIN_POWERSAVE;
import static android.net.ConnectivityManager.FIREWALL_CHAIN_RESTRICTED;
import static android.net.ConnectivityManager.FIREWALL_CHAIN_STANDBY;
+import static android.net.ConnectivityManager.FIREWALL_RULE_ALLOW;
+import static android.net.ConnectivityManager.FIREWALL_RULE_DEFAULT;
+import static android.net.ConnectivityManager.FIREWALL_RULE_DENY;
+import static android.platform.test.flag.junit.SetFlagsRule.DefaultInitValueType.DEVICE_DEFAULT;
import static android.util.DebugUtils.valueToString;
import static org.junit.Assert.assertEquals;
@@ -51,7 +58,10 @@
import android.os.Process;
import android.os.RemoteException;
import android.os.test.FakePermissionEnforcer;
+import android.platform.test.annotations.DisableFlags;
+import android.platform.test.annotations.EnableFlags;
import android.platform.test.annotations.Presubmit;
+import android.platform.test.flag.junit.SetFlagsRule;
import android.util.ArrayMap;
import androidx.test.filters.SmallTest;
@@ -62,6 +72,7 @@
import org.junit.After;
import org.junit.Before;
+import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.ArgumentCaptor;
@@ -84,6 +95,9 @@
@Mock private IBatteryStats.Stub mBatteryStatsService;
@Mock private INetd.Stub mNetdService;
+ @Rule
+ public final SetFlagsRule mSetFlagsRule = new SetFlagsRule(DEVICE_DEFAULT);
+
private static final int TEST_UID = 111;
@NonNull
@@ -254,6 +268,7 @@
}
@Test
+ @DisableFlags(Flags.FLAG_USE_METERED_FIREWALL_CHAINS)
public void testMeteredNetworkRestrictions() throws RemoteException {
// Make sure the mocked netd method returns true.
doReturn(true).when(mNetdService).bandwidthEnableDataSaver(anyBoolean());
@@ -295,6 +310,69 @@
}
@Test
+ @EnableFlags(Flags.FLAG_USE_METERED_FIREWALL_CHAINS)
+ public void testMeteredNetworkRestrictionsByAdminChain() {
+ mNMService.setFirewallUidRule(FIREWALL_CHAIN_METERED_DENY_ADMIN, TEST_UID,
+ FIREWALL_RULE_DENY);
+ verify(mCm).setUidFirewallRule(FIREWALL_CHAIN_METERED_DENY_ADMIN, TEST_UID,
+ FIREWALL_RULE_DENY);
+ assertTrue("Should be true since mobile data usage is restricted by admin chain",
+ mNMService.isNetworkRestricted(TEST_UID));
+
+ mNMService.setFirewallUidRule(FIREWALL_CHAIN_METERED_DENY_ADMIN, TEST_UID,
+ FIREWALL_RULE_DEFAULT);
+ verify(mCm).setUidFirewallRule(FIREWALL_CHAIN_METERED_DENY_ADMIN, TEST_UID,
+ FIREWALL_RULE_DEFAULT);
+ assertFalse("Should be false since mobile data usage is no longer restricted by admin",
+ mNMService.isNetworkRestricted(TEST_UID));
+ }
+
+ @Test
+ @EnableFlags(Flags.FLAG_USE_METERED_FIREWALL_CHAINS)
+ public void testMeteredNetworkRestrictionsByUserChain() {
+ mNMService.setFirewallUidRule(FIREWALL_CHAIN_METERED_DENY_USER, TEST_UID,
+ FIREWALL_RULE_DENY);
+ verify(mCm).setUidFirewallRule(FIREWALL_CHAIN_METERED_DENY_USER, TEST_UID,
+ FIREWALL_RULE_DENY);
+ assertTrue("Should be true since mobile data usage is restricted by user chain",
+ mNMService.isNetworkRestricted(TEST_UID));
+
+ mNMService.setFirewallUidRule(FIREWALL_CHAIN_METERED_DENY_USER, TEST_UID,
+ FIREWALL_RULE_DEFAULT);
+ verify(mCm).setUidFirewallRule(FIREWALL_CHAIN_METERED_DENY_USER, TEST_UID,
+ FIREWALL_RULE_DEFAULT);
+ assertFalse("Should be false since mobile data usage is no longer restricted by user",
+ mNMService.isNetworkRestricted(TEST_UID));
+ }
+
+ @Test
+ @EnableFlags(Flags.FLAG_USE_METERED_FIREWALL_CHAINS)
+ public void testDataSaverRestrictionsWithAllowChain() {
+ mNMService.setDataSaverModeEnabled(true);
+ verify(mCm).setDataSaverEnabled(true);
+
+ assertTrue("Should be true since data saver is on and the uid is not allowlisted",
+ mNMService.isNetworkRestricted(TEST_UID));
+
+ mNMService.setFirewallUidRule(FIREWALL_CHAIN_METERED_ALLOW, TEST_UID, FIREWALL_RULE_ALLOW);
+ verify(mCm).setUidFirewallRule(FIREWALL_CHAIN_METERED_ALLOW, TEST_UID, FIREWALL_RULE_ALLOW);
+ assertFalse("Should be false since data saver is on and the uid is allowlisted",
+ mNMService.isNetworkRestricted(TEST_UID));
+
+ // remove uid from allowlist and turn datasaver off again
+
+ mNMService.setFirewallUidRule(FIREWALL_CHAIN_METERED_ALLOW, TEST_UID,
+ FIREWALL_RULE_DEFAULT);
+ verify(mCm).setUidFirewallRule(FIREWALL_CHAIN_METERED_ALLOW, TEST_UID,
+ FIREWALL_RULE_DEFAULT);
+ mNMService.setDataSaverModeEnabled(false);
+ verify(mCm).setDataSaverEnabled(false);
+
+ assertFalse("Network should not be restricted when data saver is off",
+ mNMService.isNetworkRestricted(TEST_UID));
+ }
+
+ @Test
public void testFirewallChains() {
final ArrayMap<Integer, ArrayMap<Integer, Boolean>> expected = new ArrayMap<>();
// Dozable chain
diff --git a/services/tests/servicestests/src/com/android/server/people/data/DataManagerTest.java b/services/tests/servicestests/src/com/android/server/people/data/DataManagerTest.java
index 9fc46c5..2f3bca0 100644
--- a/services/tests/servicestests/src/com/android/server/people/data/DataManagerTest.java
+++ b/services/tests/servicestests/src/com/android/server/people/data/DataManagerTest.java
@@ -1786,10 +1786,10 @@
/* matchesInterruptionFilter= */ false,
/* visibilityOverride= */ 0,
/* suppressedVisualEffects= */ 0,
- mParentNotificationChannel.getImportance(),
+ mNotificationChannel.getImportance(),
/* explanation= */ null,
/* overrideGroupKey= */ null,
- mParentNotificationChannel,
+ mNotificationChannel,
/* overridePeople= */ null,
/* snoozeCriteria= */ null,
/* showBadge= */ true,
diff --git a/services/tests/servicestests/src/com/android/server/usage/AppIdleHistoryTests.java b/services/tests/servicestests/src/com/android/server/usage/AppIdleHistoryTests.java
index 06726b0..55d93fb 100644
--- a/services/tests/servicestests/src/com/android/server/usage/AppIdleHistoryTests.java
+++ b/services/tests/servicestests/src/com/android/server/usage/AppIdleHistoryTests.java
@@ -144,7 +144,9 @@
assertEquals(aih.getAppStandbyReason(PACKAGE_4, USER_ID, 3000),
REASON_MAIN_FORCED_BY_USER);
- assertTrue(aih.shouldInformListeners(PACKAGE_1, USER_ID, 5000, STANDBY_BUCKET_RARE));
+ if (!Flags.avoidIdleCheck()) {
+ assertTrue(aih.shouldInformListeners(PACKAGE_1, USER_ID, 5000, STANDBY_BUCKET_RARE));
+ }
assertFalse(aih.shouldInformListeners(PACKAGE_1, USER_ID, 5000, STANDBY_BUCKET_RARE));
assertTrue(aih.shouldInformListeners(PACKAGE_1, USER_ID, 5000, STANDBY_BUCKET_FREQUENT));
}
@@ -243,4 +245,4 @@
expectedExpiryTimeMs, actualExpiryTimeMs);
}
}
-}
\ No newline at end of file
+}
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/NotificationAttentionHelperTest.java b/services/tests/uiservicestests/src/com/android/server/notification/NotificationAttentionHelperTest.java
index 4af20a9..70a0038 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationAttentionHelperTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationAttentionHelperTest.java
@@ -24,6 +24,8 @@
import static android.app.NotificationManager.IMPORTANCE_LOW;
import static android.app.NotificationManager.IMPORTANCE_MIN;
import static android.app.NotificationManager.Policy.SUPPRESSED_EFFECT_LIGHTS;
+import static android.content.pm.PackageManager.PERMISSION_DENIED;
+import static android.content.pm.PackageManager.PERMISSION_GRANTED;
import static android.media.AudioAttributes.USAGE_NOTIFICATION;
import static android.media.AudioAttributes.USAGE_NOTIFICATION_RINGTONE;
@@ -52,6 +54,7 @@
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
+import android.Manifest.permission;
import android.annotation.SuppressLint;
import android.app.ActivityManager;
import android.app.KeyguardManager;
@@ -67,6 +70,7 @@
import android.content.pm.PackageManager;
import android.content.pm.ShortcutInfo;
import android.content.pm.UserInfo;
+import android.content.res.Resources;
import android.graphics.Color;
import android.graphics.drawable.Icon;
import android.media.AudioAttributes;
@@ -93,6 +97,7 @@
import androidx.test.filters.SmallTest;
import androidx.test.runner.AndroidJUnit4;
+import com.android.internal.R;
import com.android.internal.config.sysui.SystemUiSystemPropertiesFlags.NotificationFlags;
import com.android.internal.config.sysui.TestableFlagResolver;
import com.android.internal.logging.InstanceIdSequence;
@@ -188,6 +193,8 @@
getContext().addMockSystemService(Vibrator.class, mVibrator);
getContext().addMockSystemService(PackageManager.class, mPackageManager);
when(mPackageManager.hasSystemFeature(PackageManager.FEATURE_TELEPHONY)).thenReturn(false);
+ when(mPackageManager.checkPermission(eq(permission.RECEIVE_EMERGENCY_BROADCAST),
+ anyString())).thenReturn(PERMISSION_DENIED);
when(mAudioManager.isAudioFocusExclusive()).thenReturn(false);
when(mAudioManager.getRingtonePlayer()).thenReturn(mRingtonePlayer);
@@ -210,6 +217,16 @@
verify(mAccessibilityService).addClient(any(IAccessibilityManagerClient.class), anyInt());
assertTrue(mAccessibilityManager.isEnabled());
+ // Enable LED pulse setting by default
+ Settings.System.putInt(getContext().getContentResolver(),
+ Settings.System.NOTIFICATION_LIGHT_PULSE, 1);
+
+ Resources resources = spy(getContext().getResources());
+ when(resources.getBoolean(R.bool.config_useAttentionLight)).thenReturn(true);
+ when(resources.getBoolean(
+ com.android.internal.R.bool.config_intrusiveNotificationLed)).thenReturn(true);
+ when(getContext().getResources()).thenReturn(resources);
+
// TODO (b/291907312): remove feature flag
// Disable feature flags by default. Tests should enable as needed.
mSetFlagsRule.disableFlags(Flags.FLAG_POLITE_NOTIFICATIONS,
@@ -239,7 +256,6 @@
mAttentionHelper.setKeyguardManager(mKeyguardManager);
mAttentionHelper.setScreenOn(false);
mAttentionHelper.setInCallStateOffHook(false);
- mAttentionHelper.mNotificationPulseEnabled = true;
if (Flags.crossAppPoliteNotifications()) {
// Capture BroadcastReceiver for avalanche triggers
@@ -611,6 +627,14 @@
verify(mLight, times(1)).setFlashing(anyInt(), anyInt(), anyInt(), anyInt());
}
+ private void verifyAttentionLights() {
+ verify(mLight, times(1)).pulse();
+ }
+
+ private void verifyNeverAttentionLights() {
+ verify(mLight, never()).pulse();
+ }
+
//
// Tests
//
@@ -1524,7 +1548,10 @@
@Test
public void testLightsLightsOffGlobally() {
- mAttentionHelper.mNotificationPulseEnabled = false;
+ Settings.System.putInt(getContext().getContentResolver(),
+ Settings.System.NOTIFICATION_LIGHT_PULSE, 0);
+ initAttentionHelper(mTestFlagResolver);
+
NotificationRecord r = getLightsNotification();
mAttentionHelper.buzzBeepBlinkLocked(r, DEFAULT_SIGNALS);
verifyNeverLights();
@@ -1533,6 +1560,44 @@
}
@Test
+ public void testLightsLightsResConfigDisabled() {
+ Resources resources = spy(getContext().getResources());
+ when(resources.getBoolean(
+ com.android.internal.R.bool.config_intrusiveNotificationLed)).thenReturn(false);
+ when(getContext().getResources()).thenReturn(resources);
+ initAttentionHelper(mTestFlagResolver);
+
+ NotificationRecord r = getLightsNotification();
+ mAttentionHelper.buzzBeepBlinkLocked(r, DEFAULT_SIGNALS);
+ verifyNeverLights();
+ assertFalse(r.isInterruptive());
+ assertEquals(-1, r.getLastAudiblyAlertedMs());
+ }
+
+ @Test
+ public void testLightsUseAttentionLight() {
+ NotificationRecord r = getLightsNotification();
+ mAttentionHelper.buzzBeepBlinkLocked(r, DEFAULT_SIGNALS);
+ verifyAttentionLights();
+ assertEquals(-1, r.getLastAudiblyAlertedMs());
+ }
+
+ @Test
+ public void testLightsUseAttentionLightDisabled() {
+ Resources resources = spy(getContext().getResources());
+ when(resources.getBoolean(R.bool.config_useAttentionLight)).thenReturn(false);
+ when(resources.getBoolean(
+ com.android.internal.R.bool.config_intrusiveNotificationLed)).thenReturn(true);
+ when(getContext().getResources()).thenReturn(resources);
+ initAttentionHelper(mTestFlagResolver);
+
+ NotificationRecord r = getLightsNotification();
+ mAttentionHelper.buzzBeepBlinkLocked(r, DEFAULT_SIGNALS);
+ verifyNeverAttentionLights();
+ assertEquals(-1, r.getLastAudiblyAlertedMs());
+ }
+
+ @Test
public void testLightsDndIntercepted() {
NotificationRecord r = getLightsNotification();
r.setSuppressedVisualEffects(SUPPRESSED_EFFECT_LIGHTS);
@@ -2303,6 +2368,72 @@
}
@Test
+ public void testBeepVolume_politeNotif_Avalanche_exemptEmergency() throws Exception {
+ mSetFlagsRule.enableFlags(Flags.FLAG_POLITE_NOTIFICATIONS);
+ mSetFlagsRule.enableFlags(Flags.FLAG_CROSS_APP_POLITE_NOTIFICATIONS);
+ mSetFlagsRule.enableFlags(Flags.FLAG_POLITE_NOTIFICATIONS_ATTN_UPDATE);
+ TestableFlagResolver flagResolver = new TestableFlagResolver();
+ flagResolver.setFlagOverride(NotificationFlags.NOTIF_VOLUME1, 50);
+ flagResolver.setFlagOverride(NotificationFlags.NOTIF_VOLUME2, 0);
+ initAttentionHelper(flagResolver);
+
+ // Trigger avalanche trigger intent
+ final Intent intent = new Intent(Intent.ACTION_AIRPLANE_MODE_CHANGED);
+ intent.putExtra("state", false);
+ mAvalancheBroadcastReceiver.onReceive(getContext(), intent);
+
+ NotificationRecord r = getBeepyNotification();
+
+ // Grant RECEIVE_EMERGENCY_BROADCAST to notification's package
+ when(mPackageManager.checkPermission(eq(permission.RECEIVE_EMERGENCY_BROADCAST),
+ eq(r.getSbn().getPackageName()))).thenReturn(PERMISSION_GRANTED);
+
+ // Should beep at 100% volume
+ mAttentionHelper.buzzBeepBlinkLocked(r, DEFAULT_SIGNALS);
+ verifyBeepVolume(1.0f);
+ assertNotEquals(-1, r.getLastAudiblyAlertedMs());
+ verify(mAccessibilityService, times(1)).sendAccessibilityEvent(any(), anyInt());
+ }
+
+ @Test
+ public void testBeepVolume_politeNotif_exemptEmergency() throws Exception {
+ mSetFlagsRule.enableFlags(Flags.FLAG_POLITE_NOTIFICATIONS);
+ mSetFlagsRule.disableFlags(Flags.FLAG_CROSS_APP_POLITE_NOTIFICATIONS);
+ mSetFlagsRule.enableFlags(Flags.FLAG_POLITE_NOTIFICATIONS_ATTN_UPDATE);
+ TestableFlagResolver flagResolver = new TestableFlagResolver();
+ flagResolver.setFlagOverride(NotificationFlags.NOTIF_VOLUME1, 50);
+ flagResolver.setFlagOverride(NotificationFlags.NOTIF_VOLUME2, 0);
+ // NOTIFICATION_COOLDOWN_ALL setting is enabled
+ Settings.System.putInt(getContext().getContentResolver(),
+ Settings.System.NOTIFICATION_COOLDOWN_ALL, 1);
+ initAttentionHelper(flagResolver);
+
+ NotificationRecord r = getBeepyNotification();
+
+ // Grant RECEIVE_EMERGENCY_BROADCAST to notification's package
+ when(mPackageManager.checkPermission(eq(permission.RECEIVE_EMERGENCY_BROADCAST),
+ eq(r.getSbn().getPackageName()))).thenReturn(PERMISSION_GRANTED);
+
+ // set up internal state
+ mAttentionHelper.buzzBeepBlinkLocked(r, DEFAULT_SIGNALS);
+ Mockito.reset(mRingtonePlayer);
+
+ // update should beep at 100% volume
+ NotificationRecord r2 = getBeepyNotification();
+ mAttentionHelper.buzzBeepBlinkLocked(r2, DEFAULT_SIGNALS);
+ assertNotEquals(-1, r2.getLastAudiblyAlertedMs());
+ verifyBeepVolume(1.0f);
+
+ // 2nd update should beep at 100% volume
+ Mockito.reset(mRingtonePlayer);
+ mAttentionHelper.buzzBeepBlinkLocked(r2, DEFAULT_SIGNALS);
+ assertNotEquals(-1, r2.getLastAudiblyAlertedMs());
+ verifyBeepVolume(1.0f);
+
+ verify(mAccessibilityService, times(3)).sendAccessibilityEvent(any(), anyInt());
+ }
+
+ @Test
public void testBeepVolume_politeNotif_applyPerApp() throws Exception {
mSetFlagsRule.enableFlags(Flags.FLAG_POLITE_NOTIFICATIONS);
mSetFlagsRule.disableFlags(Flags.FLAG_CROSS_APP_POLITE_NOTIFICATIONS);
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java b/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
index 2d672b8..e564ba6 100755
--- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
@@ -99,6 +99,7 @@
import static android.service.notification.NotificationListenerService.FLAG_FILTER_TYPE_ALERTING;
import static android.service.notification.NotificationListenerService.FLAG_FILTER_TYPE_CONVERSATIONS;
import static android.service.notification.NotificationListenerService.FLAG_FILTER_TYPE_ONGOING;
+import static android.service.notification.NotificationListenerService.HINT_HOST_DISABLE_EFFECTS;
import static android.service.notification.NotificationListenerService.REASON_CANCEL;
import static android.service.notification.NotificationListenerService.REASON_LOCKDOWN;
import static android.service.notification.NotificationListenerService.Ranking.USER_SENTIMENT_NEGATIVE;
@@ -112,6 +113,7 @@
import static com.android.server.am.PendingIntentRecord.FLAG_BROADCAST_SENDER;
import static com.android.server.am.PendingIntentRecord.FLAG_SERVICE_SENDER;
import static com.android.server.notification.Flags.FLAG_ALL_NOTIFS_NEED_TTL;
+import static com.android.server.notification.Flags.FLAG_REJECT_OLD_NOTIFICATIONS;
import static com.android.server.notification.NotificationManagerService.BITMAP_DURATION;
import static com.android.server.notification.NotificationManagerService.DEFAULT_MAX_NOTIFICATION_ENQUEUE_RATE;
import static com.android.server.notification.NotificationManagerService.NOTIFICATION_TTL;
@@ -339,6 +341,7 @@
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileOutputStream;
+import java.time.Duration;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
@@ -909,7 +912,9 @@
}
mService.clearNotifications();
- TestableLooper.get(this).processAllMessages();
+ if (mTestableLooper != null) {
+ mTestableLooper.processAllMessages();
+ }
try {
mService.onDestroy();
@@ -920,14 +925,16 @@
InstrumentationRegistry.getInstrumentation()
.getUiAutomation().dropShellPermissionIdentity();
- // Remove scheduled messages that would be processed when the test is already done, and
- // could cause issues, for example, messages that remove/cancel shown toasts (this causes
- // problematic interactions with mocks when they're no longer working as expected).
- mWorkerHandler.removeCallbacksAndMessages(null);
+ if (mWorkerHandler != null) {
+ // Remove scheduled messages that would be processed when the test is already done, and
+ // could cause issues, for example, messages that remove/cancel shown toasts (this causes
+ // problematic interactions with mocks when they're no longer working as expected).
+ mWorkerHandler.removeCallbacksAndMessages(null);
+ }
- if (TestableLooper.get(this) != null) {
+ if (mTestableLooper != null) {
// Must remove static reference to this test object to prevent leak (b/261039202)
- TestableLooper.remove(this);
+ mTestableLooper.remove(this);
}
}
@@ -1009,7 +1016,9 @@
}
public void waitForIdle() {
- mTestableLooper.processAllMessages();
+ if (mTestableLooper != null) {
+ mTestableLooper.processAllMessages();
+ }
}
private void setUpPrefsForBubbles(String pkg, int uid, boolean globalEnabled,
@@ -1302,6 +1311,106 @@
return nrSummary;
}
+ private NotificationRecord createAndPostCallStyleNotification(String packageName,
+ UserHandle userHandle, String testName) throws Exception {
+ Person person = new Person.Builder().setName("caller").build();
+ Notification.Builder nb = new Notification.Builder(mContext,
+ mTestNotificationChannel.getId())
+ .setFlag(FLAG_USER_INITIATED_JOB, true)
+ .setStyle(Notification.CallStyle.forOngoingCall(person, mActivityIntent))
+ .setSmallIcon(android.R.drawable.sym_def_app_icon);
+ StatusBarNotification sbn = new StatusBarNotification(packageName, packageName, 1,
+ testName, mUid, 0, nb.build(), userHandle, null, 0);
+ NotificationRecord r = new NotificationRecord(mContext, sbn, mTestNotificationChannel);
+
+ mService.addEnqueuedNotification(r);
+ mService.new PostNotificationRunnable(r.getKey(), r.getSbn().getPackageName(),
+ r.getUid(), mPostNotificationTrackerFactory.newTracker(null)).run();
+ waitForIdle();
+
+ return mService.findNotificationLocked(
+ packageName, r.getSbn().getTag(), r.getSbn().getId(), r.getSbn().getUserId());
+ }
+
+ private NotificationRecord createAndPostNotification(Notification.Builder nb, String testName)
+ throws RemoteException {
+ StatusBarNotification sbn = new StatusBarNotification(mPkg, mPkg, 1, testName, mUid, 0,
+ nb.build(), UserHandle.getUserHandleForUid(mUid), null, 0);
+ NotificationRecord nr = new NotificationRecord(mContext, sbn, mTestNotificationChannel);
+
+ mBinderService.enqueueNotificationWithTag(mPkg, mPkg, sbn.getTag(),
+ nr.getSbn().getId(), nr.getSbn().getNotification(), nr.getSbn().getUserId());
+ waitForIdle();
+
+ return mService.findNotificationLocked(
+ mPkg, nr.getSbn().getTag(), nr.getSbn().getId(), nr.getSbn().getUserId());
+ }
+
+ private static <T extends Parcelable> T parcelAndUnparcel(T source,
+ Parcelable.Creator<T> creator) {
+ Parcel parcel = Parcel.obtain();
+ source.writeToParcel(parcel, 0);
+ parcel.setDataPosition(0);
+ return creator.createFromParcel(parcel);
+ }
+
+ private PendingIntent createPendingIntent(String action) {
+ return PendingIntent.getActivity(mContext, 0,
+ new Intent(action).setPackage(mContext.getPackageName()),
+ PendingIntent.FLAG_MUTABLE);
+ }
+
+ private void allowTestPackageToToast() throws Exception {
+ assertWithMessage("toast queue").that(mService.mToastQueue).isEmpty();
+ mService.isSystemUid = false;
+ mService.isSystemAppId = false;
+ setToastRateIsWithinQuota(true);
+ setIfPackageHasPermissionToAvoidToastRateLimiting(TEST_PACKAGE, false);
+ // package is not suspended
+ when(mPackageManager.isPackageSuspendedForUser(TEST_PACKAGE, mUserId))
+ .thenReturn(false);
+ }
+
+ private boolean enqueueToast(String testPackage, ITransientNotification callback)
+ throws RemoteException {
+ return enqueueToast((INotificationManager) mService.mService, testPackage, new Binder(),
+ callback);
+ }
+
+ private boolean enqueueToast(INotificationManager service, String testPackage,
+ IBinder token, ITransientNotification callback) throws RemoteException {
+ return service.enqueueToast(testPackage, token, callback, TOAST_DURATION, /* isUiContext= */
+ true, DEFAULT_DISPLAY);
+ }
+
+ private boolean enqueueTextToast(String testPackage, CharSequence text) throws RemoteException {
+ return enqueueTextToast(testPackage, text, /* isUiContext= */ true, DEFAULT_DISPLAY);
+ }
+
+ private boolean enqueueTextToast(String testPackage, CharSequence text, boolean isUiContext,
+ int displayId) throws RemoteException {
+ return ((INotificationManager) mService.mService).enqueueTextToast(testPackage,
+ new Binder(), text, TOAST_DURATION, isUiContext, displayId,
+ /* textCallback= */ null);
+ }
+
+ private void mockIsVisibleBackgroundUsersSupported(boolean supported) {
+ when(mUm.isVisibleBackgroundUsersSupported()).thenReturn(supported);
+ }
+
+ private void mockIsUserVisible(int displayId, boolean visible) {
+ when(mUmInternal.isUserVisible(mUserId, displayId)).thenReturn(visible);
+ }
+
+ private void mockDisplayAssignedToUser(int displayId) {
+ when(mUmInternal.getMainDisplayAssignedToUser(mUserId)).thenReturn(displayId);
+ }
+
+ private void verifyToastShownForTestPackage(String text, int displayId) {
+ verify(mStatusBar).showToast(eq(mUid), eq(TEST_PACKAGE), any(), eq(text), any(),
+ eq(TOAST_DURATION), any(), eq(displayId));
+ }
+
@Test
@DisableFlags(FLAG_ALL_NOTIFS_NEED_TTL)
public void testLimitTimeOutBroadcast() {
@@ -14069,11 +14178,12 @@
public void enqueueUpdate_whenBelowMaxEnqueueRate_accepts() throws Exception {
// Post the first version.
Notification original = generateNotificationRecord(null).getNotification();
- original.when = 111;
+ original.when = System.currentTimeMillis();
mBinderService.enqueueNotificationWithTag(mPkg, mPkg, "tag", 0, original, mUserId);
waitForIdle();
assertThat(mService.mNotificationList).hasSize(1);
- assertThat(mService.mNotificationList.get(0).getNotification().when).isEqualTo(111);
+ assertThat(mService.mNotificationList.get(0).getNotification().when)
+ .isEqualTo(original.when);
reset(mUsageStats);
when(mUsageStats.getAppEnqueueRate(eq(mPkg)))
@@ -14081,7 +14191,7 @@
// Post the update.
Notification update = generateNotificationRecord(null).getNotification();
- update.when = 222;
+ update.when = System.currentTimeMillis() + 111;
mBinderService.enqueueNotificationWithTag(mPkg, mPkg, "tag", 0, update, mUserId);
waitForIdle();
@@ -14090,18 +14200,19 @@
verify(mUsageStats, never()).registerPostedByApp(any());
verify(mUsageStats).registerUpdatedByApp(any(), any());
assertThat(mService.mNotificationList).hasSize(1);
- assertThat(mService.mNotificationList.get(0).getNotification().when).isEqualTo(222);
+ assertThat(mService.mNotificationList.get(0).getNotification().when).isEqualTo(update.when);
}
@Test
public void enqueueUpdate_whenAboveMaxEnqueueRate_rejects() throws Exception {
// Post the first version.
Notification original = generateNotificationRecord(null).getNotification();
- original.when = 111;
+ original.when = System.currentTimeMillis();
mBinderService.enqueueNotificationWithTag(mPkg, mPkg, "tag", 0, original, mUserId);
waitForIdle();
assertThat(mService.mNotificationList).hasSize(1);
- assertThat(mService.mNotificationList.get(0).getNotification().when).isEqualTo(111);
+ assertThat(mService.mNotificationList.get(0).getNotification().when)
+ .isEqualTo(original.when);
reset(mUsageStats);
when(mUsageStats.getAppEnqueueRate(eq(mPkg)))
@@ -14109,7 +14220,7 @@
// Post the update.
Notification update = generateNotificationRecord(null).getNotification();
- update.when = 222;
+ update.when = System.currentTimeMillis() + 111;
mBinderService.enqueueNotificationWithTag(mPkg, mPkg, "tag", 0, update, mUserId);
waitForIdle();
@@ -14118,7 +14229,8 @@
verify(mUsageStats, never()).registerPostedByApp(any());
verify(mUsageStats, never()).registerUpdatedByApp(any(), any());
assertThat(mService.mNotificationList).hasSize(1);
- assertThat(mService.mNotificationList.get(0).getNotification().when).isEqualTo(111); // old
+ assertThat(mService.mNotificationList.get(0).getNotification().when)
+ .isEqualTo(original.when); // old
}
@Test
@@ -15483,103 +15595,126 @@
assertThat(n.getTimeoutAfter()).isEqualTo(20);
}
- private NotificationRecord createAndPostCallStyleNotification(String packageName,
- UserHandle userHandle, String testName) throws Exception {
- Person person = new Person.Builder().setName("caller").build();
- Notification.Builder nb = new Notification.Builder(mContext,
- mTestNotificationChannel.getId())
- .setFlag(FLAG_USER_INITIATED_JOB, true)
- .setStyle(Notification.CallStyle.forOngoingCall(person, mActivityIntent))
- .setSmallIcon(android.R.drawable.sym_def_app_icon);
- StatusBarNotification sbn = new StatusBarNotification(packageName, packageName, 1,
- testName, mUid, 0, nb.build(), userHandle, null, 0);
+ @Test
+ @EnableFlags(FLAG_REJECT_OLD_NOTIFICATIONS)
+ public void testRejectOldNotification_oldWhen() throws Exception {
+ Notification n = new Notification.Builder(mContext, mTestNotificationChannel.getId())
+ .setSmallIcon(android.R.drawable.sym_def_app_icon)
+ .setWhen(System.currentTimeMillis() - Duration.ofDays(15).toMillis())
+ .build();
+ StatusBarNotification sbn = new StatusBarNotification(mPkg, mPkg, 8, null, mUid, 0,
+ n, UserHandle.getUserHandleForUid(mUid), null, 0);
NotificationRecord r = new NotificationRecord(mContext, sbn, mTestNotificationChannel);
- mService.addEnqueuedNotification(r);
- mService.new PostNotificationRunnable(r.getKey(), r.getSbn().getPackageName(),
- r.getUid(), mPostNotificationTrackerFactory.newTracker(null)).run();
- waitForIdle();
-
- return mService.findNotificationLocked(
- packageName, r.getSbn().getTag(), r.getSbn().getId(), r.getSbn().getUserId());
+ assertThat(mService.checkDisqualifyingFeatures(mUserId, mUid, 0, null, r, false, false))
+ .isFalse();
}
- private NotificationRecord createAndPostNotification(Notification.Builder nb, String testName)
- throws RemoteException {
- StatusBarNotification sbn = new StatusBarNotification(mPkg, mPkg, 1, testName, mUid, 0,
- nb.build(), UserHandle.getUserHandleForUid(mUid), null, 0);
- NotificationRecord nr = new NotificationRecord(mContext, sbn, mTestNotificationChannel);
+ @Test
+ @EnableFlags(FLAG_REJECT_OLD_NOTIFICATIONS)
+ public void testRejectOldNotification_mediumOldWhen() throws Exception {
+ Notification n = new Notification.Builder(mContext, mTestNotificationChannel.getId())
+ .setSmallIcon(android.R.drawable.sym_def_app_icon)
+ .setWhen(System.currentTimeMillis() - Duration.ofDays(13).toMillis())
+ .build();
+ StatusBarNotification sbn = new StatusBarNotification(mPkg, mPkg, 8, null, mUid, 0,
+ n, UserHandle.getUserHandleForUid(mUid), null, 0);
+ NotificationRecord r = new NotificationRecord(mContext, sbn, mTestNotificationChannel);
- mBinderService.enqueueNotificationWithTag(mPkg, mPkg, sbn.getTag(),
- nr.getSbn().getId(), nr.getSbn().getNotification(), nr.getSbn().getUserId());
- waitForIdle();
-
- return mService.findNotificationLocked(
- mPkg, nr.getSbn().getTag(), nr.getSbn().getId(), nr.getSbn().getUserId());
+ assertThat(mService.checkDisqualifyingFeatures(mUserId, mUid, 0, null, r, false, false))
+ .isTrue();
}
- private static <T extends Parcelable> T parcelAndUnparcel(T source,
- Parcelable.Creator<T> creator) {
- Parcel parcel = Parcel.obtain();
- source.writeToParcel(parcel, 0);
- parcel.setDataPosition(0);
- return creator.createFromParcel(parcel);
+ @Test
+ @EnableFlags(FLAG_REJECT_OLD_NOTIFICATIONS)
+ public void testRejectOldNotification_zeroWhen() throws Exception {
+ Notification n = new Notification.Builder(mContext, mTestNotificationChannel.getId())
+ .setSmallIcon(android.R.drawable.sym_def_app_icon)
+ .setWhen(0)
+ .build();
+ StatusBarNotification sbn = new StatusBarNotification(mPkg, mPkg, 8, null, mUid, 0,
+ n, UserHandle.getUserHandleForUid(mUid), null, 0);
+ NotificationRecord r = new NotificationRecord(mContext, sbn, mTestNotificationChannel);
+
+ assertThat(mService.checkDisqualifyingFeatures(mUserId, mUid, 0, null, r, false, false))
+ .isTrue();
}
- private PendingIntent createPendingIntent(String action) {
- return PendingIntent.getActivity(mContext, 0,
- new Intent(action).setPackage(mContext.getPackageName()),
- PendingIntent.FLAG_MUTABLE);
+ @Test
+ public void testClearUIJFromUninstallingPackage() throws Exception {
+ NotificationRecord r =
+ generateNotificationRecord(mTestNotificationChannel, 0, mUserId, "bar");
+ mService.addNotification(r);
+
+ when(mPackageManagerClient.getPackageUidAsUser(anyString(), anyInt()))
+ .thenThrow(PackageManager.NameNotFoundException.class);
+ when(mPackageManagerInternal.isSameApp(anyString(), anyInt(), anyInt())).thenReturn(false);
+
+ mInternalService.cancelNotification(mPkg, mPkg, mUid, 0, r.getSbn().getTag(),
+ r.getSbn().getId(), mUserId);
+
+ // no exception
}
- private void allowTestPackageToToast() throws Exception {
- assertWithMessage("toast queue").that(mService.mToastQueue).isEmpty();
+ @Test
+ public void testPostFromMissingPackage_throws() throws Exception {
+ NotificationRecord r =
+ generateNotificationRecord(mTestNotificationChannel, 0, mUserId, "bar");
+
+ when(mPackageManagerClient.getPackageUidAsUser(anyString(), anyInt()))
+ .thenThrow(PackageManager.NameNotFoundException.class);
+ when(mPackageManagerInternal.isSameApp(anyString(), anyInt(), anyInt())).thenReturn(false);
+
+ try {
+ mBinderService.enqueueNotificationWithTag(mPkg, mPkg, r.getSbn().getTag(),
+ r.getSbn().getId(), r.getSbn().getNotification(),
+ r.getSbn().getUserId());
+ fail("Allowed to post a notification for an absent package");
+ } catch (SecurityException e) {
+ // yay
+ }
+ }
+
+ @Test
+ public void testGetEffectsSuppressor_noSuppressor() throws Exception {
+ when(mUmInternal.getProfileIds(anyInt(), anyBoolean())).thenReturn(new int[]{mUserId});
+ when(mListeners.checkServiceTokenLocked(any())).thenReturn(mListener);
+ when(mPackageManagerInternal.isSameApp(anyString(), anyInt(), anyInt())).thenReturn(true);
+ assertThat(mBinderService.getEffectsSuppressor()).isNull();
+ }
+
+ @Test
+ public void testGetEffectsSuppressor_suppressorSameApp() throws Exception {
+ when(mUmInternal.getProfileIds(anyInt(), anyBoolean())).thenReturn(new int[]{mUserId});
+ when(mListeners.checkServiceTokenLocked(any())).thenReturn(mListener);
mService.isSystemUid = false;
mService.isSystemAppId = false;
- setToastRateIsWithinQuota(true);
- setIfPackageHasPermissionToAvoidToastRateLimiting(TEST_PACKAGE, false);
- // package is not suspended
- when(mPackageManager.isPackageSuspendedForUser(TEST_PACKAGE, mUserId))
- .thenReturn(false);
+ mBinderService.requestHintsFromListener(mock(INotificationListener.class),
+ HINT_HOST_DISABLE_EFFECTS);
+ when(mPackageManagerInternal.isSameApp(anyString(), anyInt(), anyInt())).thenReturn(true);
+ assertThat(mBinderService.getEffectsSuppressor()).isEqualTo(mListener.component);
}
- private boolean enqueueToast(String testPackage, ITransientNotification callback)
- throws RemoteException {
- return enqueueToast((INotificationManager) mService.mService, testPackage, new Binder(),
- callback);
+ @Test
+ public void testGetEffectsSuppressor_suppressorDiffApp() throws Exception {
+ when(mUmInternal.getProfileIds(anyInt(), anyBoolean())).thenReturn(new int[]{mUserId});
+ when(mListeners.checkServiceTokenLocked(any())).thenReturn(mListener);
+ mService.isSystemUid = false;
+ mService.isSystemAppId = false;
+ mBinderService.requestHintsFromListener(mock(INotificationListener.class),
+ HINT_HOST_DISABLE_EFFECTS);
+ when(mPackageManagerInternal.isSameApp(anyString(), anyInt(), anyInt())).thenReturn(false);
+ assertThat(mBinderService.getEffectsSuppressor()).isEqualTo(null);
}
- private boolean enqueueToast(INotificationManager service, String testPackage,
- IBinder token, ITransientNotification callback) throws RemoteException {
- return service.enqueueToast(testPackage, token, callback, TOAST_DURATION, /* isUiContext= */
- true, DEFAULT_DISPLAY);
- }
-
- private boolean enqueueTextToast(String testPackage, CharSequence text) throws RemoteException {
- return enqueueTextToast(testPackage, text, /* isUiContext= */ true, DEFAULT_DISPLAY);
- }
-
- private boolean enqueueTextToast(String testPackage, CharSequence text, boolean isUiContext,
- int displayId) throws RemoteException {
- return ((INotificationManager) mService.mService).enqueueTextToast(testPackage,
- new Binder(), text, TOAST_DURATION, isUiContext, displayId,
- /* textCallback= */ null);
- }
-
- private void mockIsVisibleBackgroundUsersSupported(boolean supported) {
- when(mUm.isVisibleBackgroundUsersSupported()).thenReturn(supported);
- }
-
- private void mockIsUserVisible(int displayId, boolean visible) {
- when(mUmInternal.isUserVisible(mUserId, displayId)).thenReturn(visible);
- }
-
- private void mockDisplayAssignedToUser(int displayId) {
- when(mUmInternal.getMainDisplayAssignedToUser(mUserId)).thenReturn(displayId);
- }
-
- private void verifyToastShownForTestPackage(String text, int displayId) {
- verify(mStatusBar).showToast(eq(mUid), eq(TEST_PACKAGE), any(), eq(text), any(),
- eq(TOAST_DURATION), any(), eq(displayId));
+ @Test
+ public void testGetEffectsSuppressor_suppressorDiffAppSystemCaller() throws Exception {
+ when(mUmInternal.getProfileIds(anyInt(), anyBoolean())).thenReturn(new int[]{mUserId});
+ when(mListeners.checkServiceTokenLocked(any())).thenReturn(mListener);
+ mService.isSystemUid = true;
+ mBinderService.requestHintsFromListener(mock(INotificationListener.class),
+ HINT_HOST_DISABLE_EFFECTS);
+ when(mPackageManagerInternal.isSameApp(anyString(), anyInt(), anyInt())).thenReturn(false);
+ assertThat(mBinderService.getEffectsSuppressor()).isEqualTo(mListener.component);
}
}
diff --git a/services/tests/wmtests/src/com/android/server/wm/ActivityOptionsTest.java b/services/tests/wmtests/src/com/android/server/wm/ActivityOptionsTest.java
index 37e0818..5787780 100644
--- a/services/tests/wmtests/src/com/android/server/wm/ActivityOptionsTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/ActivityOptionsTest.java
@@ -24,6 +24,8 @@
import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn;
+import static com.google.common.truth.Truth.assertThat;
+
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotEquals;
import static org.junit.Assert.assertNotNull;
@@ -250,6 +252,7 @@
case ActivityOptions.KEY_LAUNCH_ROOT_TASK_TOKEN:
case ActivityOptions.KEY_LAUNCH_TASK_FRAGMENT_TOKEN:
case ActivityOptions.KEY_TRANSIENT_LAUNCH:
+ case ActivityOptions.KEY_PENDING_INTENT_BACKGROUND_ACTIVITY_ALLOWED:
case "android:activity.animationFinishedListener":
// KEY_ANIMATION_FINISHED_LISTENER
case "android:activity.animSpecs": // KEY_ANIM_SPECS
@@ -319,7 +322,7 @@
Log.e("ActivityOptionsTests", "Unknown key " + key + " is found. "
+ "Please review if the given bundle should be protected with permissions.");
}
- assertTrue(unknownKeys.isEmpty());
+ assertThat(unknownKeys).isEmpty();
}
public static class TrampolineActivity extends Activity {
diff --git a/services/tests/wmtests/src/com/android/server/wm/ActivityRefresherTests.java b/services/tests/wmtests/src/com/android/server/wm/ActivityRefresherTests.java
new file mode 100644
index 0000000..12ab3e1
--- /dev/null
+++ b/services/tests/wmtests/src/com/android/server/wm/ActivityRefresherTests.java
@@ -0,0 +1,219 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.server.wm;
+
+import static android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM;
+import static android.app.servertransaction.ActivityLifecycleItem.ON_PAUSE;
+import static android.app.servertransaction.ActivityLifecycleItem.ON_STOP;
+
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn;
+
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyLong;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import android.app.servertransaction.RefreshCallbackItem;
+import android.app.servertransaction.ResumeActivityItem;
+import android.content.ComponentName;
+import android.content.res.Configuration;
+import android.os.Handler;
+import android.platform.test.annotations.Presubmit;
+
+import androidx.test.filters.SmallTest;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+/**
+ * Tests for {@link ActivityRefresher}.
+ *
+ * <p>Build/Install/Run:
+ * atest WmTests:ActivityRefresherTests
+ */
+@SmallTest
+@Presubmit
+@RunWith(WindowTestRunner.class)
+public class ActivityRefresherTests extends WindowTestsBase {
+ private Handler mMockHandler;
+ private LetterboxConfiguration mLetterboxConfiguration;
+
+ private ActivityRecord mActivity;
+ private ActivityRefresher mActivityRefresher;
+
+ private ActivityRefresher.Evaluator mEvaluatorFalse =
+ (activity, newConfig, lastReportedConfig) -> false;
+
+ private ActivityRefresher.Evaluator mEvaluatorTrue =
+ (activity, newConfig, lastReportedConfig) -> true;
+
+ private final Configuration mNewConfig = new Configuration();
+ private final Configuration mOldConfig = new Configuration();
+
+ @Before
+ public void setUp() throws Exception {
+ mLetterboxConfiguration = mDisplayContent.mWmService.mLetterboxConfiguration;
+ spyOn(mLetterboxConfiguration);
+ when(mLetterboxConfiguration.isCameraCompatTreatmentEnabled())
+ .thenReturn(true);
+ when(mLetterboxConfiguration.isCameraCompatRefreshEnabled())
+ .thenReturn(true);
+ when(mLetterboxConfiguration.isCameraCompatRefreshCycleThroughStopEnabled())
+ .thenReturn(true);
+
+ mMockHandler = mock(Handler.class);
+ when(mMockHandler.postDelayed(any(Runnable.class), anyLong())).thenAnswer(
+ invocation -> {
+ ((Runnable) invocation.getArgument(0)).run();
+ return null;
+ });
+
+ mActivityRefresher = new ActivityRefresher(mDisplayContent.mWmService, mMockHandler);
+ }
+
+ @Test
+ public void testShouldRefreshActivity_refreshDisabled() throws Exception {
+ when(mLetterboxConfiguration.isCameraCompatRefreshEnabled())
+ .thenReturn(false);
+ configureActivityAndDisplay();
+ mActivityRefresher.addEvaluator(mEvaluatorTrue);
+
+ mActivityRefresher.onActivityConfigurationChanging(mActivity, mNewConfig, mOldConfig);
+
+ assertActivityRefreshRequested(/* refreshRequested= */ false);
+ }
+
+ @Test
+ public void testShouldRefreshActivity_refreshDisabledForActivity() throws Exception {
+ configureActivityAndDisplay();
+ when(mActivity.mLetterboxUiController.shouldRefreshActivityForCameraCompat())
+ .thenReturn(false);
+ mActivityRefresher.addEvaluator(mEvaluatorTrue);
+
+ mActivityRefresher.onActivityConfigurationChanging(mActivity, mNewConfig, mOldConfig);
+
+ assertActivityRefreshRequested(/* refreshRequested= */ false);
+ }
+
+ @Test
+ public void testShouldRefreshActivity_noRefreshTriggerers() throws Exception {
+ configureActivityAndDisplay();
+
+ mActivityRefresher.onActivityConfigurationChanging(mActivity, mNewConfig, mOldConfig);
+
+ assertActivityRefreshRequested(/* refreshRequested= */ false);
+ }
+
+ @Test
+ public void testShouldRefreshActivity_refreshTriggerersReturnFalse() throws Exception {
+ configureActivityAndDisplay();
+ mActivityRefresher.addEvaluator(mEvaluatorFalse);
+
+ mActivityRefresher.onActivityConfigurationChanging(mActivity, mNewConfig, mOldConfig);
+
+ assertActivityRefreshRequested(/* refreshRequested= */ false);
+ }
+
+ @Test
+ public void testShouldRefreshActivity_anyRefreshTriggerersReturnTrue() throws Exception {
+ configureActivityAndDisplay();
+ mActivityRefresher.addEvaluator(mEvaluatorFalse);
+ mActivityRefresher.addEvaluator(mEvaluatorTrue);
+
+ mActivityRefresher.onActivityConfigurationChanging(mActivity, mNewConfig, mOldConfig);
+
+ assertActivityRefreshRequested(/* refreshRequested= */ true);
+ }
+
+ @Test
+ public void testOnActivityConfigurationChanging_cycleThroughStopDisabled()
+ throws Exception {
+ mActivityRefresher.addEvaluator(mEvaluatorTrue);
+ when(mLetterboxConfiguration.isCameraCompatRefreshCycleThroughStopEnabled())
+ .thenReturn(false);
+ configureActivityAndDisplay();
+
+ mActivityRefresher.onActivityConfigurationChanging(mActivity, mNewConfig, mOldConfig);
+
+ assertActivityRefreshRequested(/* refreshRequested */ true, /* cycleThroughStop */ false);
+ }
+
+ @Test
+ public void testOnActivityConfigurationChanging_cycleThroughPauseEnabledForApp()
+ throws Exception {
+ configureActivityAndDisplay();
+ mActivityRefresher.addEvaluator(mEvaluatorTrue);
+ doReturn(true).when(mActivity.mLetterboxUiController)
+ .shouldRefreshActivityViaPauseForCameraCompat();
+
+ mActivityRefresher.onActivityConfigurationChanging(mActivity, mNewConfig, mOldConfig);
+
+ assertActivityRefreshRequested(/* refreshRequested */ true, /* cycleThroughStop */ false);
+ }
+
+ @Test
+ public void testOnActivityRefreshed_setIsRefreshRequestedToFalse() throws Exception {
+ configureActivityAndDisplay();
+ mActivityRefresher.addEvaluator(mEvaluatorTrue);
+ doReturn(true).when(mActivity.mLetterboxUiController)
+ .shouldRefreshActivityViaPauseForCameraCompat();
+
+ mActivityRefresher.onActivityRefreshed(mActivity);
+
+ assertActivityRefreshRequested(false);
+ }
+
+ private void assertActivityRefreshRequested(boolean refreshRequested) throws Exception {
+ assertActivityRefreshRequested(refreshRequested, /* cycleThroughStop*/ true);
+ }
+
+ private void assertActivityRefreshRequested(boolean refreshRequested,
+ boolean cycleThroughStop) throws Exception {
+ verify(mActivity.mLetterboxUiController, times(refreshRequested ? 1 : 0))
+ .setIsRefreshRequested(true);
+
+ final RefreshCallbackItem refreshCallbackItem = RefreshCallbackItem.obtain(mActivity.token,
+ cycleThroughStop ? ON_STOP : ON_PAUSE);
+ final ResumeActivityItem resumeActivityItem = ResumeActivityItem.obtain(mActivity.token,
+ /* isForward */ false, /* shouldSendCompatFakeFocus */ false);
+
+ verify(mActivity.mAtmService.getLifecycleManager(), times(refreshRequested ? 1 : 0))
+ .scheduleTransactionAndLifecycleItems(mActivity.app.getThread(),
+ refreshCallbackItem, resumeActivityItem);
+ }
+
+ private void configureActivityAndDisplay() {
+ mActivity = new TaskBuilder(mSupervisor)
+ .setCreateActivity(true)
+ .setDisplay(mDisplayContent)
+ // Set the component to be that of the test class in order to enable compat changes
+ .setComponent(ComponentName.createRelative(mContext,
+ ActivityRefresherTests.class.getName()))
+ .setWindowingMode(WINDOWING_MODE_FREEFORM)
+ .build()
+ .getTopMostActivity();
+
+ spyOn(mActivity.mLetterboxUiController);
+ doReturn(true).when(
+ mActivity.mLetterboxUiController).shouldRefreshActivityForCameraCompat();
+
+ doReturn(true).when(mActivity).inFreeformWindowingMode();
+ }
+}
diff --git a/services/tests/wmtests/src/com/android/server/wm/DisplayRotationCompatPolicyTests.java b/services/tests/wmtests/src/com/android/server/wm/DisplayRotationCompatPolicyTests.java
index 262ba8b..c76acd7 100644
--- a/services/tests/wmtests/src/com/android/server/wm/DisplayRotationCompatPolicyTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/DisplayRotationCompatPolicyTests.java
@@ -91,6 +91,7 @@
private CameraManager mMockCameraManager;
private Handler mMockHandler;
private LetterboxConfiguration mLetterboxConfiguration;
+ private ActivityRefresher mActivityRefresher;
private DisplayRotationCompatPolicy mDisplayRotationCompatPolicy;
private CameraManager.AvailabilityCallback mCameraAvailabilityCallback;
@@ -132,8 +133,9 @@
});
CameraStateMonitor cameraStateMonitor =
new CameraStateMonitor(mDisplayContent, mMockHandler);
- mDisplayRotationCompatPolicy =
- new DisplayRotationCompatPolicy(mDisplayContent, mMockHandler, cameraStateMonitor);
+ mActivityRefresher = new ActivityRefresher(mDisplayContent.mWmService, mMockHandler);
+ mDisplayRotationCompatPolicy = new DisplayRotationCompatPolicy(mDisplayContent,
+ cameraStateMonitor, mActivityRefresher);
// Do not show the real toast.
spyOn(mDisplayRotationCompatPolicy);
@@ -606,7 +608,7 @@
private void assertActivityRefreshRequested(boolean refreshRequested,
boolean cycleThroughStop) throws Exception {
verify(mActivity.mLetterboxUiController, times(refreshRequested ? 1 : 0))
- .setIsRefreshAfterRotationRequested(true);
+ .setIsRefreshRequested(true);
final RefreshCallbackItem refreshCallbackItem = RefreshCallbackItem.obtain(mActivity.token,
cycleThroughStop ? ON_STOP : ON_PAUSE);
@@ -628,7 +630,7 @@
private void callOnActivityConfigurationChanging(
ActivityRecord activity, boolean isDisplayRotationChanging) {
- mDisplayRotationCompatPolicy.onActivityConfigurationChanging(activity,
+ mActivityRefresher.onActivityConfigurationChanging(activity,
/* newConfig */ createConfigurationWithDisplayRotation(ROTATION_0),
/* newConfig */ createConfigurationWithDisplayRotation(
isDisplayRotationChanging ? ROTATION_90 : ROTATION_0));
diff --git a/services/tests/wmtests/src/com/android/server/wm/ImeInsetsSourceProviderTest.java b/services/tests/wmtests/src/com/android/server/wm/ImeInsetsSourceProviderTest.java
index 7380aec..d8d5729 100644
--- a/services/tests/wmtests/src/com/android/server/wm/ImeInsetsSourceProviderTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/ImeInsetsSourceProviderTest.java
@@ -65,7 +65,7 @@
performSurfacePlacementAndWaitForWindowAnimator();
mImeProvider.scheduleShowImePostLayout(appWin, ImeTracker.Token.empty());
- assertTrue(mImeProvider.isReadyToShowIme());
+ assertTrue(mImeProvider.isScheduledAndReadyToShowIme());
}
/**
@@ -84,13 +84,13 @@
// Schedule (without triggering) after everything is ready.
mImeProvider.scheduleShowImePostLayout(target, ImeTracker.Token.empty());
- assertTrue(mImeProvider.isReadyToShowIme());
+ assertTrue(mImeProvider.isScheduledAndReadyToShowIme());
assertFalse(mImeProvider.isImeShowing());
// Manually trigger the show.
- mImeProvider.checkShowImePostLayout();
- // No longer ready as it was already shown.
- assertFalse(mImeProvider.isReadyToShowIme());
+ mImeProvider.checkAndStartShowImePostLayout();
+ // No longer scheduled as it was already shown.
+ assertFalse(mImeProvider.isScheduledAndReadyToShowIme());
assertTrue(mImeProvider.isImeShowing());
}
@@ -104,7 +104,7 @@
// Schedule before anything is ready.
mImeProvider.scheduleShowImePostLayout(target, ImeTracker.Token.empty());
- assertFalse(mImeProvider.isReadyToShowIme());
+ assertFalse(mImeProvider.isScheduledAndReadyToShowIme());
assertFalse(mImeProvider.isImeShowing());
final WindowState ime = createWindow(null, TYPE_INPUT_METHOD, "ime");
@@ -115,8 +115,8 @@
mDisplayContent.updateImeInputAndControlTarget(target);
// Performing surface placement picks up the show scheduled above.
performSurfacePlacementAndWaitForWindowAnimator();
- // No longer ready as it was already shown.
- assertFalse(mImeProvider.isReadyToShowIme());
+ // No longer scheduled as it was already shown.
+ assertFalse(mImeProvider.isScheduledAndReadyToShowIme());
assertTrue(mImeProvider.isImeShowing());
}
@@ -137,19 +137,19 @@
// Schedule before starting the afterPrepareSurfacesRunnable.
mImeProvider.scheduleShowImePostLayout(target, ImeTracker.Token.empty());
- assertFalse(mImeProvider.isReadyToShowIme());
+ assertFalse(mImeProvider.isScheduledAndReadyToShowIme());
assertFalse(mImeProvider.isImeShowing());
// This tries to pick up the show scheduled above, but must fail as the
// afterPrepareSurfacesRunnable was not started yet.
mDisplayContent.applySurfaceChangesTransaction();
- assertFalse(mImeProvider.isReadyToShowIme());
+ assertFalse(mImeProvider.isScheduledAndReadyToShowIme());
assertFalse(mImeProvider.isImeShowing());
// Starting the afterPrepareSurfacesRunnable picks up the show scheduled above.
mWm.mAnimator.executeAfterPrepareSurfacesRunnables();
- // No longer ready as it was already shown.
- assertFalse(mImeProvider.isReadyToShowIme());
+ // No longer scheduled as it was already shown.
+ assertFalse(mImeProvider.isScheduledAndReadyToShowIme());
assertTrue(mImeProvider.isImeShowing());
}
@@ -169,7 +169,7 @@
// Schedule before surface placement.
mImeProvider.scheduleShowImePostLayout(target, ImeTracker.Token.empty());
- assertFalse(mImeProvider.isReadyToShowIme());
+ assertFalse(mImeProvider.isScheduledAndReadyToShowIme());
assertFalse(mImeProvider.isImeShowing());
// Performing surface placement picks up the show scheduled above, and succeeds.
@@ -177,8 +177,8 @@
// applySurfaceChangesTransaction. Both of them try to trigger the show,
// but only the second one can succeed, as it comes after onPostLayout.
performSurfacePlacementAndWaitForWindowAnimator();
- // No longer ready as it was already shown.
- assertFalse(mImeProvider.isReadyToShowIme());
+ // No longer scheduled as it was already shown.
+ assertFalse(mImeProvider.isScheduledAndReadyToShowIme());
assertTrue(mImeProvider.isImeShowing());
}
diff --git a/services/tests/wmtests/src/com/android/server/wm/InsetsStateControllerTest.java b/services/tests/wmtests/src/com/android/server/wm/InsetsStateControllerTest.java
index 0e1a1af..c69faed 100644
--- a/services/tests/wmtests/src/com/android/server/wm/InsetsStateControllerTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/InsetsStateControllerTest.java
@@ -353,6 +353,17 @@
}
@Test
+ public void testControlTargetChangedWhileProviderHasNoWindow() {
+ final WindowState app = createWindow(null, TYPE_APPLICATION, "app");
+ final InsetsSourceProvider provider = getController().getOrCreateSourceProvider(
+ ID_STATUS_BAR, statusBars());
+ getController().onBarControlTargetChanged(app, null, null, null);
+ assertNull(getController().getControlsForDispatch(app));
+ provider.setWindowContainer(createWindow(null, TYPE_APPLICATION, "statusBar"), null, null);
+ assertNotNull(getController().getControlsForDispatch(app));
+ }
+
+ @Test
public void testTransientVisibilityOfFixedRotationState() {
final WindowState statusBar = createWindow(null, TYPE_APPLICATION, "statusBar");
final WindowState app = createWindow(null, TYPE_APPLICATION, "app");
diff --git a/services/tests/wmtests/src/com/android/server/wm/LetterboxUiControllerTest.java b/services/tests/wmtests/src/com/android/server/wm/LetterboxUiControllerTest.java
index a60d243..c7f5020 100644
--- a/services/tests/wmtests/src/com/android/server/wm/LetterboxUiControllerTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/LetterboxUiControllerTest.java
@@ -908,6 +908,24 @@
}
@Test
+ public void testOverrideOrientationIfNeeded_fullscreenOverride_cameraActivity_unchanged() {
+ doReturn(true).when(mLetterboxConfiguration).isCameraCompatTreatmentEnabled();
+ doReturn(true).when(mLetterboxConfiguration)
+ .isCameraCompatTreatmentEnabledAtBuildTime();
+
+ // Recreate DisplayContent with DisplayRotationCompatPolicy
+ mActivity = setUpActivityWithComponent();
+ mController = new LetterboxUiController(mWm, mActivity);
+ spyOn(mDisplayContent.mDisplayRotationCompatPolicy);
+
+ doReturn(false).when(mDisplayContent.mDisplayRotationCompatPolicy)
+ .isCameraActive(mActivity, /* mustBeFullscreen= */ true);
+
+ assertEquals(SCREEN_ORIENTATION_PORTRAIT, mController.overrideOrientationIfNeeded(
+ /* candidate */ SCREEN_ORIENTATION_PORTRAIT));
+ }
+
+ @Test
public void testOverrideOrientationIfNeeded_respectOrientationRequestOverUserFullScreen() {
spyOn(mController);
doReturn(true).when(mController).shouldApplyUserFullscreenOverride();
@@ -1594,7 +1612,7 @@
}
@Test
- @EnableFlags(Flags.FLAG_DISABLE_THIN_LETTERBOXING_REACHABILITY)
+ @EnableFlags(Flags.FLAG_DISABLE_THIN_LETTERBOXING_POLICY)
public void testAllowReachabilityForThinLetterboxWithFlagEnabled() {
spyOn(mController);
doReturn(true).when(mController).isVerticalThinLetterboxed();
@@ -1609,7 +1627,7 @@
}
@Test
- @DisableFlags(Flags.FLAG_DISABLE_THIN_LETTERBOXING_REACHABILITY)
+ @DisableFlags(Flags.FLAG_DISABLE_THIN_LETTERBOXING_POLICY)
public void testAllowReachabilityForThinLetterboxWithFlagDisabled() {
spyOn(mController);
doReturn(true).when(mController).isVerticalThinLetterboxed();
@@ -1623,6 +1641,12 @@
assertTrue(mController.allowHorizontalReachabilityForThinLetterbox());
}
+ @Test
+ public void testIsLetterboxEducationEnabled() {
+ mController.isLetterboxEducationEnabled();
+ verify(mLetterboxConfiguration).getIsEducationEnabled();
+ }
+
private void mockThatProperty(String propertyName, boolean value) throws Exception {
Property property = new Property(propertyName, /* value */ value, /* packageName */ "",
/* className */ "");
diff --git a/services/tests/wmtests/src/com/android/server/wm/TaskFragmentTest.java b/services/tests/wmtests/src/com/android/server/wm/TaskFragmentTest.java
index a90a158..d57a7e6 100644
--- a/services/tests/wmtests/src/com/android/server/wm/TaskFragmentTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/TaskFragmentTest.java
@@ -18,6 +18,7 @@
import static android.Manifest.permission.EMBED_ANY_APP_IN_UNTRUSTED_MODE;
import static android.Manifest.permission.MANAGE_ACTIVITY_TASKS;
+import static android.app.WindowConfiguration.ACTIVITY_TYPE_HOME;
import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD;
import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
import static android.app.WindowConfiguration.WINDOWING_MODE_MULTI_WINDOW;
@@ -961,6 +962,22 @@
assertEquals(appLeftTop, task.getDisplayContent().mFocusedApp);
}
+ @Test
+ public void testShouldBeVisible_invisibleForEmptyTaskFragment() {
+ final Task task = new TaskBuilder(mSupervisor).setCreateActivity(true)
+ .setWindowingMode(WINDOWING_MODE_FULLSCREEN).build();
+ final TaskFragment taskFragment = new TaskFragmentBuilder(mAtm)
+ .setParentTask(task)
+ .build();
+
+ // Empty taskFragment should be invisible
+ assertFalse(taskFragment.shouldBeVisible(null));
+
+ // Should be invisible even if it is ACTIVITY_TYPE_HOME.
+ when(taskFragment.getActivityType()).thenReturn(ACTIVITY_TYPE_HOME);
+ assertFalse(taskFragment.shouldBeVisible(null));
+ }
+
private WindowState createAppWindow(ActivityRecord app, String name) {
final WindowState win = createWindow(null, TYPE_BASE_APPLICATION, app, name,
0 /* ownerId */, false /* ownerCanAddInternalSystemWindow */, new TestIWindow());
diff --git a/telephony/java/android/telephony/SubscriptionManager.java b/telephony/java/android/telephony/SubscriptionManager.java
index 8fe45cb..76b4e005 100644
--- a/telephony/java/android/telephony/SubscriptionManager.java
+++ b/telephony/java/android/telephony/SubscriptionManager.java
@@ -99,18 +99,7 @@
import java.util.stream.Collectors;
/**
- * Subscription manager provides the mobile subscription information that are associated with the
- * calling user profile {@link UserHandle} for Android SDK 35(V) and above, while Android SDK 34(U)
- * and below can see all subscriptions as it does today.
- *
- * <p>For example, if we have
- * <ul>
- * <li> Subscription 1 associated with personal profile.
- * <li> Subscription 2 associated with work profile.
- * </ul>
- * Then for SDK 35+, if the caller identity is personal profile, then
- * {@link #getActiveSubscriptionInfoList} will return subscription 1 only and vice versa.
- *
+ * Subscription manager provides the mobile subscription information.
*/
@SystemService(Context.TELEPHONY_SUBSCRIPTION_SERVICE)
@RequiresFeature(PackageManager.FEATURE_TELEPHONY_SUBSCRIPTION)
@@ -1980,17 +1969,7 @@
}
/**
- * Get the SubscriptionInfo(s) of the currently active SIM(s) associated with the current caller
- * user profile {@link UserHandle} for Android SDK 35(V) and above, while Android SDK 34(U)
- * and below can see all subscriptions as it does today.
- *
- * <p>For example, if we have
- * <ul>
- * <li> Subscription 1 associated with personal profile.
- * <li> Subscription 2 associated with work profile.
- * </ul>
- * Then for SDK 35+, if the caller identity is personal profile, then this will return
- * subscription 1 only and vice versa.
+ * Get the SubscriptionInfo(s) of the currently active SIM(s).
*
* <p> Returned records will be sorted by {@link SubscriptionInfo#getSimSlotIndex} then by
* {@link SubscriptionInfo#getSubscriptionId}. Beginning with Android SDK 35, this method will
@@ -2259,9 +2238,7 @@
}
/**
- * Get the active subscription count associated with the current caller user profile for
- * Android SDK 35(V) and above, while Android SDK 34(U) and below can see all subscriptions as
- * it does today.
+ * Get the active subscription count.
*
* @return The current number of active subscriptions.
*
diff --git a/telephony/java/com/android/internal/telephony/ITelephony.aidl b/telephony/java/com/android/internal/telephony/ITelephony.aidl
index 65de7e4..7d845a3 100644
--- a/telephony/java/com/android/internal/telephony/ITelephony.aidl
+++ b/telephony/java/com/android/internal/telephony/ITelephony.aidl
@@ -3372,4 +3372,13 @@
+ "android.Manifest.permission.SATELLITE_COMMUNICATION)")
void unregisterForCommunicationAllowedStateChanged(int subId,
in ISatelliteCommunicationAllowedStateCallback callback);
+
+ /**
+ * This API can be used by only CTS to override the boolean configs used by the
+ * DatagramController module.
+ *
+ * @param enable Whether to enable boolean config.
+ * @return {@code true} if the boolean config is set successfully, {@code false} otherwise.
+ */
+ boolean setDatagramControllerBooleanConfig(boolean reset, int booleanType, boolean enable);
}
diff --git a/tests/BinderLeakTest/Android.bp b/tests/BinderLeakTest/Android.bp
index 78b0ede..3747d04 100644
--- a/tests/BinderLeakTest/Android.bp
+++ b/tests/BinderLeakTest/Android.bp
@@ -24,6 +24,9 @@
"androidx.test.rules",
"androidx.test.runner",
],
+ test_suites: [
+ "general-tests",
+ ],
}
// Built with target_sdk_version: current
diff --git a/tests/FlickerTests/ActivityEmbedding/AndroidManifest.xml b/tests/FlickerTests/ActivityEmbedding/AndroidManifest.xml
index 6f8f008..955b43a 100644
--- a/tests/FlickerTests/ActivityEmbedding/AndroidManifest.xml
+++ b/tests/FlickerTests/ActivityEmbedding/AndroidManifest.xml
@@ -19,7 +19,7 @@
xmlns:tools="http://schemas.android.com/tools"
package="com.android.server.wm.flicker">
- <uses-sdk android:minSdkVersion="29" android:targetSdkVersion="29"/>
+ <uses-sdk android:minSdkVersion="29" android:targetSdkVersion="35"/>
<!-- Read and write traces from external storage -->
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
@@ -46,6 +46,8 @@
<uses-permission android:name="android.permission.START_TASKS_FROM_RECENTS" />
<!-- Allow the test to connect to perfetto trace processor -->
<uses-permission android:name="android.permission.INTERNET"/>
+ <!-- Allow to query for the Launcher TestInfo on SDK 30+ -->
+ <uses-permission android:name="android.permission.QUERY_ALL_PACKAGES" />
<!-- Allow the test to write directly to /sdcard/ and connect to trace processor -->
<application android:requestLegacyExternalStorage="true"
android:networkSecurityConfig="@xml/network_security_config"
diff --git a/tests/FlickerTests/ActivityEmbedding/AndroidTestTemplate.xml b/tests/FlickerTests/ActivityEmbedding/AndroidTestTemplate.xml
index 1dc1037..82de070 100644
--- a/tests/FlickerTests/ActivityEmbedding/AndroidTestTemplate.xml
+++ b/tests/FlickerTests/ActivityEmbedding/AndroidTestTemplate.xml
@@ -80,6 +80,7 @@
value="trace_config.textproto"
/>
<option name="instrumentation-arg" key="per_run" value="true"/>
+ <option name="instrumentation-arg" key="perfetto_persist_pid_track" value="true"/>
</test>
<!-- Needed for pulling the collected trace config on to the host -->
<metrics_collector class="com.android.tradefed.device.metric.FilePullerLogCollector">
diff --git a/tests/FlickerTests/ActivityEmbedding/src/com/android/server/wm/flicker/activityembedding/open/OpenTrampolineActivityTest.kt b/tests/FlickerTests/ActivityEmbedding/src/com/android/server/wm/flicker/activityembedding/open/OpenTrampolineActivityTest.kt
index cf4edd5..67825d2 100644
--- a/tests/FlickerTests/ActivityEmbedding/src/com/android/server/wm/flicker/activityembedding/open/OpenTrampolineActivityTest.kt
+++ b/tests/FlickerTests/ActivityEmbedding/src/com/android/server/wm/flicker/activityembedding/open/OpenTrampolineActivityTest.kt
@@ -43,6 +43,7 @@
*
* To run this test: `atest FlickerTestsOther:OpenTrampolineActivityTest`
*/
+@FlakyTest(bugId = 341209752)
@RequiresDevice
@RunWith(Parameterized::class)
@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class)
@@ -168,7 +169,6 @@
}
}
- @FlakyTest(bugId = 290736037)
/** Main activity should go from fullscreen to being a split with secondary activity. */
@Test
fun mainActivityLayerGoesFromFullscreenToSplit() {
@@ -203,7 +203,6 @@
}
}
- @FlakyTest(bugId = 288591571)
@Test
override fun visibleLayersShownMoreThanOneConsecutiveEntry() {
super.visibleLayersShownMoreThanOneConsecutiveEntry()
diff --git a/tests/FlickerTests/ActivityEmbedding/src/com/android/server/wm/flicker/activityembedding/pip/SecondaryActivityEnterPipTest.kt b/tests/FlickerTests/ActivityEmbedding/src/com/android/server/wm/flicker/activityembedding/pip/SecondaryActivityEnterPipTest.kt
index bc3696b..eed9225 100644
--- a/tests/FlickerTests/ActivityEmbedding/src/com/android/server/wm/flicker/activityembedding/pip/SecondaryActivityEnterPipTest.kt
+++ b/tests/FlickerTests/ActivityEmbedding/src/com/android/server/wm/flicker/activityembedding/pip/SecondaryActivityEnterPipTest.kt
@@ -205,7 +205,8 @@
it.visibleRegion(ComponentNameMatcher.PIP_CONTENT_OVERLAY)
val secondaryVisibleRegion =
it.visibleRegion(ActivityEmbeddingAppHelper.SECONDARY_ACTIVITY_COMPONENT)
- overlayVisibleRegion.coversExactly(secondaryVisibleRegion.region)
+ // TODO(b/340992001): replace coverAtLeast with coverExactly
+ overlayVisibleRegion.coversAtLeast(secondaryVisibleRegion.region)
}
.then()
.isInvisible(ComponentNameMatcher.PIP_CONTENT_OVERLAY)
diff --git a/tests/FlickerTests/ActivityEmbedding/src/com/android/server/wm/flicker/activityembedding/splitscreen/EnterSystemSplitTest.kt b/tests/FlickerTests/ActivityEmbedding/src/com/android/server/wm/flicker/activityembedding/splitscreen/EnterSystemSplitTest.kt
index fb92583..379b45c 100644
--- a/tests/FlickerTests/ActivityEmbedding/src/com/android/server/wm/flicker/activityembedding/splitscreen/EnterSystemSplitTest.kt
+++ b/tests/FlickerTests/ActivityEmbedding/src/com/android/server/wm/flicker/activityembedding/splitscreen/EnterSystemSplitTest.kt
@@ -60,14 +60,16 @@
testApp.launchViaIntent(wmHelper)
testApp.launchSecondaryActivity(wmHelper)
secondaryApp.launchViaIntent(wmHelper)
- tapl.goHome()
- wmHelper
- .StateSyncBuilder()
- .withAppTransitionIdle()
- .withHomeActivityVisible()
- .waitForAndVerify()
startDisplayBounds =
wmHelper.currentState.layerState.physicalDisplayBounds ?: error("Display not found")
+
+ // Record the displayBounds before `goHome()` in case the launcher is fixed-portrait.
+ tapl.goHome()
+ wmHelper
+ .StateSyncBuilder()
+ .withAppTransitionIdle()
+ .withHomeActivityVisible()
+ .waitForAndVerify()
}
transitions {
SplitScreenUtils.enterSplit(
@@ -138,10 +140,6 @@
check { "ActivityEmbeddingSplitHeight" }
.that(leftAELayerRegion.region.bounds.height())
.isEqual(rightAELayerRegion.region.bounds.height())
- check { "SystemSplitHeight" }
- .that(rightAELayerRegion.region.bounds.height())
- .isEqual(secondaryAppLayerRegion.region.bounds.height())
- // TODO(b/292283182): Remove this special case handling.
check { "ActivityEmbeddingSplitWidth" }
.that(
abs(
@@ -150,14 +148,6 @@
)
)
.isLower(2)
- check { "SystemSplitWidth" }
- .that(
- abs(
- secondaryAppLayerRegion.region.bounds.width() -
- 2 * rightAELayerRegion.region.bounds.width()
- )
- )
- .isLower(2)
}
}
@@ -170,15 +160,9 @@
visibleRegion(ActivityEmbeddingAppHelper.MAIN_ACTIVITY_COMPONENT)
val rightAEWindowRegion =
visibleRegion(ActivityEmbeddingAppHelper.SECONDARY_ACTIVITY_COMPONENT)
- // There's no window for the divider bar.
- val secondaryAppLayerRegion =
- visibleRegion(ActivityOptions.SplitScreen.Primary.COMPONENT.toFlickerComponent())
check { "ActivityEmbeddingSplitHeight" }
.that(leftAEWindowRegion.region.bounds.height())
.isEqual(rightAEWindowRegion.region.bounds.height())
- check { "SystemSplitHeight" }
- .that(rightAEWindowRegion.region.bounds.height())
- .isEqual(secondaryAppLayerRegion.region.bounds.height())
check { "ActivityEmbeddingSplitWidth" }
.that(
abs(
@@ -187,14 +171,6 @@
)
)
.isLower(2)
- check { "SystemSplitWidth" }
- .that(
- abs(
- secondaryAppLayerRegion.region.bounds.width() -
- 2 * rightAEWindowRegion.region.bounds.width()
- )
- )
- .isLower(2)
}
}
diff --git a/tests/FlickerTests/AppClose/AndroidTestTemplate.xml b/tests/FlickerTests/AppClose/AndroidTestTemplate.xml
index 57a58c8..4ffb11a 100644
--- a/tests/FlickerTests/AppClose/AndroidTestTemplate.xml
+++ b/tests/FlickerTests/AppClose/AndroidTestTemplate.xml
@@ -80,6 +80,7 @@
value="trace_config.textproto"
/>
<option name="instrumentation-arg" key="per_run" value="true"/>
+ <option name="instrumentation-arg" key="perfetto_persist_pid_track" value="true"/>
</test>
<!-- Needed for pulling the collected trace config on to the host -->
<metrics_collector class="com.android.tradefed.device.metric.FilePullerLogCollector">
diff --git a/tests/FlickerTests/AppLaunch/AndroidTestTemplate.xml b/tests/FlickerTests/AppLaunch/AndroidTestTemplate.xml
index 2cb86e0..0fa4d07 100644
--- a/tests/FlickerTests/AppLaunch/AndroidTestTemplate.xml
+++ b/tests/FlickerTests/AppLaunch/AndroidTestTemplate.xml
@@ -80,6 +80,7 @@
value="trace_config.textproto"
/>
<option name="instrumentation-arg" key="per_run" value="true"/>
+ <option name="instrumentation-arg" key="perfetto_persist_pid_track" value="true"/>
</test>
<!-- Needed for pulling the collected trace config on to the host -->
<metrics_collector class="com.android.tradefed.device.metric.FilePullerLogCollector">
diff --git a/tests/FlickerTests/FlickerService/AndroidTestTemplate.xml b/tests/FlickerTests/FlickerService/AndroidTestTemplate.xml
index 2cf85fa..4d9fefb 100644
--- a/tests/FlickerTests/FlickerService/AndroidTestTemplate.xml
+++ b/tests/FlickerTests/FlickerService/AndroidTestTemplate.xml
@@ -80,6 +80,7 @@
value="trace_config.textproto"
/>
<option name="instrumentation-arg" key="per_run" value="true"/>
+ <option name="instrumentation-arg" key="perfetto_persist_pid_track" value="true"/>
</test>
<!-- Needed for pulling the collected trace config on to the host -->
<metrics_collector class="com.android.tradefed.device.metric.FilePullerLogCollector">
diff --git a/tests/FlickerTests/IME/AndroidTestTemplate.xml b/tests/FlickerTests/IME/AndroidTestTemplate.xml
index b93e1be..b879c54 100644
--- a/tests/FlickerTests/IME/AndroidTestTemplate.xml
+++ b/tests/FlickerTests/IME/AndroidTestTemplate.xml
@@ -82,6 +82,7 @@
value="trace_config.textproto"
/>
<option name="instrumentation-arg" key="per_run" value="true"/>
+ <option name="instrumentation-arg" key="perfetto_persist_pid_track" value="true"/>
</test>
<!-- Needed for pulling the collected trace config on to the host -->
<metrics_collector class="com.android.tradefed.device.metric.FilePullerLogCollector">
diff --git a/tests/FlickerTests/IME/OWNERS b/tests/FlickerTests/IME/OWNERS
index ae1098d..e3a2e67 100644
--- a/tests/FlickerTests/IME/OWNERS
+++ b/tests/FlickerTests/IME/OWNERS
@@ -1,3 +1,3 @@
# ime
# Bug component: 34867
-include /services/core/java/com/android/server/inputmethod/OWNERS
+file:/services/core/java/com/android/server/inputmethod/OWNERS
diff --git a/tests/FlickerTests/IME/src/com/android/server/wm/flicker/ime/CloseImeOnDismissPopupDialogTest.kt b/tests/FlickerTests/IME/src/com/android/server/wm/flicker/ime/CloseImeOnDismissPopupDialogTest.kt
index da8368f..2b6ddcb 100644
--- a/tests/FlickerTests/IME/src/com/android/server/wm/flicker/ime/CloseImeOnDismissPopupDialogTest.kt
+++ b/tests/FlickerTests/IME/src/com/android/server/wm/flicker/ime/CloseImeOnDismissPopupDialogTest.kt
@@ -32,6 +32,9 @@
import org.junit.runners.MethodSorters
import org.junit.runners.Parameterized
+/**
+ * To run this test: `atest FlickerTestsIme1:CloseImeOnDismissPopupDialogTest`
+ */
@RunWith(Parameterized::class)
@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class)
@FixMethodOrder(MethodSorters.NAME_ASCENDING)
diff --git a/tests/FlickerTests/IME/src/com/android/server/wm/flicker/ime/CloseImeOnGoHomeTest.kt b/tests/FlickerTests/IME/src/com/android/server/wm/flicker/ime/CloseImeOnGoHomeTest.kt
index 2f3ec63..0344197 100644
--- a/tests/FlickerTests/IME/src/com/android/server/wm/flicker/ime/CloseImeOnGoHomeTest.kt
+++ b/tests/FlickerTests/IME/src/com/android/server/wm/flicker/ime/CloseImeOnGoHomeTest.kt
@@ -33,8 +33,8 @@
import org.junit.runners.Parameterized
/**
- * Test IME window closing to home transitions. To run this test: `atest
- * FlickerTests:CloseImeWindowToHomeTest`
+ * Test IME window closing to home transitions.
+ * To run this test: `atest FlickerTestsIme1:CloseImeOnGoHomeTest`
*/
@RunWith(Parameterized::class)
@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class)
diff --git a/tests/FlickerTests/IME/src/com/android/server/wm/flicker/ime/CloseImeShownOnAppStartOnGoHomeTest.kt b/tests/FlickerTests/IME/src/com/android/server/wm/flicker/ime/CloseImeShownOnAppStartOnGoHomeTest.kt
index 8821b69..fde1373 100644
--- a/tests/FlickerTests/IME/src/com/android/server/wm/flicker/ime/CloseImeShownOnAppStartOnGoHomeTest.kt
+++ b/tests/FlickerTests/IME/src/com/android/server/wm/flicker/ime/CloseImeShownOnAppStartOnGoHomeTest.kt
@@ -42,7 +42,7 @@
*
* More details on b/190352379
*
- * To run this test: `atest FlickerTests:CloseImeAutoOpenWindowToHomeTest`
+ * To run this test: `atest FlickerTestsIme1:CloseImeShownOnAppStartOnGoHomeTest`
*/
@RunWith(Parameterized::class)
@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class)
diff --git a/tests/FlickerTests/IME/src/com/android/server/wm/flicker/ime/CloseImeShownOnAppStartToAppOnPressBackTest.kt b/tests/FlickerTests/IME/src/com/android/server/wm/flicker/ime/CloseImeShownOnAppStartToAppOnPressBackTest.kt
index d75eba6..dc50135 100644
--- a/tests/FlickerTests/IME/src/com/android/server/wm/flicker/ime/CloseImeShownOnAppStartToAppOnPressBackTest.kt
+++ b/tests/FlickerTests/IME/src/com/android/server/wm/flicker/ime/CloseImeShownOnAppStartToAppOnPressBackTest.kt
@@ -42,7 +42,7 @@
*
* More details on b/190352379
*
- * To run this test: `atest FlickerTests:CloseImeAutoOpenWindowToAppTest`
+ * To run this test: `atest FlickerTestsIme1:CloseImeShownOnAppStartToAppOnPressBackTest`
*/
@RunWith(Parameterized::class)
@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class)
diff --git a/tests/FlickerTests/IME/src/com/android/server/wm/flicker/ime/CloseImeToAppOnPressBackTest.kt b/tests/FlickerTests/IME/src/com/android/server/wm/flicker/ime/CloseImeToAppOnPressBackTest.kt
index 41d9e30..dc2bd1b 100644
--- a/tests/FlickerTests/IME/src/com/android/server/wm/flicker/ime/CloseImeToAppOnPressBackTest.kt
+++ b/tests/FlickerTests/IME/src/com/android/server/wm/flicker/ime/CloseImeToAppOnPressBackTest.kt
@@ -34,8 +34,8 @@
import org.junit.runners.Parameterized
/**
- * Test IME window closing back to app window transitions. To run this test: `atest
- * FlickerTests:CloseImeWindowToAppTest`
+ * Test IME window closing back to app window transitions.
+ * To run this test: `atest FlickerTestsIme1:CloseImeToAppOnPressBackTest`
*/
@RunWith(Parameterized::class)
@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class)
diff --git a/tests/FlickerTests/IME/src/com/android/server/wm/flicker/ime/CloseImeToHomeOnFinishActivityTest.kt b/tests/FlickerTests/IME/src/com/android/server/wm/flicker/ime/CloseImeToHomeOnFinishActivityTest.kt
index 0e7fb79..05771e8 100644
--- a/tests/FlickerTests/IME/src/com/android/server/wm/flicker/ime/CloseImeToHomeOnFinishActivityTest.kt
+++ b/tests/FlickerTests/IME/src/com/android/server/wm/flicker/ime/CloseImeToHomeOnFinishActivityTest.kt
@@ -40,7 +40,7 @@
* Unlike {@link OpenImeWindowTest} testing IME window opening transitions, this test also verify
* there is no flickering when back to the simple activity without requesting IME to show.
*
- * To run this test: `atest FlickerTests:OpenImeWindowAndCloseTest`
+ * To run this test: `atest FlickerTestsIme1:CloseImeToHomeOnFinishActivityTest`
*/
@RunWith(Parameterized::class)
@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class)
diff --git a/tests/FlickerTests/IME/src/com/android/server/wm/flicker/ime/OpenImeWindowToFixedPortraitAppTest.kt b/tests/FlickerTests/IME/src/com/android/server/wm/flicker/ime/OpenImeWindowToFixedPortraitAppTest.kt
index 47a7e1b..336fe6f 100644
--- a/tests/FlickerTests/IME/src/com/android/server/wm/flicker/ime/OpenImeWindowToFixedPortraitAppTest.kt
+++ b/tests/FlickerTests/IME/src/com/android/server/wm/flicker/ime/OpenImeWindowToFixedPortraitAppTest.kt
@@ -36,8 +36,8 @@
import org.junit.runners.Parameterized
/**
- * Test IME window shown on the app with fixing portrait orientation. To run this test: `atest
- * FlickerTests:OpenImeWindowToFixedPortraitAppTest`
+ * Test IME window shown on the app with fixing portrait orientation.
+ * To run this test: `atest FlickerTestsIme2:OpenImeWindowToFixedPortraitAppTest`
*/
@RequiresDevice
@RunWith(Parameterized::class)
diff --git a/tests/FlickerTests/IME/src/com/android/server/wm/flicker/ime/ShowImeOnAppStartWhenLaunchingAppFromFixedOrientationTest.kt b/tests/FlickerTests/IME/src/com/android/server/wm/flicker/ime/ShowImeOnAppStartWhenLaunchingAppFromFixedOrientationTest.kt
index 48ec4d1..b8f11dc 100644
--- a/tests/FlickerTests/IME/src/com/android/server/wm/flicker/ime/ShowImeOnAppStartWhenLaunchingAppFromFixedOrientationTest.kt
+++ b/tests/FlickerTests/IME/src/com/android/server/wm/flicker/ime/ShowImeOnAppStartWhenLaunchingAppFromFixedOrientationTest.kt
@@ -38,8 +38,9 @@
/**
* Test IME window layer will become visible when switching from the fixed orientation activity
- * (e.g. Launcher activity). To run this test: `atest
- * FlickerTests:ShowImeOnAppStartWhenLaunchingAppFromFixedOrientationTest`
+ * (e.g. Launcher activity).
+ * To run this test:
+ * `atest FlickerTestsIme2:ShowImeOnAppStartWhenLaunchingAppFromFixedOrientationTest`
*/
@RunWith(Parameterized::class)
@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class)
diff --git a/tests/FlickerTests/IME/src/com/android/server/wm/flicker/ime/ShowImeOnAppStartWhenLaunchingAppFromOverviewTest.kt b/tests/FlickerTests/IME/src/com/android/server/wm/flicker/ime/ShowImeOnAppStartWhenLaunchingAppFromOverviewTest.kt
index 03f3a68..34a7085 100644
--- a/tests/FlickerTests/IME/src/com/android/server/wm/flicker/ime/ShowImeOnAppStartWhenLaunchingAppFromOverviewTest.kt
+++ b/tests/FlickerTests/IME/src/com/android/server/wm/flicker/ime/ShowImeOnAppStartWhenLaunchingAppFromOverviewTest.kt
@@ -33,7 +33,8 @@
import org.junit.runners.Parameterized
/**
- * Test IME window opening transitions. To run this test: `atest FlickerTests:ReOpenImeWindowTest`
+ * Test IME window opening transitions.
+ * To run this test: `atest FlickerTestsIme2:ShowImeOnAppStartWhenLaunchingAppFromOverviewTest`
*/
@RunWith(Parameterized::class)
@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class)
diff --git a/tests/FlickerTests/IME/src/com/android/server/wm/flicker/ime/ShowImeOnAppStartWhenLaunchingAppFromQuickSwitchTest.kt b/tests/FlickerTests/IME/src/com/android/server/wm/flicker/ime/ShowImeOnAppStartWhenLaunchingAppFromQuickSwitchTest.kt
index 7b62c89..7c72c31 100644
--- a/tests/FlickerTests/IME/src/com/android/server/wm/flicker/ime/ShowImeOnAppStartWhenLaunchingAppFromQuickSwitchTest.kt
+++ b/tests/FlickerTests/IME/src/com/android/server/wm/flicker/ime/ShowImeOnAppStartWhenLaunchingAppFromQuickSwitchTest.kt
@@ -35,8 +35,8 @@
import org.junit.runners.Parameterized
/**
- * Test IME windows switching with 2-Buttons or gestural navigation. To run this test: `atest
- * FlickerTests:SwitchImeWindowsFromGestureNavTest`
+ * Test IME windows switching with 2-Buttons or gestural navigation.
+ * To run this test: `atest FlickerTestsIme2:ShowImeOnAppStartWhenLaunchingAppFromQuickSwitchTest`
*/
@RunWith(Parameterized::class)
@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class)
diff --git a/tests/FlickerTests/IME/src/com/android/server/wm/flicker/ime/ShowImeOnAppStartWhenLaunchingAppTest.kt b/tests/FlickerTests/IME/src/com/android/server/wm/flicker/ime/ShowImeOnAppStartWhenLaunchingAppTest.kt
index 53bfb4e..fe5320c 100644
--- a/tests/FlickerTests/IME/src/com/android/server/wm/flicker/ime/ShowImeOnAppStartWhenLaunchingAppTest.kt
+++ b/tests/FlickerTests/IME/src/com/android/server/wm/flicker/ime/ShowImeOnAppStartWhenLaunchingAppTest.kt
@@ -36,7 +36,7 @@
/**
* Launch an app that automatically displays the IME
*
- * To run this test: `atest FlickerTests:LaunchAppShowImeOnStartTest`
+ * To run this test: `atest FlickerTestsIme2:ShowImeOnAppStartWhenLaunchingAppTest`
*
* Actions:
* ```
diff --git a/tests/FlickerTests/IME/src/com/android/server/wm/flicker/ime/ShowImeOnUnlockScreenTest.kt b/tests/FlickerTests/IME/src/com/android/server/wm/flicker/ime/ShowImeOnUnlockScreenTest.kt
index d22bdcf..92b6b93 100644
--- a/tests/FlickerTests/IME/src/com/android/server/wm/flicker/ime/ShowImeOnUnlockScreenTest.kt
+++ b/tests/FlickerTests/IME/src/com/android/server/wm/flicker/ime/ShowImeOnUnlockScreenTest.kt
@@ -35,8 +35,8 @@
import org.junit.runners.Parameterized
/**
- * Test IME window closing on lock and opening on screen unlock. To run this test: `atest
- * FlickerTests:CloseImeWindowToHomeTest`
+ * Test IME window closing on lock and opening on screen unlock.
+ * To run this test: `atest FlickerTestsIme2:ShowImeOnUnlockScreenTest`
*/
@RunWith(Parameterized::class)
@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class)
diff --git a/tests/FlickerTests/IME/src/com/android/server/wm/flicker/ime/ShowImeWhenFocusingOnInputFieldTest.kt b/tests/FlickerTests/IME/src/com/android/server/wm/flicker/ime/ShowImeWhenFocusingOnInputFieldTest.kt
index 12290af..9eaf998 100644
--- a/tests/FlickerTests/IME/src/com/android/server/wm/flicker/ime/ShowImeWhenFocusingOnInputFieldTest.kt
+++ b/tests/FlickerTests/IME/src/com/android/server/wm/flicker/ime/ShowImeWhenFocusingOnInputFieldTest.kt
@@ -31,7 +31,10 @@
import org.junit.runners.MethodSorters
import org.junit.runners.Parameterized
-/** Test IME window opening transitions. To run this test: `atest FlickerTests:OpenImeWindowTest` */
+/**
+ * Test IME window opening transitions.
+ * To run this test: `atest FlickerTestsIme2:ShowImeWhenFocusingOnInputFieldTest`
+ */
@RunWith(Parameterized::class)
@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class)
@FixMethodOrder(MethodSorters.NAME_ASCENDING)
diff --git a/tests/FlickerTests/IME/src/com/android/server/wm/flicker/ime/ShowImeWhileDismissingThemedPopupDialogTest.kt b/tests/FlickerTests/IME/src/com/android/server/wm/flicker/ime/ShowImeWhileDismissingThemedPopupDialogTest.kt
index 0948351..7186a2c 100644
--- a/tests/FlickerTests/IME/src/com/android/server/wm/flicker/ime/ShowImeWhileDismissingThemedPopupDialogTest.kt
+++ b/tests/FlickerTests/IME/src/com/android/server/wm/flicker/ime/ShowImeWhileDismissingThemedPopupDialogTest.kt
@@ -41,7 +41,7 @@
/**
* Test IME snapshot mechanism won't apply when transitioning from non-IME focused dialog activity.
- * To run this test: `atest FlickerTests:LaunchAppShowImeAndDialogThemeAppTest`
+ * To run this test: `atest FlickerTestsIme2:ShowImeWhileDismissingThemedPopupDialogTest`
*/
@RunWith(Parameterized::class)
@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class)
diff --git a/tests/FlickerTests/IME/src/com/android/server/wm/flicker/ime/ShowImeWhileEnteringOverviewTest.kt b/tests/FlickerTests/IME/src/com/android/server/wm/flicker/ime/ShowImeWhileEnteringOverviewTest.kt
index 7aa525f..c96c760 100644
--- a/tests/FlickerTests/IME/src/com/android/server/wm/flicker/ime/ShowImeWhileEnteringOverviewTest.kt
+++ b/tests/FlickerTests/IME/src/com/android/server/wm/flicker/ime/ShowImeWhileEnteringOverviewTest.kt
@@ -37,8 +37,8 @@
import org.junit.runners.Parameterized
/**
- * Test IME window layer will be associated with the app task when going to the overview screen. To
- * run this test: `atest FlickerTests:OpenImeWindowToOverViewTest`
+ * Test IME window layer will be associated with the app task when going to the overview screen.
+ * To run this test: `atest FlickerTestsIme2:ShowImeWhileEnteringOverviewTest`
*/
@RunWith(Parameterized::class)
@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class)
diff --git a/tests/FlickerTests/Notification/AndroidTestTemplate.xml b/tests/FlickerTests/Notification/AndroidTestTemplate.xml
index 9c6a17d3..04b312a 100644
--- a/tests/FlickerTests/Notification/AndroidTestTemplate.xml
+++ b/tests/FlickerTests/Notification/AndroidTestTemplate.xml
@@ -80,6 +80,7 @@
value="trace_config.textproto"
/>
<option name="instrumentation-arg" key="per_run" value="true"/>
+ <option name="instrumentation-arg" key="perfetto_persist_pid_track" value="true"/>
</test>
<!-- Needed for pulling the collected trace config on to the host -->
<metrics_collector class="com.android.tradefed.device.metric.FilePullerLogCollector">
diff --git a/tests/FlickerTests/Notification/src/com/android/server/wm/flicker/notification/OpenAppFromLockscreenNotificationColdTest.kt b/tests/FlickerTests/Notification/src/com/android/server/wm/flicker/notification/OpenAppFromLockscreenNotificationColdTest.kt
index ffaeead..8c9ab9a 100644
--- a/tests/FlickerTests/Notification/src/com/android/server/wm/flicker/notification/OpenAppFromLockscreenNotificationColdTest.kt
+++ b/tests/FlickerTests/Notification/src/com/android/server/wm/flicker/notification/OpenAppFromLockscreenNotificationColdTest.kt
@@ -40,7 +40,7 @@
*
* This test assumes the device doesn't have AOD enabled
*
- * To run this test: `atest FlickerTests:OpenAppFromLockNotificationCold`
+ * To run this test: `atest FlickerTestsNotification:OpenAppFromLockscreenNotificationColdTest`
*/
@RequiresDevice
@RunWith(Parameterized::class)
diff --git a/tests/FlickerTests/Notification/src/com/android/server/wm/flicker/notification/OpenAppFromLockscreenNotificationWarmTest.kt b/tests/FlickerTests/Notification/src/com/android/server/wm/flicker/notification/OpenAppFromLockscreenNotificationWarmTest.kt
index 6e67e19..e595100a 100644
--- a/tests/FlickerTests/Notification/src/com/android/server/wm/flicker/notification/OpenAppFromLockscreenNotificationWarmTest.kt
+++ b/tests/FlickerTests/Notification/src/com/android/server/wm/flicker/notification/OpenAppFromLockscreenNotificationWarmTest.kt
@@ -40,7 +40,7 @@
*
* This test assumes the device doesn't have AOD enabled
*
- * To run this test: `atest FlickerTests:OpenAppFromLockNotificationWarm`
+ * To run this test: `atest FlickerTestsNotification:OpenAppFromLockscreenNotificationWarmTest`
*/
@RequiresDevice
@RunWith(Parameterized::class)
diff --git a/tests/FlickerTests/Notification/src/com/android/server/wm/flicker/notification/OpenAppFromLockscreenNotificationWithOverlayAppTest.kt b/tests/FlickerTests/Notification/src/com/android/server/wm/flicker/notification/OpenAppFromLockscreenNotificationWithOverlayAppTest.kt
index f1df8a6..fbe1d34 100644
--- a/tests/FlickerTests/Notification/src/com/android/server/wm/flicker/notification/OpenAppFromLockscreenNotificationWithOverlayAppTest.kt
+++ b/tests/FlickerTests/Notification/src/com/android/server/wm/flicker/notification/OpenAppFromLockscreenNotificationWithOverlayAppTest.kt
@@ -40,7 +40,8 @@
*
* This test assumes the device doesn't have AOD enabled
*
- * To run this test: `atest FlickerTests:OpenAppFromLockNotificationWithLockOverlayApp`
+ * To run this test:
+ * `atest FlickerTestsNotification:OpenAppFromLockscreenNotificationWithOverlayAppTest`
*/
@RequiresDevice
@RunWith(Parameterized::class)
diff --git a/tests/FlickerTests/Notification/src/com/android/server/wm/flicker/notification/OpenAppFromNotificationColdTest.kt b/tests/FlickerTests/Notification/src/com/android/server/wm/flicker/notification/OpenAppFromNotificationColdTest.kt
index b6d09d0..c8ca644 100644
--- a/tests/FlickerTests/Notification/src/com/android/server/wm/flicker/notification/OpenAppFromNotificationColdTest.kt
+++ b/tests/FlickerTests/Notification/src/com/android/server/wm/flicker/notification/OpenAppFromNotificationColdTest.kt
@@ -36,7 +36,7 @@
/**
* Test cold launching an app from a notification.
*
- * To run this test: `atest FlickerTests:OpenAppFromNotificationCold`
+ * To run this test: `atest FlickerTestsNotification:OpenAppFromNotificationColdTest`
*/
@RunWith(Parameterized::class)
@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class)
diff --git a/tests/FlickerTests/Notification/src/com/android/server/wm/flicker/notification/OpenAppFromNotificationWarmTest.kt b/tests/FlickerTests/Notification/src/com/android/server/wm/flicker/notification/OpenAppFromNotificationWarmTest.kt
index 1e607bf..c29e71c 100644
--- a/tests/FlickerTests/Notification/src/com/android/server/wm/flicker/notification/OpenAppFromNotificationWarmTest.kt
+++ b/tests/FlickerTests/Notification/src/com/android/server/wm/flicker/notification/OpenAppFromNotificationWarmTest.kt
@@ -47,7 +47,7 @@
/**
* Test cold launching an app from a notification.
*
- * To run this test: `atest FlickerTests:OpenAppFromNotificationWarm`
+ * To run this test: `atest FlickerTestsNotification:OpenAppFromNotificationWarmTest`
*/
@RunWith(Parameterized::class)
@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class)
diff --git a/tests/FlickerTests/QuickSwitch/AndroidTestTemplate.xml b/tests/FlickerTests/QuickSwitch/AndroidTestTemplate.xml
index ecbed28..8acdabc 100644
--- a/tests/FlickerTests/QuickSwitch/AndroidTestTemplate.xml
+++ b/tests/FlickerTests/QuickSwitch/AndroidTestTemplate.xml
@@ -80,6 +80,7 @@
value="trace_config.textproto"
/>
<option name="instrumentation-arg" key="per_run" value="true"/>
+ <option name="instrumentation-arg" key="perfetto_persist_pid_track" value="true"/>
</test>
<!-- Needed for pulling the collected trace config on to the host -->
<metrics_collector class="com.android.tradefed.device.metric.FilePullerLogCollector">
diff --git a/tests/FlickerTests/Rotation/AndroidTestTemplate.xml b/tests/FlickerTests/Rotation/AndroidTestTemplate.xml
index 1eacdfd..91ece21 100644
--- a/tests/FlickerTests/Rotation/AndroidTestTemplate.xml
+++ b/tests/FlickerTests/Rotation/AndroidTestTemplate.xml
@@ -80,6 +80,7 @@
value="trace_config.textproto"
/>
<option name="instrumentation-arg" key="per_run" value="true"/>
+ <option name="instrumentation-arg" key="perfetto_persist_pid_track" value="true"/>
</test>
<!-- Needed for pulling the collected trace config on to the host -->
<metrics_collector class="com.android.tradefed.device.metric.FilePullerLogCollector">
diff --git a/tests/FlickerTests/test-apps/flickerapp/AndroidManifest.xml b/tests/FlickerTests/test-apps/flickerapp/AndroidManifest.xml
index 9198ae1..3e500d9 100644
--- a/tests/FlickerTests/test-apps/flickerapp/AndroidManifest.xml
+++ b/tests/FlickerTests/test-apps/flickerapp/AndroidManifest.xml
@@ -18,7 +18,10 @@
package="com.android.server.wm.flicker.testapp">
<uses-sdk android:minSdkVersion="29"
- android:targetSdkVersion="29"/>
+ android:targetSdkVersion="35"/>
+
+ <uses-permission android:name="android.permission.POST_NOTIFICATIONS" />
+
<application android:allowBackup="false"
android:supportsRtl="true">
<uses-library android:name="androidx.window.extensions" android:required="false"/>
@@ -107,7 +110,7 @@
android:immersive="true"
android:resizeableActivity="true"
android:screenOrientation="portrait"
- android:theme="@android:style/Theme.NoTitleBar"
+ android:theme="@style/OptOutEdgeToEdge.NoTitleBar"
android:configChanges="screenSize"
android:label="PortraitImmersiveActivity"
android:exported="true">
@@ -119,7 +122,7 @@
<activity android:name=".LaunchTransparentActivity"
android:resizeableActivity="false"
android:screenOrientation="portrait"
- android:theme="@android:style/Theme"
+ android:theme="@style/OptOutEdgeToEdge"
android:taskAffinity="com.android.server.wm.flicker.testapp.LaunchTransparentActivity"
android:label="LaunchTransparentActivity"
android:exported="true">
@@ -273,7 +276,7 @@
android:exported="true"
android:label="MailActivity"
android:taskAffinity="com.android.server.wm.flicker.testapp.MailActivity"
- android:theme="@style/Theme.AppCompat.Light">
+ android:theme="@style/OptOutEdgeToEdge.AppCompatTheme">
<intent-filter>
<action android:name="android.intent.action.MAIN"/>
<category android:name="android.intent.category.LAUNCHER"/>
@@ -282,7 +285,7 @@
<activity android:name=".GameActivity"
android:taskAffinity="com.android.server.wm.flicker.testapp.GameActivity"
android:immersive="true"
- android:theme="@android:style/Theme.NoTitleBar"
+ android:theme="@style/OptOutEdgeToEdge.NoTitleBar"
android:configChanges="screenSize"
android:label="GameActivity"
android:exported="true">
diff --git a/tests/FlickerTests/test-apps/flickerapp/res/layout/activity_embedding_main_layout.xml b/tests/FlickerTests/test-apps/flickerapp/res/layout/activity_embedding_main_layout.xml
index 86c21906..917aec1 100644
--- a/tests/FlickerTests/test-apps/flickerapp/res/layout/activity_embedding_main_layout.xml
+++ b/tests/FlickerTests/test-apps/flickerapp/res/layout/activity_embedding_main_layout.xml
@@ -14,66 +14,71 @@
See the License for the specific language governing permissions and
limitations under the License.
-->
-<LinearLayout
+<ScrollView
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
- android:orientation="vertical"
android:background="@android:color/holo_orange_light">
- <Button
- android:id="@+id/launch_secondary_activity_button"
- android:layout_width="wrap_content"
- android:layout_height="48dp"
- android:onClick="launchSecondaryActivity"
- android:tag="LEFT_TO_RIGHT"
- android:text="Launch Secondary Activity" />
+ <LinearLayout
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:orientation="vertical">
- <Button
- android:id="@+id/launch_secondary_activity_rtl_button"
- android:layout_width="wrap_content"
- android:layout_height="48dp"
- android:onClick="launchSecondaryActivity"
- android:tag="RIGHT_TO_LEFT"
- android:text="Launch Secondary Activity in RTL" />
+ <Button
+ android:id="@+id/launch_secondary_activity_button"
+ android:layout_width="wrap_content"
+ android:layout_height="48dp"
+ android:onClick="launchSecondaryActivity"
+ android:tag="LEFT_TO_RIGHT"
+ android:text="Launch Secondary Activity" />
- <Button
- android:id="@+id/launch_secondary_activity_horizontally_button"
- android:layout_width="wrap_content"
- android:layout_height="48dp"
- android:onClick="launchSecondaryActivity"
- android:tag="BOTTOM_TO_TOP"
- android:text="Launch Secondary Activity Horizontally" />
+ <Button
+ android:id="@+id/launch_secondary_activity_rtl_button"
+ android:layout_width="wrap_content"
+ android:layout_height="48dp"
+ android:onClick="launchSecondaryActivity"
+ android:tag="RIGHT_TO_LEFT"
+ android:text="Launch Secondary Activity in RTL" />
- <Button
- android:id="@+id/launch_placeholder_split_button"
- android:layout_width="wrap_content"
- android:layout_height="48dp"
- android:onClick="launchPlaceholderSplit"
- android:tag="LEFT_TO_RIGHT"
- android:text="Launch Placeholder Split" />
+ <Button
+ android:id="@+id/launch_secondary_activity_horizontally_button"
+ android:layout_width="wrap_content"
+ android:layout_height="48dp"
+ android:onClick="launchSecondaryActivity"
+ android:tag="BOTTOM_TO_TOP"
+ android:text="Launch Secondary Activity Horizontally" />
- <Button
- android:id="@+id/launch_always_expand_activity_button"
- android:layout_width="wrap_content"
- android:layout_height="48dp"
- android:onClick="launchAlwaysExpandActivity"
- android:text="Launch Always Expand Activity" />
+ <Button
+ android:id="@+id/launch_placeholder_split_button"
+ android:layout_width="wrap_content"
+ android:layout_height="48dp"
+ android:onClick="launchPlaceholderSplit"
+ android:tag="LEFT_TO_RIGHT"
+ android:text="Launch Placeholder Split" />
- <Button
- android:id="@+id/launch_placeholder_split_rtl_button"
- android:layout_width="wrap_content"
- android:layout_height="48dp"
- android:onClick="launchPlaceholderSplit"
- android:tag="RIGHT_TO_LEFT"
- android:text="Launch Placeholder Split in RTL" />
+ <Button
+ android:id="@+id/launch_always_expand_activity_button"
+ android:layout_width="wrap_content"
+ android:layout_height="48dp"
+ android:onClick="launchAlwaysExpandActivity"
+ android:text="Launch Always Expand Activity" />
- <Button
- android:id="@+id/launch_trampoline_button"
- android:layout_width="wrap_content"
- android:layout_height="48dp"
- android:onClick="launchTrampolineActivity"
- android:tag="LEFT_TO_RIGHT"
- android:text="Launch Trampoline Activity" />
+ <Button
+ android:id="@+id/launch_placeholder_split_rtl_button"
+ android:layout_width="wrap_content"
+ android:layout_height="48dp"
+ android:onClick="launchPlaceholderSplit"
+ android:tag="RIGHT_TO_LEFT"
+ android:text="Launch Placeholder Split in RTL" />
-</LinearLayout>
+ <Button
+ android:id="@+id/launch_trampoline_button"
+ android:layout_width="wrap_content"
+ android:layout_height="48dp"
+ android:onClick="launchTrampolineActivity"
+ android:tag="LEFT_TO_RIGHT"
+ android:text="Launch Trampoline Activity" />
+
+ </LinearLayout>
+</ScrollView>
diff --git a/tests/FlickerTests/test-apps/flickerapp/res/values/styles.xml b/tests/FlickerTests/test-apps/flickerapp/res/values/styles.xml
index 9b742d9..47d1137 100644
--- a/tests/FlickerTests/test-apps/flickerapp/res/values/styles.xml
+++ b/tests/FlickerTests/test-apps/flickerapp/res/values/styles.xml
@@ -16,7 +16,19 @@
-->
<resources>
- <style name="DefaultTheme" parent="@android:style/Theme.DeviceDefault">
+ <style name="OptOutEdgeToEdge" parent="@android:style/Theme.DeviceDefault">
+ <item name="android:windowOptOutEdgeToEdgeEnforcement">true</item>
+ </style>
+
+ <style name="OptOutEdgeToEdge.NoTitleBar" parent="@android:style/Theme.NoTitleBar">
+ <item name="android:windowOptOutEdgeToEdgeEnforcement">true</item>
+ </style>
+
+ <style name="OptOutEdgeToEdge.AppCompatTheme" parent="@style/Theme.AppCompat.Light">
+ <item name="android:windowOptOutEdgeToEdgeEnforcement">true</item>
+ </style>
+
+ <style name="DefaultTheme" parent="@style/OptOutEdgeToEdge">
<item name="android:windowBackground">@android:color/darker_gray</item>
</style>
@@ -32,7 +44,7 @@
<item name="android:windowLayoutInDisplayCutoutMode">never</item>
</style>
- <style name="DialogTheme" parent="@android:style/Theme.DeviceDefault">
+ <style name="DialogTheme" parent="@style/OptOutEdgeToEdge">
<item name="android:windowAnimationStyle">@null</item>
<item name="android:windowIsTranslucent">true</item>
<item name="android:windowBackground">@null</item>
@@ -43,18 +55,18 @@
<item name="android:windowSoftInputMode">stateUnchanged</item>
</style>
- <style name="TransparentTheme" parent="@android:style/Theme.DeviceDefault">
+ <style name="TransparentTheme" parent="@style/OptOutEdgeToEdge">
<item name="android:windowIsTranslucent">true</item>
<item name="android:windowBackground">@android:color/transparent</item>
<item name="android:windowContentOverlay">@null</item>
<item name="android:backgroundDimEnabled">false</item>
</style>
- <style name="no_starting_window" parent="@android:style/Theme.DeviceDefault">
+ <style name="no_starting_window" parent="@style/OptOutEdgeToEdge">
<item name="android:windowDisablePreview">true</item>
</style>
- <style name="SplashscreenAppTheme" parent="@android:style/Theme.DeviceDefault">
+ <style name="SplashscreenAppTheme" parent="@style/OptOutEdgeToEdge">
<!-- Splashscreen Attributes -->
<item name="android:windowSplashScreenAnimatedIcon">@drawable/avd_anim</item>
<!-- Here we want to match the duration of our AVD -->
diff --git a/tests/FlickerTests/test-apps/flickerapp/src/com/android/server/wm/flicker/testapp/BubbleHelper.java b/tests/FlickerTests/test-apps/flickerapp/src/com/android/server/wm/flicker/testapp/BubbleHelper.java
index c92b82b..a86ba5f 100644
--- a/tests/FlickerTests/test-apps/flickerapp/src/com/android/server/wm/flicker/testapp/BubbleHelper.java
+++ b/tests/FlickerTests/test-apps/flickerapp/src/com/android/server/wm/flicker/testapp/BubbleHelper.java
@@ -125,7 +125,7 @@
.setContentTitle("BubbleChat")
.setContentIntent(PendingIntent.getActivity(mContext, 0,
new Intent(mContext, LaunchBubbleActivity.class),
- PendingIntent.FLAG_UPDATE_CURRENT))
+ PendingIntent.FLAG_UPDATE_CURRENT | PendingIntent.FLAG_MUTABLE))
.setStyle(new Notification.MessagingStyle(chatBot)
.setConversationTitle("BubbleChat")
.addMessage("BubbleChat",
@@ -140,7 +140,7 @@
Intent target = new Intent(mContext, BubbleActivity.class);
target.putExtra(EXTRA_BUBBLE_NOTIF_ID, info.id);
PendingIntent bubbleIntent = PendingIntent.getActivity(mContext, info.id, target,
- PendingIntent.FLAG_UPDATE_CURRENT);
+ PendingIntent.FLAG_UPDATE_CURRENT | PendingIntent.FLAG_MUTABLE);
return new Notification.BubbleMetadata.Builder()
.setIntent(bubbleIntent)
diff --git a/tests/FlickerTests/test-apps/flickerapp/src/com/android/server/wm/flicker/testapp/LaunchBubbleActivity.java b/tests/FlickerTests/test-apps/flickerapp/src/com/android/server/wm/flicker/testapp/LaunchBubbleActivity.java
index dea3444..37332c9 100644
--- a/tests/FlickerTests/test-apps/flickerapp/src/com/android/server/wm/flicker/testapp/LaunchBubbleActivity.java
+++ b/tests/FlickerTests/test-apps/flickerapp/src/com/android/server/wm/flicker/testapp/LaunchBubbleActivity.java
@@ -17,6 +17,9 @@
package com.android.server.wm.flicker.testapp;
+import static android.Manifest.permission.POST_NOTIFICATIONS;
+import static android.content.pm.PackageManager.PERMISSION_GRANTED;
+
import android.app.Activity;
import android.app.Person;
import android.content.Context;
@@ -24,6 +27,7 @@
import android.content.pm.ShortcutInfo;
import android.content.pm.ShortcutManager;
import android.graphics.drawable.Icon;
+import android.os.Build;
import android.os.Bundle;
import android.view.View;
@@ -36,6 +40,13 @@
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
+
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU
+ && checkSelfPermission(POST_NOTIFICATIONS) != PERMISSION_GRANTED) {
+ // POST_NOTIFICATIONS permission required for notification post sdk 33.
+ requestPermissions(new String[] { POST_NOTIFICATIONS }, 0);
+ }
+
addInboxShortcut(getApplicationContext());
mBubbleHelper = BubbleHelper.getInstance(this);
setContentView(R.layout.activity_main);
diff --git a/tests/FlickerTests/test-apps/flickerapp/src/com/android/server/wm/flicker/testapp/NotificationActivity.java b/tests/FlickerTests/test-apps/flickerapp/src/com/android/server/wm/flicker/testapp/NotificationActivity.java
index a4dd575..d6427ab 100644
--- a/tests/FlickerTests/test-apps/flickerapp/src/com/android/server/wm/flicker/testapp/NotificationActivity.java
+++ b/tests/FlickerTests/test-apps/flickerapp/src/com/android/server/wm/flicker/testapp/NotificationActivity.java
@@ -16,6 +16,9 @@
package com.android.server.wm.flicker.testapp;
+import static android.Manifest.permission.POST_NOTIFICATIONS;
+import static android.content.pm.PackageManager.PERMISSION_GRANTED;
+
import android.app.Activity;
import android.app.Notification;
import android.app.NotificationChannel;
@@ -23,6 +26,7 @@
import android.app.PendingIntent;
import android.app.TaskStackBuilder;
import android.content.Intent;
+import android.os.Build;
import android.os.Bundle;
import android.view.WindowManager;
import android.widget.Button;
@@ -34,6 +38,13 @@
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
+
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU
+ && checkSelfPermission(POST_NOTIFICATIONS) != PERMISSION_GRANTED) {
+ // POST_NOTIFICATIONS permission required for notification post sdk 33.
+ requestPermissions(new String[] { POST_NOTIFICATIONS }, 0);
+ }
+
WindowManager.LayoutParams p = getWindow().getAttributes();
p.layoutInDisplayCutoutMode = WindowManager.LayoutParams
.LAYOUT_IN_DISPLAY_CUTOUT_MODE_SHORT_EDGES;
diff --git a/tests/FlickerTests/test-apps/flickerapp/src/com/android/server/wm/flicker/testapp/PipActivity.java b/tests/FlickerTests/test-apps/flickerapp/src/com/android/server/wm/flicker/testapp/PipActivity.java
index 1ab8ddb..27eb5a0 100644
--- a/tests/FlickerTests/test-apps/flickerapp/src/com/android/server/wm/flicker/testapp/PipActivity.java
+++ b/tests/FlickerTests/test-apps/flickerapp/src/com/android/server/wm/flicker/testapp/PipActivity.java
@@ -198,7 +198,7 @@
filter.addAction(ACTION_SET_REQUESTED_ORIENTATION);
filter.addAction(ACTION_ENTER_PIP);
filter.addAction(ACTION_ASPECT_RATIO);
- registerReceiver(mBroadcastReceiver, filter);
+ registerReceiver(mBroadcastReceiver, filter, Context.RECEIVER_EXPORTED);
handleIntentExtra(getIntent());
}
@@ -222,8 +222,8 @@
private RemoteAction buildRemoteAction(Icon icon, String label, String action) {
final Intent intent = new Intent(action);
- final PendingIntent pendingIntent =
- PendingIntent.getBroadcast(this, 0, intent, PendingIntent.FLAG_CANCEL_CURRENT);
+ final PendingIntent pendingIntent = PendingIntent.getBroadcast(this, 0, intent,
+ PendingIntent.FLAG_CANCEL_CURRENT | PendingIntent.FLAG_IMMUTABLE);
return new RemoteAction(icon, label, label, pendingIntent);
}
diff --git a/tests/Input/src/android/hardware/input/KeyboardLayoutPreviewTests.kt b/tests/Input/src/android/hardware/input/KeyboardLayoutPreviewTests.kt
index 3a2a3be..ae32bda 100644
--- a/tests/Input/src/android/hardware/input/KeyboardLayoutPreviewTests.kt
+++ b/tests/Input/src/android/hardware/input/KeyboardLayoutPreviewTests.kt
@@ -16,6 +16,8 @@
package android.hardware.input
+import android.platform.test.annotations.DisableFlags
+import android.platform.test.annotations.EnableFlags
import android.content.ContextWrapper
import android.graphics.drawable.Drawable
import android.platform.test.annotations.Presubmit
@@ -54,16 +56,16 @@
}
@Test
+ @EnableFlags(Flags.FLAG_KEYBOARD_LAYOUT_PREVIEW_FLAG)
fun testKeyboardLayoutDrawable_hasCorrectDimensions() {
- setFlagsRule.enableFlags(Flags.FLAG_KEYBOARD_LAYOUT_PREVIEW_FLAG)
val drawable = createDrawable()!!
assertEquals(WIDTH, drawable.intrinsicWidth)
assertEquals(HEIGHT, drawable.intrinsicHeight)
}
@Test
+ @DisableFlags(Flags.FLAG_KEYBOARD_LAYOUT_PREVIEW_FLAG)
fun testKeyboardLayoutDrawable_isNull_ifFlagOff() {
- setFlagsRule.disableFlags(Flags.FLAG_KEYBOARD_LAYOUT_PREVIEW_FLAG)
assertNull(createDrawable())
}
}
\ No newline at end of file
diff --git a/tests/Input/src/android/hardware/input/StickyModifierStateListenerTest.kt b/tests/Input/src/android/hardware/input/StickyModifierStateListenerTest.kt
index e2b0c36..bcd56ad 100644
--- a/tests/Input/src/android/hardware/input/StickyModifierStateListenerTest.kt
+++ b/tests/Input/src/android/hardware/input/StickyModifierStateListenerTest.kt
@@ -21,6 +21,7 @@
import android.os.Handler
import android.os.HandlerExecutor
import android.os.test.TestLooper
+import android.platform.test.annotations.EnableFlags
import android.platform.test.annotations.Presubmit
import android.platform.test.flag.junit.SetFlagsRule
import android.view.KeyEvent
@@ -50,6 +51,10 @@
*/
@Presubmit
@RunWith(MockitoJUnitRunner::class)
+@EnableFlags(
+ com.android.hardware.input.Flags.FLAG_KEYBOARD_A11Y_STICKY_KEYS_FLAG,
+ com.android.input.flags.Flags.FLAG_ENABLE_INPUT_FILTER_RUST_IMPL,
+)
class StickyModifierStateListenerTest {
@get:Rule
@@ -67,10 +72,6 @@
@Before
fun setUp() {
- // Enable Sticky keys feature
- rule.enableFlags(com.android.hardware.input.Flags.FLAG_KEYBOARD_A11Y_STICKY_KEYS_FLAG)
- rule.enableFlags(com.android.input.flags.Flags.FLAG_ENABLE_INPUT_FILTER_RUST_IMPL)
-
context = Mockito.spy(ContextWrapper(ApplicationProvider.getApplicationContext()))
inputManagerGlobalSession = InputManagerGlobal.createTestSession(iInputManagerMock)
inputManager = InputManager(context)
diff --git a/tests/Input/src/com/android/test/input/InputDeviceTest.java b/tests/Input/src/com/android/test/input/InputDeviceTest.java
index 5f1bc87..87a0de6 100644
--- a/tests/Input/src/com/android/test/input/InputDeviceTest.java
+++ b/tests/Input/src/com/android/test/input/InputDeviceTest.java
@@ -61,6 +61,7 @@
assertEquals(device.getMotionRanges().size(), outDevice.getMotionRanges().size());
assertEquals(device.getHostUsiVersion(), outDevice.getHostUsiVersion());
assertEquals(device.getAssociatedDisplayId(), outDevice.getAssociatedDisplayId());
+ assertEquals(device.isEnabled(), outDevice.isEnabled());
KeyCharacterMap keyCharacterMap = device.getKeyCharacterMap();
KeyCharacterMap outKeyCharacterMap = outDevice.getKeyCharacterMap();
@@ -100,7 +101,9 @@
.setKeyboardLanguageTag("en-US")
.setKeyboardLayoutType("qwerty")
.setUsiVersion(new HostUsiVersion(2, 0))
- .setShouldSmoothScroll(true);
+ .setShouldSmoothScroll(true)
+ .setAssociatedDisplayId(Display.DEFAULT_DISPLAY)
+ .setEnabled(false);
for (int i = 0; i < 30; i++) {
deviceBuilder.addMotionRange(
diff --git a/tests/Internal/src/com/android/internal/protolog/PerfettoDataSourceTest.java b/tests/Internal/src/com/android/internal/protolog/ProtologDataSourceTest.java
similarity index 99%
rename from tests/Internal/src/com/android/internal/protolog/PerfettoDataSourceTest.java
rename to tests/Internal/src/com/android/internal/protolog/ProtologDataSourceTest.java
index 001a09a..be9fb1b 100644
--- a/tests/Internal/src/com/android/internal/protolog/PerfettoDataSourceTest.java
+++ b/tests/Internal/src/com/android/internal/protolog/ProtologDataSourceTest.java
@@ -34,7 +34,7 @@
import perfetto.protos.ProtologCommon;
import perfetto.protos.ProtologConfig;
-public class PerfettoDataSourceTest {
+public class ProtologDataSourceTest {
@Before
public void before() {
assumeTrue(android.tracing.Flags.perfettoProtologTracing());
diff --git a/tests/UsbManagerTests/src/android/hardware/usb/DeviceFilterTest.java b/tests/UsbManagerTests/src/android/hardware/usb/DeviceFilterTest.java
new file mode 100644
index 0000000..d6f3148
--- /dev/null
+++ b/tests/UsbManagerTests/src/android/hardware/usb/DeviceFilterTest.java
@@ -0,0 +1,248 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.hardware.usb;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static junit.framework.Assert.assertFalse;
+import static junit.framework.Assert.assertTrue;
+
+import static org.mockito.Mockito.any;
+import static org.mockito.Mockito.eq;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import android.hardware.usb.flags.Flags;
+
+import androidx.test.runner.AndroidJUnit4;
+
+import com.android.dx.mockito.inline.extended.ExtendedMockito;
+import com.android.internal.util.XmlUtils;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mockito;
+import org.mockito.MockitoSession;
+import org.mockito.quality.Strictness;
+import org.xmlpull.v1.XmlPullParser;
+import org.xmlpull.v1.XmlPullParserFactory;
+import org.xmlpull.v1.XmlSerializer;
+
+import java.io.StringReader;
+
+/**
+ * Unit tests for {@link android.hardware.usb.DeviceFilter}.
+ */
+@RunWith(AndroidJUnit4.class)
+public class DeviceFilterTest {
+
+ private static final int VID = 10;
+ private static final int PID = 11;
+ private static final int CLASS = 12;
+ private static final int SUBCLASS = 13;
+ private static final int PROTOCOL = 14;
+ private static final String MANUFACTURER = "Google";
+ private static final String PRODUCT = "Test";
+ private static final String SERIAL_NO = "4AL23";
+ private static final String INTERFACE_NAME = "MTP";
+
+ private MockitoSession mStaticMockSession;
+
+ @Before
+ public void setUp() throws Exception {
+ mStaticMockSession = ExtendedMockito.mockitoSession()
+ .mockStatic(Flags.class)
+ .strictness(Strictness.WARN)
+ .startMocking();
+
+ when(Flags.enableInterfaceNameDeviceFilter()).thenReturn(true);
+ }
+
+ @After
+ public void tearDown() throws Exception {
+ mStaticMockSession.finishMocking();
+ }
+
+ @Test
+ public void testConstructorFromValues_interfaceNameIsInitialized() {
+ DeviceFilter deviceFilter = new DeviceFilter(
+ VID, PID, CLASS, SUBCLASS, PROTOCOL, MANUFACTURER,
+ PRODUCT, SERIAL_NO, INTERFACE_NAME
+ );
+
+ verifyDeviceFilterConfigurationExceptInterfaceName(deviceFilter);
+ assertThat(deviceFilter.mInterfaceName).isEqualTo(INTERFACE_NAME);
+ }
+
+ @Test
+ public void testConstructorFromUsbDevice_interfaceNameIsNull() {
+ UsbDevice usbDevice = Mockito.mock(UsbDevice.class);
+ when(usbDevice.getVendorId()).thenReturn(VID);
+ when(usbDevice.getProductId()).thenReturn(PID);
+ when(usbDevice.getDeviceClass()).thenReturn(CLASS);
+ when(usbDevice.getDeviceSubclass()).thenReturn(SUBCLASS);
+ when(usbDevice.getDeviceProtocol()).thenReturn(PROTOCOL);
+ when(usbDevice.getManufacturerName()).thenReturn(MANUFACTURER);
+ when(usbDevice.getProductName()).thenReturn(PRODUCT);
+ when(usbDevice.getSerialNumber()).thenReturn(SERIAL_NO);
+
+ DeviceFilter deviceFilter = new DeviceFilter(usbDevice);
+
+ verifyDeviceFilterConfigurationExceptInterfaceName(deviceFilter);
+ assertThat(deviceFilter.mInterfaceName).isEqualTo(null);
+ }
+
+ @Test
+ public void testConstructorFromDeviceFilter_interfaceNameIsInitialized() {
+ DeviceFilter originalDeviceFilter = new DeviceFilter(
+ VID, PID, CLASS, SUBCLASS, PROTOCOL, MANUFACTURER,
+ PRODUCT, SERIAL_NO, INTERFACE_NAME
+ );
+
+ DeviceFilter deviceFilter = new DeviceFilter(originalDeviceFilter);
+
+ verifyDeviceFilterConfigurationExceptInterfaceName(deviceFilter);
+ assertThat(deviceFilter.mInterfaceName).isEqualTo(INTERFACE_NAME);
+ }
+
+
+ @Test
+ public void testReadFromXml_interfaceNamePresent_propertyIsInitialized() throws Exception {
+ DeviceFilter deviceFilter = getDeviceFilterFromXml("<usb-device interface-name=\"MTP\"/>");
+
+ assertThat(deviceFilter.mInterfaceName).isEqualTo("MTP");
+ }
+
+ @Test
+ public void testReadFromXml_interfaceNameAbsent_propertyIsNull() throws Exception {
+ DeviceFilter deviceFilter = getDeviceFilterFromXml("<usb-device vendor-id=\"1\" />");
+
+ assertThat(deviceFilter.mInterfaceName).isEqualTo(null);
+ }
+
+ @Test
+ public void testWrite_withInterfaceName() throws Exception {
+ DeviceFilter deviceFilter = getDeviceFilterFromXml("<usb-device interface-name=\"MTP\"/>");
+ XmlSerializer serializer = Mockito.mock(XmlSerializer.class);
+
+ deviceFilter.write(serializer);
+
+ verify(serializer).attribute(null, "interface-name", "MTP");
+ }
+
+ @Test
+ public void testWrite_withoutInterfaceName() throws Exception {
+ DeviceFilter deviceFilter = getDeviceFilterFromXml("<usb-device vendor-id=\"1\" />");
+ XmlSerializer serializer = Mockito.mock(XmlSerializer.class);
+
+ deviceFilter.write(serializer);
+
+ verify(serializer, times(0)).attribute(eq(null), eq("interface-name"), any());
+ }
+
+ @Test
+ public void testToString() {
+ DeviceFilter deviceFilter = new DeviceFilter(
+ VID, PID, CLASS, SUBCLASS, PROTOCOL, MANUFACTURER,
+ PRODUCT, SERIAL_NO, INTERFACE_NAME
+ );
+
+ assertThat(deviceFilter.toString()).isEqualTo(
+ "DeviceFilter[mVendorId=10,mProductId=11,mClass=12,mSubclass=13,mProtocol=14,"
+ + "mManufacturerName=Google,mProductName=Test,mSerialNumber=4AL23,"
+ + "mInterfaceName=MTP]");
+ }
+
+ @Test
+ public void testMatch_interfaceNameMatches_returnTrue() throws Exception {
+ DeviceFilter deviceFilter = getDeviceFilterFromXml(
+ "<usb-device class=\"255\" subclass=\"255\" protocol=\"0\" "
+ + "interface-name=\"MTP\"/>");
+ UsbDevice usbDevice = Mockito.mock(UsbDevice.class);
+ when(usbDevice.getInterfaceCount()).thenReturn(1);
+ when(usbDevice.getInterface(0)).thenReturn(new UsbInterface(
+ /* id= */ 0,
+ /* alternateSetting= */ 0,
+ /* name= */ "MTP",
+ /* class= */ 255,
+ /* subClass= */ 255,
+ /* protocol= */ 0));
+
+ assertTrue(deviceFilter.matches(usbDevice));
+ }
+
+ @Test
+ public void testMatch_interfaceNameMismatch_returnFalse() throws Exception {
+ DeviceFilter deviceFilter = getDeviceFilterFromXml(
+ "<usb-device class=\"255\" subclass=\"255\" protocol=\"0\" "
+ + "interface-name=\"MTP\"/>");
+ UsbDevice usbDevice = Mockito.mock(UsbDevice.class);
+ when(usbDevice.getInterfaceCount()).thenReturn(1);
+ when(usbDevice.getInterface(0)).thenReturn(new UsbInterface(
+ /* id= */ 0,
+ /* alternateSetting= */ 0,
+ /* name= */ "UVC",
+ /* class= */ 255,
+ /* subClass= */ 255,
+ /* protocol= */ 0));
+
+ assertFalse(deviceFilter.matches(usbDevice));
+ }
+
+ @Test
+ public void testMatch_interfaceNameMismatchFlagDisabled_returnTrue() throws Exception {
+ when(Flags.enableInterfaceNameDeviceFilter()).thenReturn(false);
+ DeviceFilter deviceFilter = getDeviceFilterFromXml(
+ "<usb-device class=\"255\" subclass=\"255\" protocol=\"0\" "
+ + "interface-name=\"MTP\"/>");
+ UsbDevice usbDevice = Mockito.mock(UsbDevice.class);
+ when(usbDevice.getInterfaceCount()).thenReturn(1);
+ when(usbDevice.getInterface(0)).thenReturn(new UsbInterface(
+ /* id= */ 0,
+ /* alternateSetting= */ 0,
+ /* name= */ "UVC",
+ /* class= */ 255,
+ /* subClass= */ 255,
+ /* protocol= */ 0));
+
+ assertTrue(deviceFilter.matches(usbDevice));
+ }
+
+ private void verifyDeviceFilterConfigurationExceptInterfaceName(DeviceFilter deviceFilter) {
+ assertThat(deviceFilter.mVendorId).isEqualTo(VID);
+ assertThat(deviceFilter.mProductId).isEqualTo(PID);
+ assertThat(deviceFilter.mClass).isEqualTo(CLASS);
+ assertThat(deviceFilter.mSubclass).isEqualTo(SUBCLASS);
+ assertThat(deviceFilter.mProtocol).isEqualTo(PROTOCOL);
+ assertThat(deviceFilter.mManufacturerName).isEqualTo(MANUFACTURER);
+ assertThat(deviceFilter.mProductName).isEqualTo(PRODUCT);
+ assertThat(deviceFilter.mSerialNumber).isEqualTo(SERIAL_NO);
+ }
+
+ private DeviceFilter getDeviceFilterFromXml(String xml) throws Exception {
+ XmlPullParserFactory factory = XmlPullParserFactory.newInstance();
+ XmlPullParser parser = factory.newPullParser();
+ parser.setInput(new StringReader(xml));
+ XmlUtils.nextElement(parser);
+
+ return DeviceFilter.read(parser);
+ }
+
+}
diff --git a/tools/aapt/Symbol.h b/tools/aapt/Symbol.h
index de1d60c..24c3208 100644
--- a/tools/aapt/Symbol.h
+++ b/tools/aapt/Symbol.h
@@ -40,7 +40,7 @@
};
/**
- * A specific defintion of a symbol, defined with a configuration and a definition site.
+ * A specific definition of a symbol, defined with a configuration and a definition site.
*/
struct SymbolDefinition {
inline SymbolDefinition();
@@ -92,4 +92,3 @@
}
#endif // AAPT_SYMBOL_H
-
diff --git a/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/filters/AndroidHeuristicsFilter.kt b/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/filters/AndroidHeuristicsFilter.kt
index 16785d1..6b360b7 100644
--- a/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/filters/AndroidHeuristicsFilter.kt
+++ b/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/filters/AndroidHeuristicsFilter.kt
@@ -25,6 +25,7 @@
val aidlPolicy: FilterPolicyWithReason?,
val featureFlagsPolicy: FilterPolicyWithReason?,
val syspropsPolicy: FilterPolicyWithReason?,
+ val rFilePolicy: FilterPolicyWithReason?,
fallback: OutputFilter
) : DelegatingFilter(fallback) {
override fun getPolicyForClass(className: String): FilterPolicyWithReason {
@@ -37,6 +38,9 @@
if (syspropsPolicy != null && classes.isSyspropsClass(className)) {
return syspropsPolicy
}
+ if (rFilePolicy != null && classes.isRClass(className)) {
+ return rFilePolicy
+ }
return super.getPolicyForClass(className)
}
}
@@ -74,3 +78,10 @@
return className.startsWith("android/sysprop/")
&& className.endsWith("Properties")
}
+
+/**
+ * @return if a given class "seems like" an R class or its nested classes.
+ */
+private fun ClassNodes.isRClass(className: String): Boolean {
+ return className.endsWith("/R") || className.contains("/R$")
+}
diff --git a/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/filters/TextFileFilterPolicyParser.kt b/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/filters/TextFileFilterPolicyParser.kt
index 75b5fc8..c5acd81 100644
--- a/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/filters/TextFileFilterPolicyParser.kt
+++ b/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/filters/TextFileFilterPolicyParser.kt
@@ -17,6 +17,7 @@
import com.android.hoststubgen.ParseException
import com.android.hoststubgen.asm.ClassNodes
+import com.android.hoststubgen.asm.toHumanReadableClassName
import com.android.hoststubgen.log
import com.android.hoststubgen.normalizeTextLine
import com.android.hoststubgen.whitespaceRegex
@@ -31,13 +32,17 @@
* Print a class node as a "keep" policy.
*/
fun printAsTextPolicy(pw: PrintWriter, cn: ClassNode) {
- pw.printf("class %s\t%s\n", cn.name, "keep")
+ pw.printf("class %s %s\n", cn.name.toHumanReadableClassName(), "keep")
- for (f in cn.fields ?: emptyList()) {
- pw.printf(" field %s\t%s\n", f.name, "keep")
+ cn.fields?.let {
+ for (f in it.sortedWith(compareBy({ it.name }))) {
+ pw.printf(" field %s %s\n", f.name, "keep")
+ }
}
- for (m in cn.methods ?: emptyList()) {
- pw.printf(" method %s\t%s\t%s\n", m.name, m.desc, "keep")
+ cn.methods?.let {
+ for (m in it.sortedWith(compareBy({ it.name }, { it.desc }))) {
+ pw.printf(" method %s %s %s\n", m.name, m.desc, "keep")
+ }
}
}
@@ -66,6 +71,7 @@
var aidlPolicy: FilterPolicyWithReason? = null
var featureFlagsPolicy: FilterPolicyWithReason? = null
var syspropsPolicy: FilterPolicyWithReason? = null
+ var rFilePolicy: FilterPolicyWithReason? = null
try {
BufferedReader(FileReader(filename)).use { reader ->
@@ -162,6 +168,14 @@
syspropsPolicy = policy.withReason(
"$FILTER_REASON (special-class sysprops)")
}
+ SpecialClass.RFile -> {
+ if (rFilePolicy != null) {
+ throw ParseException(
+ "Policy for R file already defined")
+ }
+ rFilePolicy = policy.withReason(
+ "$FILTER_REASON (special-class R file)")
+ }
}
}
}
@@ -225,13 +239,9 @@
throw e.withSourceInfo(filename, lineNo)
}
- var ret: OutputFilter = imf
- if (aidlPolicy != null || featureFlagsPolicy != null || syspropsPolicy != null) {
- log.d("AndroidHeuristicsFilter enabled")
- ret = AndroidHeuristicsFilter(
- classes, aidlPolicy, featureFlagsPolicy, syspropsPolicy, imf)
- }
- return ret
+ // Wrap the in-memory-filter with AHF.
+ return AndroidHeuristicsFilter(
+ classes, aidlPolicy, featureFlagsPolicy, syspropsPolicy, rFilePolicy, imf)
}
}
@@ -240,6 +250,7 @@
Aidl,
FeatureFlags,
Sysprops,
+ RFile,
}
private fun resolveSpecialClass(className: String): SpecialClass {
@@ -250,6 +261,7 @@
":aidl" -> return SpecialClass.Aidl
":feature_flags" -> return SpecialClass.FeatureFlags
":sysprops" -> return SpecialClass.Sysprops
+ ":r" -> return SpecialClass.RFile
}
throw ParseException("Invalid special class name \"$className\"")
}
diff --git a/tools/hoststubgen/hoststubgen/test-tiny-framework/golden-output/01-hoststubgen-test-tiny-framework-orig-dump.txt b/tools/hoststubgen/hoststubgen/test-tiny-framework/golden-output/01-hoststubgen-test-tiny-framework-orig-dump.txt
index fa8fe6c..931f0c5 100644
--- a/tools/hoststubgen/hoststubgen/test-tiny-framework/golden-output/01-hoststubgen-test-tiny-framework-orig-dump.txt
+++ b/tools/hoststubgen/hoststubgen/test-tiny-framework/golden-output/01-hoststubgen-test-tiny-framework-orig-dump.txt
@@ -322,6 +322,78 @@
InnerClasses:
public static #x= #x of #x; // Stub=class com/android/hoststubgen/test/tinyframework/IPretendingAidl$Stub of class com/android/hoststubgen/test/tinyframework/IPretendingAidl
public static #x= #x of #x; // Proxy=class com/android/hoststubgen/test/tinyframework/IPretendingAidl$Stub$Proxy of class com/android/hoststubgen/test/tinyframework/IPretendingAidl$Stub
+## Class: com/android/hoststubgen/test/tinyframework/R$Nested.class
+ Compiled from "R.java"
+public class com.android.hoststubgen.test.tinyframework.R$Nested
+ minor version: 0
+ major version: 61
+ flags: (0x0021) ACC_PUBLIC, ACC_SUPER
+ this_class: #x // com/android/hoststubgen/test/tinyframework/R$Nested
+ super_class: #x // java/lang/Object
+ interfaces: 0, fields: 1, methods: 2, attributes: 3
+ public static int[] ARRAY;
+ descriptor: [I
+ flags: (0x0009) ACC_PUBLIC, ACC_STATIC
+
+ public com.android.hoststubgen.test.tinyframework.R$Nested();
+ descriptor: ()V
+ flags: (0x0001) ACC_PUBLIC
+ Code:
+ stack=1, locals=1, args_size=1
+ x: aload_0
+ x: invokespecial #x // Method java/lang/Object."<init>":()V
+ x: return
+ LineNumberTable:
+ LocalVariableTable:
+ Start Length Slot Name Signature
+ 0 5 0 this Lcom/android/hoststubgen/test/tinyframework/R$Nested;
+
+ static {};
+ descriptor: ()V
+ flags: (0x0008) ACC_STATIC
+ Code:
+ stack=4, locals=0, args_size=0
+ x: iconst_1
+ x: newarray int
+ x: dup
+ x: iconst_0
+ x: iconst_1
+ x: iastore
+ x: putstatic #x // Field ARRAY:[I
+ x: return
+ LineNumberTable:
+}
+SourceFile: "R.java"
+NestHost: class com/android/hoststubgen/test/tinyframework/R
+InnerClasses:
+ public static #x= #x of #x; // Nested=class com/android/hoststubgen/test/tinyframework/R$Nested of class com/android/hoststubgen/test/tinyframework/R
+## Class: com/android/hoststubgen/test/tinyframework/R.class
+ Compiled from "R.java"
+public class com.android.hoststubgen.test.tinyframework.R
+ minor version: 0
+ major version: 61
+ flags: (0x0021) ACC_PUBLIC, ACC_SUPER
+ this_class: #x // com/android/hoststubgen/test/tinyframework/R
+ super_class: #x // java/lang/Object
+ interfaces: 0, fields: 0, methods: 1, attributes: 3
+ public com.android.hoststubgen.test.tinyframework.R();
+ descriptor: ()V
+ flags: (0x0001) ACC_PUBLIC
+ Code:
+ stack=1, locals=1, args_size=1
+ x: aload_0
+ x: invokespecial #x // Method java/lang/Object."<init>":()V
+ x: return
+ LineNumberTable:
+ LocalVariableTable:
+ Start Length Slot Name Signature
+ 0 5 0 this Lcom/android/hoststubgen/test/tinyframework/R;
+}
+SourceFile: "R.java"
+NestMembers:
+ com/android/hoststubgen/test/tinyframework/R$Nested
+InnerClasses:
+ public static #x= #x of #x; // Nested=class com/android/hoststubgen/test/tinyframework/R$Nested of class com/android/hoststubgen/test/tinyframework/R
## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkCallerCheck$Impl.class
Compiled from "TinyFrameworkCallerCheck.java"
class com.android.hoststubgen.test.tinyframework.TinyFrameworkCallerCheck$Impl
diff --git a/tools/hoststubgen/hoststubgen/test-tiny-framework/golden-output/02-hoststubgen-test-tiny-framework-host-stub-dump.txt b/tools/hoststubgen/hoststubgen/test-tiny-framework/golden-output/02-hoststubgen-test-tiny-framework-host-stub-dump.txt
index c605f76..906a81c 100644
--- a/tools/hoststubgen/hoststubgen/test-tiny-framework/golden-output/02-hoststubgen-test-tiny-framework-host-stub-dump.txt
+++ b/tools/hoststubgen/hoststubgen/test-tiny-framework/golden-output/02-hoststubgen-test-tiny-framework-host-stub-dump.txt
@@ -122,6 +122,100 @@
NestMembers:
com/android/hoststubgen/test/tinyframework/IPretendingAidl$Stub
com/android/hoststubgen/test/tinyframework/IPretendingAidl$Stub$Proxy
+## Class: com/android/hoststubgen/test/tinyframework/R$Nested.class
+ Compiled from "R.java"
+public class com.android.hoststubgen.test.tinyframework.R$Nested
+ minor version: 0
+ major version: 61
+ flags: (0x0021) ACC_PUBLIC, ACC_SUPER
+ this_class: #x // com/android/hoststubgen/test/tinyframework/R$Nested
+ super_class: #x // java/lang/Object
+ interfaces: 0, fields: 1, methods: 2, attributes: 4
+ public static int[] ARRAY;
+ descriptor: [I
+ flags: (0x0009) ACC_PUBLIC, ACC_STATIC
+ RuntimeVisibleAnnotations:
+ x: #x()
+ com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
+ x: #x()
+ com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
+
+ public com.android.hoststubgen.test.tinyframework.R$Nested();
+ descriptor: ()V
+ flags: (0x0001) ACC_PUBLIC
+ Code:
+ stack=3, locals=1, args_size=1
+ x: new #x // class java/lang/RuntimeException
+ x: dup
+ x: ldc #x // String Stub!
+ x: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
+ x: athrow
+ RuntimeVisibleAnnotations:
+ x: #x()
+ com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
+ x: #x()
+ com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
+
+ static {};
+ descriptor: ()V
+ flags: (0x0008) ACC_STATIC
+ Code:
+ stack=3, locals=0, args_size=0
+ x: new #x // class java/lang/RuntimeException
+ x: dup
+ x: ldc #x // String Stub!
+ x: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
+ x: athrow
+ RuntimeVisibleAnnotations:
+ x: #x()
+ com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
+ x: #x()
+ com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
+}
+InnerClasses:
+ public static #x= #x of #x; // Nested=class com/android/hoststubgen/test/tinyframework/R$Nested of class com/android/hoststubgen/test/tinyframework/R
+SourceFile: "R.java"
+RuntimeVisibleAnnotations:
+ x: #x()
+ com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
+ x: #x()
+ com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
+NestHost: class com/android/hoststubgen/test/tinyframework/R
+## Class: com/android/hoststubgen/test/tinyframework/R.class
+ Compiled from "R.java"
+public class com.android.hoststubgen.test.tinyframework.R
+ minor version: 0
+ major version: 61
+ flags: (0x0021) ACC_PUBLIC, ACC_SUPER
+ this_class: #x // com/android/hoststubgen/test/tinyframework/R
+ super_class: #x // java/lang/Object
+ interfaces: 0, fields: 0, methods: 1, attributes: 4
+ public com.android.hoststubgen.test.tinyframework.R();
+ descriptor: ()V
+ flags: (0x0001) ACC_PUBLIC
+ Code:
+ stack=3, locals=1, args_size=1
+ x: new #x // class java/lang/RuntimeException
+ x: dup
+ x: ldc #x // String Stub!
+ x: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
+ x: athrow
+ RuntimeVisibleAnnotations:
+ x: #x()
+ com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
+ x: #x()
+ com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
+}
+InnerClasses:
+ public static #x= #x of #x; // Nested=class com/android/hoststubgen/test/tinyframework/R$Nested of class com/android/hoststubgen/test/tinyframework/R
+SourceFile: "R.java"
+RuntimeVisibleAnnotations:
+ x: #x()
+ com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
+ x: #x()
+ com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
+NestMembers:
+ com/android/hoststubgen/test/tinyframework/R$Nested
## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkCallerCheck$Impl.class
Compiled from "TinyFrameworkCallerCheck.java"
class com.android.hoststubgen.test.tinyframework.TinyFrameworkCallerCheck$Impl
diff --git a/tools/hoststubgen/hoststubgen/test-tiny-framework/golden-output/03-hoststubgen-test-tiny-framework-host-impl-dump.txt b/tools/hoststubgen/hoststubgen/test-tiny-framework/golden-output/03-hoststubgen-test-tiny-framework-host-impl-dump.txt
index 11d5939..10bc91d 100644
--- a/tools/hoststubgen/hoststubgen/test-tiny-framework/golden-output/03-hoststubgen-test-tiny-framework-host-impl-dump.txt
+++ b/tools/hoststubgen/hoststubgen/test-tiny-framework/golden-output/03-hoststubgen-test-tiny-framework-host-impl-dump.txt
@@ -348,6 +348,108 @@
NestMembers:
com/android/hoststubgen/test/tinyframework/IPretendingAidl$Stub
com/android/hoststubgen/test/tinyframework/IPretendingAidl$Stub$Proxy
+## Class: com/android/hoststubgen/test/tinyframework/R$Nested.class
+ Compiled from "R.java"
+public class com.android.hoststubgen.test.tinyframework.R$Nested
+ minor version: 0
+ major version: 61
+ flags: (0x0021) ACC_PUBLIC, ACC_SUPER
+ this_class: #x // com/android/hoststubgen/test/tinyframework/R$Nested
+ super_class: #x // java/lang/Object
+ interfaces: 0, fields: 1, methods: 2, attributes: 4
+ public static int[] ARRAY;
+ descriptor: [I
+ flags: (0x0009) ACC_PUBLIC, ACC_STATIC
+ RuntimeVisibleAnnotations:
+ x: #x()
+ com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
+ x: #x()
+ com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
+
+ public com.android.hoststubgen.test.tinyframework.R$Nested();
+ descriptor: ()V
+ flags: (0x0001) ACC_PUBLIC
+ Code:
+ stack=1, locals=1, args_size=1
+ x: aload_0
+ x: invokespecial #x // Method java/lang/Object."<init>":()V
+ x: return
+ LineNumberTable:
+ LocalVariableTable:
+ Start Length Slot Name Signature
+ 0 5 0 this Lcom/android/hoststubgen/test/tinyframework/R$Nested;
+ RuntimeVisibleAnnotations:
+ x: #x()
+ com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
+ x: #x()
+ com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
+
+ static {};
+ descriptor: ()V
+ flags: (0x0008) ACC_STATIC
+ Code:
+ stack=4, locals=0, args_size=0
+ x: iconst_1
+ x: newarray int
+ x: dup
+ x: iconst_0
+ x: iconst_1
+ x: iastore
+ x: putstatic #x // Field ARRAY:[I
+ x: return
+ LineNumberTable:
+ RuntimeVisibleAnnotations:
+ x: #x()
+ com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
+ x: #x()
+ com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
+}
+InnerClasses:
+ public static #x= #x of #x; // Nested=class com/android/hoststubgen/test/tinyframework/R$Nested of class com/android/hoststubgen/test/tinyframework/R
+SourceFile: "R.java"
+RuntimeVisibleAnnotations:
+ x: #x()
+ com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
+ x: #x()
+ com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
+NestHost: class com/android/hoststubgen/test/tinyframework/R
+## Class: com/android/hoststubgen/test/tinyframework/R.class
+ Compiled from "R.java"
+public class com.android.hoststubgen.test.tinyframework.R
+ minor version: 0
+ major version: 61
+ flags: (0x0021) ACC_PUBLIC, ACC_SUPER
+ this_class: #x // com/android/hoststubgen/test/tinyframework/R
+ super_class: #x // java/lang/Object
+ interfaces: 0, fields: 0, methods: 1, attributes: 4
+ public com.android.hoststubgen.test.tinyframework.R();
+ descriptor: ()V
+ flags: (0x0001) ACC_PUBLIC
+ Code:
+ stack=1, locals=1, args_size=1
+ x: aload_0
+ x: invokespecial #x // Method java/lang/Object."<init>":()V
+ x: return
+ LineNumberTable:
+ LocalVariableTable:
+ Start Length Slot Name Signature
+ 0 5 0 this Lcom/android/hoststubgen/test/tinyframework/R;
+ RuntimeVisibleAnnotations:
+ x: #x()
+ com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
+ x: #x()
+ com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
+}
+InnerClasses:
+ public static #x= #x of #x; // Nested=class com/android/hoststubgen/test/tinyframework/R$Nested of class com/android/hoststubgen/test/tinyframework/R
+SourceFile: "R.java"
+RuntimeVisibleAnnotations:
+ x: #x()
+ com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
+ x: #x()
+ com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
+NestMembers:
+ com/android/hoststubgen/test/tinyframework/R$Nested
## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkCallerCheck$Impl.class
Compiled from "TinyFrameworkCallerCheck.java"
class com.android.hoststubgen.test.tinyframework.TinyFrameworkCallerCheck$Impl
diff --git a/tools/hoststubgen/hoststubgen/test-tiny-framework/golden-output/12-hoststubgen-test-tiny-framework-host-ext-stub-dump.txt b/tools/hoststubgen/hoststubgen/test-tiny-framework/golden-output/12-hoststubgen-test-tiny-framework-host-ext-stub-dump.txt
index c605f76..906a81c 100644
--- a/tools/hoststubgen/hoststubgen/test-tiny-framework/golden-output/12-hoststubgen-test-tiny-framework-host-ext-stub-dump.txt
+++ b/tools/hoststubgen/hoststubgen/test-tiny-framework/golden-output/12-hoststubgen-test-tiny-framework-host-ext-stub-dump.txt
@@ -122,6 +122,100 @@
NestMembers:
com/android/hoststubgen/test/tinyframework/IPretendingAidl$Stub
com/android/hoststubgen/test/tinyframework/IPretendingAidl$Stub$Proxy
+## Class: com/android/hoststubgen/test/tinyframework/R$Nested.class
+ Compiled from "R.java"
+public class com.android.hoststubgen.test.tinyframework.R$Nested
+ minor version: 0
+ major version: 61
+ flags: (0x0021) ACC_PUBLIC, ACC_SUPER
+ this_class: #x // com/android/hoststubgen/test/tinyframework/R$Nested
+ super_class: #x // java/lang/Object
+ interfaces: 0, fields: 1, methods: 2, attributes: 4
+ public static int[] ARRAY;
+ descriptor: [I
+ flags: (0x0009) ACC_PUBLIC, ACC_STATIC
+ RuntimeVisibleAnnotations:
+ x: #x()
+ com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
+ x: #x()
+ com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
+
+ public com.android.hoststubgen.test.tinyframework.R$Nested();
+ descriptor: ()V
+ flags: (0x0001) ACC_PUBLIC
+ Code:
+ stack=3, locals=1, args_size=1
+ x: new #x // class java/lang/RuntimeException
+ x: dup
+ x: ldc #x // String Stub!
+ x: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
+ x: athrow
+ RuntimeVisibleAnnotations:
+ x: #x()
+ com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
+ x: #x()
+ com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
+
+ static {};
+ descriptor: ()V
+ flags: (0x0008) ACC_STATIC
+ Code:
+ stack=3, locals=0, args_size=0
+ x: new #x // class java/lang/RuntimeException
+ x: dup
+ x: ldc #x // String Stub!
+ x: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
+ x: athrow
+ RuntimeVisibleAnnotations:
+ x: #x()
+ com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
+ x: #x()
+ com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
+}
+InnerClasses:
+ public static #x= #x of #x; // Nested=class com/android/hoststubgen/test/tinyframework/R$Nested of class com/android/hoststubgen/test/tinyframework/R
+SourceFile: "R.java"
+RuntimeVisibleAnnotations:
+ x: #x()
+ com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
+ x: #x()
+ com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
+NestHost: class com/android/hoststubgen/test/tinyframework/R
+## Class: com/android/hoststubgen/test/tinyframework/R.class
+ Compiled from "R.java"
+public class com.android.hoststubgen.test.tinyframework.R
+ minor version: 0
+ major version: 61
+ flags: (0x0021) ACC_PUBLIC, ACC_SUPER
+ this_class: #x // com/android/hoststubgen/test/tinyframework/R
+ super_class: #x // java/lang/Object
+ interfaces: 0, fields: 0, methods: 1, attributes: 4
+ public com.android.hoststubgen.test.tinyframework.R();
+ descriptor: ()V
+ flags: (0x0001) ACC_PUBLIC
+ Code:
+ stack=3, locals=1, args_size=1
+ x: new #x // class java/lang/RuntimeException
+ x: dup
+ x: ldc #x // String Stub!
+ x: invokespecial #x // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
+ x: athrow
+ RuntimeVisibleAnnotations:
+ x: #x()
+ com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
+ x: #x()
+ com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
+}
+InnerClasses:
+ public static #x= #x of #x; // Nested=class com/android/hoststubgen/test/tinyframework/R$Nested of class com/android/hoststubgen/test/tinyframework/R
+SourceFile: "R.java"
+RuntimeVisibleAnnotations:
+ x: #x()
+ com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
+ x: #x()
+ com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
+NestMembers:
+ com/android/hoststubgen/test/tinyframework/R$Nested
## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkCallerCheck$Impl.class
Compiled from "TinyFrameworkCallerCheck.java"
class com.android.hoststubgen.test.tinyframework.TinyFrameworkCallerCheck$Impl
diff --git a/tools/hoststubgen/hoststubgen/test-tiny-framework/golden-output/13-hoststubgen-test-tiny-framework-host-ext-impl-dump.txt b/tools/hoststubgen/hoststubgen/test-tiny-framework/golden-output/13-hoststubgen-test-tiny-framework-host-ext-impl-dump.txt
index 088bc80..fcf9a8c 100644
--- a/tools/hoststubgen/hoststubgen/test-tiny-framework/golden-output/13-hoststubgen-test-tiny-framework-host-ext-impl-dump.txt
+++ b/tools/hoststubgen/hoststubgen/test-tiny-framework/golden-output/13-hoststubgen-test-tiny-framework-host-ext-impl-dump.txt
@@ -481,6 +481,136 @@
NestMembers:
com/android/hoststubgen/test/tinyframework/IPretendingAidl$Stub
com/android/hoststubgen/test/tinyframework/IPretendingAidl$Stub$Proxy
+## Class: com/android/hoststubgen/test/tinyframework/R$Nested.class
+ Compiled from "R.java"
+public class com.android.hoststubgen.test.tinyframework.R$Nested
+ minor version: 0
+ major version: 61
+ flags: (0x0021) ACC_PUBLIC, ACC_SUPER
+ this_class: #x // com/android/hoststubgen/test/tinyframework/R$Nested
+ super_class: #x // java/lang/Object
+ interfaces: 0, fields: 1, methods: 2, attributes: 4
+ public static int[] ARRAY;
+ descriptor: [I
+ flags: (0x0009) ACC_PUBLIC, ACC_STATIC
+ RuntimeVisibleAnnotations:
+ x: #x()
+ com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
+ x: #x()
+ com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
+
+ public com.android.hoststubgen.test.tinyframework.R$Nested();
+ descriptor: ()V
+ flags: (0x0001) ACC_PUBLIC
+ Code:
+ stack=4, locals=1, args_size=1
+ x: ldc #x // class com/android/hoststubgen/test/tinyframework/R$Nested
+ x: ldc #x // String <init>
+ x: ldc #x // String ()V
+ x: ldc #x // String com.android.hoststubgen.hosthelper.HostTestUtils.logMethodCall
+ x: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.callMethodCallHook:(Ljava/lang/Class;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V
+ x: aload_0
+ x: invokespecial #x // Method java/lang/Object."<init>":()V
+ x: return
+ LineNumberTable:
+ LocalVariableTable:
+ Start Length Slot Name Signature
+ 11 5 0 this Lcom/android/hoststubgen/test/tinyframework/R$Nested;
+ RuntimeVisibleAnnotations:
+ x: #x()
+ com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
+ x: #x()
+ com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
+
+ static {};
+ descriptor: ()V
+ flags: (0x0008) ACC_STATIC
+ Code:
+ stack=4, locals=0, args_size=0
+ x: ldc #x // class com/android/hoststubgen/test/tinyframework/R$Nested
+ x: ldc #x // String <clinit>
+ x: ldc #x // String ()V
+ x: ldc #x // String com.android.hoststubgen.hosthelper.HostTestUtils.logMethodCall
+ x: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.callMethodCallHook:(Ljava/lang/Class;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V
+ x: ldc #x // class com/android/hoststubgen/test/tinyframework/R$Nested
+ x: ldc #x // String com.android.hoststubgen.hosthelper.HostTestUtils.logClassLoaded
+ x: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.onClassLoaded:(Ljava/lang/Class;Ljava/lang/String;)V
+ x: iconst_1
+ x: newarray int
+ x: dup
+ x: iconst_0
+ x: iconst_1
+ x: iastore
+ x: putstatic #x // Field ARRAY:[I
+ x: return
+ LineNumberTable:
+ RuntimeVisibleAnnotations:
+ x: #x()
+ com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
+ x: #x()
+ com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
+}
+InnerClasses:
+ public static #x= #x of #x; // Nested=class com/android/hoststubgen/test/tinyframework/R$Nested of class com/android/hoststubgen/test/tinyframework/R
+SourceFile: "R.java"
+RuntimeVisibleAnnotations:
+ x: #x()
+ com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
+ x: #x()
+ com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
+NestHost: class com/android/hoststubgen/test/tinyframework/R
+## Class: com/android/hoststubgen/test/tinyframework/R.class
+ Compiled from "R.java"
+public class com.android.hoststubgen.test.tinyframework.R
+ minor version: 0
+ major version: 61
+ flags: (0x0021) ACC_PUBLIC, ACC_SUPER
+ this_class: #x // com/android/hoststubgen/test/tinyframework/R
+ super_class: #x // java/lang/Object
+ interfaces: 0, fields: 0, methods: 2, attributes: 4
+ private static {};
+ descriptor: ()V
+ flags: (0x000a) ACC_PRIVATE, ACC_STATIC
+ Code:
+ stack=2, locals=0, args_size=0
+ x: ldc #x // class com/android/hoststubgen/test/tinyframework/R
+ x: ldc #x // String com.android.hoststubgen.hosthelper.HostTestUtils.logClassLoaded
+ x: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.onClassLoaded:(Ljava/lang/Class;Ljava/lang/String;)V
+ x: return
+
+ public com.android.hoststubgen.test.tinyframework.R();
+ descriptor: ()V
+ flags: (0x0001) ACC_PUBLIC
+ Code:
+ stack=4, locals=1, args_size=1
+ x: ldc #x // class com/android/hoststubgen/test/tinyframework/R
+ x: ldc #x // String <init>
+ x: ldc #x // String ()V
+ x: ldc #x // String com.android.hoststubgen.hosthelper.HostTestUtils.logMethodCall
+ x: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.callMethodCallHook:(Ljava/lang/Class;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V
+ x: aload_0
+ x: invokespecial #x // Method java/lang/Object."<init>":()V
+ x: return
+ LineNumberTable:
+ LocalVariableTable:
+ Start Length Slot Name Signature
+ 11 5 0 this Lcom/android/hoststubgen/test/tinyframework/R;
+ RuntimeVisibleAnnotations:
+ x: #x()
+ com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
+ x: #x()
+ com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
+}
+InnerClasses:
+ public static #x= #x of #x; // Nested=class com/android/hoststubgen/test/tinyframework/R$Nested of class com/android/hoststubgen/test/tinyframework/R
+SourceFile: "R.java"
+RuntimeVisibleAnnotations:
+ x: #x()
+ com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
+ x: #x()
+ com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
+NestMembers:
+ com/android/hoststubgen/test/tinyframework/R$Nested
## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkCallerCheck$Impl.class
Compiled from "TinyFrameworkCallerCheck.java"
class com.android.hoststubgen.test.tinyframework.TinyFrameworkCallerCheck$Impl
diff --git a/tools/hoststubgen/hoststubgen/test-tiny-framework/policy-override-tiny-framework.txt b/tools/hoststubgen/hoststubgen/test-tiny-framework/policy-override-tiny-framework.txt
index d302084..696b6d0 100644
--- a/tools/hoststubgen/hoststubgen/test-tiny-framework/policy-override-tiny-framework.txt
+++ b/tools/hoststubgen/hoststubgen/test-tiny-framework/policy-override-tiny-framework.txt
@@ -19,6 +19,9 @@
# Heuristics rule: Stub all the AIDL classes.
class :aidl stubclass
+# Heuristics rule: Stub all the R classes.
+class :r stubclass
+
# Default is "remove", so let's put all the base classes / interfaces in the stub first.
class com.android.hoststubgen.test.tinyframework.subclasstest.C1 stub
class com.android.hoststubgen.test.tinyframework.subclasstest.C2 stub
diff --git a/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/R.java b/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/R.java
new file mode 100644
index 0000000..b1bedf4
--- /dev/null
+++ b/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/R.java
@@ -0,0 +1,22 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.hoststubgen.test.tinyframework;
+
+public class R {
+ public static class Nested {
+ public static int[] ARRAY = new int[] {1};
+ }
+}
diff --git a/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-test/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkClassTest.java b/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-test/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkClassTest.java
index 762180d..37925e8 100644
--- a/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-test/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkClassTest.java
+++ b/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-test/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkClassTest.java
@@ -19,6 +19,7 @@
import static org.junit.Assert.fail;
+import com.android.hoststubgen.test.tinyframework.R.Nested;
import com.android.hoststubgen.test.tinyframework.TinyFrameworkNestedClasses.SubClass;
import org.junit.Rule;
@@ -328,4 +329,9 @@
assertThat(IPretendingAidl.Stub.addOne(1)).isEqualTo(2);
assertThat(IPretendingAidl.Stub.Proxy.addTwo(1)).isEqualTo(3);
}
+
+ @Test
+ public void testRFileHeuristics() {
+ assertThat(Nested.ARRAY.length).isEqualTo(1);
+ }
}