Merge "Force quarantined apps into the package stopped state." into main
diff --git a/AconfigFlags.bp b/AconfigFlags.bp
index b753aab..fd92979b 100644
--- a/AconfigFlags.bp
+++ b/AconfigFlags.bp
@@ -18,8 +18,11 @@
// Add java_aconfig_libraries to here to add them to the core framework
srcs: [
- ":com.android.hardware.camera2-aconfig-java{.generated_srcjars}",
+ ":android.os.flags-aconfig-java{.generated_srcjars}",
+ ":android.security.flags-aconfig-java{.generated_srcjars}",
+ ":camera_platform_flags_core_java_lib{.generated_srcjars}",
":com.android.window.flags.window-aconfig-java{.generated_srcjars}",
+ ":com.android.text.flags-aconfig-java{.generated_srcjars}",
],
}
@@ -32,15 +35,9 @@
}
// Camera
-aconfig_declarations {
- name: "com.android.hardware.camera2-aconfig",
- package: "com.android.hardware.camera2",
- srcs: ["core/java/android/hardware/camera2/camera_platform.aconfig"],
-}
-
java_aconfig_library {
- name: "com.android.hardware.camera2-aconfig-java",
- aconfig_declarations: "com.android.hardware.camera2-aconfig",
+ name: "camera_platform_flags_core_java_lib",
+ aconfig_declarations: "camera_platform_flags",
defaults: ["framework-minus-apex-aconfig-java-defaults"],
}
@@ -56,3 +53,42 @@
aconfig_declarations: "com.android.window.flags.window-aconfig",
defaults: ["framework-minus-apex-aconfig-java-defaults"],
}
+
+// Text
+aconfig_declarations {
+ name: "com.android.text.flags-aconfig",
+ package: "com.android.text.flags",
+ srcs: ["core/java/android/text/flags/*.aconfig"],
+}
+
+java_aconfig_library {
+ name: "com.android.text.flags-aconfig-java",
+ aconfig_declarations: "com.android.text.flags-aconfig",
+ defaults: ["framework-minus-apex-aconfig-java-defaults"],
+}
+
+// Security
+aconfig_declarations {
+ name: "android.security.flags-aconfig",
+ package: "android.security",
+ srcs: ["core/java/android/security/*.aconfig"],
+}
+
+java_aconfig_library {
+ name: "android.security.flags-aconfig-java",
+ aconfig_declarations: "android.security.flags-aconfig",
+ defaults: ["framework-minus-apex-aconfig-java-defaults"],
+}
+
+// OS
+aconfig_declarations {
+ name: "android.os.flags-aconfig",
+ package: "android.os",
+ srcs: ["core/java/android/os/*.aconfig"],
+}
+
+java_aconfig_library {
+ name: "android.os.flags-aconfig-java",
+ aconfig_declarations: "android.os.flags-aconfig",
+ defaults: ["framework-minus-apex-aconfig-java-defaults"],
+}
diff --git a/apex/jobscheduler/service/java/com/android/server/alarm/AlarmManagerService.java b/apex/jobscheduler/service/java/com/android/server/alarm/AlarmManagerService.java
index 2557c6a..3cbee5d0 100644
--- a/apex/jobscheduler/service/java/com/android/server/alarm/AlarmManagerService.java
+++ b/apex/jobscheduler/service/java/com/android/server/alarm/AlarmManagerService.java
@@ -741,7 +741,7 @@
private static final long DEFAULT_MIN_INTERVAL = 60 * 1000;
private static final long DEFAULT_MAX_INTERVAL = 365 * INTERVAL_DAY;
private static final long DEFAULT_MIN_WINDOW = 10 * 60 * 1000;
- private static final long DEFAULT_ALLOW_WHILE_IDLE_WHITELIST_DURATION = 10 * 1000;
+ private static final long DEFAULT_ALLOW_WHILE_IDLE_ALLOWLIST_DURATION = 10 * 1000;
private static final long DEFAULT_LISTENER_TIMEOUT = 5 * 1000;
private static final int DEFAULT_MAX_ALARMS_PER_UID = 500;
private static final long DEFAULT_APP_STANDBY_WINDOW = 60 * 60 * 1000; // 1 hr
@@ -795,7 +795,7 @@
// BroadcastOptions.setTemporaryAppWhitelistDuration() to use for FLAG_ALLOW_WHILE_IDLE.
public long ALLOW_WHILE_IDLE_WHITELIST_DURATION
- = DEFAULT_ALLOW_WHILE_IDLE_WHITELIST_DURATION;
+ = DEFAULT_ALLOW_WHILE_IDLE_ALLOWLIST_DURATION;
// Direct alarm listener callback timeout
public long LISTENER_TIMEOUT = DEFAULT_LISTENER_TIMEOUT;
@@ -996,7 +996,7 @@
case KEY_ALLOW_WHILE_IDLE_WHITELIST_DURATION:
ALLOW_WHILE_IDLE_WHITELIST_DURATION = properties.getLong(
KEY_ALLOW_WHILE_IDLE_WHITELIST_DURATION,
- DEFAULT_ALLOW_WHILE_IDLE_WHITELIST_DURATION);
+ DEFAULT_ALLOW_WHILE_IDLE_ALLOWLIST_DURATION);
updateAllowWhileIdleWhitelistDurationLocked();
break;
case KEY_LISTENER_TIMEOUT:
@@ -1608,7 +1608,7 @@
* Check all alarms in {@link #mPendingBackgroundAlarms} and send the ones that are not
* restricted.
*
- * This is only called when the power save whitelist changes, so it's okay to be slow.
+ * This is only called when the power save allowlist changes, so it's okay to be slow.
*/
@GuardedBy("mLock")
void sendAllUnrestrictedPendingBackgroundAlarmsLocked() {
@@ -2241,7 +2241,7 @@
}
}
- // Sanity check the recurrence interval. This will catch people who supply
+ // Validate the recurrence interval. This will catch people who supply
// seconds when the API expects milliseconds, or apps trying shenanigans
// around intentional period overflow, etc.
final long minInterval = mConstants.MIN_INTERVAL;
diff --git a/apex/jobscheduler/service/java/com/android/server/job/GrantedUriPermissions.java b/apex/jobscheduler/service/java/com/android/server/job/GrantedUriPermissions.java
index 7f191d4..60837cb 100644
--- a/apex/jobscheduler/service/java/com/android/server/job/GrantedUriPermissions.java
+++ b/apex/jobscheduler/service/java/com/android/server/job/GrantedUriPermissions.java
@@ -38,6 +38,7 @@
private final int mSourceUserId;
private final String mTag;
private final IBinder mPermissionOwner;
+ private final UriGrantsManagerInternal mUriGrantsManagerInternal;
private final ArrayList<Uri> mUris = new ArrayList<>();
private GrantedUriPermissions(int grantFlags, int uid, String tag)
@@ -45,13 +46,13 @@
mGrantFlags = grantFlags;
mSourceUserId = UserHandle.getUserId(uid);
mTag = tag;
- mPermissionOwner = LocalServices
- .getService(UriGrantsManagerInternal.class).newUriPermissionOwner("job: " + tag);
+ mUriGrantsManagerInternal = LocalServices.getService(UriGrantsManagerInternal.class);
+ mPermissionOwner = mUriGrantsManagerInternal.newUriPermissionOwner("job: " + tag);
}
public void revoke() {
for (int i = mUris.size()-1; i >= 0; i--) {
- LocalServices.getService(UriGrantsManagerInternal.class).revokeUriPermissionFromOwner(
+ mUriGrantsManagerInternal.revokeUriPermissionFromOwner(
mPermissionOwner, mUris.get(i), mGrantFlags, mSourceUserId);
}
mUris.clear();
diff --git a/apex/jobscheduler/service/java/com/android/server/job/JobConcurrencyManager.java b/apex/jobscheduler/service/java/com/android/server/job/JobConcurrencyManager.java
index dc608e7..5bf2eb9 100644
--- a/apex/jobscheduler/service/java/com/android/server/job/JobConcurrencyManager.java
+++ b/apex/jobscheduler/service/java/com/android/server/job/JobConcurrencyManager.java
@@ -235,7 +235,9 @@
private final Handler mHandler;
private final Injector mInjector;
+ private final ActivityManagerInternal mActivityManagerInternal;
private PowerManager mPowerManager;
+ private final UserManagerInternal mUserManagerInternal;
private boolean mCurrentInteractiveState;
private boolean mEffectiveInteractiveState;
@@ -507,6 +509,9 @@
mInjector = injector;
mNotificationCoordinator = new JobNotificationCoordinator();
+ mActivityManagerInternal = LocalServices.getService(ActivityManagerInternal.class);
+ mUserManagerInternal = LocalServices.getService(UserManagerInternal.class);
+
mHandler = AppSchedulingModuleThread.getHandler();
mGracePeriodObserver = new GracePeriodObserver(mContext);
@@ -1253,9 +1258,7 @@
return false;
case PRIVILEGED_STATE_UNDEFINED:
default:
- final ActivityManagerInternal activityManagerInternal =
- LocalServices.getService(ActivityManagerInternal.class);
- final int procState = activityManagerInternal.getUidProcessState(uid);
+ final int procState = mActivityManagerInternal.getUidProcessState(uid);
if (procState == ActivityManager.PROCESS_STATE_TOP) {
cachedPrivilegedState.put(uid, PRIVILEGED_STATE_TOP);
return true;
@@ -1266,7 +1269,7 @@
}
final BackgroundStartPrivileges bsp =
- activityManagerInternal.getBackgroundStartPrivileges(uid);
+ mActivityManagerInternal.getBackgroundStartPrivileges(uid);
if (DEBUG) {
Slog.d(TAG, "Job " + job.toShortString() + " bsp state: " + bsp);
}
@@ -2213,19 +2216,17 @@
boolean shouldRunAsFgUserJob(JobStatus job) {
if (!mShouldRestrictBgUser) return true;
int userId = job.getSourceUserId();
- UserManagerInternal um = LocalServices.getService(UserManagerInternal.class);
- UserInfo userInfo = um.getUserInfo(userId);
+ UserInfo userInfo = mUserManagerInternal.getUserInfo(userId);
// If the user has a parent user (e.g. a work profile of another user), the user should be
// treated equivalent as its parent user.
if (userInfo.profileGroupId != UserInfo.NO_PROFILE_GROUP_ID
&& userInfo.profileGroupId != userId) {
userId = userInfo.profileGroupId;
- userInfo = um.getUserInfo(userId);
+ userInfo = mUserManagerInternal.getUserInfo(userId);
}
- int currentUser = LocalServices.getService(ActivityManagerInternal.class)
- .getCurrentUserId();
+ int currentUser = mActivityManagerInternal.getCurrentUserId();
// A user is treated as foreground user if any of the followings is true:
// 1. The user is current user
// 2. The user is primary user
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 f99bcf1..e7ea7c2 100644
--- a/apex/jobscheduler/service/java/com/android/server/job/JobSchedulerService.java
+++ b/apex/jobscheduler/service/java/com/android/server/job/JobSchedulerService.java
@@ -251,6 +251,9 @@
}
};
+ @VisibleForTesting
+ public static UsageStatsManagerInternal sUsageStatsManagerInternal;
+
/** Global local for all job scheduler state. */
final Object mLock = new Object();
/** Master list of jobs. */
@@ -358,8 +361,8 @@
DeviceIdleInternal mLocalDeviceIdleController;
@VisibleForTesting
AppStateTrackerImpl mAppStateTracker;
- final UsageStatsManagerInternal mUsageStats;
private final AppStandbyInternal mAppStandbyInternal;
+ private final BatteryStatsInternal mBatteryStatsInternal;
/**
* Set to true once we are allowed to run third party apps.
@@ -2416,7 +2419,7 @@
// Set up the app standby bucketing tracker
mStandbyTracker = new StandbyTracker();
- mUsageStats = LocalServices.getService(UsageStatsManagerInternal.class);
+ sUsageStatsManagerInternal = LocalServices.getService(UsageStatsManagerInternal.class);
final Categorizer quotaCategorizer = (userId, packageName, tag) -> {
if (QUOTA_TRACKER_TIMEOUT_UIJ_TAG.equals(tag)) {
@@ -2467,6 +2470,8 @@
mAppStandbyInternal = LocalServices.getService(AppStandbyInternal.class);
mAppStandbyInternal.addListener(mStandbyTracker);
+ mBatteryStatsInternal = LocalServices.getService(BatteryStatsInternal.class);
+
// The job store needs to call back
publishLocalService(JobSchedulerInternal.class, new LocalService());
@@ -4127,7 +4132,7 @@
return;
}
- long sinceLast = mUsageStats.getTimeSinceLastJobRun(packageName, userId);
+ long sinceLast = sUsageStatsManagerInternal.getTimeSinceLastJobRun(packageName, userId);
if (sinceLast > 2 * DateUtils.DAY_IN_MILLIS) {
// Too long ago, not worth logging
sinceLast = 0L;
@@ -4137,8 +4142,6 @@
mJobs.forEachJobForSourceUid(uid, counter);
}
if (counter.numDeferred() > 0 || sinceLast > 0) {
- BatteryStatsInternal mBatteryStatsInternal = LocalServices.getService
- (BatteryStatsInternal.class);
mBatteryStatsInternal.noteJobsDeferred(uid, counter.numDeferred(), sinceLast);
FrameworkStatsLog.write_non_chained(
FrameworkStatsLog.DEFERRED_JOB_STATS_REPORTED, uid, null,
@@ -4183,10 +4186,8 @@
// Static to support external callers
public static int standbyBucketForPackage(String packageName, int userId, long elapsedNow) {
- UsageStatsManagerInternal usageStats = LocalServices.getService(
- UsageStatsManagerInternal.class);
- int bucket = usageStats != null
- ? usageStats.getAppStandbyBucket(packageName, userId, elapsedNow)
+ int bucket = sUsageStatsManagerInternal != null
+ ? sUsageStatsManagerInternal.getAppStandbyBucket(packageName, userId, elapsedNow)
: 0;
bucket = standbyBucketToBucketIndex(bucket);
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 e636f60..b737041 100644
--- a/apex/jobscheduler/service/java/com/android/server/job/JobServiceContext.java
+++ b/apex/jobscheduler/service/java/com/android/server/job/JobServiceContext.java
@@ -161,6 +161,7 @@
private final EconomyManagerInternal mEconomyManagerInternal;
private final JobPackageTracker mJobPackageTracker;
private final PowerManager mPowerManager;
+ private final UsageStatsManagerInternal mUsageStatsManagerInternal;
private PowerManager.WakeLock mWakeLock;
// Execution state.
@@ -321,6 +322,7 @@
mNotificationCoordinator = notificationCoordinator;
mCompletedListener = service;
mPowerManager = mContext.getSystemService(PowerManager.class);
+ mUsageStatsManagerInternal = LocalServices.getService(UsageStatsManagerInternal.class);
mAvailable = true;
mVerb = VERB_FINISHED;
mPreferredUid = NO_PREFERRED_UID;
@@ -536,9 +538,8 @@
// Whatever.
}
final int jobUserId = job.getSourceUserId();
- UsageStatsManagerInternal usageStats =
- LocalServices.getService(UsageStatsManagerInternal.class);
- usageStats.setLastJobRunTime(sourcePackage, jobUserId, mExecutionStartTimeElapsed);
+ mUsageStatsManagerInternal
+ .setLastJobRunTime(sourcePackage, jobUserId, mExecutionStartTimeElapsed);
mAvailable = false;
mStoppedReason = null;
mStoppedTime = 0;
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 f429966..e0c766f 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
@@ -228,6 +228,8 @@
/** The minimum possible update delay is 1 second. */
public static final long MIN_TRIGGER_MAX_DELAY = 1000;
+ private JobSchedulerInternal mJobSchedulerInternal;
+
final JobInfo job;
/**
* Uid of the package requesting this job. This can differ from the "source"
@@ -1152,8 +1154,10 @@
* exemptions.
*/
public int getEffectiveStandbyBucket() {
- final JobSchedulerInternal jsi = LocalServices.getService(JobSchedulerInternal.class);
- final boolean isBuggy = jsi.isAppConsideredBuggy(
+ if (mJobSchedulerInternal == null) {
+ mJobSchedulerInternal = LocalServices.getService(JobSchedulerInternal.class);
+ }
+ final boolean isBuggy = mJobSchedulerInternal.isAppConsideredBuggy(
getUserId(), getServiceComponent().getPackageName(),
getTimeoutBlameUserId(), getTimeoutBlamePackageName());
@@ -1262,12 +1266,15 @@
* @return true if the exemption status changed
*/
public boolean updateMediaBackupExemptionStatus() {
- final JobSchedulerInternal jsi = LocalServices.getService(JobSchedulerInternal.class);
+ if (mJobSchedulerInternal == null) {
+ mJobSchedulerInternal = LocalServices.getService(JobSchedulerInternal.class);
+ }
boolean hasMediaExemption = mHasExemptedMediaUrisOnly
&& !job.hasLateConstraint()
&& job.getRequiredNetwork() != null
&& getEffectivePriority() >= JobInfo.PRIORITY_DEFAULT
- && sourcePackageName.equals(jsi.getCloudMediaProviderPackage(sourceUserId));
+ && sourcePackageName.equals(
+ mJobSchedulerInternal.getCloudMediaProviderPackage(sourceUserId));
if (mHasMediaBackupExemption == hasMediaExemption) {
return false;
}
diff --git a/apex/jobscheduler/service/java/com/android/server/tare/InternalResourceService.java b/apex/jobscheduler/service/java/com/android/server/tare/InternalResourceService.java
index f5487dc7..b8397d2 100644
--- a/apex/jobscheduler/service/java/com/android/server/tare/InternalResourceService.java
+++ b/apex/jobscheduler/service/java/com/android/server/tare/InternalResourceService.java
@@ -151,6 +151,7 @@
private final BatteryManagerInternal mBatteryManagerInternal;
private final PackageManager mPackageManager;
private final PackageManagerInternal mPackageManagerInternal;
+ private final UserManagerInternal mUserManagerInternal;
private IAppOpsService mAppOpsService;
private IDeviceIdleController mDeviceIdleController;
@@ -357,6 +358,7 @@
mBatteryManagerInternal = LocalServices.getService(BatteryManagerInternal.class);
mPackageManager = context.getPackageManager();
mPackageManagerInternal = LocalServices.getService(PackageManagerInternal.class);
+ mUserManagerInternal = LocalServices.getService(UserManagerInternal.class);
mEconomyManagerStub = new EconomyManagerStub();
mAnalyst = new Analyst();
mScribe = new Scribe(this, mAnalyst);
@@ -589,7 +591,7 @@
}
void onExemptionListChanged() {
- final int[] userIds = LocalServices.getService(UserManagerInternal.class).getUserIds();
+ final int[] userIds = mUserManagerInternal.getUserIds();
synchronized (mLock) {
final ArraySet<String> removed = mExemptedApps;
final ArraySet<String> added = new ArraySet<>();
@@ -979,9 +981,7 @@
@GuardedBy("mLock")
private void loadInstalledPackageListLocked() {
mPkgCache.clear();
- final UserManagerInternal userManagerInternal =
- LocalServices.getService(UserManagerInternal.class);
- final int[] userIds = userManagerInternal.getUserIds();
+ final int[] userIds = mUserManagerInternal.getUserIds();
for (int userId : userIds) {
final List<PackageInfo> pkgs =
mPackageManager.getInstalledPackagesAsUser(PACKAGE_QUERY_FLAGS, userId);
@@ -1097,7 +1097,7 @@
timeSinceUsersAdded = mScribe.getRealtimeSinceUsersAddedLocked(nowElapsed);
}
- final int[] userIds = LocalServices.getService(UserManagerInternal.class).getUserIds();
+ final int[] userIds = mUserManagerInternal.getUserIds();
for (int userId : userIds) {
final long timeSinceUserAddedMs = timeSinceUsersAdded.get(userId, 0);
// Temporarily mark installers as VIPs so they aren't subject to credit
diff --git a/boot/preloaded-classes b/boot/preloaded-classes
index 2a9375c..19d6f04 100644
--- a/boot/preloaded-classes
+++ b/boot/preloaded-classes
@@ -12682,7 +12682,6 @@
com.android.internal.util.LatencyTracker$Action
com.android.internal.util.LatencyTracker$ActionProperties
com.android.internal.util.LatencyTracker$FrameworkStatsLogEvent
-com.android.internal.util.LatencyTracker$SLatencyTrackerHolder
com.android.internal.util.LatencyTracker$Session$$ExternalSyntheticLambda0
com.android.internal.util.LatencyTracker$Session
com.android.internal.util.LatencyTracker
diff --git a/cmds/app_process/app_main.cpp b/cmds/app_process/app_main.cpp
index 28db61f..a040b57 100644
--- a/cmds/app_process/app_main.cpp
+++ b/cmds/app_process/app_main.cpp
@@ -49,7 +49,7 @@
virtual void onVmCreated(JNIEnv* env)
{
- if (mClassName.isEmpty()) {
+ if (mClassName.empty()) {
return; // Zygote. Nothing to do here.
}
@@ -66,10 +66,10 @@
* executing boot class Java code and thereby deny ourselves access to
* non-boot classes.
*/
- char* slashClassName = toSlashClassName(mClassName.string());
+ char* slashClassName = toSlashClassName(mClassName.c_str());
mClass = env->FindClass(slashClassName);
if (mClass == NULL) {
- ALOGE("ERROR: could not find class '%s'\n", mClassName.string());
+ ALOGE("ERROR: could not find class '%s'\n", mClassName.c_str());
}
free(slashClassName);
@@ -98,7 +98,7 @@
virtual void onExit(int code)
{
- if (mClassName.isEmpty()) {
+ if (mClassName.empty()) {
// if zygote
IPCThreadState::self()->stopProcess();
hardware::IPCThreadState::self()->stopProcess();
@@ -179,7 +179,7 @@
argv_String.append(argv[i]);
argv_String.append("\" ");
}
- ALOGV("app_process main with argv: %s", argv_String.string());
+ ALOGV("app_process main with argv: %s", argv_String.c_str());
}
AppRuntime runtime(argv[0], computeArgBlockSize(argc, argv));
@@ -282,7 +282,7 @@
}
Vector<String8> args;
- if (!className.isEmpty()) {
+ if (!className.empty()) {
// We're not in zygote mode, the only argument we need to pass
// to RuntimeInit is the application argument.
//
@@ -300,7 +300,7 @@
restOfArgs.append(argv_new[k]);
restOfArgs.append("\" ");
}
- ALOGV("Class name = %s, args = %s", className.string(), restOfArgs.string());
+ ALOGV("Class name = %s, args = %s", className.c_str(), restOfArgs.c_str());
}
} else {
// We're in zygote mode.
@@ -328,13 +328,13 @@
}
}
- if (!niceName.isEmpty()) {
- runtime.setArgv0(niceName.string(), true /* setProcName */);
+ if (!niceName.empty()) {
+ runtime.setArgv0(niceName.c_str(), true /* setProcName */);
}
if (zygote) {
runtime.start("com.android.internal.os.ZygoteInit", args, zygote);
- } else if (!className.isEmpty()) {
+ } else if (!className.empty()) {
runtime.start("com.android.internal.os.RuntimeInit", args, zygote);
} else {
fprintf(stderr, "Error: no class name or --zygote supplied.\n");
diff --git a/cmds/bootanimation/BootAnimation.cpp b/cmds/bootanimation/BootAnimation.cpp
index b56dceb..69c161a 100644
--- a/cmds/bootanimation/BootAnimation.cpp
+++ b/cmds/bootanimation/BootAnimation.cpp
@@ -723,7 +723,7 @@
bool BootAnimation::preloadAnimation() {
ATRACE_CALL();
findBootAnimationFile();
- if (!mZipFileName.isEmpty()) {
+ if (!mZipFileName.empty()) {
mAnimation = loadAnimation(mZipFileName);
return (mAnimation != nullptr);
}
@@ -842,7 +842,7 @@
// We have no bootanimation file, so we use the stock android logo
// animation.
- if (mZipFileName.isEmpty()) {
+ if (mZipFileName.empty()) {
ALOGD("No animation file");
result = android();
} else {
@@ -1171,7 +1171,7 @@
if (!readFile(animation.zip, "desc.txt", desString)) {
return false;
}
- char const* s = desString.string();
+ char const* s = desString.c_str();
std::string dynamicColoringPartName = "";
bool postDynamicColoring = false;
@@ -1180,7 +1180,7 @@
const char* endl = strstr(s, "\n");
if (endl == nullptr) break;
String8 line(s, endl - s);
- const char* l = line.string();
+ const char* l = line.c_str();
int fps = 0;
int width = 0;
int height = 0;
@@ -1365,7 +1365,7 @@
// If there is trimData present, override the positioning defaults.
for (Animation::Part& part : animation.parts) {
- const char* trimDataStr = part.trimData.string();
+ const char* trimDataStr = part.trimData.c_str();
for (size_t frameIdx = 0; frameIdx < part.frames.size(); frameIdx++) {
const char* endl = strstr(trimDataStr, "\n");
// No more trimData for this part.
@@ -1373,7 +1373,7 @@
break;
}
String8 line(trimDataStr, endl - trimDataStr);
- const char* lineStr = line.string();
+ const char* lineStr = line.c_str();
trimDataStr = ++endl;
int width = 0, height = 0, x = 0, y = 0;
if (sscanf(lineStr, "%dx%d+%d+%d", &width, &height, &x, &y) == 4) {
@@ -1606,7 +1606,7 @@
1.0f);
ALOGD("Playing files = %s/%s, Requested repeat = %d, playUntilComplete = %s",
- animation.fileName.string(), part.path.string(), part.count,
+ animation.fileName.c_str(), part.path.c_str(), part.count,
part.playUntilComplete ? "true" : "false");
// For the last animation, if we have progress indicator from
@@ -1831,17 +1831,17 @@
ATRACE_CALL();
if (mLoadedFiles.indexOf(fn) >= 0) {
SLOGE("File \"%s\" is already loaded. Cyclic ref is not allowed",
- fn.string());
+ fn.c_str());
return nullptr;
}
ZipFileRO *zip = ZipFileRO::open(fn);
if (zip == nullptr) {
SLOGE("Failed to open animation zip \"%s\": %s",
- fn.string(), strerror(errno));
+ fn.c_str(), strerror(errno));
return nullptr;
}
- ALOGD("%s is loaded successfully", fn.string());
+ ALOGD("%s is loaded successfully", fn.c_str());
Animation *animation = new Animation;
animation->fileName = fn;
diff --git a/cmds/incident/main.cpp b/cmds/incident/main.cpp
index 6e0bd06..0d9f4e9 100644
--- a/cmds/incident/main.cpp
+++ b/cmds/incident/main.cpp
@@ -83,8 +83,8 @@
Status
StatusListener::onReportServiceStatus(const String16& service, int32_t status)
{
- fprintf(stderr, "service '%s' status %d\n", String8(service).string(), status);
- ALOGD("service '%s' status %d\n", String8(service).string(), status);
+ fprintf(stderr, "service '%s' status %d\n", String8(service).c_str(), status);
+ ALOGD("service '%s' status %d\n", String8(service).c_str(), status);
return Status::ok();
}
@@ -384,7 +384,7 @@
status = service->reportIncidentToStream(args, listener, std::move(writeEnd));
if (!status.isOk()) {
- fprintf(stderr, "reportIncident returned \"%s\"\n", status.toString8().string());
+ fprintf(stderr, "reportIncident returned \"%s\"\n", status.toString8().c_str());
return 1;
}
@@ -396,14 +396,14 @@
sp<StatusListener> listener(new StatusListener());
status = service->reportIncidentToDumpstate(std::move(writeEnd), listener);
if (!status.isOk()) {
- fprintf(stderr, "reportIncident returned \"%s\"\n", status.toString8().string());
+ fprintf(stderr, "reportIncident returned \"%s\"\n", status.toString8().c_str());
return 1;
}
return listener->getExitCodeOrElse(stream_output(fds[0], STDOUT_FILENO));
} else {
status = service->reportIncident(args);
if (!status.isOk()) {
- fprintf(stderr, "reportIncident returned \"%s\"\n", status.toString8().string());
+ fprintf(stderr, "reportIncident returned \"%s\"\n", status.toString8().c_str());
return 1;
} else {
return 0;
diff --git a/cmds/incident_helper/src/TextParserBase.cpp b/cmds/incident_helper/src/TextParserBase.cpp
index e9bc70f..e625afa 100644
--- a/cmds/incident_helper/src/TextParserBase.cpp
+++ b/cmds/incident_helper/src/TextParserBase.cpp
@@ -27,11 +27,11 @@
{
string content;
if (!ReadFdToString(in, &content)) {
- fprintf(stderr, "[%s]Failed to read data from incidentd\n", this->name.string());
+ fprintf(stderr, "[%s]Failed to read data from incidentd\n", this->name.c_str());
return -1;
}
if (!WriteStringToFd(content, out)) {
- fprintf(stderr, "[%s]Failed to write data to incidentd\n", this->name.string());
+ fprintf(stderr, "[%s]Failed to write data to incidentd\n", this->name.c_str());
return -1;
}
return NO_ERROR;
@@ -42,13 +42,13 @@
{
string content;
if (!ReadFdToString(in, &content)) {
- fprintf(stderr, "[%s]Failed to read data from incidentd\n", this->name.string());
+ fprintf(stderr, "[%s]Failed to read data from incidentd\n", this->name.c_str());
return -1;
}
// reverse the content
reverse(content.begin(), content.end());
if (!WriteStringToFd(content, out)) {
- fprintf(stderr, "[%s]Failed to write data to incidentd\n", this->name.string());
+ fprintf(stderr, "[%s]Failed to write data to incidentd\n", this->name.c_str());
return -1;
}
return NO_ERROR;
diff --git a/cmds/incident_helper/src/main.cpp b/cmds/incident_helper/src/main.cpp
index ff5fd86..cc03d4a 100644
--- a/cmds/incident_helper/src/main.cpp
+++ b/cmds/incident_helper/src/main.cpp
@@ -101,7 +101,7 @@
fprintf(stderr, "Pasring section %d...\n", sectionID);
TextParserBase* parser = selectParser(sectionID);
if (parser != nullptr) {
- fprintf(stderr, "Running parser: %s\n", parser->name.string());
+ fprintf(stderr, "Running parser: %s\n", parser->name.c_str());
status_t err = parser->Parse(STDIN_FILENO, STDOUT_FILENO);
if (err != NO_ERROR) {
fprintf(stderr, "Parse error in section %d: %s\n", sectionID, strerror(-err));
diff --git a/cmds/incident_helper/src/parsers/BatteryTypeParser.cpp b/cmds/incident_helper/src/parsers/BatteryTypeParser.cpp
index ced6cf8..2a032fb 100644
--- a/cmds/incident_helper/src/parsers/BatteryTypeParser.cpp
+++ b/cmds/incident_helper/src/parsers/BatteryTypeParser.cpp
@@ -52,9 +52,9 @@
}
if (!proto.flush(out)) {
- fprintf(stderr, "[%s]Error writing proto back\n", this->name.string());
+ fprintf(stderr, "[%s]Error writing proto back\n", this->name.c_str());
return -1;
}
- fprintf(stderr, "[%s]Proto size: %zu bytes\n", this->name.string(), proto.size());
+ fprintf(stderr, "[%s]Proto size: %zu bytes\n", this->name.c_str(), proto.size());
return NO_ERROR;
}
diff --git a/cmds/incident_helper/src/parsers/CpuFreqParser.cpp b/cmds/incident_helper/src/parsers/CpuFreqParser.cpp
index 43a12f6..c9bf4c5 100644
--- a/cmds/incident_helper/src/parsers/CpuFreqParser.cpp
+++ b/cmds/incident_helper/src/parsers/CpuFreqParser.cpp
@@ -82,9 +82,9 @@
}
if (!proto.flush(out)) {
- fprintf(stderr, "[%s]Error writing proto back\n", this->name.string());
+ fprintf(stderr, "[%s]Error writing proto back\n", this->name.c_str());
return -1;
}
- fprintf(stderr, "[%s]Proto size: %zu bytes\n", this->name.string(), proto.size());
+ fprintf(stderr, "[%s]Proto size: %zu bytes\n", this->name.c_str(), proto.size());
return NO_ERROR;
}
diff --git a/cmds/incident_helper/src/parsers/CpuInfoParser.cpp b/cmds/incident_helper/src/parsers/CpuInfoParser.cpp
index 5d525e6..77751a2f 100644
--- a/cmds/incident_helper/src/parsers/CpuInfoParser.cpp
+++ b/cmds/incident_helper/src/parsers/CpuInfoParser.cpp
@@ -130,11 +130,11 @@
record = parseRecordByColumns(line, columnIndices);
diff = record.size() - header.size();
if (diff < 0) {
- fprintf(stderr, "[%s]Line %d has %d missing fields\n%s\n", this->name.string(), nline, -diff, line.c_str());
+ fprintf(stderr, "[%s]Line %d has %d missing fields\n%s\n", this->name.c_str(), nline, -diff, line.c_str());
printRecord(record);
continue;
} else if (diff > 0) {
- fprintf(stderr, "[%s]Line %d has %d extra fields\n%s\n", this->name.string(), nline, diff, line.c_str());
+ fprintf(stderr, "[%s]Line %d has %d extra fields\n%s\n", this->name.c_str(), nline, diff, line.c_str());
printRecord(record);
continue;
}
@@ -143,7 +143,7 @@
for (int i=0; i<(int)record.size(); i++) {
if (!table.insertField(&proto, header[i], record[i])) {
fprintf(stderr, "[%s]Line %d fails to insert field %s with value %s\n",
- this->name.string(), nline, header[i].c_str(), record[i].c_str());
+ this->name.c_str(), nline, header[i].c_str(), record[i].c_str());
}
}
proto.end(token);
@@ -155,9 +155,9 @@
}
if (!proto.flush(out)) {
- fprintf(stderr, "[%s]Error writing proto back\n", this->name.string());
+ fprintf(stderr, "[%s]Error writing proto back\n", this->name.c_str());
return -1;
}
- fprintf(stderr, "[%s]Proto size: %zu bytes\n", this->name.string(), proto.size());
+ fprintf(stderr, "[%s]Proto size: %zu bytes\n", this->name.c_str(), proto.size());
return NO_ERROR;
}
diff --git a/cmds/incident_helper/src/parsers/EventLogTagsParser.cpp b/cmds/incident_helper/src/parsers/EventLogTagsParser.cpp
index 4fd6b06..0474a50 100644
--- a/cmds/incident_helper/src/parsers/EventLogTagsParser.cpp
+++ b/cmds/incident_helper/src/parsers/EventLogTagsParser.cpp
@@ -76,9 +76,9 @@
}
if (!proto.flush(out)) {
- fprintf(stderr, "[%s]Error writing proto back\n", this->name.string());
+ fprintf(stderr, "[%s]Error writing proto back\n", this->name.c_str());
return -1;
}
- fprintf(stderr, "[%s]Proto size: %zu bytes\n", this->name.string(), proto.size());
+ fprintf(stderr, "[%s]Proto size: %zu bytes\n", this->name.c_str(), proto.size());
return NO_ERROR;
}
diff --git a/cmds/incident_helper/src/parsers/KernelWakesParser.cpp b/cmds/incident_helper/src/parsers/KernelWakesParser.cpp
index 85beaf0..d16c23c 100644
--- a/cmds/incident_helper/src/parsers/KernelWakesParser.cpp
+++ b/cmds/incident_helper/src/parsers/KernelWakesParser.cpp
@@ -51,11 +51,11 @@
if (record.size() < header.size()) {
// TODO: log this to incident report!
- fprintf(stderr, "[%s]Line %d has missing fields\n%s\n", this->name.string(), nline, line.c_str());
+ fprintf(stderr, "[%s]Line %d has missing fields\n%s\n", this->name.c_str(), nline, line.c_str());
continue;
} else if (record.size() > header.size()) {
// TODO: log this to incident report!
- fprintf(stderr, "[%s]Line %d has extra fields\n%s\n", this->name.string(), nline, line.c_str());
+ fprintf(stderr, "[%s]Line %d has extra fields\n%s\n", this->name.c_str(), nline, line.c_str());
continue;
}
@@ -63,7 +63,7 @@
for (int i=0; i<(int)record.size(); i++) {
if (!table.insertField(&proto, header[i], record[i])) {
fprintf(stderr, "[%s]Line %d has bad value %s of %s\n",
- this->name.string(), nline, header[i].c_str(), record[i].c_str());
+ this->name.c_str(), nline, header[i].c_str(), record[i].c_str());
}
}
proto.end(token);
@@ -75,9 +75,9 @@
}
if (!proto.flush(out)) {
- fprintf(stderr, "[%s]Error writing proto back\n", this->name.string());
+ fprintf(stderr, "[%s]Error writing proto back\n", this->name.c_str());
return -1;
}
- fprintf(stderr, "[%s]Proto size: %zu bytes\n", this->name.string(), proto.size());
+ fprintf(stderr, "[%s]Proto size: %zu bytes\n", this->name.c_str(), proto.size());
return NO_ERROR;
}
diff --git a/cmds/incident_helper/src/parsers/PageTypeInfoParser.cpp b/cmds/incident_helper/src/parsers/PageTypeInfoParser.cpp
index 2a89c920..36710df 100644
--- a/cmds/incident_helper/src/parsers/PageTypeInfoParser.cpp
+++ b/cmds/incident_helper/src/parsers/PageTypeInfoParser.cpp
@@ -114,10 +114,10 @@
}
if (!proto.flush(out)) {
- fprintf(stderr, "[%s]Error writing proto back\n", this->name.string());
+ fprintf(stderr, "[%s]Error writing proto back\n", this->name.c_str());
return -1;
}
- fprintf(stderr, "[%s]Proto size: %zu bytes\n", this->name.string(), proto.size());
+ fprintf(stderr, "[%s]Proto size: %zu bytes\n", this->name.c_str(), proto.size());
return NO_ERROR;
}
diff --git a/cmds/incident_helper/src/parsers/ProcrankParser.cpp b/cmds/incident_helper/src/parsers/ProcrankParser.cpp
index 4763b48..997d2e5 100644
--- a/cmds/incident_helper/src/parsers/ProcrankParser.cpp
+++ b/cmds/incident_helper/src/parsers/ProcrankParser.cpp
@@ -60,7 +60,7 @@
if (record[record.size() - 1] == "TOTAL") { // TOTAL record
total = line;
} else {
- fprintf(stderr, "[%s]Line %d has missing fields\n%s\n", this->name.string(), nline,
+ fprintf(stderr, "[%s]Line %d has missing fields\n%s\n", this->name.c_str(), nline,
line.c_str());
}
continue;
@@ -70,7 +70,7 @@
for (int i=0; i<(int)record.size(); i++) {
if (!table.insertField(&proto, header[i], record[i])) {
fprintf(stderr, "[%s]Line %d has bad value %s of %s\n",
- this->name.string(), nline, header[i].c_str(), record[i].c_str());
+ this->name.c_str(), nline, header[i].c_str(), record[i].c_str());
}
}
proto.end(token);
@@ -104,9 +104,9 @@
}
if (!proto.flush(out)) {
- fprintf(stderr, "[%s]Error writing proto back\n", this->name.string());
+ fprintf(stderr, "[%s]Error writing proto back\n", this->name.c_str());
return -1;
}
- fprintf(stderr, "[%s]Proto size: %zu bytes\n", this->name.string(), proto.size());
+ fprintf(stderr, "[%s]Proto size: %zu bytes\n", this->name.c_str(), proto.size());
return NO_ERROR;
}
diff --git a/cmds/incident_helper/src/parsers/PsParser.cpp b/cmds/incident_helper/src/parsers/PsParser.cpp
index d3cb4be..55aa555 100644
--- a/cmds/incident_helper/src/parsers/PsParser.cpp
+++ b/cmds/incident_helper/src/parsers/PsParser.cpp
@@ -61,12 +61,12 @@
diff = record.size() - header.size();
if (diff < 0) {
// TODO: log this to incident report!
- fprintf(stderr, "[%s]Line %d has %d missing fields\n%s\n", this->name.string(), nline, -diff, line.c_str());
+ fprintf(stderr, "[%s]Line %d has %d missing fields\n%s\n", this->name.c_str(), nline, -diff, line.c_str());
printRecord(record);
continue;
} else if (diff > 0) {
// TODO: log this to incident report!
- fprintf(stderr, "[%s]Line %d has %d extra fields\n%s\n", this->name.string(), nline, diff, line.c_str());
+ fprintf(stderr, "[%s]Line %d has %d extra fields\n%s\n", this->name.c_str(), nline, diff, line.c_str());
printRecord(record);
continue;
}
@@ -75,7 +75,7 @@
for (int i=0; i<(int)record.size(); i++) {
if (!table.insertField(&proto, header[i], record[i])) {
fprintf(stderr, "[%s]Line %d has bad value %s of %s\n",
- this->name.string(), nline, header[i].c_str(), record[i].c_str());
+ this->name.c_str(), nline, header[i].c_str(), record[i].c_str());
}
}
proto.end(token);
@@ -87,9 +87,9 @@
}
if (!proto.flush(out)) {
- fprintf(stderr, "[%s]Error writing proto back\n", this->name.string());
+ fprintf(stderr, "[%s]Error writing proto back\n", this->name.c_str());
return -1;
}
- fprintf(stderr, "[%s]Proto size: %zu bytes\n", this->name.string(), proto.size());
+ fprintf(stderr, "[%s]Proto size: %zu bytes\n", this->name.c_str(), proto.size());
return NO_ERROR;
}
diff --git a/cmds/incident_helper/src/parsers/SystemPropertiesParser.cpp b/cmds/incident_helper/src/parsers/SystemPropertiesParser.cpp
index eba536b..86c34bc 100644
--- a/cmds/incident_helper/src/parsers/SystemPropertiesParser.cpp
+++ b/cmds/incident_helper/src/parsers/SystemPropertiesParser.cpp
@@ -219,9 +219,9 @@
}
if (!proto.flush(out)) {
- fprintf(stderr, "[%s]Error writing proto back\n", this->name.string());
+ fprintf(stderr, "[%s]Error writing proto back\n", this->name.c_str());
return -1;
}
- fprintf(stderr, "[%s]Proto size: %zu bytes\n", this->name.string(), proto.size());
+ fprintf(stderr, "[%s]Proto size: %zu bytes\n", this->name.c_str(), proto.size());
return NO_ERROR;
}
diff --git a/cmds/incidentd/src/IncidentService.cpp b/cmds/incidentd/src/IncidentService.cpp
index 4f9059f..e70748e8 100644
--- a/cmds/incidentd/src/IncidentService.cpp
+++ b/cmds/incidentd/src/IncidentService.cpp
@@ -407,8 +407,8 @@
Status IncidentService::getIncidentReportList(const String16& pkg16, const String16& cls16,
vector<String16>* result) {
status_t err;
- const string pkg(String8(pkg16).string());
- const string cls(String8(cls16).string());
+ const string pkg(String8(pkg16).c_str());
+ const string cls(String8(cls16).c_str());
// List the reports
vector<sp<ReportFile>> all;
@@ -441,9 +441,9 @@
const String16& id16, IncidentManager::IncidentReport* result) {
status_t err;
- const string pkg(String8(pkg16).string());
- const string cls(String8(cls16).string());
- const string id(String8(id16).string());
+ const string pkg(String8(pkg16).c_str());
+ const string cls(String8(cls16).c_str());
+ const string id(String8(id16).c_str());
IncidentReportArgs args;
sp<ReportFile> file = mWorkDirectory->getReport(pkg, cls, id, &args);
@@ -470,9 +470,9 @@
Status IncidentService::deleteIncidentReports(const String16& pkg16, const String16& cls16,
const String16& id16) {
- const string pkg(String8(pkg16).string());
- const string cls(String8(cls16).string());
- const string id(String8(id16).string());
+ const string pkg(String8(pkg16).c_str());
+ const string cls(String8(cls16).c_str());
+ const string id(String8(id16).c_str());
sp<ReportFile> file = mWorkDirectory->getReport(pkg, cls, id, nullptr);
if (file != nullptr) {
@@ -484,7 +484,7 @@
}
Status IncidentService::deleteAllIncidentReports(const String16& pkg16) {
- const string pkg(String8(pkg16).string());
+ const string pkg(String8(pkg16).c_str());
mWorkDirectory->commitAll(pkg);
mBroadcaster->clearPackageBroadcasts(pkg);
@@ -572,7 +572,7 @@
while (SECTION_LIST[idx] != NULL) {
const Section* section = SECTION_LIST[idx];
if (section->id == id) {
- fprintf(out, "Section[%d] %s\n", id, section->name.string());
+ fprintf(out, "Section[%d] %s\n", id, section->name.c_str());
break;
}
idx++;
@@ -596,7 +596,7 @@
static void printPrivacy(const Privacy* p, FILE* out, String8 indent) {
if (p == NULL) return;
- fprintf(out, "%sid:%d, type:%d, dest:%d\n", indent.string(), p->field_id, p->type, p->policy);
+ fprintf(out, "%sid:%d, type:%d, dest:%d\n", indent.c_str(), p->field_id, p->type, p->policy);
if (p->children == NULL) return;
for (int i = 0; p->children[i] != NULL; i++) { // NULL-terminated.
printPrivacy(p->children[i], out, indent + " ");
@@ -609,7 +609,7 @@
const int argCount = args.size();
if (argCount >= 3) {
String8 opt = args[1];
- int sectionId = atoi(args[2].string());
+ int sectionId = atoi(args[2].c_str());
const Privacy* p = get_privacy_of_section(sectionId);
if (p == NULL) {
diff --git a/cmds/incidentd/src/Reporter.cpp b/cmds/incidentd/src/Reporter.cpp
index 86a78f09..c9cf727 100644
--- a/cmds/incidentd/src/Reporter.cpp
+++ b/cmds/incidentd/src/Reporter.cpp
@@ -711,7 +711,7 @@
return NO_ERROR;
}
- ALOGD("Start incident report section %d '%s'", sectionId, section->name.string());
+ ALOGD("Start incident report section %d '%s'", sectionId, section->name.c_str());
IncidentMetadata::SectionStats* sectionMetadata = metadata->add_sections();
// Notify listener of starting
@@ -747,7 +747,7 @@
sectionId, IIncidentReportStatusListener::STATUS_FINISHED);
});
- ALOGD("Finish incident report section %d '%s'", sectionId, section->name.string());
+ ALOGD("Finish incident report section %d '%s'", sectionId, section->name.c_str());
return NO_ERROR;
}
diff --git a/cmds/incidentd/src/Section.cpp b/cmds/incidentd/src/Section.cpp
index 581367a..c2aa269 100644
--- a/cmds/incidentd/src/Section.cpp
+++ b/cmds/incidentd/src/Section.cpp
@@ -60,7 +60,7 @@
const char* GZIP[] = {"/system/bin/gzip", NULL};
static pid_t fork_execute_incident_helper(const int id, Fpipe* p2cPipe, Fpipe* c2pPipe) {
- const char* ihArgs[]{INCIDENT_HELPER, "-s", String8::format("%d", id).string(), NULL};
+ const char* ihArgs[]{INCIDENT_HELPER, "-s", String8::format("%d", id).c_str(), NULL};
return fork_execute_cmd(const_cast<char**>(ihArgs), p2cPipe, c2pPipe);
}
@@ -100,7 +100,7 @@
// add O_CLOEXEC to make sure it is closed when exec incident helper
unique_fd fd(open(mFilename, O_RDONLY | O_CLOEXEC));
if (fd.get() == -1) {
- ALOGW("[%s] failed to open file", this->name.string());
+ ALOGW("[%s] failed to open file", this->name.c_str());
// There may be some devices/architectures that won't have the file.
// Just return here without an error.
return NO_ERROR;
@@ -110,13 +110,13 @@
Fpipe c2pPipe;
// initiate pipes to pass data to/from incident_helper
if (!p2cPipe.init() || !c2pPipe.init()) {
- ALOGW("[%s] failed to setup pipes", this->name.string());
+ ALOGW("[%s] failed to setup pipes", this->name.c_str());
return -errno;
}
pid_t pid = fork_execute_incident_helper(this->id, &p2cPipe, &c2pPipe);
if (pid == -1) {
- ALOGW("[%s] failed to fork", this->name.string());
+ ALOGW("[%s] failed to fork", this->name.c_str());
return -errno;
}
@@ -128,14 +128,14 @@
writer->setSectionStats(buffer);
if (readStatus != NO_ERROR || buffer.timedOut()) {
ALOGW("[%s] failed to read data from incident helper: %s, timedout: %s",
- this->name.string(), strerror(-readStatus), buffer.timedOut() ? "true" : "false");
+ this->name.c_str(), strerror(-readStatus), buffer.timedOut() ? "true" : "false");
kill_child(pid);
return readStatus;
}
status_t ihStatus = wait_child(pid);
if (ihStatus != NO_ERROR) {
- ALOGW("[%s] abnormal child process: %s", this->name.string(), strerror(-ihStatus));
+ ALOGW("[%s] abnormal child process: %s", this->name.c_str(), strerror(-ihStatus));
return OK; // Not a fatal error.
}
@@ -169,7 +169,7 @@
index++; // look at the next file.
}
if (fd.get() == -1) {
- ALOGW("[%s] can't open all the files", this->name.string());
+ ALOGW("[%s] can't open all the files", this->name.c_str());
return NO_ERROR; // e.g. LAST_KMSG will reach here in user build.
}
FdBuffer buffer;
@@ -177,13 +177,13 @@
Fpipe c2pPipe;
// initiate pipes to pass data to/from gzip
if (!p2cPipe.init() || !c2pPipe.init()) {
- ALOGW("[%s] failed to setup pipes", this->name.string());
+ ALOGW("[%s] failed to setup pipes", this->name.c_str());
return -errno;
}
pid_t pid = fork_execute_cmd((char* const*)GZIP, &p2cPipe, &c2pPipe);
if (pid == -1) {
- ALOGW("[%s] failed to fork", this->name.string());
+ ALOGW("[%s] failed to fork", this->name.c_str());
return -errno;
}
// parent process
@@ -202,14 +202,14 @@
size_t editPos = internalBuffer->wp()->pos();
internalBuffer->wp()->move(8); // reserve 8 bytes for the varint of the data size.
size_t dataBeginAt = internalBuffer->wp()->pos();
- VLOG("[%s] editPos=%zu, dataBeginAt=%zu", this->name.string(), editPos, dataBeginAt);
+ VLOG("[%s] editPos=%zu, dataBeginAt=%zu", this->name.c_str(), editPos, dataBeginAt);
status_t readStatus = buffer.readProcessedDataInStream(
fd.get(), std::move(p2cPipe.writeFd()), std::move(c2pPipe.readFd()), this->timeoutMs,
isSysfs(mFilenames[index]));
writer->setSectionStats(buffer);
if (readStatus != NO_ERROR || buffer.timedOut()) {
- ALOGW("[%s] failed to read data from gzip: %s, timedout: %s", this->name.string(),
+ ALOGW("[%s] failed to read data from gzip: %s, timedout: %s", this->name.c_str(),
strerror(-readStatus), buffer.timedOut() ? "true" : "false");
kill_child(pid);
return readStatus;
@@ -217,7 +217,7 @@
status_t gzipStatus = wait_child(pid);
if (gzipStatus != NO_ERROR) {
- ALOGW("[%s] abnormal child process: %s", this->name.string(), strerror(-gzipStatus));
+ ALOGW("[%s] abnormal child process: %s", this->name.c_str(), strerror(-gzipStatus));
return gzipStatus;
}
// Revisit the actual size from gzip result and edit the internal buffer accordingly.
@@ -290,7 +290,7 @@
FdBuffer buffer;
err = buffer.read(data->pipe.readFd().get(), this->timeoutMs);
if (err != NO_ERROR) {
- ALOGE("[%s] reader failed with error '%s'", this->name.string(), strerror(-err));
+ ALOGE("[%s] reader failed with error '%s'", this->name.c_str(), strerror(-err));
}
// If the worker side is finished, then return its error (which may overwrite
@@ -300,7 +300,7 @@
data->pipe.close();
if (data->workerError != NO_ERROR) {
err = data->workerError;
- ALOGE("[%s] worker failed with error '%s'", this->name.string(), strerror(-err));
+ ALOGE("[%s] worker failed with error '%s'", this->name.c_str(), strerror(-err));
}
workerDone = data->workerDone;
}
@@ -309,17 +309,17 @@
if (err != NO_ERROR) {
char errMsg[128];
snprintf(errMsg, 128, "[%s] failed with error '%s'",
- this->name.string(), strerror(-err));
+ this->name.c_str(), strerror(-err));
writer->error(this, err, "WorkerThreadSection failed.");
return NO_ERROR;
}
if (buffer.truncated()) {
- ALOGW("[%s] too large, truncating", this->name.string());
+ ALOGW("[%s] too large, truncating", this->name.c_str());
// Do not write a truncated section. It won't pass through the PrivacyFilter.
return NO_ERROR;
}
if (!workerDone || buffer.timedOut()) {
- ALOGW("[%s] timed out", this->name.string());
+ ALOGW("[%s] timed out", this->name.c_str());
return NO_ERROR;
}
@@ -360,18 +360,18 @@
Fpipe ihPipe;
if (!cmdPipe.init() || !ihPipe.init()) {
- ALOGW("[%s] failed to setup pipes", this->name.string());
+ ALOGW("[%s] failed to setup pipes", this->name.c_str());
return -errno;
}
pid_t cmdPid = fork_execute_cmd((char* const*)mCommand, NULL, &cmdPipe);
if (cmdPid == -1) {
- ALOGW("[%s] failed to fork", this->name.string());
+ ALOGW("[%s] failed to fork", this->name.c_str());
return -errno;
}
pid_t ihPid = fork_execute_incident_helper(this->id, &cmdPipe, &ihPipe);
if (ihPid == -1) {
- ALOGW("[%s] failed to fork", this->name.string());
+ ALOGW("[%s] failed to fork", this->name.c_str());
return -errno;
}
@@ -381,7 +381,7 @@
writer->setSectionStats(buffer);
if (readStatus != NO_ERROR || buffer.timedOut()) {
ALOGW("[%s] failed to read data from incident helper: %s, timedout: %s",
- this->name.string(), strerror(-readStatus), buffer.timedOut() ? "true" : "false");
+ this->name.c_str(), strerror(-readStatus), buffer.timedOut() ? "true" : "false");
kill_child(cmdPid);
kill_child(ihPid);
return readStatus;
@@ -393,7 +393,7 @@
status_t ihStatus = wait_child(ihPid);
if (cmdStatus != NO_ERROR || ihStatus != NO_ERROR) {
ALOGW("[%s] abnormal child processes, return status: command: %s, incident helper: %s",
- this->name.string(), strerror(-cmdStatus), strerror(-ihStatus));
+ this->name.c_str(), strerror(-cmdStatus), strerror(-ihStatus));
// Not a fatal error.
return NO_ERROR;
}
@@ -428,7 +428,7 @@
sp<IBinder> service = defaultServiceManager()->checkService(mService);
if (service == NULL) {
- ALOGW("DumpsysSection: Can't lookup service: %s", String8(mService).string());
+ ALOGW("DumpsysSection: Can't lookup service: %s", String8(mService).c_str());
return NAME_NOT_FOUND;
}
@@ -463,14 +463,14 @@
// checkService won't wait for the service to show up like getService will.
sp<IBinder> service = defaultServiceManager()->checkService(mService);
if (service == NULL) {
- ALOGW("TextDumpsysSection: Can't lookup service: %s", String8(mService).string());
+ ALOGW("TextDumpsysSection: Can't lookup service: %s", String8(mService).c_str());
return NAME_NOT_FOUND;
}
// Create pipe
Fpipe dumpPipe;
if (!dumpPipe.init()) {
- ALOGW("[%s] failed to setup pipe", this->name.string());
+ ALOGW("[%s] failed to setup pipe", this->name.c_str());
return -errno;
}
@@ -482,7 +482,7 @@
signal(SIGPIPE, sigpipe_handler);
status_t err = service->dump(write_fd.get(), this->mArgs);
if (err != OK) {
- ALOGW("[%s] dump thread failed. Error: %s", this->name.string(), strerror(-err));
+ ALOGW("[%s] dump thread failed. Error: %s", this->name.c_str(), strerror(-err));
}
write_fd.reset();
});
@@ -490,7 +490,7 @@
// Collect dump content
FdBuffer buffer;
ProtoOutputStream proto;
- proto.write(TextDumpProto::COMMAND, std::string(name.string()));
+ proto.write(TextDumpProto::COMMAND, std::string(name.c_str()));
proto.write(TextDumpProto::DUMP_DURATION_NS, int64_t(Nanotime() - start));
buffer.write(proto.data());
@@ -504,7 +504,7 @@
dumpPipe.readFd().reset();
writer->setSectionStats(buffer);
if (readStatus != OK || buffer.timedOut()) {
- ALOGW("[%s] failed to read from dumpsys: %s, timedout: %s", this->name.string(),
+ ALOGW("[%s] failed to read from dumpsys: %s, timedout: %s", this->name.c_str(),
strerror(-readStatus), buffer.timedOut() ? "true" : "false");
worker.detach();
return readStatus;
@@ -579,7 +579,7 @@
// Hence forking a new process to prevent memory fragmentation.
pid_t pid = fork();
if (pid < 0) {
- ALOGW("[%s] failed to fork", this->name.string());
+ ALOGW("[%s] failed to fork", this->name.c_str());
return errno;
}
if (pid > 0) {
@@ -593,7 +593,7 @@
android_logger_list_free);
if (android_logger_open(loggers.get(), mLogID) == NULL) {
- ALOGE("[%s] Can't get logger.", this->name.string());
+ ALOGE("[%s] Can't get logger.", this->name.c_str());
_exit(EXIT_FAILURE);
}
@@ -610,7 +610,7 @@
// status = -EAGAIN, graceful indication for ANDRODI_LOG_NONBLOCK that this is the end.
if (status <= 0) {
if (status != -EAGAIN) {
- ALOGW("[%s] fails to read a log_msg.\n", this->name.string());
+ ALOGW("[%s] fails to read a log_msg.\n", this->name.c_str());
err = -status;
}
break;
@@ -680,7 +680,7 @@
AndroidLogEntry entry;
status = android_log_processLogBuffer(&msg.entry, &entry);
if (status != OK) {
- ALOGW("[%s] fails to process to an entry.\n", this->name.string());
+ ALOGW("[%s] fails to process to an entry.\n", this->name.c_str());
err = status;
break;
}
@@ -702,7 +702,7 @@
}
if (!proto.flush(pipeWriteFd.get())) {
if (errno == EPIPE) {
- ALOGW("[%s] wrote to a broken pipe\n", this->name.string());
+ ALOGW("[%s] wrote to a broken pipe\n", this->name.c_str());
}
err = errno;
break;
@@ -757,7 +757,7 @@
}
ssize_t exe_name_len = readlink(link_name, exe_name, EXE_NAME_LEN);
if (exe_name_len < 0 || exe_name_len >= EXE_NAME_LEN) {
- ALOGE("[%s] Can't read '%s': %s", name.string(), link_name, strerror(errno));
+ ALOGE("[%s] Can't read '%s': %s", name.c_str(), link_name, strerror(errno));
continue;
}
// readlink(2) does not put a null terminator at the end
@@ -788,7 +788,7 @@
Fpipe dumpPipe;
if (!dumpPipe.init()) {
- ALOGW("[%s] failed to setup dump pipe", this->name.string());
+ ALOGW("[%s] failed to setup dump pipe", this->name.c_str());
err = -errno;
break;
}
@@ -822,12 +822,12 @@
// Wait on the child to avoid it becoming a zombie process.
status_t cStatus = wait_child(child);
if (err != NO_ERROR) {
- ALOGW("[%s] failed to read stack dump: %d", this->name.string(), err);
+ ALOGW("[%s] failed to read stack dump: %d", this->name.c_str(), err);
dumpPipe.readFd().reset();
break;
}
if (cStatus != NO_ERROR) {
- ALOGE("[%s] child had an issue: %s\n", this->name.string(), strerror(-cStatus));
+ ALOGE("[%s] child had an issue: %s\n", this->name.c_str(), strerror(-cStatus));
}
// Resize dump buffer
@@ -852,7 +852,7 @@
dumpPipe.readFd().reset();
if (!proto.flush(pipeWriteFd.get())) {
if (errno == EPIPE) {
- ALOGE("[%s] wrote to a broken pipe\n", this->name.string());
+ ALOGE("[%s] wrote to a broken pipe\n", this->name.c_str());
}
err = errno;
break;
diff --git a/cmds/incidentd/src/report_directory.cpp b/cmds/incidentd/src/report_directory.cpp
index 7d20a74..6b2fb8e 100644
--- a/cmds/incidentd/src/report_directory.cpp
+++ b/cmds/incidentd/src/report_directory.cpp
@@ -62,8 +62,8 @@
continue;
}
String8 filename = dirbase + entry->d_name;
- if (stat(filename.string(), &st) != 0) {
- ALOGE("Unable to stat file %s", filename.string());
+ if (stat(filename.c_str(), &st) != 0) {
+ ALOGE("Unable to stat file %s", filename.c_str());
continue;
}
if (!S_ISREG(st.st_mode)) {
@@ -88,7 +88,7 @@
// Remove files until we're under our limits.
for (std::vector<std::pair<String8, struct stat>>::iterator it = files.begin();
it != files.end() && totalSize >= maxSize && totalCount >= maxCount; it++) {
- remove(it->first.string());
+ remove(it->first.c_str());
totalSize -= it->second.st_size;
totalCount--;
}
diff --git a/config/preloaded-classes b/config/preloaded-classes
index 214b12c..0351a00 100644
--- a/config/preloaded-classes
+++ b/config/preloaded-classes
@@ -12713,7 +12713,6 @@
com.android.internal.util.LatencyTracker$Action
com.android.internal.util.LatencyTracker$ActionProperties
com.android.internal.util.LatencyTracker$FrameworkStatsLogEvent
-com.android.internal.util.LatencyTracker$SLatencyTrackerHolder
com.android.internal.util.LatencyTracker$Session$$ExternalSyntheticLambda0
com.android.internal.util.LatencyTracker$Session
com.android.internal.util.LatencyTracker
diff --git a/core/api/current.txt b/core/api/current.txt
index bcb21e5..e003624 100644
--- a/core/api/current.txt
+++ b/core/api/current.txt
@@ -10560,6 +10560,7 @@
public final class ContextParams {
method @Nullable public String getAttributionTag();
method @Nullable public android.content.AttributionSource getNextAttributionSource();
+ method @NonNull public boolean shouldRegisterAttributionSource();
}
public static final class ContextParams.Builder {
@@ -10568,6 +10569,7 @@
method @NonNull public android.content.ContextParams build();
method @NonNull public android.content.ContextParams.Builder setAttributionTag(@Nullable String);
method @NonNull public android.content.ContextParams.Builder setNextAttributionSource(@Nullable android.content.AttributionSource);
+ method @NonNull public android.content.ContextParams.Builder setShouldRegisterAttributionSource(boolean);
}
public class ContextWrapper extends android.content.Context {
@@ -39349,8 +39351,10 @@
}
public final class FileIntegrityManager {
+ method @FlaggedApi(Flags.FLAG_FSVERITY_API) @Nullable public byte[] getFsverityDigest(@NonNull java.io.File) throws java.io.IOException;
method public boolean isApkVeritySupported();
method @RequiresPermission(anyOf={android.Manifest.permission.INSTALL_PACKAGES, android.Manifest.permission.REQUEST_INSTALL_PACKAGES}) public boolean isAppSourceCertificateTrusted(@NonNull java.security.cert.X509Certificate) throws java.security.cert.CertificateEncodingException;
+ method @FlaggedApi(Flags.FLAG_FSVERITY_API) public void setupFsverity(@NonNull java.io.File) throws java.io.IOException;
}
public final class KeyChain {
diff --git a/core/api/module-lib-current.txt b/core/api/module-lib-current.txt
index b1feb41..7f1e289 100644
--- a/core/api/module-lib-current.txt
+++ b/core/api/module-lib-current.txt
@@ -6,7 +6,9 @@
field public static final String CONTROL_AUTOMOTIVE_GNSS = "android.permission.CONTROL_AUTOMOTIVE_GNSS";
field public static final String GET_INTENT_SENDER_INTENT = "android.permission.GET_INTENT_SENDER_INTENT";
field public static final String MAKE_UID_VISIBLE = "android.permission.MAKE_UID_VISIBLE";
+ field public static final String MANAGE_REMOTE_AUTH = "android.permission.MANAGE_REMOTE_AUTH";
field public static final String USE_COMPANION_TRANSPORTS = "android.permission.USE_COMPANION_TRANSPORTS";
+ field public static final String USE_REMOTE_AUTH = "android.permission.USE_REMOTE_AUTH";
}
}
diff --git a/core/api/system-current.txt b/core/api/system-current.txt
index 26b0263..adbd06c 100644
--- a/core/api/system-current.txt
+++ b/core/api/system-current.txt
@@ -309,6 +309,7 @@
field public static final String REMOVE_DRM_CERTIFICATES = "android.permission.REMOVE_DRM_CERTIFICATES";
field public static final String REMOVE_TASKS = "android.permission.REMOVE_TASKS";
field public static final String RENOUNCE_PERMISSIONS = "android.permission.RENOUNCE_PERMISSIONS";
+ field public static final String REPORT_USAGE_STATS = "android.permission.REPORT_USAGE_STATS";
field @Deprecated public static final String REQUEST_NETWORK_SCORES = "android.permission.REQUEST_NETWORK_SCORES";
field public static final String REQUEST_NOTIFICATION_ASSISTANT_SERVICE = "android.permission.REQUEST_NOTIFICATION_ASSISTANT_SERVICE";
field public static final String RESET_PASSWORD = "android.permission.RESET_PASSWORD";
@@ -3792,6 +3793,10 @@
field @RequiresPermission(android.Manifest.permission.ACCESS_SHORTCUTS) public static final int FLAG_GET_PERSONS_DATA = 2048; // 0x800
}
+ public class PackageArchiver {
+ method @RequiresPermission(anyOf={android.Manifest.permission.DELETE_PACKAGES, android.Manifest.permission.REQUEST_DELETE_PACKAGES}) public void requestArchive(@NonNull String, @NonNull android.content.IntentSender) throws android.content.pm.PackageManager.NameNotFoundException;
+ }
+
public class PackageInstaller {
method @NonNull public android.content.pm.PackageInstaller.InstallInfo readInstallInfo(@NonNull java.io.File, int) throws android.content.pm.PackageInstaller.PackageParsingException;
method @NonNull public android.content.pm.PackageInstaller.InstallInfo readInstallInfo(@NonNull android.os.ParcelFileDescriptor, @Nullable String, int) throws android.content.pm.PackageInstaller.PackageParsingException;
@@ -3889,6 +3894,7 @@
method @NonNull @RequiresPermission(android.Manifest.permission.ACCESS_INSTANT_APPS) public abstract java.util.List<android.content.pm.InstantAppInfo> getInstantApps();
method @Deprecated @NonNull public abstract java.util.List<android.content.pm.IntentFilterVerificationInfo> getIntentFilterVerifications(@NonNull String);
method @Deprecated @RequiresPermission(android.Manifest.permission.INTERACT_ACROSS_USERS_FULL) public abstract int getIntentVerificationStatusAsUser(@NonNull String, int);
+ method @NonNull public android.content.pm.PackageArchiver getPackageArchiver();
method @RequiresPermission(android.Manifest.permission.INTERACT_ACROSS_USERS_FULL) public int getPackageUidAsUser(@NonNull String, @NonNull android.content.pm.PackageManager.PackageInfoFlags, int) throws android.content.pm.PackageManager.NameNotFoundException;
method @android.content.pm.PackageManager.PermissionFlags @RequiresPermission(anyOf={android.Manifest.permission.GRANT_RUNTIME_PERMISSIONS, android.Manifest.permission.REVOKE_RUNTIME_PERMISSIONS, android.Manifest.permission.GET_RUNTIME_PERMISSIONS}) public abstract int getPermissionFlags(@NonNull String, @NonNull String, @NonNull android.os.UserHandle);
method @NonNull @RequiresPermission(android.Manifest.permission.SUSPEND_APPS) public String[] getUnsuspendablePackages(@NonNull String[]);
@@ -13256,6 +13262,48 @@
method @NonNull public android.service.voice.HotwordRejectedResult.Builder setConfidenceLevel(int);
}
+ public final class HotwordTrainingAudio implements android.os.Parcelable {
+ method public int describeContents();
+ method @NonNull public android.media.AudioFormat getAudioFormat();
+ method @NonNull public int getAudioType();
+ method @NonNull public byte[] getHotwordAudio();
+ method public int getHotwordOffsetMillis();
+ method public void writeToParcel(@NonNull android.os.Parcel, int);
+ field @NonNull public static final android.os.Parcelable.Creator<android.service.voice.HotwordTrainingAudio> CREATOR;
+ field public static final int HOTWORD_OFFSET_UNSET = -1; // 0xffffffff
+ }
+
+ public static final class HotwordTrainingAudio.Builder {
+ ctor public HotwordTrainingAudio.Builder(@NonNull byte[], @NonNull android.media.AudioFormat);
+ method @NonNull public android.service.voice.HotwordTrainingAudio build();
+ method @NonNull public android.service.voice.HotwordTrainingAudio.Builder setAudioFormat(@NonNull android.media.AudioFormat);
+ method @NonNull public android.service.voice.HotwordTrainingAudio.Builder setAudioType(@NonNull int);
+ method @NonNull public android.service.voice.HotwordTrainingAudio.Builder setHotwordAudio(@NonNull byte...);
+ method @NonNull public android.service.voice.HotwordTrainingAudio.Builder setHotwordOffsetMillis(int);
+ }
+
+ public final class HotwordTrainingData implements android.os.Parcelable {
+ method public int describeContents();
+ method public static int getMaxTrainingDataSize();
+ method public int getTimeoutStage();
+ method @NonNull public java.util.List<android.service.voice.HotwordTrainingAudio> getTrainingAudios();
+ method public void writeToParcel(@NonNull android.os.Parcel, int);
+ field @NonNull public static final android.os.Parcelable.Creator<android.service.voice.HotwordTrainingData> CREATOR;
+ field public static final int TIMEOUT_STAGE_EARLY = 2; // 0x2
+ field public static final int TIMEOUT_STAGE_LATE = 4; // 0x4
+ field public static final int TIMEOUT_STAGE_MIDDLE = 3; // 0x3
+ field public static final int TIMEOUT_STAGE_UNKNOWN = 0; // 0x0
+ field public static final int TIMEOUT_STAGE_VERY_EARLY = 1; // 0x1
+ }
+
+ public static final class HotwordTrainingData.Builder {
+ ctor public HotwordTrainingData.Builder();
+ method @NonNull public android.service.voice.HotwordTrainingData.Builder addTrainingAudio(@NonNull android.service.voice.HotwordTrainingAudio);
+ method @NonNull public android.service.voice.HotwordTrainingData build();
+ method @NonNull public android.service.voice.HotwordTrainingData.Builder setTimeoutStage(int);
+ method @NonNull public android.service.voice.HotwordTrainingData.Builder setTrainingAudios(@NonNull java.util.List<android.service.voice.HotwordTrainingAudio>);
+ }
+
public interface SandboxedDetectionInitializer {
method public static int getMaxCustomInitializationStatus();
method public void onUpdateState(@Nullable android.os.PersistableBundle, @Nullable android.os.SharedMemory, long, @Nullable java.util.function.IntConsumer);
diff --git a/core/api/test-current.txt b/core/api/test-current.txt
index cff5e40..22d2999 100644
--- a/core/api/test-current.txt
+++ b/core/api/test-current.txt
@@ -28,6 +28,7 @@
field public static final String MANAGE_APP_OPS_MODES = "android.permission.MANAGE_APP_OPS_MODES";
field public static final String MANAGE_CRATES = "android.permission.MANAGE_CRATES";
field public static final String MANAGE_NOTIFICATION_LISTENERS = "android.permission.MANAGE_NOTIFICATION_LISTENERS";
+ field public static final String MANAGE_REMOTE_AUTH = "android.permission.MANAGE_REMOTE_AUTH";
field public static final String MANAGE_ROLLBACKS = "android.permission.MANAGE_ROLLBACKS";
field public static final String MANAGE_TOAST_RATE_LIMITING = "android.permission.MANAGE_TOAST_RATE_LIMITING";
field public static final String MODIFY_HDR_CONVERSION_MODE = "android.permission.MODIFY_HDR_CONVERSION_MODE";
@@ -54,6 +55,7 @@
field public static final String TEST_INPUT_METHOD = "android.permission.TEST_INPUT_METHOD";
field public static final String TEST_MANAGE_ROLLBACKS = "android.permission.TEST_MANAGE_ROLLBACKS";
field public static final String UPGRADE_RUNTIME_PERMISSIONS = "android.permission.UPGRADE_RUNTIME_PERMISSIONS";
+ field public static final String USE_REMOTE_AUTH = "android.permission.USE_REMOTE_AUTH";
field public static final String WRITE_ALLOWLISTED_DEVICE_CONFIG = "android.permission.WRITE_ALLOWLISTED_DEVICE_CONFIG";
field public static final String WRITE_DEVICE_CONFIG = "android.permission.WRITE_DEVICE_CONFIG";
field @Deprecated public static final String WRITE_MEDIA_STORAGE = "android.permission.WRITE_MEDIA_STORAGE";
@@ -497,14 +499,14 @@
method public int describeContents();
method public int getActivityType();
method @Nullable public android.graphics.Rect getAppBounds();
- method @NonNull public android.graphics.Rect getBounds();
+ method public android.graphics.Rect getBounds();
method @NonNull public android.graphics.Rect getMaxBounds();
method public int getRotation();
method public int getWindowingMode();
method public static boolean isFloating(int);
method public void setActivityType(int);
method public void setAppBounds(@Nullable android.graphics.Rect);
- method public void setBounds(@Nullable android.graphics.Rect);
+ method public void setBounds(android.graphics.Rect);
method public void setMaxBounds(@Nullable android.graphics.Rect);
method public void setRotation(int);
method public void setTo(android.app.WindowConfiguration);
diff --git a/core/java/android/app/ActivityManagerInternal.java b/core/java/android/app/ActivityManagerInternal.java
index 021f932..32c40df 100644
--- a/core/java/android/app/ActivityManagerInternal.java
+++ b/core/java/android/app/ActivityManagerInternal.java
@@ -35,6 +35,7 @@
import android.content.pm.ActivityInfo;
import android.content.pm.ActivityPresentationInfo;
import android.content.pm.ApplicationInfo;
+import android.content.pm.IPackageDataObserver;
import android.content.pm.UserInfo;
import android.net.Uri;
import android.os.Bundle;
@@ -1224,4 +1225,13 @@
*/
@NonNull
public abstract StatsEvent getCachedAppsHighWatermarkStats(int atomTag, boolean resetAfterPull);
+
+ /**
+ * Internal method for clearing app data, with the extra param that is used to indicate restore.
+ * Used by Backup service during restore operation.
+ *
+ * @hide
+ */
+ public abstract boolean clearApplicationUserData(String packageName, boolean keepState,
+ boolean isRestore, IPackageDataObserver observer, int userId);
}
diff --git a/core/java/android/app/ActivityThread.java b/core/java/android/app/ActivityThread.java
index 3dd69e4..39589fa 100644
--- a/core/java/android/app/ActivityThread.java
+++ b/core/java/android/app/ActivityThread.java
@@ -29,6 +29,8 @@
import static android.app.servertransaction.ActivityLifecycleItem.PRE_ON_CREATE;
import static android.content.ContentResolver.DEPRECATE_DATA_COLUMNS;
import static android.content.ContentResolver.DEPRECATE_DATA_PREFIX;
+import static android.content.res.Configuration.UI_MODE_TYPE_DESK;
+import static android.content.res.Configuration.UI_MODE_TYPE_MASK;
import static android.view.Display.DEFAULT_DISPLAY;
import static android.view.Display.INVALID_DISPLAY;
import static android.window.ConfigurationHelper.freeTextLayoutCachesIfNeeded;
@@ -206,6 +208,7 @@
import android.window.WindowProviderService;
import android.window.WindowTokenClientController;
+import com.android.internal.R;
import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.app.IVoiceInteractor;
@@ -223,6 +226,7 @@
import com.android.org.conscrypt.TrustedCertificateStore;
import com.android.server.am.MemInfoDumpProto;
+import dalvik.annotation.optimization.NeverCompile;
import dalvik.system.AppSpecializationHooks;
import dalvik.system.CloseGuard;
import dalvik.system.VMDebug;
@@ -1496,6 +1500,7 @@
}
}
+ @NeverCompile
@Override
public void dumpMemInfo(ParcelFileDescriptor pfd, Debug.MemoryInfo mem, boolean checkin,
boolean dumpFullInfo, boolean dumpDalvik, boolean dumpSummaryOnly,
@@ -1510,6 +1515,7 @@
}
}
+ @NeverCompile
private void dumpMemInfo(PrintWriter pw, Debug.MemoryInfo memInfo, boolean checkin,
boolean dumpFullInfo, boolean dumpDalvik, boolean dumpSummaryOnly, boolean dumpUnreachable) {
long nativeMax = Debug.getNativeHeapSize() / 1024;
@@ -1666,6 +1672,7 @@
}
}
+ @NeverCompile
@Override
public void dumpMemInfoProto(ParcelFileDescriptor pfd, Debug.MemoryInfo mem,
boolean dumpFullInfo, boolean dumpDalvik, boolean dumpSummaryOnly,
@@ -1679,6 +1686,7 @@
}
}
+ @NeverCompile
private void dumpMemInfo(ProtoOutputStream proto, Debug.MemoryInfo memInfo,
boolean dumpFullInfo, boolean dumpDalvik,
boolean dumpSummaryOnly, boolean dumpUnreachable) {
@@ -3020,6 +3028,7 @@
pw.println(String.format(format, objs));
}
+ @NeverCompile
public static void dumpMemInfoTable(PrintWriter pw, Debug.MemoryInfo memInfo, boolean checkin,
boolean dumpFullInfo, boolean dumpDalvik, boolean dumpSummaryOnly,
int pid, String processName,
@@ -3349,6 +3358,7 @@
/**
* Dump mem info data to proto.
*/
+ @NeverCompile
public static void dumpMemInfoTable(ProtoOutputStream proto, Debug.MemoryInfo memInfo,
boolean dumpDalvik, boolean dumpSummaryOnly,
long nativeMax, long nativeAllocated, long nativeFree,
@@ -6105,9 +6115,21 @@
final boolean shouldUpdateResources = hasPublicResConfigChange
|| shouldUpdateResources(activityToken, currentResConfig, newConfig,
amOverrideConfig, movedToDifferentDisplay, hasPublicResConfigChange);
- final boolean shouldReportChange = shouldReportChange(
- activity.mCurrentConfig, newConfig, r.mSizeConfigurations,
- activity.mActivityInfo.getRealConfigChanged(), alwaysReportChange);
+
+ // TODO(b/274944389): remove once a longer-term solution is implemented.
+ boolean skipActivityRelaunchWhenDocking = activity.getResources().getBoolean(
+ R.bool.config_skipActivityRelaunchWhenDocking);
+ int handledConfigChanges = activity.mActivityInfo.getRealConfigChanged();
+ if (skipActivityRelaunchWhenDocking && onlyDeskInUiModeChanged(activity.mCurrentConfig,
+ newConfig)) {
+ // If we're not relaunching this activity when docking, we should send the configuration
+ // changed event. Pretend as if the activity is handling uiMode config changes in its
+ // manifest so that we'll report any dock changes.
+ handledConfigChanges |= ActivityInfo.CONFIG_UI_MODE;
+ }
+
+ final boolean shouldReportChange = shouldReportChange(activity.mCurrentConfig, newConfig,
+ r.mSizeConfigurations, handledConfigChanges, alwaysReportChange);
// Nothing significant, don't proceed with updating and reporting.
if (!shouldUpdateResources && !shouldReportChange) {
return null;
@@ -6154,6 +6176,25 @@
}
/**
+ * Returns true if the uiMode configuration changed, and desk mode
+ * ({@link android.content.res.Configuration#UI_MODE_TYPE_DESK}) was the only change to uiMode.
+ */
+ private boolean onlyDeskInUiModeChanged(Configuration oldConfig, Configuration newConfig) {
+ boolean deskModeChanged = isInDeskUiMode(oldConfig) != isInDeskUiMode(newConfig);
+
+ // UI mode contains fields other than the UI mode type, so determine if any other fields
+ // changed.
+ boolean uiModeOtherFieldsChanged =
+ (oldConfig.uiMode & ~UI_MODE_TYPE_MASK) != (newConfig.uiMode & ~UI_MODE_TYPE_MASK);
+
+ return deskModeChanged && !uiModeOtherFieldsChanged;
+ }
+
+ private static boolean isInDeskUiMode(Configuration config) {
+ return (config.uiMode & UI_MODE_TYPE_MASK) == UI_MODE_TYPE_DESK;
+ }
+
+ /**
* Returns {@code true} if {@link Activity#onConfigurationChanged(Configuration)} should be
* dispatched.
*
diff --git a/core/java/android/app/ApplicationPackageManager.java b/core/java/android/app/ApplicationPackageManager.java
index 0255860..fcd13b8 100644
--- a/core/java/android/app/ApplicationPackageManager.java
+++ b/core/java/android/app/ApplicationPackageManager.java
@@ -64,6 +64,7 @@
import android.content.pm.IntentFilterVerificationInfo;
import android.content.pm.KeySet;
import android.content.pm.ModuleInfo;
+import android.content.pm.PackageArchiver;
import android.content.pm.PackageInfo;
import android.content.pm.PackageInstaller;
import android.content.pm.PackageItemInfo;
@@ -172,6 +173,7 @@
private volatile UserManager mUserManager;
private volatile PermissionManager mPermissionManager;
private volatile PackageInstaller mInstaller;
+ private volatile PackageArchiver mPackageArchiver;
private volatile ArtManager mArtManager;
private volatile DevicePolicyManager mDevicePolicyManager;
private volatile String mPermissionsControllerPackageName;
@@ -3282,6 +3284,18 @@
}
@Override
+ public PackageArchiver getPackageArchiver() {
+ if (mPackageArchiver == null) {
+ try {
+ mPackageArchiver = new PackageArchiver(mContext, mPM.getPackageArchiverService());
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+ return mPackageArchiver;
+ }
+
+ @Override
public boolean isPackageAvailable(String packageName) {
try {
return mPM.isPackageAvailable(packageName, getUserId());
diff --git a/core/java/android/app/ContextImpl.java b/core/java/android/app/ContextImpl.java
index 5feafbe..a538247 100644
--- a/core/java/android/app/ContextImpl.java
+++ b/core/java/android/app/ContextImpl.java
@@ -3455,20 +3455,20 @@
mOpPackageName = overrideOpPackageName != null ? overrideOpPackageName : opPackageName;
mParams = Objects.requireNonNull(params);
mAttributionSource = createAttributionSource(attributionTag, nextAttributionSource,
- params.getRenouncedPermissions());
+ params.getRenouncedPermissions(), params.shouldRegisterAttributionSource());
mContentResolver = new ApplicationContentResolver(this, mainThread);
}
private @NonNull AttributionSource createAttributionSource(@Nullable String attributionTag,
@Nullable AttributionSource nextAttributionSource,
- @Nullable Set<String> renouncedPermissions) {
+ @Nullable Set<String> renouncedPermissions, boolean shouldRegister) {
AttributionSource attributionSource = new AttributionSource(Process.myUid(),
Process.myPid(), mOpPackageName, attributionTag,
(renouncedPermissions != null) ? renouncedPermissions.toArray(new String[0]) : null,
getDeviceId(), nextAttributionSource);
// If we want to access protected data on behalf of another app we need to
// tell the OS that we opt in to participate in the attribution chain.
- if (nextAttributionSource != null) {
+ if (nextAttributionSource != null || shouldRegister) {
attributionSource = getSystemService(PermissionManager.class)
.registerAttributionSource(attributionSource);
}
diff --git a/core/java/android/app/LocaleConfig.java b/core/java/android/app/LocaleConfig.java
index 729e555..0857c96 100644
--- a/core/java/android/app/LocaleConfig.java
+++ b/core/java/android/app/LocaleConfig.java
@@ -40,7 +40,7 @@
import java.lang.annotation.RetentionPolicy;
import java.util.Arrays;
import java.util.Collections;
-import java.util.HashSet;
+import java.util.LinkedHashSet;
import java.util.List;
import java.util.Locale;
import java.util.Set;
@@ -195,7 +195,8 @@
XmlUtils.beginDocument(parser, TAG_LOCALE_CONFIG);
int outerDepth = parser.getDepth();
AttributeSet attrs = Xml.asAttributeSet(parser);
- Set<String> localeNames = new HashSet<String>();
+ // LinkedHashSet to preserve insertion order
+ Set<String> localeNames = new LinkedHashSet<>();
while (XmlUtils.nextElementWithin(parser, outerDepth)) {
if (TAG_LOCALE.equals(parser.getName())) {
final TypedArray attributes = res.obtainAttributes(
diff --git a/core/java/android/app/SystemServiceRegistry.java b/core/java/android/app/SystemServiceRegistry.java
index fbb97ff..cbbf4e0 100644
--- a/core/java/android/app/SystemServiceRegistry.java
+++ b/core/java/android/app/SystemServiceRegistry.java
@@ -868,10 +868,6 @@
@Override
public VirtualDeviceManager createService(ContextImpl ctx)
throws ServiceNotFoundException {
- if (!ctx.getPackageManager().hasSystemFeature(
- PackageManager.FEATURE_COMPANION_DEVICE_SETUP)) {
- return null;
- }
if (!ctx.getResources().getBoolean(R.bool.config_enableVirtualDeviceManager)) {
return null;
}
diff --git a/core/java/android/app/WindowConfiguration.java b/core/java/android/app/WindowConfiguration.java
index e3055e5..bf238c3 100644
--- a/core/java/android/app/WindowConfiguration.java
+++ b/core/java/android/app/WindowConfiguration.java
@@ -267,11 +267,12 @@
}
};
+ // TODO(b/297672475): make this take @Nullable
/**
* Sets the bounds to the provided {@link Rect}.
* @param rect the new bounds value.
*/
- public void setBounds(@Nullable Rect rect) {
+ public void setBounds(Rect rect) {
if (rect == null) {
mBounds.setEmpty();
return;
@@ -363,8 +364,8 @@
return mAppBounds;
}
+ // TODO(b/297672475): make this return @NonNull
/** @see #setBounds(Rect) */
- @NonNull
public Rect getBounds() {
return mBounds;
}
diff --git a/core/java/android/app/usage/UsageStatsManager.java b/core/java/android/app/usage/UsageStatsManager.java
index ecf16439..2a10ed1 100644
--- a/core/java/android/app/usage/UsageStatsManager.java
+++ b/core/java/android/app/usage/UsageStatsManager.java
@@ -1106,6 +1106,7 @@
* <p><em>This method is only for use by the system</em>
* @hide
*/
+ @RequiresPermission(android.Manifest.permission.REPORT_USAGE_STATS)
public void reportUserInteraction(@NonNull String packageName, int userId) {
try {
mService.reportUserInteraction(packageName, userId);
@@ -1396,6 +1397,7 @@
* {@link UsageEvents}
* @hide
*/
+ @RequiresPermission(android.Manifest.permission.REPORT_USAGE_STATS)
public void reportChooserSelection(String packageName, int userId, String contentType,
String[] annotations, String action) {
try {
diff --git a/core/java/android/content/ContextParams.java b/core/java/android/content/ContextParams.java
index 5cc3a24..988a9c0 100644
--- a/core/java/android/content/ContextParams.java
+++ b/core/java/android/content/ContextParams.java
@@ -50,17 +50,20 @@
private final @Nullable String mAttributionTag;
private final @Nullable AttributionSource mNext;
private final @NonNull Set<String> mRenouncedPermissions;
+ private final boolean mShouldRegisterAttributionSource;
/** {@hide} */
public static final ContextParams EMPTY = new ContextParams.Builder().build();
private ContextParams(@Nullable String attributionTag,
@Nullable AttributionSource next,
- @Nullable Set<String> renouncedPermissions) {
+ @Nullable Set<String> renouncedPermissions,
+ boolean shouldRegister) {
mAttributionTag = attributionTag;
mNext = next;
mRenouncedPermissions = (renouncedPermissions != null)
? renouncedPermissions : Collections.emptySet();
+ mShouldRegisterAttributionSource = shouldRegister;
}
/**
@@ -95,12 +98,22 @@
}
/**
+ * @return Whether the attribution source associated with the Context being created should be
+ * registered.
+ */
+ @NonNull
+ public boolean shouldRegisterAttributionSource() {
+ return mShouldRegisterAttributionSource;
+ }
+
+ /**
* Builder for creating a {@link ContextParams}.
*/
public static final class Builder {
private @Nullable String mAttributionTag;
private @NonNull Set<String> mRenouncedPermissions = Collections.emptySet();
private @Nullable AttributionSource mNext;
+ private boolean mShouldRegisterAttributionSource;
/**
* Create a new builder.
@@ -159,6 +172,19 @@
}
/**
+ * Sets whether the attribution source associated with the context created from these params
+ * should be registered.
+ *
+ * @param shouldRegister Whether the attribution source associated with the Context being
+ * created should be registered.
+ */
+ @NonNull
+ public Builder setShouldRegisterAttributionSource(boolean shouldRegister) {
+ mShouldRegisterAttributionSource = shouldRegister;
+ return this;
+ }
+
+ /**
* Sets permissions which have been voluntarily "renounced" by the
* calling app.
* <p>
@@ -205,7 +231,7 @@
@NonNull
public ContextParams build() {
return new ContextParams(mAttributionTag, mNext,
- mRenouncedPermissions);
+ mRenouncedPermissions, mShouldRegisterAttributionSource);
}
}
}
diff --git a/core/java/android/content/Intent.java b/core/java/android/content/Intent.java
index afeb3d29..31f6418 100644
--- a/core/java/android/content/Intent.java
+++ b/core/java/android/content/Intent.java
@@ -6705,6 +6705,15 @@
public static final String EXTRA_VISIBILITY_ALLOW_LIST =
"android.intent.extra.VISIBILITY_ALLOW_LIST";
+ /**
+ * A boolean extra used with {@link #ACTION_PACKAGE_DATA_CLEARED} which indicates if the intent
+ * is broadcast as part of a restore operation.
+ *
+ * @hide
+ */
+ public static final String EXTRA_IS_RESTORE =
+ "android.intent.extra.IS_RESTORE";
+
// ---------------------------------------------------------------------
// ---------------------------------------------------------------------
// Intent flags (see mFlags variable).
diff --git a/core/java/android/content/pm/ArchivedPackageParcel.aidl b/core/java/android/content/pm/ArchivedPackageParcel.aidl
new file mode 100644
index 0000000..b34b708
--- /dev/null
+++ b/core/java/android/content/pm/ArchivedPackageParcel.aidl
@@ -0,0 +1,38 @@
+/*
+ * 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 android.content.pm;
+
+import android.content.pm.SigningDetails;
+
+/**
+ * Contains fields required for archived package installation,
+ * i.e. installation without an APK.
+ * @hide
+ */
+parcelable ArchivedPackageParcel {
+ String packageName;
+ SigningDetails signingDetails;
+ int versionCode;
+ int versionCodeMajor;
+ int targetSdkVersion;
+ boolean clearUserDataAllowed;
+ boolean backupAllowed;
+ boolean defaultToDeviceProtectedStorage;
+ boolean requestLegacyExternalStorage;
+ boolean userDataFragile;
+ boolean clearUserDataOnFailedRestoreAllowed;
+}
diff --git a/core/java/android/content/pm/IPackageArchiverService.aidl b/core/java/android/content/pm/IPackageArchiverService.aidl
new file mode 100644
index 0000000..fc471c4
--- /dev/null
+++ b/core/java/android/content/pm/IPackageArchiverService.aidl
@@ -0,0 +1,26 @@
+/*
+ * 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 android.content.pm;
+
+import android.content.IntentSender;
+import android.os.UserHandle;
+
+/** {@hide} */
+interface IPackageArchiverService {
+
+ @JavaPassthrough(annotation="@android.annotation.RequiresPermission(anyOf={android.Manifest.permission.DELETE_PACKAGES,android.Manifest.permission.REQUEST_DELETE_PACKAGES})")
+ void requestArchive(String packageName, String callerPackageName, in IntentSender statusReceiver, in UserHandle userHandle);
+}
\ No newline at end of file
diff --git a/core/java/android/content/pm/IPackageManager.aidl b/core/java/android/content/pm/IPackageManager.aidl
index ea0f5ff..916c249 100644
--- a/core/java/android/content/pm/IPackageManager.aidl
+++ b/core/java/android/content/pm/IPackageManager.aidl
@@ -22,9 +22,11 @@
import android.content.IntentFilter;
import android.content.pm.ActivityInfo;
import android.content.pm.ApplicationInfo;
+import android.content.pm.ArchivedPackageParcel;
import android.content.pm.ChangedPackages;
import android.content.pm.InstantAppInfo;
import android.content.pm.FeatureInfo;
+import android.content.pm.IPackageArchiverService;
import android.content.pm.IDexModuleRegisterCallback;
import android.content.pm.InstallSourceInfo;
import android.content.pm.IOnChecksumsReadyListener;
@@ -650,6 +652,8 @@
@UnsupportedAppUsage(maxTargetSdk = 30, trackingBug = 170729553)
IPackageInstaller getPackageInstaller();
+ IPackageArchiverService getPackageArchiverService();
+
@EnforcePermission("DELETE_PACKAGES")
boolean setBlockUninstallForUser(String packageName, boolean blockUninstall, int userId);
@UnsupportedAppUsage
@@ -830,4 +834,6 @@
void registerPackageMonitorCallback(IRemoteCallback callback, int userId);
void unregisterPackageMonitorCallback(IRemoteCallback callback);
+
+ ArchivedPackageParcel getArchivedPackage(in String apkPath);
}
diff --git a/core/java/android/content/pm/PackageArchiver.java b/core/java/android/content/pm/PackageArchiver.java
new file mode 100644
index 0000000..d739d50
--- /dev/null
+++ b/core/java/android/content/pm/PackageArchiver.java
@@ -0,0 +1,79 @@
+/*
+ * 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 android.content.pm;
+
+import android.Manifest;
+import android.annotation.NonNull;
+import android.annotation.RequiresPermission;
+import android.annotation.SystemApi;
+import android.content.Context;
+import android.content.IntentSender;
+import android.content.pm.PackageManager.NameNotFoundException;
+import android.os.ParcelableException;
+import android.os.RemoteException;
+
+/**
+ * {@code ArchiveManager} is used to archive apps. During the archival process, the apps APKs and
+ * cache are removed from the device while the user data is kept. Through the
+ * {@code requestUnarchive()} call, apps can be restored again through their responsible app store.
+ *
+ * <p> Archived apps are returned as displayable apps through the {@link LauncherApps} APIs and
+ * will be displayed to users with UI treatment to highlight that said apps are archived. If
+ * a user taps on an archived app, the app will be unarchived and the restoration process is
+ * communicated.
+ *
+ * @hide
+ */
+// TODO(b/278560219) Improve public documentation.
+@SystemApi
+public class PackageArchiver {
+
+ private final Context mContext;
+ private final IPackageArchiverService mService;
+
+ /**
+ * @hide
+ */
+ public PackageArchiver(Context context, IPackageArchiverService service) {
+ mContext = context;
+ mService = service;
+ }
+
+ /**
+ * Requests to archive a package which is currently installed.
+ *
+ * @param statusReceiver Callback used to notify when the operation is completed.
+ * @throws NameNotFoundException If {@code packageName} isn't found or not available to the
+ * caller.
+ * @hide
+ */
+ @RequiresPermission(anyOf = {
+ Manifest.permission.DELETE_PACKAGES,
+ Manifest.permission.REQUEST_DELETE_PACKAGES})
+ @SystemApi
+ public void requestArchive(@NonNull String packageName, @NonNull IntentSender statusReceiver)
+ throws NameNotFoundException {
+ try {
+ mService.requestArchive(packageName, mContext.getPackageName(), statusReceiver,
+ mContext.getUser());
+ } catch (ParcelableException e) {
+ e.maybeRethrow(NameNotFoundException.class);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+}
diff --git a/core/java/android/content/pm/PackageManager.java b/core/java/android/content/pm/PackageManager.java
index 885e67e1..c384389 100644
--- a/core/java/android/content/pm/PackageManager.java
+++ b/core/java/android/content/pm/PackageManager.java
@@ -1682,6 +1682,13 @@
public static final int INSTALL_FROM_MANAGED_USER_OR_PROFILE = 1 << 26;
/**
+ * Flag parameter for {@link PackageInstaller.SessionParams} to indicate that this
+ * session is for archived package installation.
+ * @hide
+ */
+ public static final int INSTALL_ARCHIVED = 1 << 27;
+
+ /**
* Flag parameter for {@link #installPackage} to force a non-staged update of an APEX. This is
* a development-only feature and should not be used on end user devices.
*
@@ -9934,6 +9941,16 @@
public abstract @NonNull PackageInstaller getPackageInstaller();
/**
+ * {@link PackageArchiver} can be used to archive and restore archived packages.
+ *
+ * @hide
+ */
+ @SystemApi
+ public @NonNull PackageArchiver getPackageArchiver() {
+ throw new UnsupportedOperationException(
+ "getPackageArchiver not implemented in subclass");
+ }
+ /**
* Adds a {@code CrossProfileIntentFilter}. After calling this method all
* intents sent from the user with id sourceUserId can also be be resolved
* by activities in the user with id targetUserId if they match the
diff --git a/core/java/android/content/pm/PackageParser.java b/core/java/android/content/pm/PackageParser.java
index 9387ae1..41ba1dc 100644
--- a/core/java/android/content/pm/PackageParser.java
+++ b/core/java/android/content/pm/PackageParser.java
@@ -1456,8 +1456,8 @@
private static AssetManager newConfiguredAssetManager() {
AssetManager assetManager = new AssetManager();
- assetManager.setConfiguration(0, 0, null, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
- Build.VERSION.RESOURCES_SDK_INT);
+ assetManager.setConfiguration(0, 0, null, null, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, Build.VERSION.RESOURCES_SDK_INT);
return assetManager;
}
@@ -9011,8 +9011,8 @@
}
AssetManager assets = new AssetManager();
- assets.setConfiguration(0, 0, null, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
- Build.VERSION.RESOURCES_SDK_INT);
+ assets.setConfiguration(0, 0, null, null, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, Build.VERSION.RESOURCES_SDK_INT);
assets.setApkAssets(apkAssets, false /*invalidateCaches*/);
mCachedAssetManager = assets;
@@ -9086,8 +9086,8 @@
private static AssetManager createAssetManagerWithAssets(ApkAssets[] apkAssets) {
final AssetManager assets = new AssetManager();
- assets.setConfiguration(0, 0, null, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
- Build.VERSION.RESOURCES_SDK_INT);
+ assets.setConfiguration(0, 0, null, null, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, Build.VERSION.RESOURCES_SDK_INT);
assets.setApkAssets(apkAssets, false /*invalidateCaches*/);
return assets;
}
diff --git a/core/java/android/content/pm/SigningDetails.aidl b/core/java/android/content/pm/SigningDetails.aidl
new file mode 100644
index 0000000..95f3ca7
--- /dev/null
+++ b/core/java/android/content/pm/SigningDetails.aidl
@@ -0,0 +1,18 @@
+/*
+ * 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 android.content.pm;
+
+parcelable SigningDetails;
diff --git a/core/java/android/content/pm/parsing/ApkLite.java b/core/java/android/content/pm/parsing/ApkLite.java
index 269bec2..0127adc 100644
--- a/core/java/android/content/pm/parsing/ApkLite.java
+++ b/core/java/android/content/pm/parsing/ApkLite.java
@@ -18,6 +18,7 @@
import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.content.pm.ArchivedPackageParcel;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.content.pm.SigningDetails;
@@ -184,6 +185,42 @@
mIsSdkLibrary = isSdkLibrary;
}
+ public ApkLite(String path, ArchivedPackageParcel archivedPackage) {
+ mPath = path;
+ mPackageName = archivedPackage.packageName;
+ mSplitName = null; // base.apk
+ mSplitTypes = null;
+ mFeatureSplit = false;
+ mConfigForSplit = null;
+ mUsesSplitName = null;
+ mRequiredSplitTypes = null;
+ mSplitRequired = hasAnyRequiredSplitTypes();
+ mVersionCode = archivedPackage.versionCode;
+ mVersionCodeMajor = archivedPackage.versionCodeMajor;
+ mRevisionCode = 0;
+ mInstallLocation = PackageInfo.INSTALL_LOCATION_UNSPECIFIED;
+ mVerifiers = new VerifierInfo[]{};
+ mSigningDetails = archivedPackage.signingDetails;
+ mCoreApp = false;
+ mDebuggable = false;
+ mProfileableByShell = false;
+ mMultiArch = false;
+ mUse32bitAbi = false;
+ mUseEmbeddedDex = false;
+ mExtractNativeLibs = false;
+ mIsolatedSplits = false;
+ mTargetPackageName = null;
+ mOverlayIsStatic = false;
+ mOverlayPriority = 0;
+ mRequiredSystemPropertyName = null;
+ mRequiredSystemPropertyValue = null;
+ mMinSdkVersion = ApkLiteParseUtils.DEFAULT_MIN_SDK_VERSION;
+ mTargetSdkVersion = archivedPackage.targetSdkVersion;
+ mRollbackDataPolicy = 0;
+ mHasDeviceAdminReceiver = false;
+ mIsSdkLibrary = false;
+ }
+
/**
* Return {@link #mVersionCode} and {@link #mVersionCodeMajor} combined together as a
* single long value. The {@link #mVersionCodeMajor} is placed in the upper 32 bits.
diff --git a/core/java/android/content/pm/parsing/ApkLiteParseUtils.java b/core/java/android/content/pm/parsing/ApkLiteParseUtils.java
index 4f6bcb6..7e67396 100644
--- a/core/java/android/content/pm/parsing/ApkLiteParseUtils.java
+++ b/core/java/android/content/pm/parsing/ApkLiteParseUtils.java
@@ -73,7 +73,7 @@
// Constants copied from services.jar side since they're not accessible
private static final String ANDROID_RES_NAMESPACE =
"http://schemas.android.com/apk/res/android";
- private static final int DEFAULT_MIN_SDK_VERSION = 1;
+ public static final int DEFAULT_MIN_SDK_VERSION = 1;
private static final int DEFAULT_TARGET_SDK_VERSION = 0;
public static final String ANDROID_MANIFEST_FILENAME = "AndroidManifest.xml";
private static final int PARSE_IS_SYSTEM_DIR = 1 << 4;
diff --git a/core/java/android/content/pm/parsing/PackageLite.java b/core/java/android/content/pm/parsing/PackageLite.java
index e2789c9..51dbde3 100644
--- a/core/java/android/content/pm/parsing/PackageLite.java
+++ b/core/java/android/content/pm/parsing/PackageLite.java
@@ -19,6 +19,7 @@
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.content.pm.PackageInfo;
+import android.content.pm.SigningDetails;
import android.content.pm.VerifierInfo;
import com.android.internal.util.ArrayUtils;
@@ -78,6 +79,8 @@
private final int mInstallLocation;
/** Information about a package verifiers as used during package verification */
private final @NonNull VerifierInfo[] mVerifiers;
+ /** Signing-related data of an application package */
+ private final @NonNull SigningDetails mSigningDetails;
/** Indicate whether any split APKs that are features. Ordered by splitName */
private final @Nullable boolean[] mIsFeatureSplits;
@@ -123,6 +126,7 @@
mVersionCodeMajor = baseApk.getVersionCodeMajor();
mInstallLocation = baseApk.getInstallLocation();
mVerifiers = baseApk.getVerifiers();
+ mSigningDetails = baseApk.getSigningDetails();
mBaseRevisionCode = baseApk.getRevisionCode();
mCoreApp = baseApk.isCoreApp();
mDebuggable = baseApk.isDebuggable();
@@ -325,6 +329,14 @@
}
/**
+ * Signing-related data of an application package
+ */
+ @DataClass.Generated.Member
+ public @NonNull SigningDetails getSigningDetails() {
+ return mSigningDetails;
+ }
+
+ /**
* Indicate whether any split APKs that are features. Ordered by splitName
*/
@DataClass.Generated.Member
@@ -415,11 +427,10 @@
}
@DataClass.Generated(
- time = 1643132127068L,
+ time = 1693264166050L,
codegenVersion = "1.0.23",
sourceFile = "frameworks/base/core/java/android/content/pm/parsing/PackageLite.java",
- inputSignatures =
- "private final @android.annotation.NonNull java.lang.String mPackageName\nprivate final @android.annotation.NonNull java.lang.String mPath\nprivate final @android.annotation.NonNull java.lang.String mBaseApkPath\nprivate final @android.annotation.Nullable java.lang.String[] mSplitApkPaths\nprivate final @android.annotation.Nullable java.lang.String[] mSplitNames\nprivate final @android.annotation.Nullable java.lang.String[] mUsesSplitNames\nprivate final @android.annotation.Nullable java.lang.String[] mConfigForSplit\nprivate final @android.annotation.Nullable java.util.Set<java.lang.String> mBaseRequiredSplitTypes\nprivate final @android.annotation.Nullable java.util.Set<java.lang.String>[] mRequiredSplitTypes\nprivate final @android.annotation.Nullable java.util.Set<java.lang.String>[] mSplitTypes\nprivate final int mVersionCodeMajor\nprivate final int mVersionCode\nprivate final int mTargetSdk\nprivate final int mBaseRevisionCode\nprivate final @android.annotation.Nullable int[] mSplitRevisionCodes\nprivate final int mInstallLocation\nprivate final @android.annotation.NonNull android.content.pm.VerifierInfo[] mVerifiers\nprivate final @android.annotation.Nullable boolean[] mIsFeatureSplits\nprivate final boolean mIsolatedSplits\nprivate final boolean mSplitRequired\nprivate final boolean mCoreApp\nprivate final boolean mDebuggable\nprivate final boolean mMultiArch\nprivate final boolean mUse32bitAbi\nprivate final boolean mExtractNativeLibs\nprivate final boolean mProfileableByShell\nprivate final boolean mUseEmbeddedDex\nprivate final boolean mIsSdkLibrary\npublic java.util.List<java.lang.String> getAllApkPaths()\npublic long getLongVersionCode()\nprivate boolean hasAnyRequiredSplitTypes()\nclass PackageLite extends java.lang.Object implements []\n@com.android.internal.util.DataClass(genConstructor=false, genConstDefs=false)")
+ inputSignatures = "private final @android.annotation.NonNull java.lang.String mPackageName\nprivate final @android.annotation.NonNull java.lang.String mPath\nprivate final @android.annotation.NonNull java.lang.String mBaseApkPath\nprivate final @android.annotation.Nullable java.lang.String[] mSplitApkPaths\nprivate final @android.annotation.Nullable java.lang.String[] mSplitNames\nprivate final @android.annotation.Nullable java.lang.String[] mUsesSplitNames\nprivate final @android.annotation.Nullable java.lang.String[] mConfigForSplit\nprivate final @android.annotation.Nullable java.util.Set<java.lang.String> mBaseRequiredSplitTypes\nprivate final @android.annotation.Nullable java.util.Set<java.lang.String>[] mRequiredSplitTypes\nprivate final @android.annotation.Nullable java.util.Set<java.lang.String>[] mSplitTypes\nprivate final int mVersionCodeMajor\nprivate final int mVersionCode\nprivate final int mTargetSdk\nprivate final int mBaseRevisionCode\nprivate final @android.annotation.Nullable int[] mSplitRevisionCodes\nprivate final int mInstallLocation\nprivate final @android.annotation.NonNull android.content.pm.VerifierInfo[] mVerifiers\nprivate final @android.annotation.NonNull android.content.pm.SigningDetails mSigningDetails\nprivate final @android.annotation.Nullable boolean[] mIsFeatureSplits\nprivate final boolean mIsolatedSplits\nprivate final boolean mSplitRequired\nprivate final boolean mCoreApp\nprivate final boolean mDebuggable\nprivate final boolean mMultiArch\nprivate final boolean mUse32bitAbi\nprivate final boolean mExtractNativeLibs\nprivate final boolean mProfileableByShell\nprivate final boolean mUseEmbeddedDex\nprivate final boolean mIsSdkLibrary\npublic java.util.List<java.lang.String> getAllApkPaths()\npublic long getLongVersionCode()\nprivate boolean hasAnyRequiredSplitTypes()\nclass PackageLite extends java.lang.Object implements []\n@com.android.internal.util.DataClass(genConstructor=false, genConstDefs=false)")
@Deprecated
private void __metadata() {}
diff --git a/core/java/android/content/res/AssetManager.java b/core/java/android/content/res/AssetManager.java
index b225de4..23b9d0b 100644
--- a/core/java/android/content/res/AssetManager.java
+++ b/core/java/android/content/res/AssetManager.java
@@ -1480,9 +1480,13 @@
int screenWidth, int screenHeight, int smallestScreenWidthDp, int screenWidthDp,
int screenHeightDp, int screenLayout, int uiMode, int colorMode, int grammaticalGender,
int majorVersion) {
- synchronized (this) {
- ensureValidLocked();
- nativeSetConfiguration(mObject, mcc, mnc, locale, orientation, touchscreen, density,
+ if (locale != null) {
+ setConfiguration(mcc, mnc, null, new String[]{locale}, orientation, touchscreen,
+ density, keyboard, keyboardHidden, navigation, screenWidth, screenHeight,
+ smallestScreenWidthDp, screenWidthDp, screenHeightDp, screenLayout, uiMode,
+ colorMode, grammaticalGender, majorVersion);
+ } else {
+ setConfiguration(mcc, mnc, null, null, orientation, touchscreen, density,
keyboard, keyboardHidden, navigation, screenWidth, screenHeight,
smallestScreenWidthDp, screenWidthDp, screenHeightDp, screenLayout, uiMode,
colorMode, grammaticalGender, majorVersion);
@@ -1490,6 +1494,25 @@
}
/**
+ * Change the configuration used when retrieving resources. Not for use by
+ * applications.
+ * @hide
+ */
+ public void setConfiguration(int mcc, int mnc, String defaultLocale, String[] locales,
+ int orientation, int touchscreen, int density, int keyboard, int keyboardHidden,
+ int navigation, int screenWidth, int screenHeight, int smallestScreenWidthDp,
+ int screenWidthDp, int screenHeightDp, int screenLayout, int uiMode, int colorMode,
+ int grammaticalGender, int majorVersion) {
+ synchronized (this) {
+ ensureValidLocked();
+ nativeSetConfiguration(mObject, mcc, mnc, defaultLocale, locales, orientation,
+ touchscreen, density, keyboard, keyboardHidden, navigation, screenWidth,
+ screenHeight, smallestScreenWidthDp, screenWidthDp, screenHeightDp,
+ screenLayout, uiMode, colorMode, grammaticalGender, majorVersion);
+ }
+ }
+
+ /**
* @hide
*/
@UnsupportedAppUsage
@@ -1572,10 +1595,11 @@
private static native void nativeSetApkAssets(long ptr, @NonNull ApkAssets[] apkAssets,
boolean invalidateCaches);
private static native void nativeSetConfiguration(long ptr, int mcc, int mnc,
- @Nullable String locale, int orientation, int touchscreen, int density, int keyboard,
- int keyboardHidden, int navigation, int screenWidth, int screenHeight,
- int smallestScreenWidthDp, int screenWidthDp, int screenHeightDp, int screenLayout,
- int uiMode, int colorMode, int grammaticalGender, int majorVersion);
+ @Nullable String defaultLocale, @NonNull String[] locales, int orientation,
+ int touchscreen, int density, int keyboard, int keyboardHidden, int navigation,
+ int screenWidth, int screenHeight, int smallestScreenWidthDp, int screenWidthDp,
+ int screenHeightDp, int screenLayout, int uiMode, int colorMode, int grammaticalGender,
+ int majorVersion);
private static native @NonNull SparseArray<String> nativeGetAssignedPackageIdentifiers(
long ptr, boolean includeOverlays, boolean includeLoaders);
diff --git a/core/java/android/content/res/ResourcesImpl.java b/core/java/android/content/res/ResourcesImpl.java
index 395fef2..76b29e6 100644
--- a/core/java/android/content/res/ResourcesImpl.java
+++ b/core/java/android/content/res/ResourcesImpl.java
@@ -425,14 +425,12 @@
mConfiguration.setLocales(locales);
}
+ String[] selectedLocales = null;
+ String defaultLocale = null;
if ((configChanges & ActivityInfo.CONFIG_LOCALE) != 0) {
if (locales.size() > 1) {
String[] availableLocales;
-
- LocaleList localeList = ResourcesManager.getInstance().getLocaleList();
- if (!localeList.isEmpty()) {
- availableLocales = localeList.toLanguageTags().split(",");
- } else {
+ if (ResourcesManager.getInstance().getLocaleList().isEmpty()) {
// The LocaleList has changed. We must query the AssetManager's
// available Locales and figure out the best matching Locale in the new
// LocaleList.
@@ -444,13 +442,35 @@
availableLocales = null;
}
}
- }
- if (availableLocales != null) {
- final Locale bestLocale = locales.getFirstMatchWithEnglishSupported(
- availableLocales);
- if (bestLocale != null && bestLocale != locales.get(0)) {
- mConfiguration.setLocales(new LocaleList(bestLocale, locales));
+
+ if (availableLocales != null) {
+ final Locale bestLocale = locales.getFirstMatchWithEnglishSupported(
+ availableLocales);
+ if (bestLocale != null) {
+ selectedLocales = new String[]{
+ adjustLanguageTag(bestLocale.toLanguageTag())};
+ if (!bestLocale.equals(locales.get(0))) {
+ mConfiguration.setLocales(
+ new LocaleList(bestLocale, locales));
+ }
+ }
}
+ } else {
+ selectedLocales = locales.getIntersection(
+ ResourcesManager.getInstance().getLocaleList());
+ defaultLocale = ResourcesManager.getInstance()
+ .getLocaleList().get(0).toLanguageTag();
+ }
+ }
+ }
+ if (selectedLocales == null) {
+ if (ResourcesManager.getInstance().getLocaleList().isEmpty()) {
+ selectedLocales = new String[]{
+ adjustLanguageTag(locales.get(0).toLanguageTag())};
+ } else {
+ selectedLocales = new String[locales.size()];
+ for (int i = 0; i < locales.size(); i++) {
+ selectedLocales[i] = adjustLanguageTag(locales.get(i).toLanguageTag());
}
}
}
@@ -488,7 +508,8 @@
}
mAssets.setConfiguration(mConfiguration.mcc, mConfiguration.mnc,
- adjustLanguageTag(mConfiguration.getLocales().get(0).toLanguageTag()),
+ defaultLocale,
+ selectedLocales,
mConfiguration.orientation,
mConfiguration.touchscreen,
mConfiguration.densityDpi, mConfiguration.keyboard,
diff --git a/core/java/android/hardware/SensorAdditionalInfo.java b/core/java/android/hardware/SensorAdditionalInfo.java
index 59def9f..d1e101d 100644
--- a/core/java/android/hardware/SensorAdditionalInfo.java
+++ b/core/java/android/hardware/SensorAdditionalInfo.java
@@ -257,7 +257,7 @@
public static SensorAdditionalInfo createLocalGeomagneticField(
float strength, float declination, float inclination) {
if (strength < 10 || strength > 100 // much beyond extreme values on earth
- || declination < 0 || declination > Math.PI
+ || declination < -Math.PI / 2 || declination > Math.PI / 2
|| inclination < -Math.PI / 2 || inclination > Math.PI / 2) {
throw new IllegalArgumentException("Geomagnetic field info out of range");
}
diff --git a/core/java/android/hardware/camera2/CaptureRequest.java b/core/java/android/hardware/camera2/CaptureRequest.java
index 5940819..703f165 100644
--- a/core/java/android/hardware/camera2/CaptureRequest.java
+++ b/core/java/android/hardware/camera2/CaptureRequest.java
@@ -1468,6 +1468,13 @@
* <p>Only constrains auto-exposure (AE) algorithm, not
* manual control of {@link CaptureRequest#SENSOR_EXPOSURE_TIME android.sensor.exposureTime} and
* {@link CaptureRequest#SENSOR_FRAME_DURATION android.sensor.frameDuration}.</p>
+ * <p>To start a CaptureSession with a target FPS range different from the
+ * capture request template's default value, the application
+ * is strongly recommended to call
+ * {@link SessionConfiguration#setSessionParameters }
+ * with the target fps range before creating the capture session. The aeTargetFpsRange is
+ * typically a session parameter. Specifying it at session creation time helps avoid
+ * session reconfiguration delays in cases like 60fps or high speed recording.</p>
* <p><b>Units</b>: Frames per second (FPS)</p>
* <p><b>Range of valid values:</b><br>
* Any of the entries in {@link CameraCharacteristics#CONTROL_AE_AVAILABLE_TARGET_FPS_RANGES android.control.aeAvailableTargetFpsRanges}</p>
@@ -2140,6 +2147,12 @@
* {@link CaptureRequest#CONTROL_VIDEO_STABILIZATION_MODE android.control.videoStabilizationMode} field will return
* OFF if the recording output is not stabilized, or if there are no output
* Surface types that can be stabilized.</p>
+ * <p>The application is strongly recommended to call
+ * {@link SessionConfiguration#setSessionParameters }
+ * with the desired video stabilization mode before creating the capture session.
+ * Video stabilization mode is a session parameter on many devices. Specifying
+ * it at session creation time helps avoid reconfiguration delay caused by difference
+ * between the default value and the first CaptureRequest.</p>
* <p>If a camera device supports both this mode and OIS
* ({@link CaptureRequest#LENS_OPTICAL_STABILIZATION_MODE android.lens.opticalStabilizationMode}), turning both modes on may
* produce undesirable interaction, so it is recommended not to enable
diff --git a/core/java/android/hardware/camera2/CaptureResult.java b/core/java/android/hardware/camera2/CaptureResult.java
index 905f98d..746648b 100644
--- a/core/java/android/hardware/camera2/CaptureResult.java
+++ b/core/java/android/hardware/camera2/CaptureResult.java
@@ -887,6 +887,13 @@
* <p>Only constrains auto-exposure (AE) algorithm, not
* manual control of {@link CaptureRequest#SENSOR_EXPOSURE_TIME android.sensor.exposureTime} and
* {@link CaptureRequest#SENSOR_FRAME_DURATION android.sensor.frameDuration}.</p>
+ * <p>To start a CaptureSession with a target FPS range different from the
+ * capture request template's default value, the application
+ * is strongly recommended to call
+ * {@link SessionConfiguration#setSessionParameters }
+ * with the target fps range before creating the capture session. The aeTargetFpsRange is
+ * typically a session parameter. Specifying it at session creation time helps avoid
+ * session reconfiguration delays in cases like 60fps or high speed recording.</p>
* <p><b>Units</b>: Frames per second (FPS)</p>
* <p><b>Range of valid values:</b><br>
* Any of the entries in {@link CameraCharacteristics#CONTROL_AE_AVAILABLE_TARGET_FPS_RANGES android.control.aeAvailableTargetFpsRanges}</p>
@@ -2365,6 +2372,12 @@
* {@link CaptureRequest#CONTROL_VIDEO_STABILIZATION_MODE android.control.videoStabilizationMode} field will return
* OFF if the recording output is not stabilized, or if there are no output
* Surface types that can be stabilized.</p>
+ * <p>The application is strongly recommended to call
+ * {@link SessionConfiguration#setSessionParameters }
+ * with the desired video stabilization mode before creating the capture session.
+ * Video stabilization mode is a session parameter on many devices. Specifying
+ * it at session creation time helps avoid reconfiguration delay caused by difference
+ * between the default value and the first CaptureRequest.</p>
* <p>If a camera device supports both this mode and OIS
* ({@link CaptureRequest#LENS_OPTICAL_STABILIZATION_MODE android.lens.opticalStabilizationMode}), turning both modes on may
* produce undesirable interaction, so it is recommended not to enable
diff --git a/core/java/android/hardware/camera2/camera_platform.aconfig b/core/java/android/hardware/camera2/camera_platform.aconfig
deleted file mode 100644
index 67f6300..0000000
--- a/core/java/android/hardware/camera2/camera_platform.aconfig
+++ /dev/null
@@ -1,8 +0,0 @@
-package: "com.android.hardware.camera2"
-
-flag {
- namespace: "camera_platform"
- name: "initial_test_flag"
- description: "Flag infrastructure test flag"
- bug: "292631208"
-}
diff --git a/core/java/android/hardware/camera2/impl/CameraAdvancedExtensionSessionImpl.java b/core/java/android/hardware/camera2/impl/CameraAdvancedExtensionSessionImpl.java
index c7e74c0..4ef4572 100644
--- a/core/java/android/hardware/camera2/impl/CameraAdvancedExtensionSessionImpl.java
+++ b/core/java/android/hardware/camera2/impl/CameraAdvancedExtensionSessionImpl.java
@@ -673,8 +673,8 @@
synchronized (mInterfaceLock) {
try {
if (mSessionProcessor != null) {
- mSessionProcessor.onCaptureSessionStart(mRequestProcessor);
mInitialized = true;
+ mSessionProcessor.onCaptureSessionStart(mRequestProcessor);
} else {
Log.v(TAG, "Failed to start capture session, session " +
" released before extension start!");
diff --git a/core/java/android/hardware/camera2/impl/CameraDeviceImpl.java b/core/java/android/hardware/camera2/impl/CameraDeviceImpl.java
index 181ab2c..994037b 100644
--- a/core/java/android/hardware/camera2/impl/CameraDeviceImpl.java
+++ b/core/java/android/hardware/camera2/impl/CameraDeviceImpl.java
@@ -1149,11 +1149,7 @@
"remove holder for requestId %d, "
+ "because lastFrame is %d.",
requestId, lastFrameNumber));
- }
- }
- if (holder != null) {
- if (DEBUG) {
Log.v(TAG, "immediately trigger onCaptureSequenceAborted because"
+ " request did not reach HAL");
}
@@ -2180,11 +2176,9 @@
final CaptureCallbackHolder holder =
CameraDeviceImpl.this.mCaptureCallbackMap.get(requestId);
- final CaptureRequest request = holder.getRequest(resultExtras.getSubsequenceId());
boolean isPartialResult =
(resultExtras.getPartialResultCount() < mTotalPartialCount);
- int requestType = request.getRequestType();
// Check if we have a callback for this
if (holder == null) {
@@ -2194,12 +2188,11 @@
+ frameNumber);
}
- updateTracker(requestId, frameNumber, requestType, /*result*/null,
- isPartialResult);
-
return;
}
+ final CaptureRequest request = holder.getRequest(resultExtras.getSubsequenceId());
+ int requestType = request.getRequestType();
if (isClosed()) {
if (DEBUG) {
Log.d(TAG,
diff --git a/core/java/android/hardware/display/DisplayManager.java b/core/java/android/hardware/display/DisplayManager.java
index 4323bf8..f0c87a1 100644
--- a/core/java/android/hardware/display/DisplayManager.java
+++ b/core/java/android/hardware/display/DisplayManager.java
@@ -554,7 +554,8 @@
EVENT_FLAG_DISPLAY_CHANGED,
EVENT_FLAG_DISPLAY_REMOVED,
EVENT_FLAG_DISPLAY_BRIGHTNESS,
- EVENT_FLAG_HDR_SDR_RATIO_CHANGED
+ EVENT_FLAG_HDR_SDR_RATIO_CHANGED,
+ EVENT_FLAG_DISPLAY_CONNECTION_CHANGED,
})
@Retention(RetentionPolicy.SOURCE)
public @interface EventsMask {}
@@ -610,6 +611,13 @@
*/
public static final long EVENT_FLAG_HDR_SDR_RATIO_CHANGED = 1L << 4;
+ /**
+ * Event flag to register for a display's connection changed.
+ *
+ * @hide
+ */
+ public static final long EVENT_FLAG_DISPLAY_CONNECTION_CHANGED = 1L << 5;
+
/** @hide */
public DisplayManager(Context context) {
mContext = context;
@@ -891,6 +899,25 @@
}
/**
+ * Enable a connected display that is currently disabled.
+ * @hide
+ */
+ @RequiresPermission("android.permission.MANAGE_DISPLAYS")
+ public void enableConnectedDisplay(int displayId) {
+ mGlobal.enableConnectedDisplay(displayId);
+ }
+
+
+ /**
+ * Disable a connected display that is currently enabled.
+ * @hide
+ */
+ @RequiresPermission("android.permission.MANAGE_DISPLAYS")
+ public void disableConnectedDisplay(int displayId) {
+ mGlobal.disableConnectedDisplay(displayId);
+ }
+
+ /**
* Set the level of color saturation to apply to the display.
* @param level The amount of saturation to apply, between 0 and 1 inclusive.
* 0 produces a grayscale image, 1 is normal.
@@ -1633,6 +1660,24 @@
* @param displayId The id of the logical display that changed.
*/
void onDisplayChanged(int displayId);
+
+ /**
+ * Called when a display is connected, but not necessarily used.
+ *
+ * A display is always connected before being added.
+ * @hide
+ */
+ default void onDisplayConnected(int displayId) { }
+
+ /**
+ * Called when a display is disconnected.
+ *
+ * If a display was added, a display is only disconnected after it has been removed. Note,
+ * however, that the display may have been disconnected by the time the removed event is
+ * received by the listener.
+ * @hide
+ */
+ default void onDisplayDisconnected(int displayId) { }
}
/**
diff --git a/core/java/android/hardware/display/DisplayManagerGlobal.java b/core/java/android/hardware/display/DisplayManagerGlobal.java
index 3be82bc..6d6085b 100644
--- a/core/java/android/hardware/display/DisplayManagerGlobal.java
+++ b/core/java/android/hardware/display/DisplayManagerGlobal.java
@@ -90,6 +90,8 @@
EVENT_DISPLAY_REMOVED,
EVENT_DISPLAY_BRIGHTNESS_CHANGED,
EVENT_DISPLAY_HDR_SDR_RATIO_CHANGED,
+ EVENT_DISPLAY_CONNECTED,
+ EVENT_DISPLAY_DISCONNECTED,
})
@Retention(RetentionPolicy.SOURCE)
public @interface DisplayEvent {}
@@ -99,6 +101,8 @@
public static final int EVENT_DISPLAY_REMOVED = 3;
public static final int EVENT_DISPLAY_BRIGHTNESS_CHANGED = 4;
public static final int EVENT_DISPLAY_HDR_SDR_RATIO_CHANGED = 5;
+ public static final int EVENT_DISPLAY_CONNECTED = 6;
+ public static final int EVENT_DISPLAY_DISCONNECTED = 7;
@UnsupportedAppUsage
private static DisplayManagerGlobal sInstance;
@@ -414,6 +418,9 @@
private void updateCallbackIfNeededLocked() {
int mask = calculateEventsMaskLocked();
+ if (DEBUG) {
+ Log.d(TAG, "Mask for listener: " + mask);
+ }
if (mask != mRegisteredEventsMask) {
try {
mDm.registerCallbackWithEventMask(mCallback, mask);
@@ -459,6 +466,33 @@
}
}
+ /**
+ * Enable a connected display that is currently disabled.
+ * @hide
+ */
+ @RequiresPermission("android.permission.MANAGE_DISPLAYS")
+ public void enableConnectedDisplay(int displayId) {
+ try {
+ mDm.enableConnectedDisplay(displayId);
+ } catch (RemoteException ex) {
+ Log.e(TAG, "Error trying to enable external display", ex);
+ }
+ }
+
+
+ /**
+ * Disable a connected display that is currently enabled.
+ * @hide
+ */
+ @RequiresPermission("android.permission.MANAGE_DISPLAYS")
+ public void disableConnectedDisplay(int displayId) {
+ try {
+ mDm.disableConnectedDisplay(displayId);
+ } catch (RemoteException ex) {
+ Log.e(TAG, "Error trying to enable external display", ex);
+ }
+ }
+
public void startWifiDisplayScan() {
synchronized (mLock) {
if (mWifiDisplayScanNestCount++ == 0) {
@@ -1179,6 +1213,16 @@
mListener.onDisplayChanged(msg.arg1);
}
break;
+ case EVENT_DISPLAY_CONNECTED:
+ if ((mEventsMask & DisplayManager.EVENT_FLAG_DISPLAY_CONNECTION_CHANGED) != 0) {
+ mListener.onDisplayConnected(msg.arg1);
+ }
+ break;
+ case EVENT_DISPLAY_DISCONNECTED:
+ if ((mEventsMask & DisplayManager.EVENT_FLAG_DISPLAY_CONNECTION_CHANGED) != 0) {
+ mListener.onDisplayDisconnected(msg.arg1);
+ }
+ break;
}
if (DEBUG) {
Trace.endSection();
@@ -1302,6 +1346,10 @@
return "BRIGHTNESS_CHANGED";
case EVENT_DISPLAY_HDR_SDR_RATIO_CHANGED:
return "HDR_SDR_RATIO_CHANGED";
+ case EVENT_DISPLAY_CONNECTED:
+ return "EVENT_DISPLAY_CONNECTED";
+ case EVENT_DISPLAY_DISCONNECTED:
+ return "EVENT_DISPLAY_DISCONNECTED";
}
return "UNKNOWN";
}
diff --git a/core/java/android/hardware/display/IDisplayManager.aidl b/core/java/android/hardware/display/IDisplayManager.aidl
index 18edbdb..83de4e4 100644
--- a/core/java/android/hardware/display/IDisplayManager.aidl
+++ b/core/java/android/hardware/display/IDisplayManager.aidl
@@ -227,4 +227,12 @@
// Query overlay properties of the device
OverlayProperties getOverlaySupport();
+
+ // Enable a connected display that is disabled.
+ @EnforcePermission("MANAGE_DISPLAYS")
+ void enableConnectedDisplay(int displayId);
+
+ // Disable a connected display that is enabled.
+ @EnforcePermission("MANAGE_DISPLAYS")
+ void disableConnectedDisplay(int displayId);
}
diff --git a/core/java/android/hardware/radio/UniqueProgramIdentifier.aidl b/core/java/android/hardware/radio/UniqueProgramIdentifier.aidl
new file mode 100644
index 0000000..2ed2bcc
--- /dev/null
+++ b/core/java/android/hardware/radio/UniqueProgramIdentifier.aidl
@@ -0,0 +1,20 @@
+/**
+ * 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 android.hardware.radio;
+
+/** @hide */
+parcelable UniqueProgramIdentifier;
diff --git a/core/java/android/hardware/radio/UniqueProgramIdentifier.java b/core/java/android/hardware/radio/UniqueProgramIdentifier.java
new file mode 100644
index 0000000..ea8948e
--- /dev/null
+++ b/core/java/android/hardware/radio/UniqueProgramIdentifier.java
@@ -0,0 +1,163 @@
+/**
+ * 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 android.hardware.radio;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import java.util.Arrays;
+import java.util.List;
+import java.util.Objects;
+import java.util.stream.Stream;
+
+/**
+ * Identifier that can uniquely identifies a program.
+ *
+ * This is a transport class used for internal communication between
+ * Broadcast Radio Service and Radio Manager. Do not use it directly.
+ *
+ * @hide
+ */
+public final class UniqueProgramIdentifier implements Parcelable {
+
+ @NonNull private final ProgramSelector.Identifier mPrimaryId;
+ @NonNull private final ProgramSelector.Identifier[] mCriticalSecondaryIds;
+
+ /**
+ * Check whether some secondary identifier is needed to uniquely specify a program for
+ * a given primary identifier type
+ *
+ * @param type primary identifier type {@link ProgramSelector.IdentifierType}
+ * @return whether some secondary identifier is needed to uniquely specify a program.
+ */
+ public static boolean requireCriticalSecondaryIds(@ProgramSelector.IdentifierType int type) {
+ return type == ProgramSelector.IDENTIFIER_TYPE_DAB_DMB_SID_EXT || type
+ == ProgramSelector.IDENTIFIER_TYPE_DAB_SID_EXT;
+ }
+
+ public UniqueProgramIdentifier(ProgramSelector selector) {
+ Objects.requireNonNull(selector, "Program selector can not be null");
+ mPrimaryId = selector.getPrimaryId();
+ switch (mPrimaryId.getType()) {
+ case ProgramSelector.IDENTIFIER_TYPE_DAB_DMB_SID_EXT:
+ case ProgramSelector.IDENTIFIER_TYPE_DAB_SID_EXT:
+ ProgramSelector.Identifier ensembleId = null;
+ ProgramSelector.Identifier frequencyId = null;
+ ProgramSelector.Identifier[] secondaryIds = selector.getSecondaryIds();
+ for (int i = 0; i < secondaryIds.length; i++) {
+ if (ensembleId == null && secondaryIds[i].getType()
+ == ProgramSelector.IDENTIFIER_TYPE_DAB_ENSEMBLE) {
+ ensembleId = selector.getSecondaryIds()[i];
+ } else if (frequencyId == null && secondaryIds[i].getType()
+ == ProgramSelector.IDENTIFIER_TYPE_DAB_FREQUENCY) {
+ frequencyId = secondaryIds[i];
+ }
+ if (ensembleId != null && frequencyId != null) {
+ break;
+ }
+ }
+ if (ensembleId == null) {
+ if (frequencyId == null) {
+ mCriticalSecondaryIds = new ProgramSelector.Identifier[]{};
+ } else {
+ mCriticalSecondaryIds = new ProgramSelector.Identifier[]{frequencyId};
+ }
+ } else if (frequencyId == null) {
+ mCriticalSecondaryIds = new ProgramSelector.Identifier[]{ensembleId};
+ } else {
+ mCriticalSecondaryIds = new ProgramSelector.Identifier[]{ensembleId,
+ frequencyId};
+ }
+ break;
+ default:
+ mCriticalSecondaryIds = new ProgramSelector.Identifier[]{};
+ }
+
+ }
+
+ public UniqueProgramIdentifier(ProgramSelector.Identifier primaryId) {
+ mPrimaryId = primaryId;
+ mCriticalSecondaryIds = new ProgramSelector.Identifier[]{};
+ }
+
+ @NonNull
+ public ProgramSelector.Identifier getPrimaryId() {
+ return mPrimaryId;
+ }
+
+ @NonNull
+ public List<ProgramSelector.Identifier> getCriticalSecondaryIds() {
+ return List.of(mCriticalSecondaryIds);
+ }
+
+ @NonNull
+ @Override
+ public String toString() {
+ return new StringBuilder("UniqueProgramIdentifier(primary=").append(mPrimaryId)
+ .append(", criticalSecondary=")
+ .append(Arrays.toString(mCriticalSecondaryIds)).append(")")
+ .toString();
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(mPrimaryId, Arrays.hashCode(mCriticalSecondaryIds));
+ }
+
+ @Override
+ public boolean equals(@Nullable Object obj) {
+ if (this == obj) return true;
+ if (!(obj instanceof UniqueProgramIdentifier)) return false;
+ UniqueProgramIdentifier other = (UniqueProgramIdentifier) obj;
+ return other.mPrimaryId.equals(mPrimaryId)
+ && Arrays.equals(other.mCriticalSecondaryIds, mCriticalSecondaryIds);
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ private UniqueProgramIdentifier(Parcel in) {
+ mPrimaryId = in.readTypedObject(ProgramSelector.Identifier.CREATOR);
+ mCriticalSecondaryIds = in.createTypedArray(ProgramSelector.Identifier.CREATOR);
+ }
+
+ @Override
+ public void writeToParcel(@NonNull Parcel dest, int flags) {
+ dest.writeTypedObject(mPrimaryId, 0);
+ dest.writeTypedArray(mCriticalSecondaryIds, 0);
+ if (Stream.of(mCriticalSecondaryIds).anyMatch(Objects::isNull)) {
+ throw new IllegalArgumentException(
+ "criticalSecondaryIds list must not contain nulls");
+ }
+ }
+
+ @NonNull
+ public static final Parcelable.Creator<UniqueProgramIdentifier> CREATOR =
+ new Parcelable.Creator<UniqueProgramIdentifier>() {
+ public UniqueProgramIdentifier createFromParcel(Parcel in) {
+ return new UniqueProgramIdentifier(in);
+ }
+
+ public UniqueProgramIdentifier[] newArray(int size) {
+ return new UniqueProgramIdentifier[size];
+ }
+ };
+}
diff --git a/core/java/android/inputmethodservice/InputMethodService.java b/core/java/android/inputmethodservice/InputMethodService.java
index 8f653b3..c0a44b1 100644
--- a/core/java/android/inputmethodservice/InputMethodService.java
+++ b/core/java/android/inputmethodservice/InputMethodService.java
@@ -650,8 +650,6 @@
private InlineSuggestionSessionController mInlineSuggestionSessionController;
- private boolean mHideNavBarForKeyboard;
- private boolean mIsAutomotive;
private @NonNull OptionalInt mHandwritingRequestId = OptionalInt.empty();
private InputEventReceiver mHandwritingEventReceiver;
private Handler mHandler;
@@ -1622,7 +1620,7 @@
// shown the first time (cold start).
mSettingsObserver.shouldShowImeWithHardKeyboard();
- mHideNavBarForKeyboard = getApplicationContext().getResources().getBoolean(
+ final boolean hideNavBarForKeyboard = getApplicationContext().getResources().getBoolean(
com.android.internal.R.bool.config_hideNavBarForKeyboard);
initConfigurationTracker();
@@ -1668,7 +1666,7 @@
// screen real estate. When this happens, the IME window should animate from the
// bottom of the screen to reduce the jank that happens from the lack of synchronization
// between the bottom system window and the IME window.
- if (mHideNavBarForKeyboard) {
+ if (hideNavBarForKeyboard) {
window.setDecorFitsSystemWindows(false);
}
}
@@ -2366,9 +2364,7 @@
public void setExtractView(View view) {
mExtractFrame.removeAllViews();
- mExtractFrame.addView(view, new FrameLayout.LayoutParams(
- ViewGroup.LayoutParams.MATCH_PARENT,
- ViewGroup.LayoutParams.MATCH_PARENT));
+ mExtractFrame.addView(view, new FrameLayout.LayoutParams(MATCH_PARENT, MATCH_PARENT));
mExtractView = view;
if (view != null) {
mExtractEditText = view.findViewById(
@@ -2387,7 +2383,7 @@
mExtractAction = null;
}
}
-
+
/**
* Replaces the current candidates view with a new one. You only need to
* call this when dynamically changing the view; normally, you should
@@ -2396,11 +2392,9 @@
*/
public void setCandidatesView(View view) {
mCandidatesFrame.removeAllViews();
- mCandidatesFrame.addView(view, new FrameLayout.LayoutParams(
- ViewGroup.LayoutParams.MATCH_PARENT,
- ViewGroup.LayoutParams.WRAP_CONTENT));
+ mCandidatesFrame.addView(view, new FrameLayout.LayoutParams(MATCH_PARENT, WRAP_CONTENT));
}
-
+
/**
* Replaces the current input view with a new one. You only need to
* call this when dynamically changing the view; normally, you should
@@ -2409,12 +2403,10 @@
*/
public void setInputView(View view) {
mInputFrame.removeAllViews();
- mInputFrame.addView(view, new FrameLayout.LayoutParams(
- ViewGroup.LayoutParams.MATCH_PARENT,
- ViewGroup.LayoutParams.WRAP_CONTENT));
+ mInputFrame.addView(view, new FrameLayout.LayoutParams(MATCH_PARENT, WRAP_CONTENT));
mInputView = view;
}
-
+
/**
* Called by the framework to create the layout for showing extracted text.
* Only called when in fullscreen mode. The returned view hierarchy must
@@ -3448,9 +3440,12 @@
return false;
}
+ /**
+ * Not implemented in this class.
+ */
public void onAppPrivateCommand(String action, Bundle data) {
}
-
+
/**
* Handle a request by the system to toggle the soft input area.
*/
@@ -4092,11 +4087,6 @@
| (isInputViewShown() ? IME_VISIBLE : 0);
}
- private boolean isAutomotive() {
- return getApplicationContext().getPackageManager().hasSystemFeature(
- PackageManager.FEATURE_AUTOMOTIVE);
- }
-
/**
* Performs a dump of the InputMethodService's internal state. Override
* to add your own information to the dump.
diff --git a/core/java/android/os/INetworkManagementService.aidl b/core/java/android/os/INetworkManagementService.aidl
index ed14652..1a3dcee 100644
--- a/core/java/android/os/INetworkManagementService.aidl
+++ b/core/java/android/os/INetworkManagementService.aidl
@@ -126,50 +126,65 @@
/**
* Returns true if IP forwarding is enabled
*/
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = 34, trackingBug = 170729553,
+ publicAlternatives = "Use {@code android.net.INetd#ipfwdEnabled}")
boolean getIpForwardingEnabled();
/**
* Enables/Disables IP Forwarding
*/
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = 34, trackingBug = 170729553,
+ publicAlternatives = "Avoid using this directly. Instead, enable tethering with "
+ + "{@code android.net.TetheringManager#startTethering}. See also "
+ + "{@code INetd#ipfwdEnableForwarding(String)}.")
void setIpForwardingEnabled(boolean enabled);
/**
* Start tethering services with the specified dhcp server range
* arg is a set of start end pairs defining the ranges.
*/
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = 34, trackingBug = 170729553,
+ publicAlternatives = "{@code android.net.TetheringManager#startTethering}")
void startTethering(in String[] dhcpRanges);
/**
* Stop currently running tethering services
*/
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = 34, trackingBug = 170729553,
+ publicAlternatives = "{@code android.net.TetheringManager#stopTethering(int)}")
void stopTethering();
/**
* Returns true if tethering services are started
*/
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = 34, trackingBug = 170729553,
+ publicAlternatives = "Generally track your own tethering requests. "
+ + "See also {@code android.net.INetd#tetherIsEnabled()}")
boolean isTetheringStarted();
/**
* Tethers the specified interface
*/
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = 34, trackingBug = 170729553,
+ publicAlternatives = "Avoid using this directly. Instead, enable tethering with "
+ + "{@code android.net.TetheringManager#startTethering}. See also "
+ + "{@code com.android.net.module.util.NetdUtils#tetherInterface}.")
void tetherInterface(String iface);
/**
* Untethers the specified interface
*/
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = 34, trackingBug = 170729553,
+ publicAlternatives = "Avoid using this directly. Instead, disable "
+ + "tethering with {@code android.net.TetheringManager#stopTethering(int)}. "
+ + "See also {@code NetdUtils#untetherInterface}.")
void untetherInterface(String iface);
/**
* Returns a list of currently tethered interfaces
*/
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = 34, trackingBug = 170729553,
+ publicAlternatives = "{@code android.net.TetheringManager#getTetheredIfaces()}")
String[] listTetheredInterfaces();
/**
@@ -177,13 +192,17 @@
* The address and netmask of the external interface is used for
* the NAT'ed network.
*/
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = 34, trackingBug = 170729553,
+ publicAlternatives = "Avoid using this directly. Instead, enable tethering with "
+ + "{@code android.net.TetheringManager#startTethering}.")
void enableNat(String internalInterface, String externalInterface);
/**
* Disables Network Address Translation between two interfaces.
*/
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = 34, trackingBug = 170729553,
+ publicAlternatives = "Avoid using this directly. Instead, disable tethering with "
+ + "{@code android.net.TetheringManager#stopTethering(int)}.")
void disableNat(String internalInterface, String externalInterface);
/**
diff --git a/core/java/android/os/LocaleList.java b/core/java/android/os/LocaleList.java
index b74bb33..82cdd28 100644
--- a/core/java/android/os/LocaleList.java
+++ b/core/java/android/os/LocaleList.java
@@ -30,6 +30,7 @@
import java.util.Arrays;
import java.util.Collection;
import java.util.HashSet;
+import java.util.List;
import java.util.Locale;
/**
@@ -151,6 +152,25 @@
}
/**
+ * Find the intersection between this LocaleList and another
+ * @return a String array of the Locales in both LocaleLists
+ * {@hide}
+ */
+ @NonNull
+ public String[] getIntersection(@NonNull LocaleList other) {
+ List<String> intersection = new ArrayList<>();
+ for (Locale l1 : mList) {
+ for (Locale l2 : other.mList) {
+ if (matchesLanguageAndScript(l2, l1)) {
+ intersection.add(l1.toLanguageTag());
+ break;
+ }
+ }
+ }
+ return intersection.toArray(new String[0]);
+ }
+
+ /**
* Creates a new {@link LocaleList}.
*
* If two or more same locales are passed, the repeated locales will be dropped.
diff --git a/core/java/android/os/flags.aconfig b/core/java/android/os/flags.aconfig
new file mode 100644
index 0000000..851aa6d
--- /dev/null
+++ b/core/java/android/os/flags.aconfig
@@ -0,0 +1,8 @@
+package: "android.os"
+
+flag {
+ name: "disallow_cellular_null_ciphers_restriction"
+ namespace: "cellular_security"
+ description: "Guards a new UserManager user restriction that admins can use to require cellular encryption on their managed devices."
+ bug: "276752881"
+}
diff --git a/core/java/android/os/storage/StorageManagerInternal.java b/core/java/android/os/storage/StorageManagerInternal.java
index 22e8251..8961846 100644
--- a/core/java/android/os/storage/StorageManagerInternal.java
+++ b/core/java/android/os/storage/StorageManagerInternal.java
@@ -20,8 +20,11 @@
import android.annotation.Nullable;
import android.annotation.UserIdInt;
import android.content.pm.UserInfo;
+import android.os.IInstalld;
import android.os.IVold;
+import android.os.ParcelFileDescriptor;
+import java.io.IOException;
import java.util.List;
import java.util.Set;
@@ -185,4 +188,17 @@
public abstract void prepareUserStorageForMove(String fromVolumeUuid, String toVolumeUuid,
List<UserInfo> users);
+ /**
+ * A proxy call to the corresponding method in Installer.
+ * @see com.android.server.pm.Installer#createFsveritySetupAuthToken()
+ */
+ public abstract IInstalld.IFsveritySetupAuthToken createFsveritySetupAuthToken(
+ ParcelFileDescriptor authFd, int appUid, @UserIdInt int userId) throws IOException;
+
+ /**
+ * A proxy call to the corresponding method in Installer.
+ * @see com.android.server.pm.Installer#enableFsverity()
+ */
+ public abstract int enableFsverity(IInstalld.IFsveritySetupAuthToken authToken, String filePath,
+ String packageName) throws IOException;
}
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index b9a6583..c3c802b 100644
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -5311,7 +5311,6 @@
public static final Uri DEFAULT_RINGTONE_URI = getUriFor(RINGTONE);
/** {@hide} */
- @Readable
public static final String RINGTONE_CACHE = "ringtone_cache";
/** {@hide} */
public static final Uri RINGTONE_CACHE_URI = getUriFor(RINGTONE_CACHE);
@@ -5886,15 +5885,6 @@
public static final String MULTI_AUDIO_FOCUS_ENABLED = "multi_audio_focus_enabled";
/**
- * Whether desktop mode is enabled or not.
- * 0 = off
- * 1 = on
- * @hide
- */
- @Readable
- public static final String DESKTOP_MODE = "desktop_mode";
-
- /**
* The information of locale preference. This records user's preference to avoid
* unsynchronized and existing locale preference in
* {@link Locale#getDefault(Locale.Category)}.
@@ -6067,7 +6057,6 @@
PRIVATE_SETTINGS.add(SHOW_BATTERY_PERCENT);
PRIVATE_SETTINGS.add(DISPLAY_COLOR_MODE);
PRIVATE_SETTINGS.add(DISPLAY_COLOR_MODE_VENDOR_HINT);
- PRIVATE_SETTINGS.add(DESKTOP_MODE);
PRIVATE_SETTINGS.add(LOCALE_PREFERENCES);
PRIVATE_SETTINGS.add(TOUCHPAD_POINTER_SPEED);
PRIVATE_SETTINGS.add(TOUCHPAD_NATURAL_SCROLLING);
@@ -11657,6 +11646,21 @@
"accessibility_floating_menu_migration_tooltip_prompt";
/**
+ * For the force dark theme feature which inverts any apps that don't already support dark
+ * theme.
+ *
+ * If true, it will automatically invert any app that is mainly light.
+ *
+ * This is related to the force dark override setting, however it will always force the apps
+ * colors and will ignore any developer hints or opt-out APIs.
+ *
+ * @hide
+ */
+ @Readable
+ public static final String ACCESSIBILITY_FORCE_INVERT_COLOR_ENABLED =
+ "accessibility_force_invert_color_enabled";
+
+ /**
* Whether the Adaptive connectivity option is enabled.
*
* @hide
diff --git a/core/java/android/security/FileIntegrityManager.java b/core/java/android/security/FileIntegrityManager.java
index 266046e..7869404 100644
--- a/core/java/android/security/FileIntegrityManager.java
+++ b/core/java/android/security/FileIntegrityManager.java
@@ -16,12 +16,21 @@
package android.security;
+import android.annotation.FlaggedApi;
import android.annotation.NonNull;
+import android.annotation.Nullable;
import android.annotation.RequiresPermission;
import android.annotation.SystemService;
import android.content.Context;
+import android.os.IInstalld.IFsveritySetupAuthToken;
+import android.os.ParcelFileDescriptor;
import android.os.RemoteException;
+import android.system.ErrnoException;
+import com.android.internal.security.VerityUtils;
+
+import java.io.File;
+import java.io.IOException;
import java.security.cert.CertificateEncodingException;
import java.security.cert.X509Certificate;
@@ -55,6 +64,67 @@
}
/**
+ * Enables fs-verity to the owned file under the calling app's private directory. It always uses
+ * the common configuration, i.e. SHA-256 digest algorithm, 4K block size, and without salt.
+ *
+ * The operation can only succeed when the file is not opened as writable by any process.
+ *
+ * It takes O(file size) time to build the underlying data structure for continuous
+ * verification. The operation is atomic, i.e. it's either enabled or not, even in case of
+ * power failure during or after the call.
+ *
+ * Note for the API users: When the file's authenticity is crucial, the app typical needs to
+ * perform a signature check by itself before using the file. The signature is often delivered
+ * as a separate file and stored next to the targeting file in the filesystem. The public key of
+ * the signer (normally the same app developer) can be put in the APK, and the app can use the
+ * public key to verify the signature to the file's actual fs-verity digest (from {@link
+ * #getFsverityDigest}) before using the file. The exact format is not prescribed by the
+ * framework. App developers may choose to use common practices like JCA for the signing and
+ * verification, or their own preferred approach.
+ *
+ * @param file The file to enable fs-verity. It should be an absolute path.
+ *
+ * @see <a href="https://www.kernel.org/doc/html/next/filesystems/fsverity.html">Kernel doc</a>
+ */
+ @FlaggedApi(Flags.FLAG_FSVERITY_API)
+ public void setupFsverity(@NonNull File file) throws IOException {
+ if (!file.isAbsolute()) {
+ throw new IllegalArgumentException("Expect an absolute path");
+ }
+ IFsveritySetupAuthToken authToken;
+ // fs-verity setup requires no writable fd to the file. Make sure it's closed before
+ // continue.
+ try (var authFd = ParcelFileDescriptor.open(file, ParcelFileDescriptor.MODE_READ_WRITE)) {
+ authToken = mService.createAuthToken(authFd);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+
+ try {
+ int errno = mService.setupFsverity(authToken, file.getPath(),
+ mContext.getPackageName());
+ if (errno != 0) {
+ new ErrnoException("setupFsverity", errno).rethrowAsIOException();
+ }
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
+ * Returns the fs-verity digest for the owned file under the calling app's
+ * private directory, or null when the file does not have fs-verity enabled.
+ *
+ * @param file The file to measure the fs-verity digest.
+ * @return The fs-verity digeset in byte[], null if none.
+ * @see <a href="https://www.kernel.org/doc/html/next/filesystems/fsverity.html">Kernel doc</a>
+ */
+ @FlaggedApi(Flags.FLAG_FSVERITY_API)
+ public @Nullable byte[] getFsverityDigest(@NonNull File file) throws IOException {
+ return VerityUtils.getFsverityDigest(file.getPath());
+ }
+
+ /**
* Returns whether the given certificate can be used to prove app's install source. Always
* return false if the feature is not supported.
*
diff --git a/core/java/android/security/IFileIntegrityService.aidl b/core/java/android/security/IFileIntegrityService.aidl
index dff347e..1a6cf88 100644
--- a/core/java/android/security/IFileIntegrityService.aidl
+++ b/core/java/android/security/IFileIntegrityService.aidl
@@ -16,6 +16,9 @@
package android.security;
+import android.os.ParcelFileDescriptor;
+import android.os.IInstalld;
+
/**
* Binder interface to communicate with FileIntegrityService.
* @hide
@@ -23,4 +26,8 @@
interface IFileIntegrityService {
boolean isApkVeritySupported();
boolean isAppSourceCertificateTrusted(in byte[] certificateBytes, in String packageName);
+
+ IInstalld.IFsveritySetupAuthToken createAuthToken(in ParcelFileDescriptor authFd);
+ int setupFsverity(IInstalld.IFsveritySetupAuthToken authToken, in String filePath,
+ in String packageName);
}
diff --git a/core/java/android/security/OWNERS b/core/java/android/security/OWNERS
index 22b1f02..96c0be7 100644
--- a/core/java/android/security/OWNERS
+++ b/core/java/android/security/OWNERS
@@ -6,4 +6,5 @@
per-file *NetworkSecurityPolicy.java = file:net/OWNERS
per-file Confirmation*.java = file:/keystore/OWNERS
-per-file FileIntegrityManager.java = victorhsieh@google.com
+per-file FileIntegrityManager.java = file:platform/system/security:/fsverity/OWNERS
+per-file IFileIntegrityService.aidl = file:platform/system/security:/fsverity/OWNERS
diff --git a/core/java/android/security/flags.aconfig b/core/java/android/security/flags.aconfig
new file mode 100644
index 0000000..b27dac2
--- /dev/null
+++ b/core/java/android/security/flags.aconfig
@@ -0,0 +1,8 @@
+package: "android.security"
+
+flag {
+ name: "fsverity_api"
+ namespace: "hardware_backed_security"
+ description: "Feature flag for fs-verity API"
+ bug: "285185747"
+}
diff --git a/core/java/android/service/credentials/Action.java b/core/java/android/service/credentials/Action.java
index 55133ae..9b1132a 100644
--- a/core/java/android/service/credentials/Action.java
+++ b/core/java/android/service/credentials/Action.java
@@ -46,7 +46,14 @@
* <p> See details on usage of {@code Action} for various actionable entries in
* {@link BeginCreateCredentialResponse} and {@link BeginGetCredentialResponse}.
*
- * @param slice the display content to be displayed on the UI, along with this action
+ * @param slice the slice containing the metadata to be shown on the UI, must be constructed
+ * through the {@link androidx.credentials.provider} Jetpack library;
+ * If constructed manually, the {@code slice} object must
+ * contain the non-null properties of the
+ * {@link androidx.credentials.provider.Action} class populated as slice items
+ * against specific hints as used in the class's {@code toSlice} method,
+ * since the Android System uses this library to parse the {@code slice} and
+ * extract the required attributes
*/
public Action(@NonNull Slice slice) {
Objects.requireNonNull(slice, "slice must not be null");
diff --git a/core/java/android/service/credentials/CreateEntry.java b/core/java/android/service/credentials/CreateEntry.java
index 6a9f09f..2495c7d 100644
--- a/core/java/android/service/credentials/CreateEntry.java
+++ b/core/java/android/service/credentials/CreateEntry.java
@@ -66,7 +66,14 @@
/**
* Constructs a CreateEntry to be displayed on the UI.
*
- * @param slice the display content to be displayed on the UI, along with this entry
+ * @param slice the slice containing the metadata to be shown on the UI, must be constructed
+ * through the {@link androidx.credentials.provider} Jetpack library;
+ * If constructed manually, the {@code slice} object must
+ * contain the non-null properties of the
+ * {@link androidx.credentials.provider.CreateEntry} class populated as slice items
+ * against specific hints as used in the class's {@code toSlice} method,
+ * since the Android System uses this library to parse the {@code slice} and
+ * extract the required attributes
*/
public CreateEntry(
@NonNull Slice slice) {
diff --git a/core/java/android/service/credentials/CredentialEntry.java b/core/java/android/service/credentials/CredentialEntry.java
index 512d833..53094e8 100644
--- a/core/java/android/service/credentials/CredentialEntry.java
+++ b/core/java/android/service/credentials/CredentialEntry.java
@@ -69,8 +69,14 @@
* receive the complete corresponding
* {@link GetCredentialRequest}.
* @param type the type of the credential for which this credential entry is being created
- * @param slice the slice containing the metadata to be shown on the UI. Must be
- * constructed through the androidx.credentials jetpack library.
+ * @param slice the slice containing the metadata to be shown on the UI, must be constructed
+ * through the {@link androidx.credentials.provider} Jetpack library;
+ * If constructed manually, the {@code slice} object must
+ * contain the non-null properties of the
+ * {@link androidx.credentials.provider.CredentialEntry} class populated as slice
+ * items against specific hints as used in the class's {@code toSlice} method,
+ * since the Android System uses this library to parse the {@code slice} and
+ * extract the required attributes
*
* @throws IllegalArgumentException If {@code beginGetCredentialOptionId} or {@code type}
* is null, or empty
diff --git a/core/java/android/service/credentials/RemoteEntry.java b/core/java/android/service/credentials/RemoteEntry.java
index 5b3218a..5fd9925 100644
--- a/core/java/android/service/credentials/RemoteEntry.java
+++ b/core/java/android/service/credentials/RemoteEntry.java
@@ -73,7 +73,14 @@
/**
* Constructs a RemoteEntry to be displayed on the UI.
*
- * @param slice the display content to be displayed on the UI, along with this entry
+ * @param slice the slice containing the metadata to be shown on the UI, must be constructed
+ * through the {@link androidx.credentials.provider} Jetpack library;
+ * If constructed manually, the {@code slice} object must
+ * contain the non-null properties of the
+ * {@link androidx.credentials.provider.RemoteEntry} class populated as slice items
+ * against specific hints as used in the class's {@code toSlice} method,
+ * since the Android System uses this library to parse the {@code slice} and
+ * extract the required attributes
*/
public RemoteEntry(
@NonNull Slice slice) {
diff --git a/core/java/android/service/dreams/OWNERS b/core/java/android/service/dreams/OWNERS
index 489a5f6..77bcee8 100644
--- a/core/java/android/service/dreams/OWNERS
+++ b/core/java/android/service/dreams/OWNERS
@@ -4,5 +4,7 @@
dsandler@google.com
galinap@google.com
jjaggi@google.com
+lusilva@google.com
michaelwr@google.com
santoscordon@google.com
+wxyz@google.com
diff --git a/core/java/android/service/trust/OWNERS b/core/java/android/service/trust/OWNERS
index a895f7f..16eb19a 100644
--- a/core/java/android/service/trust/OWNERS
+++ b/core/java/android/service/trust/OWNERS
@@ -1,4 +1,3 @@
# Bug component: 36824
-cbrubaker@google.com
jacobhobbie@google.com
diff --git a/core/java/android/service/voice/HotwordTrainingAudio.aidl b/core/java/android/service/voice/HotwordTrainingAudio.aidl
new file mode 100644
index 0000000..4dd2289
--- /dev/null
+++ b/core/java/android/service/voice/HotwordTrainingAudio.aidl
@@ -0,0 +1,19 @@
+/*
+ * 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 android.service.voice;
+
+parcelable HotwordTrainingAudio;
\ No newline at end of file
diff --git a/core/java/android/service/voice/HotwordTrainingAudio.java b/core/java/android/service/voice/HotwordTrainingAudio.java
new file mode 100644
index 0000000..895b0c0
--- /dev/null
+++ b/core/java/android/service/voice/HotwordTrainingAudio.java
@@ -0,0 +1,367 @@
+/*
+ * 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 android.service.voice;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.SystemApi;
+import android.media.AudioFormat;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import com.android.internal.util.DataClass;
+
+/**
+ * Represents audio supporting hotword model training.
+ *
+ * @hide
+ */
+@DataClass(
+ genConstructor = false,
+ genBuilder = true,
+ genEqualsHashCode = true,
+ genHiddenConstDefs = true,
+ genParcelable = true,
+ genToString = true
+)
+@SystemApi
+public final class HotwordTrainingAudio implements Parcelable {
+ /** Represents unset value for the hotword offset. */
+ public static final int HOTWORD_OFFSET_UNSET = -1;
+
+ /** Buffer of hotword audio data for training models. */
+ @NonNull
+ private final byte[] mHotwordAudio;
+
+ private String hotwordAudioToString() {
+ return "length=" + mHotwordAudio.length;
+ }
+
+ /**
+ * The {@link AudioFormat} of the {@link HotwordTrainingAudio#mHotwordAudio}.
+ */
+ @NonNull
+ private final AudioFormat mAudioFormat;
+
+ /**
+ * App-defined identifier to distinguish hotword training audio instances.
+ */
+ @NonNull
+ private final int mAudioType;
+
+ private static int defaultAudioType() {
+ return 0;
+ }
+
+ /**
+ * App-defined offset in milliseconds relative to start of
+ * {@link HotwordTrainingAudio#mHotwordAudio}. Default value is
+ * {@link HotwordTrainingAudio#HOTWORD_OFFSET_UNSET}.
+ */
+ private int mHotwordOffsetMillis = HOTWORD_OFFSET_UNSET;
+
+
+
+ // Code below generated by codegen v1.0.23.
+ //
+ // DO NOT MODIFY!
+ // CHECKSTYLE:OFF Generated code
+ //
+ // To regenerate run:
+ // $ codegen $ANDROID_BUILD_TOP/frameworks/base/core/java/android/service/voice/HotwordTrainingAudio.java
+ //
+ // To exclude the generated code from IntelliJ auto-formatting enable (one-time):
+ // Settings > Editor > Code Style > Formatter Control
+ //@formatter:off
+
+
+ @DataClass.Generated.Member
+ /* package-private */ HotwordTrainingAudio(
+ @NonNull byte[] hotwordAudio,
+ @NonNull AudioFormat audioFormat,
+ @NonNull int audioType,
+ int hotwordOffsetMillis) {
+ this.mHotwordAudio = hotwordAudio;
+ com.android.internal.util.AnnotationValidations.validate(
+ NonNull.class, null, mHotwordAudio);
+ this.mAudioFormat = audioFormat;
+ com.android.internal.util.AnnotationValidations.validate(
+ NonNull.class, null, mAudioFormat);
+ this.mAudioType = audioType;
+ com.android.internal.util.AnnotationValidations.validate(
+ NonNull.class, null, mAudioType);
+ this.mHotwordOffsetMillis = hotwordOffsetMillis;
+
+ // onConstructed(); // You can define this method to get a callback
+ }
+
+ /**
+ * Buffer of hotword audio data for training models.
+ */
+ @DataClass.Generated.Member
+ public @NonNull byte[] getHotwordAudio() {
+ return mHotwordAudio;
+ }
+
+ /**
+ * The {@link AudioFormat} of the {@link HotwordTrainingAudio#mHotwordAudio}.
+ */
+ @DataClass.Generated.Member
+ public @NonNull AudioFormat getAudioFormat() {
+ return mAudioFormat;
+ }
+
+ /**
+ * App-defined identifier to distinguish hotword training audio instances.
+ */
+ @DataClass.Generated.Member
+ public @NonNull int getAudioType() {
+ return mAudioType;
+ }
+
+ /**
+ * App-defined offset in milliseconds relative to start of
+ * {@link HotwordTrainingAudio#mHotwordAudio}. Default value is
+ * {@link HotwordTrainingAudio#HOTWORD_OFFSET_UNSET}.
+ */
+ @DataClass.Generated.Member
+ public int getHotwordOffsetMillis() {
+ return mHotwordOffsetMillis;
+ }
+
+ @Override
+ @DataClass.Generated.Member
+ public String toString() {
+ // You can override field toString logic by defining methods like:
+ // String fieldNameToString() { ... }
+
+ return "HotwordTrainingAudio { " +
+ "hotwordAudio = " + hotwordAudioToString() + ", " +
+ "audioFormat = " + mAudioFormat + ", " +
+ "audioType = " + mAudioType + ", " +
+ "hotwordOffsetMillis = " + mHotwordOffsetMillis +
+ " }";
+ }
+
+ @Override
+ @DataClass.Generated.Member
+ public boolean equals(@Nullable Object o) {
+ // You can override field equality logic by defining either of the methods like:
+ // boolean fieldNameEquals(HotwordTrainingAudio other) { ... }
+ // boolean fieldNameEquals(FieldType otherValue) { ... }
+
+ if (this == o) return true;
+ if (o == null || getClass() != o.getClass()) return false;
+ @SuppressWarnings("unchecked")
+ HotwordTrainingAudio that = (HotwordTrainingAudio) o;
+ //noinspection PointlessBooleanExpression
+ return true
+ && java.util.Arrays.equals(mHotwordAudio, that.mHotwordAudio)
+ && java.util.Objects.equals(mAudioFormat, that.mAudioFormat)
+ && mAudioType == that.mAudioType
+ && mHotwordOffsetMillis == that.mHotwordOffsetMillis;
+ }
+
+ @Override
+ @DataClass.Generated.Member
+ public int hashCode() {
+ // You can override field hashCode logic by defining methods like:
+ // int fieldNameHashCode() { ... }
+
+ int _hash = 1;
+ _hash = 31 * _hash + java.util.Arrays.hashCode(mHotwordAudio);
+ _hash = 31 * _hash + java.util.Objects.hashCode(mAudioFormat);
+ _hash = 31 * _hash + mAudioType;
+ _hash = 31 * _hash + mHotwordOffsetMillis;
+ return _hash;
+ }
+
+ @Override
+ @DataClass.Generated.Member
+ public void writeToParcel(@NonNull Parcel dest, int flags) {
+ // You can override field parcelling by defining methods like:
+ // void parcelFieldName(Parcel dest, int flags) { ... }
+
+ dest.writeByteArray(mHotwordAudio);
+ dest.writeTypedObject(mAudioFormat, flags);
+ dest.writeInt(mAudioType);
+ dest.writeInt(mHotwordOffsetMillis);
+ }
+
+ @Override
+ @DataClass.Generated.Member
+ public int describeContents() { return 0; }
+
+ /** @hide */
+ @SuppressWarnings({"unchecked", "RedundantCast"})
+ @DataClass.Generated.Member
+ /* package-private */ HotwordTrainingAudio(@NonNull Parcel in) {
+ // You can override field unparcelling by defining methods like:
+ // static FieldType unparcelFieldName(Parcel in) { ... }
+
+ byte[] hotwordAudio = in.createByteArray();
+ AudioFormat audioFormat = (AudioFormat) in.readTypedObject(AudioFormat.CREATOR);
+ int audioType = in.readInt();
+ int hotwordOffsetMillis = in.readInt();
+
+ this.mHotwordAudio = hotwordAudio;
+ com.android.internal.util.AnnotationValidations.validate(
+ NonNull.class, null, mHotwordAudio);
+ this.mAudioFormat = audioFormat;
+ com.android.internal.util.AnnotationValidations.validate(
+ NonNull.class, null, mAudioFormat);
+ this.mAudioType = audioType;
+ com.android.internal.util.AnnotationValidations.validate(
+ NonNull.class, null, mAudioType);
+ this.mHotwordOffsetMillis = hotwordOffsetMillis;
+
+ // onConstructed(); // You can define this method to get a callback
+ }
+
+ @DataClass.Generated.Member
+ public static final @NonNull Parcelable.Creator<HotwordTrainingAudio> CREATOR
+ = new Parcelable.Creator<HotwordTrainingAudio>() {
+ @Override
+ public HotwordTrainingAudio[] newArray(int size) {
+ return new HotwordTrainingAudio[size];
+ }
+
+ @Override
+ public HotwordTrainingAudio createFromParcel(@NonNull Parcel in) {
+ return new HotwordTrainingAudio(in);
+ }
+ };
+
+ /**
+ * A builder for {@link HotwordTrainingAudio}
+ */
+ @SuppressWarnings("WeakerAccess")
+ @DataClass.Generated.Member
+ public static final class Builder {
+
+ private @NonNull byte[] mHotwordAudio;
+ private @NonNull AudioFormat mAudioFormat;
+ private @NonNull int mAudioType;
+ private int mHotwordOffsetMillis;
+
+ private long mBuilderFieldsSet = 0L;
+
+ /**
+ * Creates a new Builder.
+ *
+ * @param hotwordAudio
+ * Buffer of hotword audio data for training models.
+ * @param audioFormat
+ * The {@link AudioFormat} of the {@link HotwordTrainingAudio#mHotwordAudio}.
+ */
+ public Builder(
+ @NonNull byte[] hotwordAudio,
+ @NonNull AudioFormat audioFormat) {
+ mHotwordAudio = hotwordAudio;
+ com.android.internal.util.AnnotationValidations.validate(
+ NonNull.class, null, mHotwordAudio);
+ mAudioFormat = audioFormat;
+ com.android.internal.util.AnnotationValidations.validate(
+ NonNull.class, null, mAudioFormat);
+ }
+
+ /**
+ * Buffer of hotword audio data for training models.
+ */
+ @DataClass.Generated.Member
+ public @NonNull Builder setHotwordAudio(@NonNull byte... value) {
+ checkNotUsed();
+ mBuilderFieldsSet |= 0x1;
+ mHotwordAudio = value;
+ return this;
+ }
+
+ /**
+ * The {@link AudioFormat} of the {@link HotwordTrainingAudio#mHotwordAudio}.
+ */
+ @DataClass.Generated.Member
+ public @NonNull Builder setAudioFormat(@NonNull AudioFormat value) {
+ checkNotUsed();
+ mBuilderFieldsSet |= 0x2;
+ mAudioFormat = value;
+ return this;
+ }
+
+ /**
+ * App-defined identifier to distinguish hotword training audio instances.
+ */
+ @DataClass.Generated.Member
+ public @NonNull Builder setAudioType(@NonNull int value) {
+ checkNotUsed();
+ mBuilderFieldsSet |= 0x4;
+ mAudioType = value;
+ return this;
+ }
+
+ /**
+ * App-defined offset in milliseconds relative to start of
+ * {@link HotwordTrainingAudio#mHotwordAudio}. Default value is
+ * {@link HotwordTrainingAudio#HOTWORD_OFFSET_UNSET}.
+ */
+ @DataClass.Generated.Member
+ public @NonNull Builder setHotwordOffsetMillis(int value) {
+ checkNotUsed();
+ mBuilderFieldsSet |= 0x8;
+ mHotwordOffsetMillis = value;
+ return this;
+ }
+
+ /** Builds the instance. This builder should not be touched after calling this! */
+ public @NonNull HotwordTrainingAudio build() {
+ checkNotUsed();
+ mBuilderFieldsSet |= 0x10; // Mark builder used
+
+ if ((mBuilderFieldsSet & 0x4) == 0) {
+ mAudioType = defaultAudioType();
+ }
+ if ((mBuilderFieldsSet & 0x8) == 0) {
+ mHotwordOffsetMillis = HOTWORD_OFFSET_UNSET;
+ }
+ HotwordTrainingAudio o = new HotwordTrainingAudio(
+ mHotwordAudio,
+ mAudioFormat,
+ mAudioType,
+ mHotwordOffsetMillis);
+ return o;
+ }
+
+ private void checkNotUsed() {
+ if ((mBuilderFieldsSet & 0x10) != 0) {
+ throw new IllegalStateException(
+ "This Builder should not be reused. Use a new Builder instance instead");
+ }
+ }
+ }
+
+ @DataClass.Generated(
+ time = 1692837160437L,
+ codegenVersion = "1.0.23",
+ sourceFile = "frameworks/base/core/java/android/service/voice/HotwordTrainingAudio.java",
+ inputSignatures = "public static final int HOTWORD_OFFSET_UNSET\nprivate final @android.annotation.NonNull byte[] mHotwordAudio\nprivate final @android.annotation.NonNull android.media.AudioFormat mAudioFormat\nprivate final @android.annotation.NonNull int mAudioType\nprivate int mHotwordOffsetMillis\nprivate java.lang.String hotwordAudioToString()\nprivate static int defaultAudioType()\nclass HotwordTrainingAudio extends java.lang.Object implements [android.os.Parcelable]\n@com.android.internal.util.DataClass(genConstructor=false, genBuilder=true, genEqualsHashCode=true, genHiddenConstDefs=true, genParcelable=true, genToString=true)")
+ @Deprecated
+ private void __metadata() {}
+
+
+ //@formatter:on
+ // End of generated code
+
+}
diff --git a/core/java/android/service/voice/HotwordTrainingData.aidl b/core/java/android/service/voice/HotwordTrainingData.aidl
new file mode 100644
index 0000000..03cc841
--- /dev/null
+++ b/core/java/android/service/voice/HotwordTrainingData.aidl
@@ -0,0 +1,19 @@
+/*
+ * 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 android.service.voice;
+
+parcelable HotwordTrainingData;
diff --git a/core/java/android/service/voice/HotwordTrainingData.java b/core/java/android/service/voice/HotwordTrainingData.java
new file mode 100644
index 0000000..9dca77e
--- /dev/null
+++ b/core/java/android/service/voice/HotwordTrainingData.java
@@ -0,0 +1,371 @@
+/*
+ * 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 android.service.voice;
+
+import android.annotation.IntDef;
+import android.annotation.NonNull;
+import android.annotation.SystemApi;
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.text.TextUtils;
+
+import com.android.internal.util.DataClass;
+import com.android.internal.util.Preconditions;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+
+/**
+ * Contains training data related to hotword detection service.
+ *
+ * <p>The constructed object's size must be within
+ * {@link HotwordTrainingData#getMaxTrainingDataSize()} or an
+ * {@link IllegalArgumentException} will be thrown on construction. Size of the object is calculated
+ * by converting object to a {@link Parcel} and using the {@link Parcel#dataSize()}.
+ *
+ * @hide
+ */
+@DataClass(
+ genConstructor = false,
+ genBuilder = true,
+ genEqualsHashCode = true,
+ genHiddenConstDefs = true,
+ genParcelable = true,
+ genToString = true)
+@SystemApi
+public final class HotwordTrainingData implements Parcelable {
+ /** Max size for hotword training data. */
+ public static int getMaxTrainingDataSize() {
+ return 1024 * 1024; // 1 MB;
+ }
+
+ /** The list containing hotword audio that is useful for training. */
+ @NonNull
+ @DataClass.PluralOf("trainingAudio")
+ private final List<HotwordTrainingAudio> mTrainingAudios;
+
+ private static List<HotwordTrainingAudio> defaultTrainingAudios() {
+ return Collections.emptyList();
+ }
+
+ /** Timeout stage is unknown. */
+ public static final int TIMEOUT_STAGE_UNKNOWN = 0;
+
+ /**
+ * Timeout stage value that represents that the model timed out very early while detecting
+ * hotword.
+ */
+ public static final int TIMEOUT_STAGE_VERY_EARLY = 1;
+
+ /**
+ * Timeout stage value that represents that the model timed out early while detecting
+ * hotword.
+ */
+ public static final int TIMEOUT_STAGE_EARLY = 2;
+
+ /**
+ * Timeout stage value that represents that the model timed out in the middle while detecting
+ * hotword.
+ */
+ public static final int TIMEOUT_STAGE_MIDDLE = 3;
+
+ /**
+ * Timeout stage value that represents that the model timed out late while detecting
+ * hotword.
+ */
+ public static final int TIMEOUT_STAGE_LATE = 4;
+
+ /** @hide */
+ @IntDef(prefix = {"TIMEOUT_STAGE"}, value = {
+ TIMEOUT_STAGE_UNKNOWN,
+ TIMEOUT_STAGE_VERY_EARLY,
+ TIMEOUT_STAGE_EARLY,
+ TIMEOUT_STAGE_MIDDLE,
+ TIMEOUT_STAGE_LATE,
+ })
+ @interface HotwordTimeoutStage {}
+
+ /** Stage when timeout occurred. */
+ @HotwordTimeoutStage
+ private final int mTimeoutStage;
+
+ private static int defaultTimeoutStage() {
+ return TIMEOUT_STAGE_UNKNOWN;
+ }
+
+ private void onConstructed() {
+ // Verify size of object is within limit.
+ Parcel parcel = Parcel.obtain();
+ parcel.writeValue(this);
+ int dataSizeBytes = parcel.dataSize();
+ parcel.recycle();
+ Preconditions.checkArgument(
+ dataSizeBytes < getMaxTrainingDataSize(),
+ TextUtils.formatSimple(
+ "Hotword training data of size %s exceeds size limit of %s!",
+ dataSizeBytes, getMaxTrainingDataSize()));
+ }
+
+
+
+ // Code below generated by codegen v1.0.23.
+ //
+ // DO NOT MODIFY!
+ // CHECKSTYLE:OFF Generated code
+ //
+ // To regenerate run:
+ // $ codegen $ANDROID_BUILD_TOP/frameworks/base/core/java/android/service/voice/HotwordTrainingData.java
+ //
+ // To exclude the generated code from IntelliJ auto-formatting enable (one-time):
+ // Settings > Editor > Code Style > Formatter Control
+ //@formatter:off
+
+
+ /** @hide */
+ @IntDef(prefix = "TIMEOUT_STAGE_", value = {
+ TIMEOUT_STAGE_UNKNOWN,
+ TIMEOUT_STAGE_VERY_EARLY,
+ TIMEOUT_STAGE_EARLY,
+ TIMEOUT_STAGE_MIDDLE,
+ TIMEOUT_STAGE_LATE
+ })
+ @java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy.SOURCE)
+ @DataClass.Generated.Member
+ public @interface TimeoutStage {}
+
+ /** @hide */
+ @DataClass.Generated.Member
+ public static String timeoutStageToString(@TimeoutStage int value) {
+ switch (value) {
+ case TIMEOUT_STAGE_UNKNOWN:
+ return "TIMEOUT_STAGE_UNKNOWN";
+ case TIMEOUT_STAGE_VERY_EARLY:
+ return "TIMEOUT_STAGE_VERY_EARLY";
+ case TIMEOUT_STAGE_EARLY:
+ return "TIMEOUT_STAGE_EARLY";
+ case TIMEOUT_STAGE_MIDDLE:
+ return "TIMEOUT_STAGE_MIDDLE";
+ case TIMEOUT_STAGE_LATE:
+ return "TIMEOUT_STAGE_LATE";
+ default: return Integer.toHexString(value);
+ }
+ }
+
+ @DataClass.Generated.Member
+ /* package-private */ HotwordTrainingData(
+ @NonNull List<HotwordTrainingAudio> trainingAudios,
+ @HotwordTimeoutStage int timeoutStage) {
+ this.mTrainingAudios = trainingAudios;
+ com.android.internal.util.AnnotationValidations.validate(
+ NonNull.class, null, mTrainingAudios);
+ this.mTimeoutStage = timeoutStage;
+ com.android.internal.util.AnnotationValidations.validate(
+ HotwordTimeoutStage.class, null, mTimeoutStage);
+
+ onConstructed();
+ }
+
+ /**
+ * The list containing hotword audio that is useful for training.
+ */
+ @DataClass.Generated.Member
+ public @NonNull List<HotwordTrainingAudio> getTrainingAudios() {
+ return mTrainingAudios;
+ }
+
+ /**
+ * Stage when timeout occurred.
+ */
+ @DataClass.Generated.Member
+ public @HotwordTimeoutStage int getTimeoutStage() {
+ return mTimeoutStage;
+ }
+
+ @Override
+ @DataClass.Generated.Member
+ public String toString() {
+ // You can override field toString logic by defining methods like:
+ // String fieldNameToString() { ... }
+
+ return "HotwordTrainingData { " +
+ "trainingAudios = " + mTrainingAudios + ", " +
+ "timeoutStage = " + mTimeoutStage +
+ " }";
+ }
+
+ @Override
+ @DataClass.Generated.Member
+ public boolean equals(@android.annotation.Nullable Object o) {
+ // You can override field equality logic by defining either of the methods like:
+ // boolean fieldNameEquals(HotwordTrainingData other) { ... }
+ // boolean fieldNameEquals(FieldType otherValue) { ... }
+
+ if (this == o) return true;
+ if (o == null || getClass() != o.getClass()) return false;
+ @SuppressWarnings("unchecked")
+ HotwordTrainingData that = (HotwordTrainingData) o;
+ //noinspection PointlessBooleanExpression
+ return true
+ && java.util.Objects.equals(mTrainingAudios, that.mTrainingAudios)
+ && mTimeoutStage == that.mTimeoutStage;
+ }
+
+ @Override
+ @DataClass.Generated.Member
+ public int hashCode() {
+ // You can override field hashCode logic by defining methods like:
+ // int fieldNameHashCode() { ... }
+
+ int _hash = 1;
+ _hash = 31 * _hash + java.util.Objects.hashCode(mTrainingAudios);
+ _hash = 31 * _hash + mTimeoutStage;
+ return _hash;
+ }
+
+ @Override
+ @DataClass.Generated.Member
+ public void writeToParcel(@NonNull Parcel dest, int flags) {
+ // You can override field parcelling by defining methods like:
+ // void parcelFieldName(Parcel dest, int flags) { ... }
+
+ dest.writeParcelableList(mTrainingAudios, flags);
+ dest.writeInt(mTimeoutStage);
+ }
+
+ @Override
+ @DataClass.Generated.Member
+ public int describeContents() { return 0; }
+
+ /** @hide */
+ @SuppressWarnings({"unchecked", "RedundantCast"})
+ @DataClass.Generated.Member
+ /* package-private */ HotwordTrainingData(@NonNull Parcel in) {
+ // You can override field unparcelling by defining methods like:
+ // static FieldType unparcelFieldName(Parcel in) { ... }
+
+ List<HotwordTrainingAudio> trainingAudios = new ArrayList<>();
+ in.readParcelableList(trainingAudios, HotwordTrainingAudio.class.getClassLoader());
+ int timeoutStage = in.readInt();
+
+ this.mTrainingAudios = trainingAudios;
+ com.android.internal.util.AnnotationValidations.validate(
+ NonNull.class, null, mTrainingAudios);
+ this.mTimeoutStage = timeoutStage;
+ com.android.internal.util.AnnotationValidations.validate(
+ HotwordTimeoutStage.class, null, mTimeoutStage);
+
+ onConstructed();
+ }
+
+ @DataClass.Generated.Member
+ public static final @NonNull Parcelable.Creator<HotwordTrainingData> CREATOR
+ = new Parcelable.Creator<HotwordTrainingData>() {
+ @Override
+ public HotwordTrainingData[] newArray(int size) {
+ return new HotwordTrainingData[size];
+ }
+
+ @Override
+ public HotwordTrainingData createFromParcel(@NonNull Parcel in) {
+ return new HotwordTrainingData(in);
+ }
+ };
+
+ /**
+ * A builder for {@link HotwordTrainingData}
+ */
+ @SuppressWarnings("WeakerAccess")
+ @DataClass.Generated.Member
+ public static final class Builder {
+
+ private @NonNull List<HotwordTrainingAudio> mTrainingAudios;
+ private @HotwordTimeoutStage int mTimeoutStage;
+
+ private long mBuilderFieldsSet = 0L;
+
+ public Builder() {
+ }
+
+ /**
+ * The list containing hotword audio that is useful for training.
+ */
+ @DataClass.Generated.Member
+ public @NonNull Builder setTrainingAudios(@NonNull List<HotwordTrainingAudio> value) {
+ checkNotUsed();
+ mBuilderFieldsSet |= 0x1;
+ mTrainingAudios = value;
+ return this;
+ }
+
+ /** @see #setTrainingAudios */
+ @DataClass.Generated.Member
+ public @NonNull Builder addTrainingAudio(@NonNull HotwordTrainingAudio value) {
+ if (mTrainingAudios == null) setTrainingAudios(new ArrayList<>());
+ mTrainingAudios.add(value);
+ return this;
+ }
+
+ /**
+ * Stage when timeout occurred.
+ */
+ @DataClass.Generated.Member
+ public @NonNull Builder setTimeoutStage(@HotwordTimeoutStage int value) {
+ checkNotUsed();
+ mBuilderFieldsSet |= 0x2;
+ mTimeoutStage = value;
+ return this;
+ }
+
+ /** Builds the instance. This builder should not be touched after calling this! */
+ public @NonNull HotwordTrainingData build() {
+ checkNotUsed();
+ mBuilderFieldsSet |= 0x4; // Mark builder used
+
+ if ((mBuilderFieldsSet & 0x1) == 0) {
+ mTrainingAudios = defaultTrainingAudios();
+ }
+ if ((mBuilderFieldsSet & 0x2) == 0) {
+ mTimeoutStage = defaultTimeoutStage();
+ }
+ HotwordTrainingData o = new HotwordTrainingData(
+ mTrainingAudios,
+ mTimeoutStage);
+ return o;
+ }
+
+ private void checkNotUsed() {
+ if ((mBuilderFieldsSet & 0x4) != 0) {
+ throw new IllegalStateException(
+ "This Builder should not be reused. Use a new Builder instance instead");
+ }
+ }
+ }
+
+ @DataClass.Generated(
+ time = 1693313864628L,
+ codegenVersion = "1.0.23",
+ sourceFile = "frameworks/base/core/java/android/service/voice/HotwordTrainingData.java",
+ inputSignatures = "private final @android.annotation.NonNull @com.android.internal.util.DataClass.PluralOf(\"trainingAudio\") java.util.List<android.service.voice.HotwordTrainingAudio> mTrainingAudios\npublic static final int TIMEOUT_STAGE_UNKNOWN\npublic static final int TIMEOUT_STAGE_VERY_EARLY\npublic static final int TIMEOUT_STAGE_EARLY\npublic static final int TIMEOUT_STAGE_MIDDLE\npublic static final int TIMEOUT_STAGE_LATE\nprivate final @android.service.voice.HotwordTrainingData.HotwordTimeoutStage int mTimeoutStage\npublic static int getMaxTrainingDataSize()\nprivate static java.util.List<android.service.voice.HotwordTrainingAudio> defaultTrainingAudios()\nprivate static int defaultTimeoutStage()\nprivate void onConstructed()\nclass HotwordTrainingData extends java.lang.Object implements [android.os.Parcelable]\n@com.android.internal.util.DataClass(genConstructor=false, genBuilder=true, genEqualsHashCode=true, genHiddenConstDefs=true, genParcelable=true, genToString=true)")
+ @Deprecated
+ private void __metadata() {}
+
+
+ //@formatter:on
+ // End of generated code
+
+}
diff --git a/core/java/android/text/flags/use_bounds_for_width.aconfig b/core/java/android/text/flags/use_bounds_for_width.aconfig
new file mode 100644
index 0000000..d89d5f4
--- /dev/null
+++ b/core/java/android/text/flags/use_bounds_for_width.aconfig
@@ -0,0 +1,8 @@
+package: "com.android.text.flags"
+
+flag {
+ name: "use_bounds_for_width"
+ namespace: "text"
+ description: "Feature flag for preventing horizontal clipping."
+ bug: "63938206"
+}
diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java
index 8703609..9a4cb72 100644
--- a/core/java/android/view/View.java
+++ b/core/java/android/view/View.java
@@ -5318,11 +5318,6 @@
@Retention(RetentionPolicy.SOURCE)
public @interface LayerType {}
- @ViewDebug.ExportedProperty(category = "drawing", mapping = {
- @ViewDebug.IntToString(from = LAYER_TYPE_NONE, to = "NONE"),
- @ViewDebug.IntToString(from = LAYER_TYPE_SOFTWARE, to = "SOFTWARE"),
- @ViewDebug.IntToString(from = LAYER_TYPE_HARDWARE, to = "HARDWARE")
- })
int mLayerType = LAYER_TYPE_NONE;
Paint mLayerPaint;
@@ -22625,6 +22620,11 @@
@EnumEntry(value = LAYER_TYPE_SOFTWARE, name = "software"),
@EnumEntry(value = LAYER_TYPE_HARDWARE, name = "hardware")
})
+ @ViewDebug.ExportedProperty(category = "drawing", mapping = {
+ @ViewDebug.IntToString(from = LAYER_TYPE_NONE, to = "NONE"),
+ @ViewDebug.IntToString(from = LAYER_TYPE_SOFTWARE, to = "SOFTWARE"),
+ @ViewDebug.IntToString(from = LAYER_TYPE_HARDWARE, to = "HARDWARE")
+ })
@LayerType
public int getLayerType() {
return mLayerType;
diff --git a/core/java/android/window/TaskConstants.java b/core/java/android/window/TaskConstants.java
index 69d79b4..44bb33db 100644
--- a/core/java/android/window/TaskConstants.java
+++ b/core/java/android/window/TaskConstants.java
@@ -81,6 +81,12 @@
public static final int TASK_CHILD_LAYER_RESIZE_VEIL = 6 * TASK_CHILD_LAYER_REGION_SIZE;
/**
+ * Floating menus belonging to a task (e.g. maximize menu).
+ * @hide
+ */
+ public static final int TASK_CHILD_LAYER_FLOATING_MENU = 7 * TASK_CHILD_LAYER_REGION_SIZE;
+
+ /**
* Z-orders of task child layers other than activities, task fragments and layers interleaved
* with them, e.g. IME windows. [-10000, 10000) is reserved for these layers.
* @hide
diff --git a/core/java/android/window/TransitionRequestInfo.java b/core/java/android/window/TransitionRequestInfo.java
index 1404694..edea297 100644
--- a/core/java/android/window/TransitionRequestInfo.java
+++ b/core/java/android/window/TransitionRequestInfo.java
@@ -16,8 +16,6 @@
package android.window;
-import static android.view.WindowManager.transitTypeToString;
-
import android.annotation.Nullable;
import android.app.ActivityManager;
import android.app.WindowConfiguration;
@@ -51,14 +49,26 @@
* The reliable parts should be flags, rotation start/end (if rotating), and start/end bounds
* (if size is changing).
*/
- private @Nullable DisplayChange mDisplayChange;
+ private @Nullable TransitionRequestInfo.DisplayChange mDisplayChange;
+
+ /** The transition flags known at the time of the request. These may not be complete. */
+ private final int mFlags;
/** constructor override */
public TransitionRequestInfo(
@WindowManager.TransitionType int type,
@Nullable ActivityManager.RunningTaskInfo triggerTask,
@Nullable RemoteTransition remoteTransition) {
- this(type, triggerTask, remoteTransition, null /* displayChange */);
+ this(type, triggerTask, remoteTransition, null /* displayChange */, 0 /* flags */);
+ }
+
+ /** constructor override */
+ public TransitionRequestInfo(
+ @WindowManager.TransitionType int type,
+ @Nullable ActivityManager.RunningTaskInfo triggerTask,
+ @Nullable RemoteTransition remoteTransition,
+ int flags) {
+ this(type, triggerTask, remoteTransition, null /* displayChange */, flags);
}
/** Requested change to a display. */
@@ -236,7 +246,7 @@
};
@DataClass.Generated(
- time = 1648141181315L,
+ time = 1691627678294L,
codegenVersion = "1.0.23",
sourceFile = "frameworks/base/core/java/android/window/TransitionRequestInfo.java",
inputSignatures = "private final int mDisplayId\nprivate @android.annotation.Nullable android.graphics.Rect mStartAbsBounds\nprivate @android.annotation.Nullable android.graphics.Rect mEndAbsBounds\nprivate int mStartRotation\nprivate int mEndRotation\nprivate boolean mPhysicalDisplayChanged\nclass DisplayChange extends java.lang.Object implements [android.os.Parcelable]\n@com.android.internal.util.DataClass(genToString=true, genSetters=true, genBuilder=false, genConstructor=false)")
@@ -279,19 +289,23 @@
* If non-null, this request was triggered by this display change. This will not be complete:
* The reliable parts should be flags, rotation start/end (if rotating), and start/end bounds
* (if size is changing).
+ * @param flags
+ * The transition flags known at the time of the request. These may not be complete.
*/
@DataClass.Generated.Member
public TransitionRequestInfo(
@WindowManager.TransitionType int type,
@Nullable ActivityManager.RunningTaskInfo triggerTask,
@Nullable RemoteTransition remoteTransition,
- @Nullable DisplayChange displayChange) {
+ @Nullable TransitionRequestInfo.DisplayChange displayChange,
+ int flags) {
this.mType = type;
com.android.internal.util.AnnotationValidations.validate(
WindowManager.TransitionType.class, null, mType);
this.mTriggerTask = triggerTask;
this.mRemoteTransition = remoteTransition;
this.mDisplayChange = displayChange;
+ this.mFlags = flags;
// onConstructed(); // You can define this method to get a callback
}
@@ -327,11 +341,19 @@
* (if size is changing).
*/
@DataClass.Generated.Member
- public @Nullable DisplayChange getDisplayChange() {
+ public @Nullable TransitionRequestInfo.DisplayChange getDisplayChange() {
return mDisplayChange;
}
/**
+ * The transition flags known at the time of the request. These may not be complete.
+ */
+ @DataClass.Generated.Member
+ public int getFlags() {
+ return mFlags;
+ }
+
+ /**
* If non-null, If non-null, the task containing the activity whose lifecycle change (start or
* finish) has caused this transition to occur.
*/
@@ -356,7 +378,7 @@
* (if size is changing).
*/
@DataClass.Generated.Member
- public @android.annotation.NonNull TransitionRequestInfo setDisplayChange(@android.annotation.NonNull DisplayChange value) {
+ public @android.annotation.NonNull TransitionRequestInfo setDisplayChange(@android.annotation.NonNull TransitionRequestInfo.DisplayChange value) {
mDisplayChange = value;
return this;
}
@@ -368,10 +390,11 @@
// String fieldNameToString() { ... }
return "TransitionRequestInfo { " +
- "type = " + transitTypeToString(mType) + ", " +
+ "type = " + mType + ", " +
"triggerTask = " + mTriggerTask + ", " +
"remoteTransition = " + mRemoteTransition + ", " +
- "displayChange = " + mDisplayChange +
+ "displayChange = " + mDisplayChange + ", " +
+ "flags = " + mFlags +
" }";
}
@@ -390,6 +413,7 @@
if (mTriggerTask != null) dest.writeTypedObject(mTriggerTask, flags);
if (mRemoteTransition != null) dest.writeTypedObject(mRemoteTransition, flags);
if (mDisplayChange != null) dest.writeTypedObject(mDisplayChange, flags);
+ dest.writeInt(mFlags);
}
@Override
@@ -407,7 +431,8 @@
int type = in.readInt();
ActivityManager.RunningTaskInfo triggerTask = (flg & 0x2) == 0 ? null : (ActivityManager.RunningTaskInfo) in.readTypedObject(ActivityManager.RunningTaskInfo.CREATOR);
RemoteTransition remoteTransition = (flg & 0x4) == 0 ? null : (RemoteTransition) in.readTypedObject(RemoteTransition.CREATOR);
- DisplayChange displayChange = (flg & 0x8) == 0 ? null : (DisplayChange) in.readTypedObject(DisplayChange.CREATOR);
+ TransitionRequestInfo.DisplayChange displayChange = (flg & 0x8) == 0 ? null : (TransitionRequestInfo.DisplayChange) in.readTypedObject(TransitionRequestInfo.DisplayChange.CREATOR);
+ int flags = in.readInt();
this.mType = type;
com.android.internal.util.AnnotationValidations.validate(
@@ -415,6 +440,7 @@
this.mTriggerTask = triggerTask;
this.mRemoteTransition = remoteTransition;
this.mDisplayChange = displayChange;
+ this.mFlags = flags;
// onConstructed(); // You can define this method to get a callback
}
@@ -434,10 +460,10 @@
};
@DataClass.Generated(
- time = 1639445520938L,
+ time = 1691627678327L,
codegenVersion = "1.0.23",
sourceFile = "frameworks/base/core/java/android/window/TransitionRequestInfo.java",
- inputSignatures = "private final @android.view.WindowManager.TransitionType int mType\nprivate @android.annotation.Nullable android.app.ActivityManager.RunningTaskInfo mTriggerTask\nprivate @android.annotation.Nullable android.window.RemoteTransition mRemoteTransition\nprivate @android.annotation.Nullable android.window.TransitionRequestInfo.DisplayChange mDisplayChange\nclass TransitionRequestInfo extends java.lang.Object implements [android.os.Parcelable]\n@com.android.internal.util.DataClass(genToString=true, genSetters=true, genAidl=true)")
+ inputSignatures = "private final @android.view.WindowManager.TransitionType int mType\nprivate @android.annotation.Nullable android.app.ActivityManager.RunningTaskInfo mTriggerTask\nprivate @android.annotation.Nullable android.window.RemoteTransition mRemoteTransition\nprivate @android.annotation.Nullable android.window.TransitionRequestInfo.DisplayChange mDisplayChange\nprivate final int mFlags\nclass TransitionRequestInfo extends java.lang.Object implements [android.os.Parcelable]\n@com.android.internal.util.DataClass(genToString=true, genSetters=true, genAidl=true)")
@Deprecated
private void __metadata() {}
diff --git a/core/java/com/android/internal/app/procstats/ProcessState.java b/core/java/com/android/internal/app/procstats/ProcessState.java
index fff778c..755113b 100644
--- a/core/java/com/android/internal/app/procstats/ProcessState.java
+++ b/core/java/com/android/internal/app/procstats/ProcessState.java
@@ -33,6 +33,7 @@
import static com.android.internal.app.procstats.ProcessStats.STATE_CACHED;
import static com.android.internal.app.procstats.ProcessStats.STATE_COUNT;
import static com.android.internal.app.procstats.ProcessStats.STATE_FGS;
+import static com.android.internal.app.procstats.ProcessStats.STATE_FROZEN;
import static com.android.internal.app.procstats.ProcessStats.STATE_HEAVY_WEIGHT;
import static com.android.internal.app.procstats.ProcessStats.STATE_HOME;
import static com.android.internal.app.procstats.ProcessStats.STATE_IMPORTANT_BACKGROUND;
@@ -142,6 +143,7 @@
private ProcessState mCommonProcess;
private int mCurCombinedState = STATE_NOTHING;
private long mStartTime;
+ private int mStateBeforeFrozen = STATE_NOTHING;
private int mLastPssState = STATE_NOTHING;
private long mLastPssTime;
@@ -423,6 +425,27 @@
}
/**
+ * Used to notify that this process was frozen.
+ */
+ public void onProcessFrozen(long now,
+ ArrayMap<String, ProcessStateHolder> pkgList) {
+ mStateBeforeFrozen = mCurCombinedState % STATE_COUNT;
+ int currentMemFactor = mCurCombinedState / STATE_COUNT;
+ int combinedState = STATE_FROZEN + (currentMemFactor * STATE_COUNT);
+ setCombinedState(combinedState, now, pkgList);
+ }
+
+ /**
+ * Used to notify that this process was unfrozen.
+ */
+ public void onProcessUnfrozen(long now,
+ ArrayMap<String, ProcessStateHolder> pkgList) {
+ int currentMemFactor = mCurCombinedState / STATE_COUNT;
+ int combinedState = mStateBeforeFrozen + (currentMemFactor * STATE_COUNT);
+ setCombinedState(combinedState, now, pkgList);
+ }
+
+ /**
* Update the current state of the given list of processes.
*
* @param state Current ActivityManager.PROCESS_STATE_*
@@ -434,13 +457,20 @@
ArrayMap<String, ProcessStateHolder> pkgList) {
if (state < 0) {
state = mNumStartedServices > 0
- ? (STATE_SERVICE_RESTARTING+(memFactor*STATE_COUNT)) : STATE_NOTHING;
+ ? (STATE_SERVICE_RESTARTING + (memFactor * STATE_COUNT)) : STATE_NOTHING;
} else {
- state = PROCESS_STATE_TO_STATE[state] + (memFactor*STATE_COUNT);
+ state = PROCESS_STATE_TO_STATE[state] + (memFactor * STATE_COUNT);
}
+ setCombinedState(state, now, pkgList);
+ }
+ /**
+ * Sets combined state on the corresponding ProcessState objects.
+ */
+ void setCombinedState(int state, long now,
+ ArrayMap<String, ProcessStateHolder> pkgList) {
// First update the common process.
- mCommonProcess.setCombinedState(state, now);
+ mCommonProcess.setCombinedStateIdv(state, now);
// If the common process is not multi-package, there is nothing else to do.
if (!mCommonProcess.mMultiPackage) {
@@ -449,12 +479,15 @@
if (pkgList != null) {
for (int ip=pkgList.size()-1; ip>=0; ip--) {
- pullFixedProc(pkgList, ip).setCombinedState(state, now);
+ pullFixedProc(pkgList, ip).setCombinedStateIdv(state, now);
}
}
}
- public void setCombinedState(int state, long now) {
+ /**
+ * Sets the combined state for this individual ProcessState object.
+ */
+ void setCombinedStateIdv(int state, long now) {
ensureNotDead();
if (!mDead && (mCurCombinedState != state)) {
//Slog.i(TAG, "Setting state in " + mName + "/" + mPackage + ": " + state);
@@ -545,7 +578,7 @@
}
mNumStartedServices++;
if (mNumStartedServices == 1 && mCurCombinedState == STATE_NOTHING) {
- setCombinedState(STATE_SERVICE_RESTARTING + (memFactor*STATE_COUNT), now);
+ setCombinedStateIdv(STATE_SERVICE_RESTARTING + (memFactor * STATE_COUNT), now);
}
}
@@ -561,7 +594,7 @@
}
mNumStartedServices--;
if (mNumStartedServices == 0 && (mCurCombinedState %STATE_COUNT) == STATE_SERVICE_RESTARTING) {
- setCombinedState(STATE_NOTHING, now);
+ setCombinedStateIdv(STATE_NOTHING, now);
} else if (mNumStartedServices < 0) {
Slog.wtfStack(TAG, "Proc started services underrun: pkg="
+ mPackage + " uid=" + mUid + " name=" + mName);
@@ -1588,7 +1621,9 @@
case STATE_CACHED:
cachedMs += duration;
break;
- // TODO (b/261910877) Add support for tracking frozenMs.
+ case STATE_FROZEN:
+ frozenMs += duration;
+ break;
}
}
statsEventOutput.write(
diff --git a/core/java/com/android/internal/config/sysui/SystemUiDeviceConfigFlags.java b/core/java/com/android/internal/config/sysui/SystemUiDeviceConfigFlags.java
index 0a69ea8..048912c 100644
--- a/core/java/com/android/internal/config/sysui/SystemUiDeviceConfigFlags.java
+++ b/core/java/com/android/internal/config/sysui/SystemUiDeviceConfigFlags.java
@@ -118,9 +118,6 @@
*/
public static final String NAS_DEFAULT_SERVICE = "nas_default_service";
- /** (boolean) Whether notify() calls to NMS should acquire and hold WakeLocks. */
- public static final String NOTIFY_WAKELOCK = "nms_notify_wakelock";
-
// Flags related to media notifications
/**
diff --git a/core/java/com/android/internal/config/sysui/SystemUiSystemPropertiesFlags.java b/core/java/com/android/internal/config/sysui/SystemUiSystemPropertiesFlags.java
index 9233050..8de448b 100644
--- a/core/java/com/android/internal/config/sysui/SystemUiSystemPropertiesFlags.java
+++ b/core/java/com/android/internal/config/sysui/SystemUiSystemPropertiesFlags.java
@@ -74,10 +74,6 @@
public static final Flag LOG_DND_STATE_EVENTS =
releasedFlag("persist.sysui.notification.log_dnd_state_events");
- /** Gating the holding of WakeLocks until NLSes are told about a new notification. */
- public static final Flag WAKE_LOCK_FOR_POSTING_NOTIFICATION =
- releasedFlag("persist.sysui.notification.wake_lock_for_posting_notification");
-
/** Gating storing NotificationRankingUpdate ranking map in shared memory. */
public static final Flag RANKING_UPDATE_ASHMEM = devFlag(
"persist.sysui.notification.ranking_update_ashmem");
diff --git a/core/java/com/android/internal/util/LatencyTracker.java b/core/java/com/android/internal/util/LatencyTracker.java
index 116c301c..3e9458d 100644
--- a/core/java/com/android/internal/util/LatencyTracker.java
+++ b/core/java/com/android/internal/util/LatencyTracker.java
@@ -342,7 +342,11 @@
synchronized (mLock) {
int samplingInterval = properties.getInt(SETTINGS_SAMPLING_INTERVAL_KEY,
DEFAULT_SAMPLING_INTERVAL);
+ boolean wasEnabled = mEnabled;
mEnabled = properties.getBoolean(SETTINGS_ENABLED_KEY, DEFAULT_ENABLED);
+ if (wasEnabled != mEnabled) {
+ Log.d(TAG, "Latency tracker " + (mEnabled ? "enabled" : "disabled") + ".");
+ }
for (int action : ACTIONS_ALL) {
String actionName = getNameOfAction(STATSD_ACTION[action]).toLowerCase(Locale.ROOT);
int legacyActionTraceThreshold = properties.getInt(
diff --git a/core/jni/android_app_NativeActivity.cpp b/core/jni/android_app_NativeActivity.cpp
index 21369f9..d6c5593 100644
--- a/core/jni/android_app_NativeActivity.cpp
+++ b/core/jni/android_app_NativeActivity.cpp
@@ -352,7 +352,7 @@
const char* dirStr = env->GetStringUTFChars(internalDataDir, NULL);
code->internalDataPathObj = dirStr;
- code->internalDataPath = code->internalDataPathObj.string();
+ code->internalDataPath = code->internalDataPathObj.c_str();
env->ReleaseStringUTFChars(internalDataDir, dirStr);
if (externalDataDir != NULL) {
@@ -360,7 +360,7 @@
code->externalDataPathObj = dirStr;
env->ReleaseStringUTFChars(externalDataDir, dirStr);
}
- code->externalDataPath = code->externalDataPathObj.string();
+ code->externalDataPath = code->externalDataPathObj.c_str();
code->sdkVersion = sdkVersion;
@@ -372,7 +372,7 @@
code->obbPathObj = dirStr;
env->ReleaseStringUTFChars(obbDir, dirStr);
}
- code->obbPath = code->obbPathObj.string();
+ code->obbPath = code->obbPathObj.c_str();
jbyte* rawSavedState = NULL;
jsize rawSavedSize = 0;
diff --git a/core/jni/android_app_backup_FullBackup.cpp b/core/jni/android_app_backup_FullBackup.cpp
index 339a7d3..5e096d7 100644
--- a/core/jni/android_app_backup_FullBackup.cpp
+++ b/core/jni/android_app_backup_FullBackup.cpp
@@ -106,15 +106,14 @@
: NULL;
if (path.length() < rootpath.length()) {
- ALOGE("file path [%s] shorter than root path [%s]",
- path.string(), rootpath.string());
+ ALOGE("file path [%s] shorter than root path [%s]", path.c_str(), rootpath.c_str());
return (jint) -1;
}
off64_t tarSize = 0;
jint err = write_tarfile(packageName, domain, rootpath, path, &tarSize, writer);
if (!err) {
- ALOGI("measured [%s] at %lld", path.string(), (long long)tarSize);
+ ALOGI("measured [%s] at %lld", path.c_str(), (long long)tarSize);
env->CallVoidMethod(dataOutputObj, sFullBackupDataOutput.addSize, (jlong) tarSize);
}
diff --git a/core/jni/android_backup_BackupDataInput.cpp b/core/jni/android_backup_BackupDataInput.cpp
index 79fa2a2..fc081a7 100644
--- a/core/jni/android_backup_BackupDataInput.cpp
+++ b/core/jni/android_backup_BackupDataInput.cpp
@@ -76,7 +76,7 @@
return err < 0 ? err : -1;
}
// TODO: Set the fields in the entity object
- jstring keyStr = env->NewStringUTF(key.string());
+ jstring keyStr = env->NewStringUTF(key.c_str());
env->SetObjectField(entity, s_keyField, keyStr);
env->SetIntField(entity, s_dataSizeField, dataSize);
return 0;
diff --git a/core/jni/android_backup_BackupHelperDispatcher.cpp b/core/jni/android_backup_BackupHelperDispatcher.cpp
index efe7d0b..efce8e1 100644
--- a/core/jni/android_backup_BackupHelperDispatcher.cpp
+++ b/core/jni/android_backup_BackupHelperDispatcher.cpp
@@ -118,7 +118,7 @@
}
env->SetIntField(headerObj, s_chunkSizeField, flattenedHeader.dataSize);
- env->SetObjectField(headerObj, s_keyPrefixField, env->NewStringUTF(keyPrefix.string()));
+ env->SetObjectField(headerObj, s_keyPrefixField, env->NewStringUTF(keyPrefix.c_str()));
return (jint) 0;
}
diff --git a/core/jni/android_database_CursorWindow.cpp b/core/jni/android_database_CursorWindow.cpp
index 2435406..60da2c2 100644
--- a/core/jni/android_database_CursorWindow.cpp
+++ b/core/jni/android_database_CursorWindow.cpp
@@ -58,13 +58,13 @@
msg.appendFormat("Couldn't read row %d, col %d from CursorWindow. "
"Make sure the Cursor is initialized correctly before accessing data from it.",
row, column);
- jniThrowException(env, "java/lang/IllegalStateException", msg.string());
+ jniThrowException(env, "java/lang/IllegalStateException", msg.c_str());
}
static void throwUnknownTypeException(JNIEnv * env, jint type) {
String8 msg;
msg.appendFormat("UNKNOWN type %d", type);
- jniThrowException(env, "java/lang/IllegalStateException", msg.string());
+ jniThrowException(env, "java/lang/IllegalStateException", msg.c_str());
}
static int getFdCount() {
@@ -107,7 +107,7 @@
fail:
jniThrowExceptionFmt(env, "android/database/CursorWindowAllocationException",
"Could not allocate CursorWindow '%s' of size %d due to error %d.",
- name.string(), cursorWindowSize, status);
+ name.c_str(), cursorWindowSize, status);
return 0;
}
@@ -139,7 +139,7 @@
static jstring nativeGetName(JNIEnv* env, jclass clazz, jlong windowPtr) {
CursorWindow* window = reinterpret_cast<CursorWindow*>(windowPtr);
- return env->NewStringUTF(window->name().string());
+ return env->NewStringUTF(window->name().c_str());
}
static void nativeWriteToParcel(JNIEnv * env, jclass clazz, jlong windowPtr,
@@ -151,7 +151,7 @@
if (status) {
String8 msg;
msg.appendFormat("Could not write CursorWindow to Parcel due to error %d.", status);
- jniThrowRuntimeException(env, msg.string());
+ jniThrowRuntimeException(env, msg.c_str());
}
}
@@ -267,7 +267,7 @@
// doesn't like UTF-8 strings with high codepoints. It actually expects
// Modified UTF-8 with encoded surrogate pairs.
String16 utf16(value, sizeIncludingNull - 1);
- return env->NewString(reinterpret_cast<const jchar*>(utf16.string()), utf16.size());
+ return env->NewString(reinterpret_cast<const jchar*>(utf16.c_str()), utf16.size());
} else if (type == CursorWindow::FIELD_TYPE_INTEGER) {
int64_t value = window->getFieldSlotValueLong(fieldSlot);
char buf[32];
diff --git a/core/jni/android_database_SQLiteCommon.cpp b/core/jni/android_database_SQLiteCommon.cpp
index daa2087..c6a7511 100644
--- a/core/jni/android_database_SQLiteCommon.cpp
+++ b/core/jni/android_database_SQLiteCommon.cpp
@@ -229,7 +229,7 @@
fullMessage.append(": ");
fullMessage.append(message);
}
- jniThrowException(env, exceptionClass, fullMessage.string());
+ jniThrowException(env, exceptionClass, fullMessage.c_str());
} else {
jniThrowException(env, exceptionClass, message);
}
diff --git a/core/jni/android_database_SQLiteConnection.cpp b/core/jni/android_database_SQLiteConnection.cpp
index 29520c2..893cc98 100644
--- a/core/jni/android_database_SQLiteConnection.cpp
+++ b/core/jni/android_database_SQLiteConnection.cpp
@@ -91,15 +91,14 @@
// Called each time a statement begins execution, when tracing is enabled.
static void sqliteTraceCallback(void *data, const char *sql) {
SQLiteConnection* connection = static_cast<SQLiteConnection*>(data);
- ALOG(LOG_VERBOSE, SQLITE_TRACE_TAG, "%s: \"%s\"\n",
- connection->label.string(), sql);
+ ALOG(LOG_VERBOSE, SQLITE_TRACE_TAG, "%s: \"%s\"\n", connection->label.c_str(), sql);
}
// Called each time a statement finishes execution, when profiling is enabled.
static void sqliteProfileCallback(void *data, const char *sql, sqlite3_uint64 tm) {
SQLiteConnection* connection = static_cast<SQLiteConnection*>(data);
- ALOG(LOG_VERBOSE, SQLITE_PROFILE_TAG, "%s: \"%s\" took %0.3f ms\n",
- connection->label.string(), sql, tm * 0.000001f);
+ ALOG(LOG_VERBOSE, SQLITE_PROFILE_TAG, "%s: \"%s\" took %0.3f ms\n", connection->label.c_str(),
+ sql, tm * 0.000001f);
}
// Called after each SQLite VM instruction when cancelation is enabled.
@@ -130,7 +129,7 @@
env->ReleaseStringUTFChars(labelStr, labelChars);
sqlite3* db;
- int err = sqlite3_open_v2(path.string(), &db, sqliteFlags, NULL);
+ int err = sqlite3_open_v2(path.c_str(), &db, sqliteFlags, NULL);
if (err != SQLITE_OK) {
throw_sqlite3_exception_errcode(env, err, "Could not open database");
return 0;
@@ -180,7 +179,7 @@
sqlite3_profile(db, &sqliteProfileCallback, connection);
}
- ALOGV("Opened connection %p with label '%s'", db, label.string());
+ ALOGV("Opened connection %p with label '%s'", db, label.c_str());
return reinterpret_cast<jlong>(connection);
}
@@ -760,7 +759,7 @@
if (status) {
String8 msg;
msg.appendFormat("Failed to clear the cursor window, status=%d", status);
- throw_sqlite3_exception(env, connection->db, msg.string());
+ throw_sqlite3_exception(env, connection->db, msg.c_str());
return 0;
}
@@ -770,7 +769,7 @@
String8 msg;
msg.appendFormat("Failed to set the cursor window column count to %d, status=%d",
numColumns, status);
- throw_sqlite3_exception(env, connection->db, msg.string());
+ throw_sqlite3_exception(env, connection->db, msg.c_str());
return 0;
}
@@ -845,7 +844,7 @@
String8 msg;
msg.appendFormat("Row too big to fit into CursorWindow requiredPos=%d, totalRows=%d",
requiredPos, totalRows);
- throw_sqlite3_exception(env, SQLITE_TOOBIG, NULL, msg.string());
+ throw_sqlite3_exception(env, SQLITE_TOOBIG, NULL, msg.c_str());
return 0;
}
diff --git a/core/jni/android_ddm_DdmHandleNativeHeap.cpp b/core/jni/android_ddm_DdmHandleNativeHeap.cpp
index 2ca4500..d36d29c 100644
--- a/core/jni/android_ddm_DdmHandleNativeHeap.cpp
+++ b/core/jni/android_ddm_DdmHandleNativeHeap.cpp
@@ -100,8 +100,8 @@
if (array != NULL) {
env->SetByteArrayRegion(array, 0,
sizeof(header), reinterpret_cast<jbyte*>(&header));
- env->SetByteArrayRegion(array, sizeof(header),
- maps.size(), reinterpret_cast<const jbyte*>(maps.string()));
+ env->SetByteArrayRegion(array, sizeof(header), maps.size(),
+ reinterpret_cast<const jbyte*>(maps.c_str()));
env->SetByteArrayRegion(array, sizeof(header) + maps.size(),
header.allocSize, reinterpret_cast<jbyte*>(leak_info.buffer));
}
diff --git a/core/jni/android_hardware_Camera.cpp b/core/jni/android_hardware_Camera.cpp
index 1c59742..5f3a1b5 100644
--- a/core/jni/android_hardware_Camera.cpp
+++ b/core/jni/android_hardware_Camera.cpp
@@ -874,11 +874,11 @@
if (camera == 0) return 0;
String8 params8 = camera->getParameters();
- if (params8.isEmpty()) {
+ if (params8.empty()) {
jniThrowRuntimeException(env, "getParameters failed (empty parameters)");
return 0;
}
- return env->NewStringUTF(params8.string());
+ return env->NewStringUTF(params8.c_str());
}
static void android_hardware_Camera_reconnect(JNIEnv *env, jobject thiz)
diff --git a/core/jni/android_hardware_camera2_CameraMetadata.cpp b/core/jni/android_hardware_camera2_CameraMetadata.cpp
index 5293c58..041fed7 100644
--- a/core/jni/android_hardware_camera2_CameraMetadata.cpp
+++ b/core/jni/android_hardware_camera2_CameraMetadata.cpp
@@ -514,7 +514,7 @@
ssize_t res;
while ((res = TEMP_FAILURE_RETRY(read(readFd, &out[0], /*count*/1))) > 0) {
if (out[0] == '\n') {
- ALOGD("%s", logLine.string());
+ ALOGD("%s", logLine.c_str());
logLine.clear();
} else {
logLine.append(out);
@@ -526,8 +526,8 @@
"Failed to read from fd (errno = %#x, message = '%s')",
errno, strerror(errno));
//return;
- } else if (!logLine.isEmpty()) {
- ALOGD("%s", logLine.string());
+ } else if (!logLine.empty()) {
+ ALOGD("%s", logLine.c_str());
}
close(readFd);
@@ -956,8 +956,8 @@
return OK;
} else if (!res.isOk()) {
VendorTagDescriptor::clearGlobalVendorTagDescriptor();
- ALOGE("%s: Failed to setup vendor tag descriptors: %s",
- __FUNCTION__, res.toString8().string());
+ ALOGE("%s: Failed to setup vendor tag descriptors: %s", __FUNCTION__,
+ res.toString8().c_str());
return res.serviceSpecificErrorCode();
}
if (0 < desc->getTagCount()) {
@@ -971,8 +971,8 @@
return OK;
} else if (!res.isOk()) {
VendorTagDescriptorCache::clearGlobalVendorTagCache();
- ALOGE("%s: Failed to setup vendor tag cache: %s",
- __FUNCTION__, res.toString8().string());
+ ALOGE("%s: Failed to setup vendor tag cache: %s", __FUNCTION__,
+ res.toString8().c_str());
return res.serviceSpecificErrorCode();
}
diff --git a/core/jni/android_hardware_camera2_DngCreator.cpp b/core/jni/android_hardware_camera2_DngCreator.cpp
index c947fba..30e546c 100644
--- a/core/jni/android_hardware_camera2_DngCreator.cpp
+++ b/core/jni/android_hardware_camera2_DngCreator.cpp
@@ -1543,7 +1543,8 @@
String8 captureTime = nativeContext->getCaptureTime();
if (writer->addEntry(TAG_DATETIME, NativeContext::DATETIME_COUNT,
- reinterpret_cast<const uint8_t*>(captureTime.string()), TIFF_IFD_0) != OK) {
+ reinterpret_cast<const uint8_t*>(captureTime.c_str()),
+ TIFF_IFD_0) != OK) {
jniThrowExceptionFmt(env, "java/lang/IllegalArgumentException",
"Invalid metadata for tag %x", TAG_DATETIME);
return nullptr;
@@ -1551,7 +1552,8 @@
// datetime original
if (writer->addEntry(TAG_DATETIMEORIGINAL, NativeContext::DATETIME_COUNT,
- reinterpret_cast<const uint8_t*>(captureTime.string()), TIFF_IFD_0) != OK) {
+ reinterpret_cast<const uint8_t*>(captureTime.c_str()),
+ TIFF_IFD_0) != OK) {
jniThrowExceptionFmt(env, "java/lang/IllegalArgumentException",
"Invalid metadata for tag %x", TAG_DATETIMEORIGINAL);
return nullptr;
@@ -1879,8 +1881,10 @@
cameraModel += brand.c_str();
BAIL_IF_INVALID_RET_NULL_SP(writer->addEntry(TAG_UNIQUECAMERAMODEL, cameraModel.size() + 1,
- reinterpret_cast<const uint8_t*>(cameraModel.string()), TIFF_IFD_0), env,
- TAG_UNIQUECAMERAMODEL, writer);
+ reinterpret_cast<const uint8_t*>(
+ cameraModel.c_str()),
+ TIFF_IFD_0),
+ env, TAG_UNIQUECAMERAMODEL, writer);
}
{
@@ -2165,7 +2169,8 @@
String8 description = nativeContext->getDescription();
size_t len = description.bytes() + 1;
if (writer->addEntry(TAG_IMAGEDESCRIPTION, len,
- reinterpret_cast<const uint8_t*>(description.string()), TIFF_IFD_0) != OK) {
+ reinterpret_cast<const uint8_t*>(description.c_str()),
+ TIFF_IFD_0) != OK) {
jniThrowExceptionFmt(env, "java/lang/IllegalArgumentException",
"Invalid metadata for tag %x", TAG_IMAGEDESCRIPTION);
}
diff --git a/core/jni/android_os_HwParcel.cpp b/core/jni/android_os_HwParcel.cpp
index 4c4443f..0e3c510 100644
--- a/core/jni/android_os_HwParcel.cpp
+++ b/core/jni/android_os_HwParcel.cpp
@@ -285,7 +285,7 @@
hardware::Parcel *parcel =
JHwParcel::GetNativeContext(env, thiz)->getParcel();
- status_t err = parcel->writeInterfaceToken(nameCopy.string());
+ status_t err = parcel->writeInterfaceToken(nameCopy.c_str());
signalExceptionForError(env, err);
}
}
@@ -687,9 +687,7 @@
static jstring MakeStringObjFromHidlString(JNIEnv *env, const hidl_string &s) {
String16 utf16String(s.c_str(), s.size());
- return env->NewString(
- reinterpret_cast<const jchar *>(utf16String.string()),
- utf16String.size());
+ return env->NewString(reinterpret_cast<const jchar *>(utf16String.c_str()), utf16String.size());
}
static jstring JHwParcel_native_readString(JNIEnv *env, jobject thiz) {
diff --git a/core/jni/android_os_UEventObserver.cpp b/core/jni/android_os_UEventObserver.cpp
index eda5075..43a8be1 100644
--- a/core/jni/android_os_UEventObserver.cpp
+++ b/core/jni/android_os_UEventObserver.cpp
@@ -51,8 +51,8 @@
const char* field = buffer;
const char* end = buffer + length + 1;
do {
- if (strstr(field, match.string())) {
- ALOGV("Matched uevent message with pattern: %s", match.string());
+ if (strstr(field, match.c_str())) {
+ ALOGV("Matched uevent message with pattern: %s", match.c_str());
return true;
}
field += strlen(field) + 1;
diff --git a/core/jni/android_util_AssetManager.cpp b/core/jni/android_util_AssetManager.cpp
index f04b901..3ee15ab 100644
--- a/core/jni/android_util_AssetManager.cpp
+++ b/core/jni/android_util_AssetManager.cpp
@@ -347,14 +347,23 @@
}
static void NativeSetConfiguration(JNIEnv* env, jclass /*clazz*/, jlong ptr, jint mcc, jint mnc,
- jstring locale, jint orientation, jint touchscreen, jint density,
- jint keyboard, jint keyboard_hidden, jint navigation,
- jint screen_width, jint screen_height,
- jint smallest_screen_width_dp, jint screen_width_dp,
- jint screen_height_dp, jint screen_layout, jint ui_mode,
- jint color_mode, jint grammatical_gender, jint major_version) {
+ jstring default_locale, jobjectArray locales, jint orientation,
+ jint touchscreen, jint density, jint keyboard,
+ jint keyboard_hidden, jint navigation, jint screen_width,
+ jint screen_height, jint smallest_screen_width_dp,
+ jint screen_width_dp, jint screen_height_dp, jint screen_layout,
+ jint ui_mode, jint color_mode, jint grammatical_gender,
+ jint major_version) {
ATRACE_NAME("AssetManager::SetConfiguration");
+ const jsize locale_count = (locales == NULL) ? 0 : env->GetArrayLength(locales);
+
+ // Constants duplicated from Java class android.content.res.Configuration.
+ static const jint kScreenLayoutRoundMask = 0x300;
+ static const jint kScreenLayoutRoundShift = 8;
+
+ std::vector<ResTable_config> configs;
+
ResTable_config configuration;
memset(&configuration, 0, sizeof(configuration));
configuration.mcc = static_cast<uint16_t>(mcc);
@@ -375,25 +384,37 @@
configuration.colorMode = static_cast<uint8_t>(color_mode);
configuration.grammaticalInflection = static_cast<uint8_t>(grammatical_gender);
configuration.sdkVersion = static_cast<uint16_t>(major_version);
-
- if (locale != nullptr) {
- ScopedUtfChars locale_utf8(env, locale);
- CHECK(locale_utf8.c_str() != nullptr);
- configuration.setBcp47Locale(locale_utf8.c_str());
- }
-
- // Constants duplicated from Java class android.content.res.Configuration.
- static const jint kScreenLayoutRoundMask = 0x300;
- static const jint kScreenLayoutRoundShift = 8;
-
// In Java, we use a 32bit integer for screenLayout, while we only use an 8bit integer
// in C++. We must extract the round qualifier out of the Java screenLayout and put it
// into screenLayout2.
configuration.screenLayout2 =
- static_cast<uint8_t>((screen_layout & kScreenLayoutRoundMask) >> kScreenLayoutRoundShift);
+ static_cast<uint8_t>((screen_layout & kScreenLayoutRoundMask) >> kScreenLayoutRoundShift);
+
+ if (locale_count > 0) {
+ configs.resize(locale_count, configuration);
+ for (int i = 0; i < locale_count; i++) {
+ jstring locale = (jstring)(env->GetObjectArrayElement(locales, i));
+ ScopedUtfChars locale_utf8(env, locale);
+ CHECK(locale_utf8.c_str() != nullptr);
+ configs[i].setBcp47Locale(locale_utf8.c_str());
+ }
+ } else {
+ configs.push_back(configuration);
+ }
+
+ uint32_t default_locale_int = 0;
+ if (default_locale != nullptr) {
+ ResTable_config config;
+ static_assert(std::is_same_v<decltype(config.locale), decltype(default_locale_int)>);
+ ScopedUtfChars locale_utf8(env, default_locale);
+ CHECK(locale_utf8.c_str() != nullptr);
+ config.setBcp47Locale(locale_utf8.c_str());
+ default_locale_int = config.locale;
+ }
auto assetmanager = LockAndStartAssetManager(ptr);
- assetmanager->SetConfiguration(configuration);
+ assetmanager->SetConfigurations(configs);
+ assetmanager->SetDefaultLocale(default_locale_int);
}
static jobject NativeGetAssignedPackageIdentifiers(JNIEnv* env, jclass /*clazz*/, jlong ptr,
@@ -1498,94 +1519,97 @@
// JNI registration.
static const JNINativeMethod gAssetManagerMethods[] = {
- // AssetManager setup methods.
- {"nativeCreate", "()J", (void*)NativeCreate},
- {"nativeDestroy", "(J)V", (void*)NativeDestroy},
- {"nativeSetApkAssets", "(J[Landroid/content/res/ApkAssets;Z)V", (void*)NativeSetApkAssets},
- {"nativeSetConfiguration", "(JIILjava/lang/String;IIIIIIIIIIIIIIII)V",
- (void*)NativeSetConfiguration},
- {"nativeGetAssignedPackageIdentifiers", "(JZZ)Landroid/util/SparseArray;",
- (void*)NativeGetAssignedPackageIdentifiers},
+ // AssetManager setup methods.
+ {"nativeCreate", "()J", (void*)NativeCreate},
+ {"nativeDestroy", "(J)V", (void*)NativeDestroy},
+ {"nativeSetApkAssets", "(J[Landroid/content/res/ApkAssets;Z)V", (void*)NativeSetApkAssets},
+ {"nativeSetConfiguration", "(JIILjava/lang/String;[Ljava/lang/String;IIIIIIIIIIIIIIII)V",
+ (void*)NativeSetConfiguration},
+ {"nativeGetAssignedPackageIdentifiers", "(JZZ)Landroid/util/SparseArray;",
+ (void*)NativeGetAssignedPackageIdentifiers},
- // AssetManager file methods.
- {"nativeContainsAllocatedTable", "(J)Z", (void*)ContainsAllocatedTable},
- {"nativeList", "(JLjava/lang/String;)[Ljava/lang/String;", (void*)NativeList},
- {"nativeOpenAsset", "(JLjava/lang/String;I)J", (void*)NativeOpenAsset},
- {"nativeOpenAssetFd", "(JLjava/lang/String;[J)Landroid/os/ParcelFileDescriptor;",
- (void*)NativeOpenAssetFd},
- {"nativeOpenNonAsset", "(JILjava/lang/String;I)J", (void*)NativeOpenNonAsset},
- {"nativeOpenNonAssetFd", "(JILjava/lang/String;[J)Landroid/os/ParcelFileDescriptor;",
- (void*)NativeOpenNonAssetFd},
- {"nativeOpenXmlAsset", "(JILjava/lang/String;)J", (void*)NativeOpenXmlAsset},
- {"nativeOpenXmlAssetFd", "(JILjava/io/FileDescriptor;)J", (void*)NativeOpenXmlAssetFd},
+ // AssetManager file methods.
+ {"nativeContainsAllocatedTable", "(J)Z", (void*)ContainsAllocatedTable},
+ {"nativeList", "(JLjava/lang/String;)[Ljava/lang/String;", (void*)NativeList},
+ {"nativeOpenAsset", "(JLjava/lang/String;I)J", (void*)NativeOpenAsset},
+ {"nativeOpenAssetFd", "(JLjava/lang/String;[J)Landroid/os/ParcelFileDescriptor;",
+ (void*)NativeOpenAssetFd},
+ {"nativeOpenNonAsset", "(JILjava/lang/String;I)J", (void*)NativeOpenNonAsset},
+ {"nativeOpenNonAssetFd", "(JILjava/lang/String;[J)Landroid/os/ParcelFileDescriptor;",
+ (void*)NativeOpenNonAssetFd},
+ {"nativeOpenXmlAsset", "(JILjava/lang/String;)J", (void*)NativeOpenXmlAsset},
+ {"nativeOpenXmlAssetFd", "(JILjava/io/FileDescriptor;)J", (void*)NativeOpenXmlAssetFd},
- // AssetManager resource methods.
- {"nativeGetResourceValue", "(JISLandroid/util/TypedValue;Z)I", (void*)NativeGetResourceValue},
- {"nativeGetResourceBagValue", "(JIILandroid/util/TypedValue;)I",
- (void*)NativeGetResourceBagValue},
- {"nativeGetStyleAttributes", "(JI)[I", (void*)NativeGetStyleAttributes},
- {"nativeGetResourceStringArray", "(JI)[Ljava/lang/String;",
- (void*)NativeGetResourceStringArray},
- {"nativeGetResourceStringArrayInfo", "(JI)[I", (void*)NativeGetResourceStringArrayInfo},
- {"nativeGetResourceIntArray", "(JI)[I", (void*)NativeGetResourceIntArray},
- {"nativeGetResourceArraySize", "(JI)I", (void*)NativeGetResourceArraySize},
- {"nativeGetResourceArray", "(JI[I)I", (void*)NativeGetResourceArray},
- {"nativeGetParentThemeIdentifier", "(JI)I",
- (void*)NativeGetParentThemeIdentifier},
+ // AssetManager resource methods.
+ {"nativeGetResourceValue", "(JISLandroid/util/TypedValue;Z)I",
+ (void*)NativeGetResourceValue},
+ {"nativeGetResourceBagValue", "(JIILandroid/util/TypedValue;)I",
+ (void*)NativeGetResourceBagValue},
+ {"nativeGetStyleAttributes", "(JI)[I", (void*)NativeGetStyleAttributes},
+ {"nativeGetResourceStringArray", "(JI)[Ljava/lang/String;",
+ (void*)NativeGetResourceStringArray},
+ {"nativeGetResourceStringArrayInfo", "(JI)[I", (void*)NativeGetResourceStringArrayInfo},
+ {"nativeGetResourceIntArray", "(JI)[I", (void*)NativeGetResourceIntArray},
+ {"nativeGetResourceArraySize", "(JI)I", (void*)NativeGetResourceArraySize},
+ {"nativeGetResourceArray", "(JI[I)I", (void*)NativeGetResourceArray},
+ {"nativeGetParentThemeIdentifier", "(JI)I", (void*)NativeGetParentThemeIdentifier},
- // AssetManager resource name/ID methods.
- {"nativeGetResourceIdentifier", "(JLjava/lang/String;Ljava/lang/String;Ljava/lang/String;)I",
- (void*)NativeGetResourceIdentifier},
- {"nativeGetResourceName", "(JI)Ljava/lang/String;", (void*)NativeGetResourceName},
- {"nativeGetResourcePackageName", "(JI)Ljava/lang/String;", (void*)NativeGetResourcePackageName},
- {"nativeGetResourceTypeName", "(JI)Ljava/lang/String;", (void*)NativeGetResourceTypeName},
- {"nativeGetResourceEntryName", "(JI)Ljava/lang/String;", (void*)NativeGetResourceEntryName},
- {"nativeSetResourceResolutionLoggingEnabled", "(JZ)V",
- (void*) NativeSetResourceResolutionLoggingEnabled},
- {"nativeGetLastResourceResolution", "(J)Ljava/lang/String;",
- (void*) NativeGetLastResourceResolution},
- {"nativeGetLocales", "(JZ)[Ljava/lang/String;", (void*)NativeGetLocales},
- {"nativeGetSizeConfigurations", "(J)[Landroid/content/res/Configuration;",
- (void*)NativeGetSizeConfigurations},
- {"nativeGetSizeAndUiModeConfigurations", "(J)[Landroid/content/res/Configuration;",
- (void*)NativeGetSizeAndUiModeConfigurations},
+ // AssetManager resource name/ID methods.
+ {"nativeGetResourceIdentifier",
+ "(JLjava/lang/String;Ljava/lang/String;Ljava/lang/String;)I",
+ (void*)NativeGetResourceIdentifier},
+ {"nativeGetResourceName", "(JI)Ljava/lang/String;", (void*)NativeGetResourceName},
+ {"nativeGetResourcePackageName", "(JI)Ljava/lang/String;",
+ (void*)NativeGetResourcePackageName},
+ {"nativeGetResourceTypeName", "(JI)Ljava/lang/String;", (void*)NativeGetResourceTypeName},
+ {"nativeGetResourceEntryName", "(JI)Ljava/lang/String;", (void*)NativeGetResourceEntryName},
+ {"nativeSetResourceResolutionLoggingEnabled", "(JZ)V",
+ (void*)NativeSetResourceResolutionLoggingEnabled},
+ {"nativeGetLastResourceResolution", "(J)Ljava/lang/String;",
+ (void*)NativeGetLastResourceResolution},
+ {"nativeGetLocales", "(JZ)[Ljava/lang/String;", (void*)NativeGetLocales},
+ {"nativeGetSizeConfigurations", "(J)[Landroid/content/res/Configuration;",
+ (void*)NativeGetSizeConfigurations},
+ {"nativeGetSizeAndUiModeConfigurations", "(J)[Landroid/content/res/Configuration;",
+ (void*)NativeGetSizeAndUiModeConfigurations},
- // Style attribute related methods.
- {"nativeAttributeResolutionStack", "(JJIII)[I", (void*)NativeAttributeResolutionStack},
- {"nativeApplyStyle", "(JJIIJ[IJJ)V", (void*)NativeApplyStyle},
- {"nativeResolveAttrs", "(JJII[I[I[I[I)Z", (void*)NativeResolveAttrs},
- {"nativeRetrieveAttributes", "(JJ[I[I[I)Z", (void*)NativeRetrieveAttributes},
+ // Style attribute related methods.
+ {"nativeAttributeResolutionStack", "(JJIII)[I", (void*)NativeAttributeResolutionStack},
+ {"nativeApplyStyle", "(JJIIJ[IJJ)V", (void*)NativeApplyStyle},
+ {"nativeResolveAttrs", "(JJII[I[I[I[I)Z", (void*)NativeResolveAttrs},
+ {"nativeRetrieveAttributes", "(JJ[I[I[I)Z", (void*)NativeRetrieveAttributes},
- // Theme related methods.
- {"nativeThemeCreate", "(J)J", (void*)NativeThemeCreate},
- {"nativeGetThemeFreeFunction", "()J", (void*)NativeGetThemeFreeFunction},
- {"nativeThemeApplyStyle", "(JJIZ)V", (void*)NativeThemeApplyStyle},
- {"nativeThemeRebase", "(JJ[I[ZI)V", (void*)NativeThemeRebase},
+ // Theme related methods.
+ {"nativeThemeCreate", "(J)J", (void*)NativeThemeCreate},
+ {"nativeGetThemeFreeFunction", "()J", (void*)NativeGetThemeFreeFunction},
+ {"nativeThemeApplyStyle", "(JJIZ)V", (void*)NativeThemeApplyStyle},
+ {"nativeThemeRebase", "(JJ[I[ZI)V", (void*)NativeThemeRebase},
- {"nativeThemeCopy", "(JJJJ)V", (void*)NativeThemeCopy},
- {"nativeThemeGetAttributeValue", "(JJILandroid/util/TypedValue;Z)I",
- (void*)NativeThemeGetAttributeValue},
- {"nativeThemeDump", "(JJILjava/lang/String;Ljava/lang/String;)V", (void*)NativeThemeDump},
- {"nativeThemeGetChangingConfigurations", "(J)I", (void*)NativeThemeGetChangingConfigurations},
+ {"nativeThemeCopy", "(JJJJ)V", (void*)NativeThemeCopy},
+ {"nativeThemeGetAttributeValue", "(JJILandroid/util/TypedValue;Z)I",
+ (void*)NativeThemeGetAttributeValue},
+ {"nativeThemeDump", "(JJILjava/lang/String;Ljava/lang/String;)V", (void*)NativeThemeDump},
+ {"nativeThemeGetChangingConfigurations", "(J)I",
+ (void*)NativeThemeGetChangingConfigurations},
- // AssetInputStream methods.
- {"nativeAssetDestroy", "(J)V", (void*)NativeAssetDestroy},
- {"nativeAssetReadChar", "(J)I", (void*)NativeAssetReadChar},
- {"nativeAssetRead", "(J[BII)I", (void*)NativeAssetRead},
- {"nativeAssetSeek", "(JJI)J", (void*)NativeAssetSeek},
- {"nativeAssetGetLength", "(J)J", (void*)NativeAssetGetLength},
- {"nativeAssetGetRemainingLength", "(J)J", (void*)NativeAssetGetRemainingLength},
+ // AssetInputStream methods.
+ {"nativeAssetDestroy", "(J)V", (void*)NativeAssetDestroy},
+ {"nativeAssetReadChar", "(J)I", (void*)NativeAssetReadChar},
+ {"nativeAssetRead", "(J[BII)I", (void*)NativeAssetRead},
+ {"nativeAssetSeek", "(JJI)J", (void*)NativeAssetSeek},
+ {"nativeAssetGetLength", "(J)J", (void*)NativeAssetGetLength},
+ {"nativeAssetGetRemainingLength", "(J)J", (void*)NativeAssetGetRemainingLength},
- // System/idmap related methods.
- {"nativeGetOverlayableMap", "(JLjava/lang/String;)Ljava/util/Map;",
- (void*)NativeGetOverlayableMap},
- {"nativeGetOverlayablesToString", "(JLjava/lang/String;)Ljava/lang/String;",
- (void*)NativeGetOverlayablesToString},
+ // System/idmap related methods.
+ {"nativeGetOverlayableMap", "(JLjava/lang/String;)Ljava/util/Map;",
+ (void*)NativeGetOverlayableMap},
+ {"nativeGetOverlayablesToString", "(JLjava/lang/String;)Ljava/lang/String;",
+ (void*)NativeGetOverlayablesToString},
- // Global management/debug methods.
- {"getGlobalAssetCount", "()I", (void*)NativeGetGlobalAssetCount},
- {"getAssetAllocations", "()Ljava/lang/String;", (void*)NativeGetAssetAllocations},
- {"getGlobalAssetManagerCount", "()I", (void*)NativeGetGlobalAssetManagerCount},
+ // Global management/debug methods.
+ {"getGlobalAssetCount", "()I", (void*)NativeGetGlobalAssetCount},
+ {"getAssetAllocations", "()Ljava/lang/String;", (void*)NativeGetAssetAllocations},
+ {"getGlobalAssetManagerCount", "()I", (void*)NativeGetGlobalAssetManagerCount},
};
int register_android_content_AssetManager(JNIEnv* env) {
diff --git a/core/jni/android_util_Binder.cpp b/core/jni/android_util_Binder.cpp
index 9dce5e3..6ed0a8a 100644
--- a/core/jni/android_util_Binder.cpp
+++ b/core/jni/android_util_Binder.cpp
@@ -953,8 +953,10 @@
String8 msg;
msg.appendFormat("Unknown binder error code. 0x%" PRIx32, err);
// RemoteException is a checked exception, only throw from certain methods.
- jniThrowException(env, canThrowRemoteException
- ? "android/os/RemoteException" : "java/lang/RuntimeException", msg.string());
+ jniThrowException(env,
+ canThrowRemoteException ? "android/os/RemoteException"
+ : "java/lang/RuntimeException",
+ msg.c_str());
break;
}
}
@@ -1286,8 +1288,7 @@
IBinder* target = getBPNativeData(env, obj)->mObject.get();
if (target != NULL) {
const String16& desc = target->getInterfaceDescriptor();
- return env->NewString(reinterpret_cast<const jchar*>(desc.string()),
- desc.size());
+ return env->NewString(reinterpret_cast<const jchar*>(desc.c_str()), desc.size());
}
jniThrowException(env, "java/lang/RuntimeException",
"No binder found for object");
diff --git a/core/jni/android_util_Process.cpp b/core/jni/android_util_Process.cpp
index 4f2bf4a..18c60a7 100644
--- a/core/jni/android_util_Process.cpp
+++ b/core/jni/android_util_Process.cpp
@@ -148,7 +148,7 @@
const size_t N = name8.size();
if (N > 0) {
- const char* str = name8.string();
+ const char* str = name8.c_str();
for (size_t i=0; i<N; i++) {
if (str[i] < '0' || str[i] > '9') {
struct passwd* pwd = getpwnam(str);
@@ -180,7 +180,7 @@
const size_t N = name8.size();
if (N > 0) {
- const char* str = name8.string();
+ const char* str = name8.c_str();
for (size_t i=0; i<N; i++) {
if (str[i] < '0' || str[i] > '9') {
struct group* grp = getgrnam(str);
@@ -583,8 +583,8 @@
env->ReleaseStringCritical(name, str);
}
- if (!name8.isEmpty()) {
- AndroidRuntime::getRuntime()->setArgv0(name8.string(), true /* setProcName */);
+ if (!name8.empty()) {
+ AndroidRuntime::getRuntime()->setArgv0(name8.c_str(), true /* setProcName */);
}
}
@@ -690,7 +690,7 @@
return;
}
- int fd = open(file.string(), O_RDONLY | O_CLOEXEC);
+ int fd = open(file.c_str(), O_RDONLY | O_CLOEXEC);
if (fd >= 0) {
//ALOGI("Clearing %" PRId32 " sizes", count);
@@ -704,7 +704,7 @@
close(fd);
if (len < 0) {
- ALOGW("Unable to read %s", file.string());
+ ALOGW("Unable to read %s", file.c_str());
len = 0;
}
buffer[len] = 0;
@@ -717,7 +717,7 @@
//ALOGI("Parsing at: %s", p);
for (i=0; i<count; i++) {
const String8& field = fields[i];
- if (strncmp(p, field.string(), field.length()) == 0) {
+ if (strncmp(p, field.c_str(), field.length()) == 0) {
p += field.length();
while (*p == ' ' || *p == '\t') p++;
char* num = p;
@@ -729,7 +729,7 @@
}
char* end;
sizesArray[i] = strtoll(num, &end, 10);
- //ALOGI("Field %s = %" PRId64, field.string(), sizesArray[i]);
+ // ALOGI("Field %s = %" PRId64, field.c_str(), sizesArray[i]);
foundCount++;
break;
}
@@ -746,7 +746,7 @@
free(buffer);
} else {
- ALOGW("Unable to open %s", file.string());
+ ALOGW("Unable to open %s", file.c_str());
}
//ALOGI("Done!");
diff --git a/core/proto/android/providers/settings/secure.proto b/core/proto/android/providers/settings/secure.proto
index ed0081c..ad88092 100644
--- a/core/proto/android/providers/settings/secure.proto
+++ b/core/proto/android/providers/settings/secure.proto
@@ -97,6 +97,7 @@
optional SettingProto accessibility_magnification_joystick_enabled = 50 [ (android.privacy).dest = DEST_AUTOMATIC ];
// Settings for font scaling
optional SettingProto accessibility_font_scaling_has_been_changed = 51 [ (android.privacy).dest = DEST_AUTOMATIC ];
+ optional SettingProto accessibility_force_invert_color_enabled = 52 [ (android.privacy).dest = DEST_AUTOMATIC ];
}
optional Accessibility accessibility = 2;
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index c5b9ee9..d898a23 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -2094,6 +2094,16 @@
<permission android:name="android.permission.MANAGE_TEST_NETWORKS"
android:protectionLevel="signature" />
+ <!-- Allows direct access to the <RemoteAuth>Service interfaces.
+ @hide @TestApi @SystemApi(client=android.annotation.SystemApi.Client.MODULE_LIBRARIES) -->
+ <permission android:name="android.permission.MANAGE_REMOTE_AUTH"
+ android:protectionLevel="signature" />
+
+ <!-- Allows direct access to the <RemoteAuth>Service authentication methods.
+ @hide @TestApi @SystemApi(client=android.annotation.SystemApi.Client.MODULE_LIBRARIES) -->
+ <permission android:name="android.permission.USE_REMOTE_AUTH"
+ android:protectionLevel="signature" />
+
<!-- @SystemApi @hide Allows applications to read Wi-Fi credential.
<p>Not for use by third-party applications. -->
<permission android:name="android.permission.READ_WIFI_CREDENTIAL"
@@ -6099,6 +6109,10 @@
android:protectionLevel="signature|privileged|development|appop|retailDemo" />
<uses-permission android:name="android.permission.PACKAGE_USAGE_STATS" />
+ <!-- @SystemApi @hide Allows trusted system components to report events to UsageStatsManager -->
+ <permission android:name="android.permission.REPORT_USAGE_STATS"
+ android:protectionLevel="signature|module" />
+
<!-- Allows an application to query broadcast response stats (see
{@link android.app.usage.BroadcastResponseStats}).
@SystemApi
@@ -7706,6 +7720,15 @@
<permission android:name="android.permission.WRITE_FLAGS"
android:protectionLevel="signature" />
+ <!-- @hide Allows internal applications to manage displays.
+ <p>This means intercept internal signals about displays being (dis-)connected
+ and being able to enable or disable the external displays.
+ <p>Not for use by third-party applications.
+ <p>Protection level: signature
+ -->
+ <permission android:name="android.permission.MANAGE_DISPLAYS"
+ android:protectionLevel="signature" />
+
<!-- Attribution for Geofencing service. -->
<attribution android:tag="GeofencingService" android:label="@string/geofencing_service"/>
<!-- Attribution for Country Detector. -->
diff --git a/core/res/res/drawable/ic_private_profile_badge.xml b/core/res/res/drawable/ic_private_profile_badge.xml
new file mode 100644
index 0000000..28c0f8a
--- /dev/null
+++ b/core/res/res/drawable/ic_private_profile_badge.xml
@@ -0,0 +1,25 @@
+<!--
+ ~ 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 xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24"
+ android:viewportHeight="24">
+ <path
+ android:pathData="M10.5,15H13.5L12.925,11.775C13.258,11.608 13.517,11.367 13.7,11.05C13.9,10.733 14,10.383 14,10C14,9.45 13.8,8.983 13.4,8.6C13.017,8.2 12.55,8 12,8C11.45,8 10.975,8.2 10.575,8.6C10.192,8.983 10,9.45 10,10C10,10.383 10.092,10.733 10.275,11.05C10.475,11.367 10.742,11.608 11.075,11.775L10.5,15ZM12,22C9.683,21.417 7.767,20.092 6.25,18.025C4.75,15.942 4,13.633 4,11.1V5L12,2L20,5V11.1C20,13.633 19.242,15.942 17.725,18.025C16.225,20.092 14.317,21.417 12,22ZM12,19.9C13.733,19.35 15.167,18.25 16.3,16.6C17.433,14.95 18,13.117 18,11.1V6.375L12,4.125L6,6.375V11.1C6,13.117 6.567,14.95 7.7,16.6C8.833,18.25 10.267,19.35 12,19.9Z"
+ android:fillColor="@android:color/system_accent1_900"/>
+</vector>
\ No newline at end of file
diff --git a/core/res/res/drawable/ic_private_profile_icon_badge.xml b/core/res/res/drawable/ic_private_profile_icon_badge.xml
new file mode 100644
index 0000000..5cb6a9d
--- /dev/null
+++ b/core/res/res/drawable/ic_private_profile_icon_badge.xml
@@ -0,0 +1,31 @@
+<!--
+ ~ 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 xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="64dp"
+ android:height="64dp"
+ android:viewportWidth="64"
+ android:viewportHeight="64">
+ <group
+ android:scaleX=".66"
+ android:scaleY=".66"
+ android:translateX="42"
+ android:translateY="42">
+ <path
+ android:pathData="M10.5,15H13.5L12.925,11.775C13.258,11.608 13.517,11.367 13.7,11.05C13.9,10.733 14,10.383 14,10C14,9.45 13.8,8.983 13.4,8.6C13.017,8.2 12.55,8 12,8C11.45,8 10.975,8.2 10.575,8.6C10.192,8.983 10,9.45 10,10C10,10.383 10.092,10.733 10.275,11.05C10.475,11.367 10.742,11.608 11.075,11.775L10.5,15ZM12,22C9.683,21.417 7.767,20.092 6.25,18.025C4.75,15.942 4,13.633 4,11.1V5L12,2L20,5V11.1C20,13.633 19.242,15.942 17.725,18.025C16.225,20.092 14.317,21.417 12,22ZM12,19.9C13.733,19.35 15.167,18.25 16.3,16.6C17.433,14.95 18,13.117 18,11.1V6.375L12,4.125L6,6.375V11.1C6,13.117 6.567,14.95 7.7,16.6C8.833,18.25 10.267,19.35 12,19.9Z"
+ android:fillColor="@android:color/system_accent1_900"/>
+ </group>
+</vector>
\ No newline at end of file
diff --git a/core/res/res/drawable/stat_sys_private_profile_status.xml b/core/res/res/drawable/stat_sys_private_profile_status.xml
new file mode 100644
index 0000000..98cc88d
--- /dev/null
+++ b/core/res/res/drawable/stat_sys_private_profile_status.xml
@@ -0,0 +1,25 @@
+<!--
+ ~ 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 xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="17dp"
+ android:height="17dp"
+ android:viewportWidth="24"
+ android:viewportHeight="24">
+ <path
+ android:fillColor="@android:color/white"
+ android:pathData="M10.5,15H13.5L12.925,11.775C13.258,11.608 13.517,11.367 13.7,11.05C13.9,10.733 14,10.383 14,10C14,9.45 13.8,8.983 13.4,8.6C13.017,8.2 12.55,8 12,8C11.45,8 10.975,8.2 10.575,8.6C10.192,8.983 10,9.45 10,10C10,10.383 10.092,10.733 10.275,11.05C10.475,11.367 10.742,11.608 11.075,11.775L10.5,15ZM12,22C9.683,21.417 7.767,20.092 6.25,18.025C4.75,15.942 4,13.633 4,11.1V5L12,2L20,5V11.1C20,13.633 19.242,15.942 17.725,18.025C16.225,20.092 14.317,21.417 12,22ZM12,19.9C13.733,19.35 15.167,18.25 16.3,16.6C17.433,14.95 18,13.117 18,11.1V6.375L12,4.125L6,6.375V11.1C6,13.117 6.567,14.95 7.7,16.6C8.833,18.25 10.267,19.35 12,19.9Z"/>
+</vector>
\ No newline at end of file
diff --git a/core/res/res/values-af/strings.xml b/core/res/res/values-af/strings.xml
index 7675da5..46fb80c 100644
--- a/core/res/res/values-af/strings.xml
+++ b/core/res/res/values-af/strings.xml
@@ -669,14 +669,14 @@
</string-array>
<string name="fingerprint_error_vendor_unknown" msgid="4170002184907291065">"Iets is fout. Probeer weer."</string>
<string name="fingerprint_icon_content_description" msgid="4741068463175388817">"Vingerafdrukikoon"</string>
+ <string name="device_unlock_notification_name" msgid="2632928999862915709">"Toestelontsluiting"</string>
+ <string name="alternative_unlock_setup_notification_title" msgid="6241508547901933544">"Probeer ’n ander manier om te ontsluit"</string>
+ <string name="alternative_face_setup_notification_content" msgid="3384959224091897331">"Gebruik Gesigslot wanneer jou vingerafdruk nie herken word nie, soos wanneer jou vingers nat is"</string>
+ <string name="alternative_fp_setup_notification_content" msgid="7454096947415721639">"Gebruik Vingerafdrukslot wanneer jou gesig nie herken word nie, soos wanneer daar nie genoeg lig is nie"</string>
<string name="face_recalibrate_notification_name" msgid="7311163114750748686">"Gesigslot"</string>
<string name="face_recalibrate_notification_title" msgid="2524791952735579082">"Kwessie met Gesigslot"</string>
<string name="face_recalibrate_notification_content" msgid="3064513770251355594">"Tik om jou gesigmodel uit te vee en voeg jou gesig dan weer by"</string>
- <string name="face_setup_notification_title" msgid="8843461561970741790">"Stel Gesigslot op"</string>
- <string name="face_setup_notification_content" msgid="5463999831057751676">"Ontsluit jou foon deur daarna te kyk"</string>
<string name="face_sensor_privacy_enabled" msgid="7407126963510598508">"Skakel "<b>"kameratoegang"</b>" in Instellings > Privaatheid aan om Gesigslot te gebruik"</string>
- <string name="fingerprint_setup_notification_title" msgid="2002630611398849495">"Stel meer maniere op om te ontsluit"</string>
- <string name="fingerprint_setup_notification_content" msgid="205578121848324852">"Tik om \'n vingerafdruk by te voeg"</string>
<string name="fingerprint_recalibrate_notification_name" msgid="1414578431898579354">"Vingerafdrukslot"</string>
<string name="fingerprint_recalibrate_notification_title" msgid="2406561052064558497">"Kan nie vingerafdruksensor gebruik nie"</string>
<string name="fingerprint_recalibrate_notification_content" msgid="8519935717822194943">"Besoek \'n verskaffer wat herstelwerk doen."</string>
@@ -2031,7 +2031,8 @@
<string name="autofill_update_title" msgid="3630695947047069136">"Dateer op in "<b>"<xliff:g id="LABEL">%1$s</xliff:g>"</b>"?"</string>
<string name="autofill_update_title_with_type" msgid="5264152633488495704">"Dateer <xliff:g id="TYPE">%1$s</xliff:g> op in "<b>"<xliff:g id="LABEL">%2$s</xliff:g>"</b>"?"</string>
<string name="autofill_update_title_with_2types" msgid="1797514386321086273">"Dateer <xliff:g id="TYPE_0">%1$s</xliff:g> en <xliff:g id="TYPE_1">%2$s</xliff:g> op in "<b>"<xliff:g id="LABEL">%3$s</xliff:g>"</b>"?"</string>
- <string name="autofill_update_title_with_3types" msgid="1312232153076212291">"Dateer hierdie items op in "<b>"<xliff:g id="LABEL">%4$s</xliff:g>"</b>": <xliff:g id="TYPE_0">%1$s</xliff:g>, <xliff:g id="TYPE_1">%2$s</xliff:g> en <xliff:g id="TYPE_2">%3$s</xliff:g>?"</string>
+ <!-- no translation found for autofill_update_title_with_3types (8285767070604652626) -->
+ <skip />
<string name="autofill_save_yes" msgid="8035743017382012850">"Stoor"</string>
<string name="autofill_save_no" msgid="9212826374207023544">"Nee, dankie"</string>
<string name="autofill_save_notnow" msgid="2853932672029024195">"Nie nou nie"</string>
diff --git a/core/res/res/values-am/strings.xml b/core/res/res/values-am/strings.xml
index 8a03761..40246bef 100644
--- a/core/res/res/values-am/strings.xml
+++ b/core/res/res/values-am/strings.xml
@@ -249,7 +249,7 @@
<string name="global_action_emergency" msgid="1387617624177105088">"ድንገተኛ አደጋ"</string>
<string name="global_action_bug_report" msgid="5127867163044170003">"የሳንካ ሪፖርት"</string>
<string name="global_action_logout" msgid="6093581310002476511">"ክፍለ-ጊዜን አብቃ"</string>
- <string name="global_action_screenshot" msgid="2610053466156478564">"ቅጽበታዊ ገፅ እይታ"</string>
+ <string name="global_action_screenshot" msgid="2610053466156478564">"ቅጽበታዊ ገፅ ዕይታ"</string>
<string name="bugreport_title" msgid="8549990811777373050">"የሳንካ ሪፖርት"</string>
<string name="bugreport_message" msgid="5212529146119624326">"ይሄ እንደ የኢሜይል መልዕክት አድርጎ የሚልከውን ስለመሣሪያዎ የአሁኑ ሁኔታ መረጃ ይሰበስባል። የሳንካ ሪፖርቱን ከመጀመር ጀምሮ እስኪላክ ድረስ ትንሽ ጊዜ ይወስዳል፤ እባክዎ ይታገሱ።"</string>
<string name="bugreport_option_interactive_title" msgid="7968287837902871289">"መስተጋብራዊ ሪፖርት"</string>
@@ -257,7 +257,7 @@
<string name="bugreport_option_full_title" msgid="7681035745950045690">"ሙሉ ሪፖርት"</string>
<string name="bugreport_option_full_summary" msgid="1975130009258435885">"መሣሪያዎ ምላሽ የማይሰጥ ወይም በጣም ቀርፋፋ ከሆነ፣ ወይም ሁሉንም የሪፖርት ክፍሎች የሚያስፈልገዎት ከሆነ ለዝቅተኛ የስርዓት ጣልቃ-ገብነት ይህን አማራጭ ይጠቀሙ። ተጨማሪ ዝርዝሮችን እንዲያስገቡ ወይም ተጨማሪ ቅጽበታዊ ገፅ እይታዎችን እንዲያነሱ አያስችልዎትም።"</string>
<string name="bugreport_countdown" msgid="6418620521782120755">"{count,plural, =1{በ# ሰከንድ ውስጥ ለሳንካ ሪፖርት ቅጽበታዊ ገፅ ዕይታን በማንሳት ላይ።}one{በ# ሰከንዶች ውስጥ ለሳንካ ሪፖርት ቅጽበታዊ ገፅ ዕይታን በማንሳት ላይ።}other{በ# ሰከንዶች ውስጥ ለሳንካ ሪፖርት ቅጽበታዊ ገፅ ዕይታን በማንሳት ላይ።}}"</string>
- <string name="bugreport_screenshot_success_toast" msgid="7986095104151473745">"ቅጽበታዊ ገፅ እይታ ከሳንካ ሪፖርት ጋር ተነስቷል"</string>
+ <string name="bugreport_screenshot_success_toast" msgid="7986095104151473745">"ቅጽበታዊ ገፅ ዕይታ ከሳንካ ሪፖርት ጋር ተነስቷል"</string>
<string name="bugreport_screenshot_failure_toast" msgid="6736320861311294294">"ቅጽበታዊ ገፅ እይታን ከሳንካ ሪፖርት ጋር ማንሳት አልተሳካም"</string>
<string name="global_action_toggle_silent_mode" msgid="8464352592860372188">"የፀጥታ ሁነታ"</string>
<string name="global_action_silent_mode_on_status" msgid="2371892537738632013">"ድምፅ ጠፍቷል"</string>
@@ -351,8 +351,8 @@
<string name="permdesc_statusBarService" msgid="6652917399085712557">"የኹናቴ አሞሌ እንዲሆን ለመተግበሪያው ይፈቅዳሉ።"</string>
<string name="permlab_expandStatusBar" msgid="1184232794782141698">"የሁኔታ አሞሌ ዘርጋ/ሰብስብ"</string>
<string name="permdesc_expandStatusBar" msgid="7180756900448498536">"የሁኔታ አሞሌን ለመዝረጋት እና ለመሰብሰብ ለመተግበሪያው ይፈቅዳሉ።"</string>
- <string name="permlab_fullScreenIntent" msgid="4310888199502509104">"በአንድ የተቆለፈ መሣሪያ ላይ ማሳወቂያዎችን እንደ የሙሉ ገፅ እይታ እንቅስቃሴዎችን ማሳየት"</string>
- <string name="permdesc_fullScreenIntent" msgid="1100721419406643997">"መተግበሪያው በአንድ የተቆለፈ መሣሪያ ላይ ማሳወቂያዎችን እንደ የሙሉ ገፅ እይታ እንቅስቃሴዎች አድርጎ እንዲያሳይ ያስችለዋል"</string>
+ <string name="permlab_fullScreenIntent" msgid="4310888199502509104">"በአንድ የተቆለፈ መሣሪያ ላይ ማሳወቂያዎችን እንደ የሙሉ ገፅ ዕይታ እንቅስቃሴዎችን ማሳየት"</string>
+ <string name="permdesc_fullScreenIntent" msgid="1100721419406643997">"መተግበሪያው በአንድ የተቆለፈ መሣሪያ ላይ ማሳወቂያዎችን እንደ የሙሉ ገፅ ዕይታ እንቅስቃሴዎች አድርጎ እንዲያሳይ ያስችለዋል"</string>
<string name="permlab_install_shortcut" msgid="7451554307502256221">"አቋራጮችን ይጭናል"</string>
<string name="permdesc_install_shortcut" msgid="4476328467240212503">"አንድ መተግበሪያ ያለተጠቃሚ ጣልቃ-ገብነት የመነሻ ማያ ገፅ አቋራጮችን እንዲያክል ያስችለዋል።"</string>
<string name="permlab_uninstall_shortcut" msgid="295263654781900390">"አቋራጮችን ያራግፋል"</string>
@@ -488,7 +488,7 @@
<string name="permlab_recordBackgroundAudio" msgid="5891032812308878254">"በበስተጀርባ ኦዲዮን ይቅዱ"</string>
<string name="permdesc_recordBackgroundAudio" msgid="1992623135737407516">"ይህ መተግበሪያ በማናቸውም ጊዜ ማይክራፎኑን በመጠቀም ኦዲዮን መቅዳት ይችላል።"</string>
<string name="permlab_detectScreenCapture" msgid="4447042362828799433">"የመተግበሪያ መስኮቶች የማያ ገፅ ቀረጻዎችን ማወቅ"</string>
- <string name="permdesc_detectScreenCapture" msgid="3485784917960342284">"መተግበሪያው በጥቅም ላይ ሳለ ቅጽበታዊ ገፅ እይታ ሲነሳ ይህ መተግበሪያ ማሳወቂያ ይደርሰዋል።"</string>
+ <string name="permdesc_detectScreenCapture" msgid="3485784917960342284">"መተግበሪያው በጥቅም ላይ ሳለ ቅጽበታዊ ገፅ ዕይታ ሲነሳ ይህ መተግበሪያ ማሳወቂያ ይደርሰዋል።"</string>
<string name="permlab_sim_communication" msgid="176788115994050692">"ወደ ሲሙ ትዕዛዞችን መላክ"</string>
<string name="permdesc_sim_communication" msgid="4179799296415957960">"መተግበሪያው ትዕዛዞችን ወደ ሲሙ እንዲልክ ያስችለዋል። ይሄ በጣማ አደገኛ ነው።"</string>
<string name="permlab_activityRecognition" msgid="1782303296053990884">"አካላዊ እንቅስቃሴን ለይቶ ማወቅ"</string>
@@ -669,14 +669,14 @@
</string-array>
<string name="fingerprint_error_vendor_unknown" msgid="4170002184907291065">"የሆነ ችግር ተፈጥሯል። እንደገና ይሞክሩ።"</string>
<string name="fingerprint_icon_content_description" msgid="4741068463175388817">"የጣት አሻራ አዶ"</string>
+ <string name="device_unlock_notification_name" msgid="2632928999862915709">"የመሣሪያ መክፈቻ"</string>
+ <string name="alternative_unlock_setup_notification_title" msgid="6241508547901933544">"ለመክፈት ሌላ ዘዴ ይሞክሩ"</string>
+ <string name="alternative_face_setup_notification_content" msgid="3384959224091897331">"የጣት አሻራዎ ሳይለይ ሲቀር ለምሳሌ ጣትዎ እርጥብ ሲሆን በመልክ መክፈትን ይጠቀሙ"</string>
+ <string name="alternative_fp_setup_notification_content" msgid="7454096947415721639">"መልክዎ ሳይለይ ሲቀር ለምሳሌ በቂ ብርሃን በማይኖርበት ወቅት የጣት አሻራን ይጠቀሙ"</string>
<string name="face_recalibrate_notification_name" msgid="7311163114750748686">"በመልክ መክፈት"</string>
<string name="face_recalibrate_notification_title" msgid="2524791952735579082">"ከመልክ መክፈት ጋር በተያያዘ ችግር"</string>
<string name="face_recalibrate_notification_content" msgid="3064513770251355594">"የእርስዎ የመልክ ሞዴል ለመሰረዝ መታ ያድርጉ፣ ከዚያ መልክዎን እንደገና ያክሉ"</string>
- <string name="face_setup_notification_title" msgid="8843461561970741790">"በመልክ መክፈትን ያዋቅሩ"</string>
- <string name="face_setup_notification_content" msgid="5463999831057751676">"ስልክዎን በመመልከት ያስከፍቱት"</string>
<string name="face_sensor_privacy_enabled" msgid="7407126963510598508">"በመልክ መክፈትን ለመጠቀም "<b>"የካሜራ መዳረሻ"</b>"ን በቅንብሮች እና ግላዊነት ውስጥ ያብሩ"</string>
- <string name="fingerprint_setup_notification_title" msgid="2002630611398849495">"የሚከፍቱባቸው ተጨማሪ መንገዶችን ያቀናብሩ"</string>
- <string name="fingerprint_setup_notification_content" msgid="205578121848324852">"የጣት አሻራን ለማከል መታ ያድርጉ"</string>
<string name="fingerprint_recalibrate_notification_name" msgid="1414578431898579354">"በጣት አሻራ መክፈቻ"</string>
<string name="fingerprint_recalibrate_notification_title" msgid="2406561052064558497">"የጣት አሻራ ዳሳሽን መጠቀም አይቻልም"</string>
<string name="fingerprint_recalibrate_notification_content" msgid="8519935717822194943">"የጥገና አገልግሎት ሰጪን ይጎብኙ።"</string>
@@ -2031,7 +2031,8 @@
<string name="autofill_update_title" msgid="3630695947047069136">"በ"<b>"<xliff:g id="LABEL">%1$s</xliff:g>"</b>" ውስጥ ይዘመን?"</string>
<string name="autofill_update_title_with_type" msgid="5264152633488495704">"<xliff:g id="TYPE">%1$s</xliff:g> በ"<b>"<xliff:g id="LABEL">%2$s</xliff:g>"</b>" ውስጥ ይዘመን?"</string>
<string name="autofill_update_title_with_2types" msgid="1797514386321086273">"<xliff:g id="TYPE_0">%1$s</xliff:g> እና <xliff:g id="TYPE_1">%2$s</xliff:g> በ"<b>"<xliff:g id="LABEL">%3$s</xliff:g>"</b>" ውስጥ ይዘመኑ?"</string>
- <string name="autofill_update_title_with_3types" msgid="1312232153076212291">"እነዚህ ንጥሎች በ"<b>"<xliff:g id="LABEL">%4$s</xliff:g>"</b>" ውስጥ ይዘመኑ፦ <xliff:g id="TYPE_0">%1$s</xliff:g>፣ <xliff:g id="TYPE_1">%2$s</xliff:g> እና <xliff:g id="TYPE_2">%3$s</xliff:g>?"</string>
+ <!-- no translation found for autofill_update_title_with_3types (8285767070604652626) -->
+ <skip />
<string name="autofill_save_yes" msgid="8035743017382012850">"አስቀምጥ"</string>
<string name="autofill_save_no" msgid="9212826374207023544">"አይ፣ አመሰግናለሁ"</string>
<string name="autofill_save_notnow" msgid="2853932672029024195">"አሁን አይደለም"</string>
@@ -2139,7 +2140,7 @@
<string name="accessibility_system_action_quick_settings_label" msgid="4583900123506773783">"ፈጣን ቅንብሮች"</string>
<string name="accessibility_system_action_power_dialog_label" msgid="8095341821683910781">"የኃይል መገናኛ"</string>
<string name="accessibility_system_action_lock_screen_label" msgid="5484190691945563838">"የማያ ገፅ ቁልፍ"</string>
- <string name="accessibility_system_action_screenshot_label" msgid="3581566515062741676">"ቅጽበታዊ ገፅ እይታ"</string>
+ <string name="accessibility_system_action_screenshot_label" msgid="3581566515062741676">"ቅጽበታዊ ገፅ ዕይታ"</string>
<string name="accessibility_system_action_headset_hook_label" msgid="8524691721287425468">"የማዳመጫ መንጠቆ"</string>
<string name="accessibility_system_action_on_screen_a11y_shortcut_label" msgid="8488701469459210309">"የማያ ገፅ ላይ ተደራሽነት አቋራጭ"</string>
<string name="accessibility_system_action_on_screen_a11y_shortcut_chooser_label" msgid="1057878690209817886">"የማያ ገፅ ላይ ተደራሽነት አቋራጭ መራጭ"</string>
@@ -2159,8 +2160,8 @@
<string name="unread_convo_overflow" msgid="920517615597353833">"<xliff:g id="MAX_UNREAD_COUNT">%1$d</xliff:g>+"</string>
<string name="resolver_personal_tab" msgid="2051260504014442073">"የግል"</string>
<string name="resolver_work_tab" msgid="2690019516263167035">"ሥራ"</string>
- <string name="resolver_personal_tab_accessibility" msgid="5739524949153091224">"የግል እይታ"</string>
- <string name="resolver_work_tab_accessibility" msgid="4753168230363802734">"የስራ እይታ"</string>
+ <string name="resolver_personal_tab_accessibility" msgid="5739524949153091224">"የግል ዕይታ"</string>
+ <string name="resolver_work_tab_accessibility" msgid="4753168230363802734">"የስራ ዕይታ"</string>
<string name="resolver_cross_profile_blocked" msgid="3014597376026044840">"በእርስዎ የአይቲ አስተዳዳሪ ታግዷል"</string>
<string name="resolver_cant_share_with_work_apps_explanation" msgid="9071442683080586643">"ይህ ይዘት በሥራ መተግበሪያዎች መጋራት አይችልም"</string>
<string name="resolver_cant_access_work_apps_explanation" msgid="1129960195389373279">"ይህ ይዘት በሥራ መተግበሪያዎች መከፈት አይችልም"</string>
diff --git a/core/res/res/values-ar/strings.xml b/core/res/res/values-ar/strings.xml
index fea0e8f..0286d42 100644
--- a/core/res/res/values-ar/strings.xml
+++ b/core/res/res/values-ar/strings.xml
@@ -673,14 +673,14 @@
</string-array>
<string name="fingerprint_error_vendor_unknown" msgid="4170002184907291065">"حدث خطأ، يُرجى إعادة المحاولة."</string>
<string name="fingerprint_icon_content_description" msgid="4741068463175388817">"رمز بصمة الإصبع"</string>
+ <string name="device_unlock_notification_name" msgid="2632928999862915709">"فتح قفل الجهاز"</string>
+ <string name="alternative_unlock_setup_notification_title" msgid="6241508547901933544">"تجربة طريقة أخرى لفتح القفل"</string>
+ <string name="alternative_face_setup_notification_content" msgid="3384959224091897331">"استخدِم ميزة \"فتح الجهاز بالتعرف على الوجه\" عندما لا يتم التعرف على بصمة إصبعك، مثلاً عندما تكون أصابعك مبتلة."</string>
+ <string name="alternative_fp_setup_notification_content" msgid="7454096947415721639">"استخدِم ميزة \"فتح الجهاز ببصمة الإصبع\" عندما لا يتم التعرف على وجهك، مثلاً عند عدم وجود ضوء كافٍ."</string>
<string name="face_recalibrate_notification_name" msgid="7311163114750748686">"فتح الجهاز بالتعرف على الوجه"</string>
<string name="face_recalibrate_notification_title" msgid="2524791952735579082">"مشكلة متعلّقة بميزة \"فتح الجهاز بالتعرف على الوجه\""</string>
<string name="face_recalibrate_notification_content" msgid="3064513770251355594">"انقر لحذف نموذج الوجه ثم أضِف نموذجًا لوجهك مرة أخرى."</string>
- <string name="face_setup_notification_title" msgid="8843461561970741790">"إعداد ميزة \"فتح الجهاز بالتعرف على الوجه\""</string>
- <string name="face_setup_notification_content" msgid="5463999831057751676">"يمكنك فتح قفل هاتفك بمجرّد النظر إلى الشاشة."</string>
<string name="face_sensor_privacy_enabled" msgid="7407126963510598508">"لاستخدام ميزة \"فتح الجهاز بالتعرف على الوجه\"، عليك منح إذن "<b>"الوصول إلى الكاميرا"</b>" في الإعدادات > الخصوصية."</string>
- <string name="fingerprint_setup_notification_title" msgid="2002630611398849495">"إعداد المزيد من الطرق لفتح قفل الجهاز"</string>
- <string name="fingerprint_setup_notification_content" msgid="205578121848324852">"انقر لإضافة بصمة إصبع."</string>
<string name="fingerprint_recalibrate_notification_name" msgid="1414578431898579354">"فتح الجهاز ببصمة الإصبع"</string>
<string name="fingerprint_recalibrate_notification_title" msgid="2406561052064558497">"لا يمكن استخدام مستشعر بصمات الإصبع"</string>
<string name="fingerprint_recalibrate_notification_content" msgid="8519935717822194943">"يُرجى التواصل مع مقدِّم خدمات إصلاح."</string>
@@ -1600,7 +1600,7 @@
<string name="data_usage_restricted_body" msgid="5338694433686077733">"انقر لإزالة القيد."</string>
<string name="data_usage_rapid_title" msgid="2950192123248740375">"استخدام مرتفع لبيانات الجوّال"</string>
<string name="data_usage_rapid_body" msgid="3886676853263693432">"استخدمت تطبيقاتك بيانات أكثر من المعتاد"</string>
- <string name="data_usage_rapid_app_body" msgid="5425779218506513861">"استخدمَ تطبيق <xliff:g id="APP">%s</xliff:g> بيانات أكثر من المعتاد"</string>
+ <string name="data_usage_rapid_app_body" msgid="5425779218506513861">"استخدمَ تطبيق \"<xliff:g id="APP">%s</xliff:g>\" بيانات أكثر من المعتاد"</string>
<string name="ssl_certificate" msgid="5690020361307261997">"شهادة الأمان"</string>
<string name="ssl_certificate_is_valid" msgid="7293675884598527081">"هذه الشهادة صالحة."</string>
<string name="issued_to" msgid="5975877665505297662">"إصدار لـ:"</string>
@@ -2035,7 +2035,8 @@
<string name="autofill_update_title" msgid="3630695947047069136">"هل تريد التحديث في "<b>"<xliff:g id="LABEL">%1$s</xliff:g>"</b>"؟"</string>
<string name="autofill_update_title_with_type" msgid="5264152633488495704">"هل تريد تحديث <xliff:g id="TYPE">%1$s</xliff:g> في "<b>"<xliff:g id="LABEL">%2$s</xliff:g>"</b>"؟"</string>
<string name="autofill_update_title_with_2types" msgid="1797514386321086273">"هل تريد تحديث <xliff:g id="TYPE_0">%1$s</xliff:g> و<xliff:g id="TYPE_1">%2$s</xliff:g> في "<b>"<xliff:g id="LABEL">%3$s</xliff:g>"</b>"؟"</string>
- <string name="autofill_update_title_with_3types" msgid="1312232153076212291">"هل تريد تحديث هذه العناصر في "<b>"<xliff:g id="LABEL">%4$s</xliff:g>"</b>": <xliff:g id="TYPE_0">%1$s</xliff:g> و<xliff:g id="TYPE_1">%2$s</xliff:g> و<xliff:g id="TYPE_2">%3$s</xliff:g>؟"</string>
+ <!-- no translation found for autofill_update_title_with_3types (8285767070604652626) -->
+ <skip />
<string name="autofill_save_yes" msgid="8035743017382012850">"حفظ"</string>
<string name="autofill_save_no" msgid="9212826374207023544">"لا، شكرًا"</string>
<string name="autofill_save_notnow" msgid="2853932672029024195">"ليس الآن"</string>
diff --git a/core/res/res/values-as/strings.xml b/core/res/res/values-as/strings.xml
index 24d2932..a1679b3 100644
--- a/core/res/res/values-as/strings.xml
+++ b/core/res/res/values-as/strings.xml
@@ -669,14 +669,14 @@
</string-array>
<string name="fingerprint_error_vendor_unknown" msgid="4170002184907291065">"কিবা ভুল হ’ল। পুনৰ চেষ্টা কৰক।"</string>
<string name="fingerprint_icon_content_description" msgid="4741068463175388817">"ফিংগাৰপ্ৰিণ্ট আইকন"</string>
+ <string name="device_unlock_notification_name" msgid="2632928999862915709">"ডিভাইচ আনলক"</string>
+ <string name="alternative_unlock_setup_notification_title" msgid="6241508547901933544">"আনলক কৰিবলৈ আন এটা উপায় ব্যৱহাৰ কৰি চাওক"</string>
+ <string name="alternative_face_setup_notification_content" msgid="3384959224091897331">"আপোনাৰ আঙুলিকেইটা তিতি থকাৰ দৰে পৰিস্থিতিত, আপোনাৰ ফিংগাৰপ্ৰিণ্ট চিনাক্ত কৰিব নোৱাৰিলে ফে’চ আনলক সুবিধাটো ব্যৱহাৰ কৰক"</string>
+ <string name="alternative_fp_setup_notification_content" msgid="7454096947415721639">"পৰ্যাপ্ত পোহৰ নথকাৰ দৰে পৰিস্থিতিত, আপোনাৰ মুখাৱয়ব চিনাক্ত কৰিব নোৱাৰিলে ফিংগাৰপ্ৰিণ্ট আনলক সুবিধাটো ব্যৱহাৰ কৰক"</string>
<string name="face_recalibrate_notification_name" msgid="7311163114750748686">"ফেচ আনলক"</string>
<string name="face_recalibrate_notification_title" msgid="2524791952735579082">"ফেচ আনলক ব্যৱহাৰ কৰোঁতে সমস্যা হৈছে"</string>
<string name="face_recalibrate_notification_content" msgid="3064513770251355594">"আপোনাৰ মুখাৱয়বৰ মডেলটো মচিবলৈ টিপক, তাৰ পাছত পুনৰ আপোনাৰ মুখাৱয়ব যোগ দিয়ক"</string>
- <string name="face_setup_notification_title" msgid="8843461561970741790">"ফেচ আনলক সুবিধাটো ছেট আপ কৰক"</string>
- <string name="face_setup_notification_content" msgid="5463999831057751676">"আপোনাৰ ফ’নটোলৈ চাই সেইটো আনলক কৰক"</string>
<string name="face_sensor_privacy_enabled" msgid="7407126963510598508">"ফেচ আনলক সুবিধাটো ব্যৱহাৰ কৰিবলৈ ছেটিং > গোপনীয়তাত "<b>"কেমেৰাৰ এক্সেছ"</b>" অন কৰক"</string>
- <string name="fingerprint_setup_notification_title" msgid="2002630611398849495">"আনলক কৰাৰ অধিক উপায় ছেট আপ কৰক"</string>
- <string name="fingerprint_setup_notification_content" msgid="205578121848324852">"এটা ফিংগাৰপ্ৰিণ্ট যোগ দিবলৈ টিপক"</string>
<string name="fingerprint_recalibrate_notification_name" msgid="1414578431898579354">"ফিংগাৰপ্ৰিন্ট আনলক"</string>
<string name="fingerprint_recalibrate_notification_title" msgid="2406561052064558497">"ফিংগাৰপ্ৰিণ্ট ছেন্সৰ ব্যৱহাৰ কৰিব নোৱাৰি"</string>
<string name="fingerprint_recalibrate_notification_content" msgid="8519935717822194943">"মেৰামতি সেৱা প্ৰদানকাৰী কোনো প্ৰতিষ্ঠানলৈ যাওক।"</string>
@@ -2031,7 +2031,8 @@
<string name="autofill_update_title" msgid="3630695947047069136"><b>"<xliff:g id="LABEL">%1$s</xliff:g>"</b>"ত আপডে’ট কৰিবনে?"</string>
<string name="autofill_update_title_with_type" msgid="5264152633488495704"><b>"<xliff:g id="LABEL">%2$s</xliff:g>"</b>"ত <xliff:g id="TYPE">%1$s</xliff:g> আপডে’ট কৰিবনে?"</string>
<string name="autofill_update_title_with_2types" msgid="1797514386321086273"><b>"<xliff:g id="LABEL">%3$s</xliff:g>"</b>"ত <xliff:g id="TYPE_0">%1$s</xliff:g> আৰু <xliff:g id="TYPE_1">%2$s</xliff:g> আপডে’ট কৰিবনে?"</string>
- <string name="autofill_update_title_with_3types" msgid="1312232153076212291">"এই তথ্যবোৰ "<b>"<xliff:g id="LABEL">%4$s</xliff:g>"</b>": <xliff:g id="TYPE_0">%1$s</xliff:g>, <xliff:g id="TYPE_1">%2$s</xliff:g> আৰু <xliff:g id="TYPE_2">%3$s</xliff:g>ত আপডে’ট কৰিবনে ?"</string>
+ <!-- no translation found for autofill_update_title_with_3types (8285767070604652626) -->
+ <skip />
<string name="autofill_save_yes" msgid="8035743017382012850">"ছেভ কৰক"</string>
<string name="autofill_save_no" msgid="9212826374207023544">"নালাগে, ধন্যবাদ"</string>
<string name="autofill_save_notnow" msgid="2853932672029024195">"এতিয়া নহয়"</string>
diff --git a/core/res/res/values-az/strings.xml b/core/res/res/values-az/strings.xml
index 4a6d7ff..49a599e 100644
--- a/core/res/res/values-az/strings.xml
+++ b/core/res/res/values-az/strings.xml
@@ -669,14 +669,14 @@
</string-array>
<string name="fingerprint_error_vendor_unknown" msgid="4170002184907291065">"Xəta oldu. Yenə cəhd edin."</string>
<string name="fingerprint_icon_content_description" msgid="4741068463175388817">"Barmaq izi ikonası"</string>
+ <string name="device_unlock_notification_name" msgid="2632928999862915709">"Cihazın kiliddən çıxarılması"</string>
+ <string name="alternative_unlock_setup_notification_title" msgid="6241508547901933544">"Başqa yolla kiliddən çıxarın"</string>
+ <string name="alternative_face_setup_notification_content" msgid="3384959224091897331">"Məsələn, barmaqlar yaş olduğu üçün barmaq izi tanınmadıqda Üzlə Kilidaçmadan istifadə edin"</string>
+ <string name="alternative_fp_setup_notification_content" msgid="7454096947415721639">"Məsələn, kifayət qədər işıq olmadığı üçün üz tanınmadıqda Barmaqla Kilidaçmadan istifadə edin"</string>
<string name="face_recalibrate_notification_name" msgid="7311163114750748686">"Üz ilə kiliddən çıxarma"</string>
<string name="face_recalibrate_notification_title" msgid="2524791952735579082">"Üz ilə kiliddən çıxarma problemi"</string>
<string name="face_recalibrate_notification_content" msgid="3064513770251355594">"Üz modelinizi silmək üçün toxunun, sonra yenidən üzünüzü əlavə edin"</string>
- <string name="face_setup_notification_title" msgid="8843461561970741790">"Üz ilə kiliddən çıxarmanı ayarlayın"</string>
- <string name="face_setup_notification_content" msgid="5463999831057751676">"Telefona baxaraq onu kiliddən çıxarın"</string>
<string name="face_sensor_privacy_enabled" msgid="7407126963510598508">"Üz ilə Kiliddən Açma funksiyasını istifadə etmək üçün Ayarlar > Məxfilik bölməsində "<b>"Kameraya girişi"</b>" aktiv edin"</string>
- <string name="fingerprint_setup_notification_title" msgid="2002630611398849495">"Kiliddən çıxarmağın daha çox yolunu ayarlayın"</string>
- <string name="fingerprint_setup_notification_content" msgid="205578121848324852">"Barmaq izi əlavə etmək üçün toxunun"</string>
<string name="fingerprint_recalibrate_notification_name" msgid="1414578431898579354">"Barmaq izi ilə kiliddən çıxarma"</string>
<string name="fingerprint_recalibrate_notification_title" msgid="2406561052064558497">"Barmaq izi sensorundan istifadə etmək mümkün deyil"</string>
<string name="fingerprint_recalibrate_notification_content" msgid="8519935717822194943">"Təmir provayderini ziyarət edin."</string>
@@ -2031,7 +2031,8 @@
<string name="autofill_update_title" msgid="3630695947047069136"><b>"<xliff:g id="LABEL">%1$s</xliff:g>"</b>" ünvanında yenilənsin?"</string>
<string name="autofill_update_title_with_type" msgid="5264152633488495704">"<xliff:g id="TYPE">%1$s</xliff:g> "<b>"<xliff:g id="LABEL">%2$s</xliff:g>"</b>" ünvanında yenilənsin?"</string>
<string name="autofill_update_title_with_2types" msgid="1797514386321086273">"<xliff:g id="TYPE_0">%1$s</xliff:g> və <xliff:g id="TYPE_1">%2$s</xliff:g> "<b>"<xliff:g id="LABEL">%3$s</xliff:g>"</b>" ünvanında yenilənsin?"</string>
- <string name="autofill_update_title_with_3types" msgid="1312232153076212291">"Bu elementlər "<b>"<xliff:g id="LABEL">%4$s</xliff:g>"</b>" ünvanında yenilənsin: <xliff:g id="TYPE_0">%1$s</xliff:g>, <xliff:g id="TYPE_1">%2$s</xliff:g> və <xliff:g id="TYPE_2">%3$s</xliff:g> ?"</string>
+ <!-- no translation found for autofill_update_title_with_3types (8285767070604652626) -->
+ <skip />
<string name="autofill_save_yes" msgid="8035743017382012850">"Yadda saxlayın"</string>
<string name="autofill_save_no" msgid="9212826374207023544">"Xeyr, çox sağ olun"</string>
<string name="autofill_save_notnow" msgid="2853932672029024195">"İndi yox"</string>
diff --git a/core/res/res/values-b+sr+Latn/strings.xml b/core/res/res/values-b+sr+Latn/strings.xml
index 7a07cac..97483a4 100644
--- a/core/res/res/values-b+sr+Latn/strings.xml
+++ b/core/res/res/values-b+sr+Latn/strings.xml
@@ -670,14 +670,14 @@
</string-array>
<string name="fingerprint_error_vendor_unknown" msgid="4170002184907291065">"Došlo je do problema. Probajte ponovo."</string>
<string name="fingerprint_icon_content_description" msgid="4741068463175388817">"Ikona otiska prsta"</string>
+ <string name="device_unlock_notification_name" msgid="2632928999862915709">"Otključavanje uređaja"</string>
+ <string name="alternative_unlock_setup_notification_title" msgid="6241508547901933544">"Probajte drugi način otključavanja"</string>
+ <string name="alternative_face_setup_notification_content" msgid="3384959224091897331">"Koristite otključavanje licem kada vam se otisak prsta ne prepozna, na primer, kada su vam prsti vlažni"</string>
+ <string name="alternative_fp_setup_notification_content" msgid="7454096947415721639">"Koristite otključavanje otiskom prsta kada vam se lice ne prepozna, na primer, kada je osvetljenje slabo"</string>
<string name="face_recalibrate_notification_name" msgid="7311163114750748686">"Otključavanje licem"</string>
<string name="face_recalibrate_notification_title" msgid="2524791952735579082">"Problem sa otključavanje licem"</string>
<string name="face_recalibrate_notification_content" msgid="3064513770251355594">"Dodirnite da biste izbrisali model lica, pa ponovo dodajte svoje lice"</string>
- <string name="face_setup_notification_title" msgid="8843461561970741790">"Podesite otključavanje licem"</string>
- <string name="face_setup_notification_content" msgid="5463999831057751676">"Otključajte telefon tako što ćete ga pogledati"</string>
<string name="face_sensor_privacy_enabled" msgid="7407126963510598508">"Da biste koristili otključavanje licem, uključite "<b>"pristup kameri"</b>" u odeljku Podešavanja > Privatnost"</string>
- <string name="fingerprint_setup_notification_title" msgid="2002630611398849495">"Podesite još načina za otključavanje"</string>
- <string name="fingerprint_setup_notification_content" msgid="205578121848324852">"Dodirnite da biste dodali otisak prsta"</string>
<string name="fingerprint_recalibrate_notification_name" msgid="1414578431898579354">"Otključavanje otiskom prsta"</string>
<string name="fingerprint_recalibrate_notification_title" msgid="2406561052064558497">"Ne možete da koristite senzor za otisak prsta"</string>
<string name="fingerprint_recalibrate_notification_content" msgid="8519935717822194943">"Posetite dobavljača za popravke."</string>
@@ -2032,7 +2032,8 @@
<string name="autofill_update_title" msgid="3630695947047069136">"Želite li da ažurirate u usluzi "<b>"<xliff:g id="LABEL">%1$s</xliff:g>"</b>"?"</string>
<string name="autofill_update_title_with_type" msgid="5264152633488495704">"Želite li da ažurirate stavku <xliff:g id="TYPE">%1$s</xliff:g> u usluzi "<b>"<xliff:g id="LABEL">%2$s</xliff:g>"</b>"?"</string>
<string name="autofill_update_title_with_2types" msgid="1797514386321086273">"Želite li da ažurirate stavke <xliff:g id="TYPE_0">%1$s</xliff:g> i <xliff:g id="TYPE_1">%2$s</xliff:g> u usluzi "<b>"<xliff:g id="LABEL">%3$s</xliff:g>"</b>"?"</string>
- <string name="autofill_update_title_with_3types" msgid="1312232153076212291">"Želite li da ažurirate ove stavke u usluzi "<b>"<xliff:g id="LABEL">%4$s</xliff:g>"</b>": <xliff:g id="TYPE_0">%1$s</xliff:g>, <xliff:g id="TYPE_1">%2$s</xliff:g> i <xliff:g id="TYPE_2">%3$s</xliff:g>?"</string>
+ <!-- no translation found for autofill_update_title_with_3types (8285767070604652626) -->
+ <skip />
<string name="autofill_save_yes" msgid="8035743017382012850">"Sačuvaj"</string>
<string name="autofill_save_no" msgid="9212826374207023544">"Ne, hvala"</string>
<string name="autofill_save_notnow" msgid="2853932672029024195">"Ne sada"</string>
diff --git a/core/res/res/values-be/strings.xml b/core/res/res/values-be/strings.xml
index 2c352f9d..cd87b52 100644
--- a/core/res/res/values-be/strings.xml
+++ b/core/res/res/values-be/strings.xml
@@ -671,14 +671,18 @@
</string-array>
<string name="fingerprint_error_vendor_unknown" msgid="4170002184907291065">"Нешта пайшло не так. Паўтарыце спробу."</string>
<string name="fingerprint_icon_content_description" msgid="4741068463175388817">"Значок адбіткаў пальцаў"</string>
+ <!-- no translation found for device_unlock_notification_name (2632928999862915709) -->
+ <skip />
+ <!-- no translation found for alternative_unlock_setup_notification_title (6241508547901933544) -->
+ <skip />
+ <!-- no translation found for alternative_face_setup_notification_content (3384959224091897331) -->
+ <skip />
+ <!-- no translation found for alternative_fp_setup_notification_content (7454096947415721639) -->
+ <skip />
<string name="face_recalibrate_notification_name" msgid="7311163114750748686">"Распазнаванне твару"</string>
<string name="face_recalibrate_notification_title" msgid="2524791952735579082">"Праблема з распазнаваннем твару"</string>
<string name="face_recalibrate_notification_content" msgid="3064513770251355594">"Націсніце, каб выдаліць мадэль твару, пасля дадайце твар яшчэ раз"</string>
- <string name="face_setup_notification_title" msgid="8843461561970741790">"Наладзьце распазнаванне твару"</string>
- <string name="face_setup_notification_content" msgid="5463999831057751676">"Разблакіруйце свой тэлефон, паглядзеўшы на яго"</string>
<string name="face_sensor_privacy_enabled" msgid="7407126963510598508">"Каб выкарыстоўваць распазнаванне твару, уключыце "<b>"доступ да камеры"</b>" праз раздзел \"Налады > Прыватнасць\""</string>
- <string name="fingerprint_setup_notification_title" msgid="2002630611398849495">"Наладзьце дадатковыя спосабы разблакіроўкі"</string>
- <string name="fingerprint_setup_notification_content" msgid="205578121848324852">"Націсніце, каб дадаць адбітак пальца"</string>
<string name="fingerprint_recalibrate_notification_name" msgid="1414578431898579354">"Разблакіроўка адбіткам пальца"</string>
<string name="fingerprint_recalibrate_notification_title" msgid="2406561052064558497">"Не ўдалося скарыстаць сканер адбіткаў пальцаў"</string>
<string name="fingerprint_recalibrate_notification_content" msgid="8519935717822194943">"Звярніцеся ў сэрвісны цэнтр."</string>
@@ -2033,7 +2037,8 @@
<string name="autofill_update_title" msgid="3630695947047069136">"Абнавіць у сэрвісе "<b>"<xliff:g id="LABEL">%1$s</xliff:g>"</b>"?"</string>
<string name="autofill_update_title_with_type" msgid="5264152633488495704">"Абнавіць даныя \"<xliff:g id="TYPE">%1$s</xliff:g>\" у сэрвісе "<b>"<xliff:g id="LABEL">%2$s</xliff:g>"</b>"?"</string>
<string name="autofill_update_title_with_2types" msgid="1797514386321086273">"Абнавіць даныя \"<xliff:g id="TYPE_0">%1$s</xliff:g>\" і \"<xliff:g id="TYPE_1">%2$s</xliff:g>\" у сэрвісе "<b>"<xliff:g id="LABEL">%3$s</xliff:g>"</b>"?"</string>
- <string name="autofill_update_title_with_3types" msgid="1312232153076212291">"Абнавіць наступныя элементы ў сэрвісе "<b>"<xliff:g id="LABEL">%4$s</xliff:g>"</b>": <xliff:g id="TYPE_0">%1$s</xliff:g>, <xliff:g id="TYPE_1">%2$s</xliff:g> і <xliff:g id="TYPE_2">%3$s</xliff:g>?"</string>
+ <!-- no translation found for autofill_update_title_with_3types (8285767070604652626) -->
+ <skip />
<string name="autofill_save_yes" msgid="8035743017382012850">"Захаваць"</string>
<string name="autofill_save_no" msgid="9212826374207023544">"Не, дзякуй"</string>
<string name="autofill_save_notnow" msgid="2853932672029024195">"Не зараз"</string>
diff --git a/core/res/res/values-bg/strings.xml b/core/res/res/values-bg/strings.xml
index 078ec85..7810776 100644
--- a/core/res/res/values-bg/strings.xml
+++ b/core/res/res/values-bg/strings.xml
@@ -669,14 +669,14 @@
</string-array>
<string name="fingerprint_error_vendor_unknown" msgid="4170002184907291065">"Нещо се обърка. Опитайте отново."</string>
<string name="fingerprint_icon_content_description" msgid="4741068463175388817">"Икона за отпечатък"</string>
+ <string name="device_unlock_notification_name" msgid="2632928999862915709">"Отключване на устройството"</string>
+ <string name="alternative_unlock_setup_notification_title" msgid="6241508547901933544">"Изпробвайте друг начин за отключване"</string>
+ <string name="alternative_face_setup_notification_content" msgid="3384959224091897331">"Използвайте „Отключване с лице“, когато отпечатъкът ви не бъде разпознат, например когато пръстите ви са мокри"</string>
+ <string name="alternative_fp_setup_notification_content" msgid="7454096947415721639">"Използвайте „Отключване с отпечатък“, когато лицето ви не бъде разпознато, например когато няма достатъчно светлина"</string>
<string name="face_recalibrate_notification_name" msgid="7311163114750748686">"Отключване с лице"</string>
<string name="face_recalibrate_notification_title" msgid="2524791952735579082">"Проблем с отключването с лице"</string>
<string name="face_recalibrate_notification_content" msgid="3064513770251355594">"Докоснете, за да изтриете модела на лицето си, след което добавете лицето си отново"</string>
- <string name="face_setup_notification_title" msgid="8843461561970741790">"Настройване на отключването с лице"</string>
- <string name="face_setup_notification_content" msgid="5463999831057751676">"Отключвайте телефона си, като го погледнете"</string>
<string name="face_sensor_privacy_enabled" msgid="7407126963510598508">"За да използвате функцията „Отключване с лице“, включете "<b>"достъпа до камерата"</b>" от „Настройки > Поверителност“"</string>
- <string name="fingerprint_setup_notification_title" msgid="2002630611398849495">"Настройване на още начини за отключване на телефона"</string>
- <string name="fingerprint_setup_notification_content" msgid="205578121848324852">"Докоснете, за да добавите отпечатък"</string>
<string name="fingerprint_recalibrate_notification_name" msgid="1414578431898579354">"Отключване с отпечатък"</string>
<string name="fingerprint_recalibrate_notification_title" msgid="2406561052064558497">"Сензорът за отпечатъци не може да се използва"</string>
<string name="fingerprint_recalibrate_notification_content" msgid="8519935717822194943">"Посетете оторизиран сервиз."</string>
@@ -2031,7 +2031,8 @@
<string name="autofill_update_title" msgid="3630695947047069136">"Искате ли да актуализирате в(ъв) "<b>"<xliff:g id="LABEL">%1$s</xliff:g>"</b>"?"</string>
<string name="autofill_update_title_with_type" msgid="5264152633488495704">"Искате ли да актуализирате <xliff:g id="TYPE">%1$s</xliff:g> в(ъв) "<b>"<xliff:g id="LABEL">%2$s</xliff:g>"</b>"?"</string>
<string name="autofill_update_title_with_2types" msgid="1797514386321086273">"Искате ли да актуализирате <xliff:g id="TYPE_0">%1$s</xliff:g> и <xliff:g id="TYPE_1">%2$s</xliff:g> в(ъв) "<b>"<xliff:g id="LABEL">%3$s</xliff:g>"</b>"?"</string>
- <string name="autofill_update_title_with_3types" msgid="1312232153076212291">"Искате ли да актуализирате тези елементи в(ъв) "<b>"<xliff:g id="LABEL">%4$s</xliff:g>"</b>": <xliff:g id="TYPE_0">%1$s</xliff:g>, <xliff:g id="TYPE_1">%2$s</xliff:g> и <xliff:g id="TYPE_2">%3$s</xliff:g>?"</string>
+ <!-- no translation found for autofill_update_title_with_3types (8285767070604652626) -->
+ <skip />
<string name="autofill_save_yes" msgid="8035743017382012850">"Запазване"</string>
<string name="autofill_save_no" msgid="9212826374207023544">"Не, благодаря"</string>
<string name="autofill_save_notnow" msgid="2853932672029024195">"Не сега"</string>
diff --git a/core/res/res/values-bn/strings.xml b/core/res/res/values-bn/strings.xml
index f451da3..02bfaae 100644
--- a/core/res/res/values-bn/strings.xml
+++ b/core/res/res/values-bn/strings.xml
@@ -669,14 +669,14 @@
</string-array>
<string name="fingerprint_error_vendor_unknown" msgid="4170002184907291065">"কোনও সমস্যা হয়েছে। আবার করে দেখুন।"</string>
<string name="fingerprint_icon_content_description" msgid="4741068463175388817">"আঙ্গুলের ছাপ আইকন"</string>
+ <string name="device_unlock_notification_name" msgid="2632928999862915709">"ডিভাইস আনলক করুন"</string>
+ <string name="alternative_unlock_setup_notification_title" msgid="6241508547901933544">"অন্য কোনওভাবে আনলক করার চেষ্টা করুন"</string>
+ <string name="alternative_face_setup_notification_content" msgid="3384959224091897331">"আপনার \'ফিঙ্গারপ্রিন্ট\' শনাক্ত করা না গেলে \'ফেস আনলক\' ব্যবহার করুন, যেমন যখন আপনার আঙুল ভিজে থাকে"</string>
+ <string name="alternative_fp_setup_notification_content" msgid="7454096947415721639">"আপনার মুখ শনাক্ত করা না গেলে \'ফিঙ্গারপ্রিন্ট আনলক\' ব্যবহার করুন, যেমন যখন পর্যাপ্ত আলো নেই"</string>
<string name="face_recalibrate_notification_name" msgid="7311163114750748686">"ফেস আনলক"</string>
<string name="face_recalibrate_notification_title" msgid="2524791952735579082">"\'ফেস আনলক\' ফিচার ব্যবহার করার ক্ষেত্রে হওয়া সমস্যা"</string>
<string name="face_recalibrate_notification_content" msgid="3064513770251355594">"আপনার ফেস মডেল মুছে দেওয়ার জন্য ট্যাপ করুন এবং তারপরে আবার ফেস যোগ করুন"</string>
- <string name="face_setup_notification_title" msgid="8843461561970741790">"\'ফেস আনলক\' সেট-আপ করুন"</string>
- <string name="face_setup_notification_content" msgid="5463999831057751676">"আপনার ফোনের দিকে তাকিয়ে এটিকে আনলক করুন"</string>
<string name="face_sensor_privacy_enabled" msgid="7407126963510598508">"\'ফেস আনলক\' ফিচার ব্যবহার করতে \'সেটিংস ও গোপনীয়তা\' বিকল্পে গিয়ে "<b>"ক্যামেরায় অ্যাক্সেস দিন"</b></string>
- <string name="fingerprint_setup_notification_title" msgid="2002630611398849495">"আনলক করার জন্য বিভিন্ন উপায়ে সেট আপ করুন"</string>
- <string name="fingerprint_setup_notification_content" msgid="205578121848324852">"একটি আঙ্গুলের ছাপ যোগ করতে ট্যাপ করুন"</string>
<string name="fingerprint_recalibrate_notification_name" msgid="1414578431898579354">"ফিঙ্গারপ্রিন্ট আনলক"</string>
<string name="fingerprint_recalibrate_notification_title" msgid="2406561052064558497">"আঙ্গুলের ছাপের সেন্সর ব্যবহার করা যাচ্ছে না"</string>
<string name="fingerprint_recalibrate_notification_content" msgid="8519935717822194943">"একজন মেরামতি মিস্ত্রির কাছে যান।"</string>
@@ -2031,7 +2031,8 @@
<string name="autofill_update_title" msgid="3630695947047069136"><b>"<xliff:g id="LABEL">%1$s</xliff:g>"</b>"-এ আপডেট করতে চান?"</string>
<string name="autofill_update_title_with_type" msgid="5264152633488495704"><b>"<xliff:g id="LABEL">%2$s</xliff:g>"</b>"-এ <xliff:g id="TYPE">%1$s</xliff:g> আপডেট করতে চান?"</string>
<string name="autofill_update_title_with_2types" msgid="1797514386321086273"><b>"<xliff:g id="LABEL">%3$s</xliff:g>"</b>"-এ <xliff:g id="TYPE_0">%1$s</xliff:g> এবং <xliff:g id="TYPE_1">%2$s</xliff:g> আপডেট করতে চান?"</string>
- <string name="autofill_update_title_with_3types" msgid="1312232153076212291"><b>"<xliff:g id="LABEL">%4$s</xliff:g>"</b>"-এ এই আইটেমগুলি আপডেট করতে চান: <xliff:g id="TYPE_0">%1$s</xliff:g>, <xliff:g id="TYPE_1">%2$s</xliff:g> এবং <xliff:g id="TYPE_2">%3$s</xliff:g>?"</string>
+ <!-- no translation found for autofill_update_title_with_3types (8285767070604652626) -->
+ <skip />
<string name="autofill_save_yes" msgid="8035743017382012850">"সেভ করুন"</string>
<string name="autofill_save_no" msgid="9212826374207023544">"না থাক"</string>
<string name="autofill_save_notnow" msgid="2853932672029024195">"এখনই নয়"</string>
diff --git a/core/res/res/values-bs/strings.xml b/core/res/res/values-bs/strings.xml
index fb02763..00aa307 100644
--- a/core/res/res/values-bs/strings.xml
+++ b/core/res/res/values-bs/strings.xml
@@ -670,14 +670,14 @@
</string-array>
<string name="fingerprint_error_vendor_unknown" msgid="4170002184907291065">"Nešto nije uredu. Pokušajte ponovo."</string>
<string name="fingerprint_icon_content_description" msgid="4741068463175388817">"Ikona za otisak prsta"</string>
+ <string name="device_unlock_notification_name" msgid="2632928999862915709">"Otključavanje uređaja"</string>
+ <string name="alternative_unlock_setup_notification_title" msgid="6241508547901933544">"Pokušajte s drugim načinom zaključavanja"</string>
+ <string name="alternative_face_setup_notification_content" msgid="3384959224091897331">"Koristite otključavanje licem kada se vaš otisak prsta ne prepozna, naprimjer kada su vam prsti mokri"</string>
+ <string name="alternative_fp_setup_notification_content" msgid="7454096947415721639">"Koristite otključavanje otiskom prsta kada se vaše lice ne prepozna, naprimjer kada nema dovoljno svjetla"</string>
<string name="face_recalibrate_notification_name" msgid="7311163114750748686">"Otključavanje licem"</string>
<string name="face_recalibrate_notification_title" msgid="2524791952735579082">"Problem s otključavanjem licem"</string>
<string name="face_recalibrate_notification_content" msgid="3064513770251355594">"Dodirnite da izbrišete model lica, a zatim ponovo dodajte lice"</string>
- <string name="face_setup_notification_title" msgid="8843461561970741790">"Postavite otključavanje licem"</string>
- <string name="face_setup_notification_content" msgid="5463999831057751676">"Otključajte telefon gledajući u njega"</string>
<string name="face_sensor_privacy_enabled" msgid="7407126963510598508">"Da koristite otključavanje licem, uključite "<b>"Pristup kameri"</b>" u meniju Postavke > Privatnost"</string>
- <string name="fingerprint_setup_notification_title" msgid="2002630611398849495">"Postavite više načina otključavanja"</string>
- <string name="fingerprint_setup_notification_content" msgid="205578121848324852">"Dodirnite da dodate otisak prsta"</string>
<string name="fingerprint_recalibrate_notification_name" msgid="1414578431898579354">"Otključavanje otiskom prsta"</string>
<string name="fingerprint_recalibrate_notification_title" msgid="2406561052064558497">"Nije moguće koristiti senzor za otisak prsta"</string>
<string name="fingerprint_recalibrate_notification_content" msgid="8519935717822194943">"Posjetite pružaoca usluga za popravke."</string>
@@ -2032,7 +2032,8 @@
<string name="autofill_update_title" msgid="3630695947047069136">"Ažurirati u "<b>"<xliff:g id="LABEL">%1$s</xliff:g>"</b>"?"</string>
<string name="autofill_update_title_with_type" msgid="5264152633488495704">"Ažurirati <xliff:g id="TYPE">%1$s</xliff:g> u "<b>"<xliff:g id="LABEL">%2$s</xliff:g>"</b>"?"</string>
<string name="autofill_update_title_with_2types" msgid="1797514386321086273">"Ažurirati <xliff:g id="TYPE_0">%1$s</xliff:g> i <xliff:g id="TYPE_1">%2$s</xliff:g> u "<b>"<xliff:g id="LABEL">%3$s</xliff:g>"</b>"?"</string>
- <string name="autofill_update_title_with_3types" msgid="1312232153076212291">"Ažurirati ove stavke u "<b>"<xliff:g id="LABEL">%4$s</xliff:g>"</b>": <xliff:g id="TYPE_0">%1$s</xliff:g>, <xliff:g id="TYPE_1">%2$s</xliff:g> i <xliff:g id="TYPE_2">%3$s</xliff:g>?"</string>
+ <!-- no translation found for autofill_update_title_with_3types (8285767070604652626) -->
+ <skip />
<string name="autofill_save_yes" msgid="8035743017382012850">"Sačuvaj"</string>
<string name="autofill_save_no" msgid="9212826374207023544">"Ne, hvala"</string>
<string name="autofill_save_notnow" msgid="2853932672029024195">"Ne sada"</string>
diff --git a/core/res/res/values-ca/strings.xml b/core/res/res/values-ca/strings.xml
index edfd9d1..42e631b 100644
--- a/core/res/res/values-ca/strings.xml
+++ b/core/res/res/values-ca/strings.xml
@@ -670,14 +670,14 @@
</string-array>
<string name="fingerprint_error_vendor_unknown" msgid="4170002184907291065">"S\'ha produït un error. Torna-ho a provar."</string>
<string name="fingerprint_icon_content_description" msgid="4741068463175388817">"Icona d\'empremta digital"</string>
+ <string name="device_unlock_notification_name" msgid="2632928999862915709">"Desbloqueig del dispositiu"</string>
+ <string name="alternative_unlock_setup_notification_title" msgid="6241508547901933544">"Prova una altra manera de desbloquejar"</string>
+ <string name="alternative_face_setup_notification_content" msgid="3384959224091897331">"Utilitza Desbloqueig facial quan no es reconegui la teva empremta digital, com ara quan tens els dits mullats"</string>
+ <string name="alternative_fp_setup_notification_content" msgid="7454096947415721639">"Utilitza Desbloqueig amb empremta digital quan no es reconegui la teva cara, com ara quan no hi ha prou llum"</string>
<string name="face_recalibrate_notification_name" msgid="7311163114750748686">"Desbloqueig facial"</string>
<string name="face_recalibrate_notification_title" msgid="2524791952735579082">"Problema amb Desbloqueig facial"</string>
<string name="face_recalibrate_notification_content" msgid="3064513770251355594">"Toca per suprimir el teu model facial i, a continuació, torna a afegir la teva cara"</string>
- <string name="face_setup_notification_title" msgid="8843461561970741790">"Configura Desbloqueig facial"</string>
- <string name="face_setup_notification_content" msgid="5463999831057751676">"Mira el telèfon per desbloquejar-lo"</string>
<string name="face_sensor_privacy_enabled" msgid="7407126963510598508">"Per utilitzar Desbloqueig facial, activa "<b>"Accés a la càmera"</b>" a Configuració > Privadesa"</string>
- <string name="fingerprint_setup_notification_title" msgid="2002630611398849495">"Configura més maneres de desbloquejar el dispositiu"</string>
- <string name="fingerprint_setup_notification_content" msgid="205578121848324852">"Toca per afegir una empremta digital"</string>
<string name="fingerprint_recalibrate_notification_name" msgid="1414578431898579354">"Desbloqueig amb empremta digital"</string>
<string name="fingerprint_recalibrate_notification_title" msgid="2406561052064558497">"No es pot utilitzar el sensor d\'empremtes digitals"</string>
<string name="fingerprint_recalibrate_notification_content" msgid="8519935717822194943">"Visita un proveïdor de reparacions."</string>
@@ -2032,7 +2032,8 @@
<string name="autofill_update_title" msgid="3630695947047069136">"Vols actualitzar-ho a "<b>"<xliff:g id="LABEL">%1$s</xliff:g>"</b>"?"</string>
<string name="autofill_update_title_with_type" msgid="5264152633488495704">"Vols actualitzar <xliff:g id="TYPE">%1$s</xliff:g> a "<b>"<xliff:g id="LABEL">%2$s</xliff:g>"</b>"?"</string>
<string name="autofill_update_title_with_2types" msgid="1797514386321086273">"Vols actualitzar <xliff:g id="TYPE_0">%1$s</xliff:g> i <xliff:g id="TYPE_1">%2$s</xliff:g> a "<b>"<xliff:g id="LABEL">%3$s</xliff:g>"</b>"?"</string>
- <string name="autofill_update_title_with_3types" msgid="1312232153076212291">"Vols actualitzar <xliff:g id="TYPE_0">%1$s</xliff:g>, <xliff:g id="TYPE_1">%2$s</xliff:g> i <xliff:g id="TYPE_2">%3$s</xliff:g> a "<b>"<xliff:g id="LABEL">%4$s</xliff:g>"</b>"?"</string>
+ <!-- no translation found for autofill_update_title_with_3types (8285767070604652626) -->
+ <skip />
<string name="autofill_save_yes" msgid="8035743017382012850">"Desa"</string>
<string name="autofill_save_no" msgid="9212826374207023544">"No, gràcies"</string>
<string name="autofill_save_notnow" msgid="2853932672029024195">"Ara no"</string>
diff --git a/core/res/res/values-cs/strings.xml b/core/res/res/values-cs/strings.xml
index d2baa9b..772ef53 100644
--- a/core/res/res/values-cs/strings.xml
+++ b/core/res/res/values-cs/strings.xml
@@ -671,14 +671,14 @@
</string-array>
<string name="fingerprint_error_vendor_unknown" msgid="4170002184907291065">"Došlo k chybě. Zkuste to znovu."</string>
<string name="fingerprint_icon_content_description" msgid="4741068463175388817">"Ikona otisku prstů"</string>
+ <string name="device_unlock_notification_name" msgid="2632928999862915709">"Odemknutí zařízení"</string>
+ <string name="alternative_unlock_setup_notification_title" msgid="6241508547901933544">"Použijte jiný způsob odemknutí"</string>
+ <string name="alternative_face_setup_notification_content" msgid="3384959224091897331">"Pokud váš otisk prstu nebude rozpoznán (např. když budete mít mokré prsty), použijte odemknutí obličejem"</string>
+ <string name="alternative_fp_setup_notification_content" msgid="7454096947415721639">"Pokud váš obličej nebude rozpoznán (např. když bude málo světla), použijte odemknutí otiskem prstu"</string>
<string name="face_recalibrate_notification_name" msgid="7311163114750748686">"Odemknutí obličejem"</string>
<string name="face_recalibrate_notification_title" msgid="2524791952735579082">"Problém s odemykáním obličejem"</string>
<string name="face_recalibrate_notification_content" msgid="3064513770251355594">"Klepnutím svůj model obličeje smažte a potom ho přidejte znovu"</string>
- <string name="face_setup_notification_title" msgid="8843461561970741790">"Nastavte odemknutí obličejem"</string>
- <string name="face_setup_notification_content" msgid="5463999831057751676">"Telefon odemknete pohledem"</string>
<string name="face_sensor_privacy_enabled" msgid="7407126963510598508">"Pokud chcete používat odemknutí obličejem, v Nastavení > Soukromí zapnetě "<b>"přístup k fotoaparátu"</b></string>
- <string name="fingerprint_setup_notification_title" msgid="2002630611398849495">"Nastavte si více způsobů odemykání"</string>
- <string name="fingerprint_setup_notification_content" msgid="205578121848324852">"Klepnutím přidáte otisk prstu"</string>
<string name="fingerprint_recalibrate_notification_name" msgid="1414578431898579354">"Odemknutí otiskem prstu"</string>
<string name="fingerprint_recalibrate_notification_title" msgid="2406561052064558497">"Snímač otisků prstů nelze použít"</string>
<string name="fingerprint_recalibrate_notification_content" msgid="8519935717822194943">"Navštivte servis"</string>
@@ -2033,7 +2033,8 @@
<string name="autofill_update_title" msgid="3630695947047069136">"Aktualizovat ve službě "<b>"<xliff:g id="LABEL">%1$s</xliff:g>"</b>"?"</string>
<string name="autofill_update_title_with_type" msgid="5264152633488495704">"Aktualizovat údaj <xliff:g id="TYPE">%1$s</xliff:g> ve službě "<b>"<xliff:g id="LABEL">%2$s</xliff:g>"</b>"?"</string>
<string name="autofill_update_title_with_2types" msgid="1797514386321086273">"Aktualizovat údaje <xliff:g id="TYPE_0">%1$s</xliff:g> a <xliff:g id="TYPE_1">%2$s</xliff:g> ve službě "<b>"<xliff:g id="LABEL">%3$s</xliff:g>"</b>"?"</string>
- <string name="autofill_update_title_with_3types" msgid="1312232153076212291">"Aktualizovat tyto položky ve službě "<b>"<xliff:g id="LABEL">%4$s</xliff:g>"</b>": <xliff:g id="TYPE_0">%1$s</xliff:g>, <xliff:g id="TYPE_1">%2$s</xliff:g> a <xliff:g id="TYPE_2">%3$s</xliff:g>?"</string>
+ <!-- no translation found for autofill_update_title_with_3types (8285767070604652626) -->
+ <skip />
<string name="autofill_save_yes" msgid="8035743017382012850">"Uložit"</string>
<string name="autofill_save_no" msgid="9212826374207023544">"Ne, děkuji"</string>
<string name="autofill_save_notnow" msgid="2853932672029024195">"Teď ne"</string>
diff --git a/core/res/res/values-da/strings.xml b/core/res/res/values-da/strings.xml
index 6de92f9..697800b 100644
--- a/core/res/res/values-da/strings.xml
+++ b/core/res/res/values-da/strings.xml
@@ -669,14 +669,14 @@
</string-array>
<string name="fingerprint_error_vendor_unknown" msgid="4170002184907291065">"Noget gik galt. Prøv igen."</string>
<string name="fingerprint_icon_content_description" msgid="4741068463175388817">"Ikon for fingeraftryk"</string>
+ <string name="device_unlock_notification_name" msgid="2632928999862915709">"Enhedsoplåsning"</string>
+ <string name="alternative_unlock_setup_notification_title" msgid="6241508547901933544">"Prøv en anden metode til oplåsning"</string>
+ <string name="alternative_face_setup_notification_content" msgid="3384959224091897331">"Brug ansigtslås, hvis dit fingeraftryk ikke genkendes, f.eks. når du har våde fingre"</string>
+ <string name="alternative_fp_setup_notification_content" msgid="7454096947415721639">"Brug oplåsning med fingeraftryk, hvis dit ansigt ikke genkendes, f.eks. når der ikke er nok lys"</string>
<string name="face_recalibrate_notification_name" msgid="7311163114750748686">"Ansigtslås"</string>
<string name="face_recalibrate_notification_title" msgid="2524791952735579082">"Der er et problem med Ansigtslås"</string>
<string name="face_recalibrate_notification_content" msgid="3064513770251355594">"Tryk for at slette din ansigtsmodel, og tilføj derefter dit ansigt igen"</string>
- <string name="face_setup_notification_title" msgid="8843461561970741790">"Konfigurer ansigtslås"</string>
- <string name="face_setup_notification_content" msgid="5463999831057751676">"Lås din telefon op ved at kigge på den"</string>
<string name="face_sensor_privacy_enabled" msgid="7407126963510598508">"Hvis du vil bruge ansigtslåsen, skal du aktivere "<b>"Kameraadgang"</b>" under Indstillinger > Privatliv"</string>
- <string name="fingerprint_setup_notification_title" msgid="2002630611398849495">"Konfigurer flere måder at låse op på"</string>
- <string name="fingerprint_setup_notification_content" msgid="205578121848324852">"Tryk for at tilføje et fingeraftryk"</string>
<string name="fingerprint_recalibrate_notification_name" msgid="1414578431898579354">"Oplåsning med fingeraftryk"</string>
<string name="fingerprint_recalibrate_notification_title" msgid="2406561052064558497">"Fingeraftrykssensoren kan ikke bruges"</string>
<string name="fingerprint_recalibrate_notification_content" msgid="8519935717822194943">"Få den repareret."</string>
@@ -2031,7 +2031,8 @@
<string name="autofill_update_title" msgid="3630695947047069136">"Vil du opdatere i "<b>"<xliff:g id="LABEL">%1$s</xliff:g>"</b>"?"</string>
<string name="autofill_update_title_with_type" msgid="5264152633488495704">"Vil du opdatere <xliff:g id="TYPE">%1$s</xliff:g> i "<b>"<xliff:g id="LABEL">%2$s</xliff:g>"</b>"?"</string>
<string name="autofill_update_title_with_2types" msgid="1797514386321086273">"Vil du opdatere <xliff:g id="TYPE_0">%1$s</xliff:g> og <xliff:g id="TYPE_1">%2$s</xliff:g> i "<b>"<xliff:g id="LABEL">%3$s</xliff:g>"</b>"?"</string>
- <string name="autofill_update_title_with_3types" msgid="1312232153076212291">"Vil du opdatere disse elementer i "<b>"<xliff:g id="LABEL">%4$s</xliff:g>"</b>": <xliff:g id="TYPE_0">%1$s</xliff:g>, <xliff:g id="TYPE_1">%2$s</xliff:g> og <xliff:g id="TYPE_2">%3$s</xliff:g>?"</string>
+ <!-- no translation found for autofill_update_title_with_3types (8285767070604652626) -->
+ <skip />
<string name="autofill_save_yes" msgid="8035743017382012850">"Gem"</string>
<string name="autofill_save_no" msgid="9212826374207023544">"Nej tak"</string>
<string name="autofill_save_notnow" msgid="2853932672029024195">"Ikke nu"</string>
diff --git a/core/res/res/values-de/strings.xml b/core/res/res/values-de/strings.xml
index ec8bb7b..b5e38b9 100644
--- a/core/res/res/values-de/strings.xml
+++ b/core/res/res/values-de/strings.xml
@@ -669,14 +669,18 @@
</string-array>
<string name="fingerprint_error_vendor_unknown" msgid="4170002184907291065">"Ein Problem ist aufgetreten. Versuch es noch einmal."</string>
<string name="fingerprint_icon_content_description" msgid="4741068463175388817">"Fingerabdruck-Symbol"</string>
+ <!-- no translation found for device_unlock_notification_name (2632928999862915709) -->
+ <skip />
+ <!-- no translation found for alternative_unlock_setup_notification_title (6241508547901933544) -->
+ <skip />
+ <!-- no translation found for alternative_face_setup_notification_content (3384959224091897331) -->
+ <skip />
+ <!-- no translation found for alternative_fp_setup_notification_content (7454096947415721639) -->
+ <skip />
<string name="face_recalibrate_notification_name" msgid="7311163114750748686">"Entsperrung per Gesichtserkennung"</string>
<string name="face_recalibrate_notification_title" msgid="2524791952735579082">"Problem bei der Entsperrung per Gesichtserkennung"</string>
<string name="face_recalibrate_notification_content" msgid="3064513770251355594">"Tippe, um dein Gesichtsmodell zu löschen, und füge es dann noch einmal hinzu"</string>
- <string name="face_setup_notification_title" msgid="8843461561970741790">"Entsperrung per Gesichtserkennung einrichten"</string>
- <string name="face_setup_notification_content" msgid="5463999831057751676">"Entsperre dein Smartphone, indem du es ansiehst"</string>
<string name="face_sensor_privacy_enabled" msgid="7407126963510598508">"Wenn du die Entsperrung per Gesichtserkennung verwenden möchtest, aktiviere in den Einstellungen unter „Datenschutz“ die Option "<b>"Kamerazugriff"</b></string>
- <string name="fingerprint_setup_notification_title" msgid="2002630611398849495">"Weitere Möglichkeiten zum Entsperren einrichten"</string>
- <string name="fingerprint_setup_notification_content" msgid="205578121848324852">"Tippe, um einen Fingerabdruck hinzuzufügen"</string>
<string name="fingerprint_recalibrate_notification_name" msgid="1414578431898579354">"Entsperrung per Fingerabdruck"</string>
<string name="fingerprint_recalibrate_notification_title" msgid="2406561052064558497">"Der Fingerabdrucksensor kann nicht verwendet werden"</string>
<string name="fingerprint_recalibrate_notification_content" msgid="8519935717822194943">"Suche einen Reparaturdienstleister auf."</string>
@@ -2031,7 +2035,8 @@
<string name="autofill_update_title" msgid="3630695947047069136">"In "<b>"<xliff:g id="LABEL">%1$s</xliff:g>"</b>" aktualisieren?"</string>
<string name="autofill_update_title_with_type" msgid="5264152633488495704">"<xliff:g id="TYPE">%1$s</xliff:g> in "<b>"<xliff:g id="LABEL">%2$s</xliff:g>"</b>" aktualisieren?"</string>
<string name="autofill_update_title_with_2types" msgid="1797514386321086273">"<xliff:g id="TYPE_0">%1$s</xliff:g> und <xliff:g id="TYPE_1">%2$s</xliff:g> in "<b>"<xliff:g id="LABEL">%3$s</xliff:g>"</b>" aktualisieren?"</string>
- <string name="autofill_update_title_with_3types" msgid="1312232153076212291">"<xliff:g id="TYPE_0">%1$s</xliff:g>, <xliff:g id="TYPE_1">%2$s</xliff:g> und <xliff:g id="TYPE_2">%3$s</xliff:g> in "<b>"<xliff:g id="LABEL">%4$s</xliff:g>"</b>" aktualisieren?"</string>
+ <!-- no translation found for autofill_update_title_with_3types (8285767070604652626) -->
+ <skip />
<string name="autofill_save_yes" msgid="8035743017382012850">"Speichern"</string>
<string name="autofill_save_no" msgid="9212826374207023544">"Nein danke"</string>
<string name="autofill_save_notnow" msgid="2853932672029024195">"Nicht jetzt"</string>
diff --git a/core/res/res/values-el/strings.xml b/core/res/res/values-el/strings.xml
index 66148a9..a0a4474 100644
--- a/core/res/res/values-el/strings.xml
+++ b/core/res/res/values-el/strings.xml
@@ -669,14 +669,14 @@
</string-array>
<string name="fingerprint_error_vendor_unknown" msgid="4170002184907291065">"Παρουσιάστηκε κάποιο πρόβλημα. Δοκιμάστε ξανά."</string>
<string name="fingerprint_icon_content_description" msgid="4741068463175388817">"Εικονίδιο δακτυλικών αποτυπωμάτων"</string>
+ <string name="device_unlock_notification_name" msgid="2632928999862915709">"Ξεκλείδωμα συσκευής"</string>
+ <string name="alternative_unlock_setup_notification_title" msgid="6241508547901933544">"Δοκιμάστε άλλον τρόπο για το ξεκλείδωμα"</string>
+ <string name="alternative_face_setup_notification_content" msgid="3384959224091897331">"Χρησιμοποιήστε το Ξεκλείδωμα με το πρόσωπο όταν δεν αναγνωρίζεται το δακτυλικό αποτύπωμά σας, όπως όταν τα δάχτυλά σας είναι βρεγμένα"</string>
+ <string name="alternative_fp_setup_notification_content" msgid="7454096947415721639">"Χρησιμοποιήστε το Ξεκλείδωμα με δακτυλικό αποτύπωμα όταν δεν αναγνωρίζεται το πρόσωπό σας, όπως όταν δεν υπάρχει επαρκής φωτισμός"</string>
<string name="face_recalibrate_notification_name" msgid="7311163114750748686">"Ξεκλείδωμα με το πρόσωπο"</string>
<string name="face_recalibrate_notification_title" msgid="2524791952735579082">"Πρόβλημα με το Ξεκλείδωμα με το πρόσωπο"</string>
<string name="face_recalibrate_notification_content" msgid="3064513770251355594">"Πατήστε για να διαγράψετε το μοντέλο προσώπου και, στη συνέχεια, προσθέστε το πρόσωπό σας ξανά."</string>
- <string name="face_setup_notification_title" msgid="8843461561970741790">"Ρύθμιση της λειτουργίας Ξεκλείδωμα με το πρόσωπο"</string>
- <string name="face_setup_notification_content" msgid="5463999831057751676">"Ξεκλειδώστε το τηλέφωνό σας απλώς κοιτώντας το"</string>
<string name="face_sensor_privacy_enabled" msgid="7407126963510598508">"Για να χρησιμοποιήσετε τη λειτουργία Ξεκλείδωμα με το πρόσωπο, ενεργοποιήστε την επιλογή "<b>"Πρόσβαση στην κάμερα"</b>" από το μενού Ρυθμίσεις > Απόρρητο"</string>
- <string name="fingerprint_setup_notification_title" msgid="2002630611398849495">"Ρυθμίστε περισσότερους τρόπους ξεκλειδώματος"</string>
- <string name="fingerprint_setup_notification_content" msgid="205578121848324852">"Πατήστε για να προσθέσετε δακτυλικό αποτύπωμα"</string>
<string name="fingerprint_recalibrate_notification_name" msgid="1414578431898579354">"Ξεκλείδωμα με δακτυλικό αποτύπωμα"</string>
<string name="fingerprint_recalibrate_notification_title" msgid="2406561052064558497">"Δεν είναι δυνατή η χρήση του αισθητήρα δακτυλικών αποτυπωμάτων"</string>
<string name="fingerprint_recalibrate_notification_content" msgid="8519935717822194943">"Επισκεφτείτε έναν πάροχο υπηρεσιών επισκευής."</string>
@@ -2031,7 +2031,8 @@
<string name="autofill_update_title" msgid="3630695947047069136">"Ενημέρωση σε "<b>"<xliff:g id="LABEL">%1$s</xliff:g>"</b>";"</string>
<string name="autofill_update_title_with_type" msgid="5264152633488495704">"Ενημέρωση <xliff:g id="TYPE">%1$s</xliff:g> σε "<b>"<xliff:g id="LABEL">%2$s</xliff:g>"</b>";"</string>
<string name="autofill_update_title_with_2types" msgid="1797514386321086273">"Ενημέρωση <xliff:g id="TYPE_0">%1$s</xliff:g> και <xliff:g id="TYPE_1">%2$s</xliff:g> σε "<b>"<xliff:g id="LABEL">%3$s</xliff:g>"</b>";"</string>
- <string name="autofill_update_title_with_3types" msgid="1312232153076212291">"Ενημέρωση αυτών των στοιχείων "<b>"<xliff:g id="LABEL">%4$s</xliff:g>"</b>": <xliff:g id="TYPE_0">%1$s</xliff:g>, <xliff:g id="TYPE_1">%2$s</xliff:g> και <xliff:g id="TYPE_2">%3$s</xliff:g>;"</string>
+ <!-- no translation found for autofill_update_title_with_3types (8285767070604652626) -->
+ <skip />
<string name="autofill_save_yes" msgid="8035743017382012850">"Αποθήκευση"</string>
<string name="autofill_save_no" msgid="9212826374207023544">"Όχι, ευχαριστώ"</string>
<string name="autofill_save_notnow" msgid="2853932672029024195">"Όχι τώρα"</string>
diff --git a/core/res/res/values-en-rAU/strings.xml b/core/res/res/values-en-rAU/strings.xml
index 906e94c..a931c58 100644
--- a/core/res/res/values-en-rAU/strings.xml
+++ b/core/res/res/values-en-rAU/strings.xml
@@ -669,14 +669,14 @@
</string-array>
<string name="fingerprint_error_vendor_unknown" msgid="4170002184907291065">"Something went wrong. Try again."</string>
<string name="fingerprint_icon_content_description" msgid="4741068463175388817">"Fingerprint icon"</string>
+ <string name="device_unlock_notification_name" msgid="2632928999862915709">"Device unlock"</string>
+ <string name="alternative_unlock_setup_notification_title" msgid="6241508547901933544">"Try another way to unlock"</string>
+ <string name="alternative_face_setup_notification_content" msgid="3384959224091897331">"Use Face Unlock when your fingerprint isn\'t recognised, like when your fingers are wet"</string>
+ <string name="alternative_fp_setup_notification_content" msgid="7454096947415721639">"Use Fingerprint Unlock when your face isn\'t recognised, like when there\'s not enough light"</string>
<string name="face_recalibrate_notification_name" msgid="7311163114750748686">"Face Unlock"</string>
<string name="face_recalibrate_notification_title" msgid="2524791952735579082">"Issue with Face Unlock"</string>
<string name="face_recalibrate_notification_content" msgid="3064513770251355594">"Tap to delete your face model, then add your face again"</string>
- <string name="face_setup_notification_title" msgid="8843461561970741790">"Set up Face Unlock"</string>
- <string name="face_setup_notification_content" msgid="5463999831057751676">"Unlock your phone by looking at it"</string>
<string name="face_sensor_privacy_enabled" msgid="7407126963510598508">"To use Face Unlock, turn on "<b>"Camera access"</b>" in Settings > Privacy"</string>
- <string name="fingerprint_setup_notification_title" msgid="2002630611398849495">"Set up more ways to unlock"</string>
- <string name="fingerprint_setup_notification_content" msgid="205578121848324852">"Tap to add a fingerprint"</string>
<string name="fingerprint_recalibrate_notification_name" msgid="1414578431898579354">"Fingerprint Unlock"</string>
<string name="fingerprint_recalibrate_notification_title" msgid="2406561052064558497">"Can’t use fingerprint sensor"</string>
<string name="fingerprint_recalibrate_notification_content" msgid="8519935717822194943">"Visit a repair provider."</string>
@@ -2031,7 +2031,8 @@
<string name="autofill_update_title" msgid="3630695947047069136">"Update in "<b>"<xliff:g id="LABEL">%1$s</xliff:g>"</b>"?"</string>
<string name="autofill_update_title_with_type" msgid="5264152633488495704">"Update <xliff:g id="TYPE">%1$s</xliff:g> in "<b>"<xliff:g id="LABEL">%2$s</xliff:g>"</b>"?"</string>
<string name="autofill_update_title_with_2types" msgid="1797514386321086273">"Update <xliff:g id="TYPE_0">%1$s</xliff:g> and <xliff:g id="TYPE_1">%2$s</xliff:g> in "<b>"<xliff:g id="LABEL">%3$s</xliff:g>"</b>"?"</string>
- <string name="autofill_update_title_with_3types" msgid="1312232153076212291">"Update these items in "<b>"<xliff:g id="LABEL">%4$s</xliff:g>"</b>": <xliff:g id="TYPE_0">%1$s</xliff:g>, <xliff:g id="TYPE_1">%2$s</xliff:g> and <xliff:g id="TYPE_2">%3$s</xliff:g>?"</string>
+ <!-- no translation found for autofill_update_title_with_3types (8285767070604652626) -->
+ <skip />
<string name="autofill_save_yes" msgid="8035743017382012850">"Save"</string>
<string name="autofill_save_no" msgid="9212826374207023544">"No, thanks"</string>
<string name="autofill_save_notnow" msgid="2853932672029024195">"Not now"</string>
diff --git a/core/res/res/values-en-rCA/strings.xml b/core/res/res/values-en-rCA/strings.xml
index 2d7ba8d..1d657c7 100644
--- a/core/res/res/values-en-rCA/strings.xml
+++ b/core/res/res/values-en-rCA/strings.xml
@@ -669,14 +669,14 @@
</string-array>
<string name="fingerprint_error_vendor_unknown" msgid="4170002184907291065">"Something went wrong. Try again."</string>
<string name="fingerprint_icon_content_description" msgid="4741068463175388817">"Fingerprint icon"</string>
+ <string name="device_unlock_notification_name" msgid="2632928999862915709">"Device unlock"</string>
+ <string name="alternative_unlock_setup_notification_title" msgid="6241508547901933544">"Try another way to unlock"</string>
+ <string name="alternative_face_setup_notification_content" msgid="3384959224091897331">"Use Face Unlock when your fingerprint isn\'t recognized, like when your fingers are wet"</string>
+ <string name="alternative_fp_setup_notification_content" msgid="7454096947415721639">"Use Fingerprint Unlock when your face isn\'t recognized, like when there\'s not enough light"</string>
<string name="face_recalibrate_notification_name" msgid="7311163114750748686">"Face Unlock"</string>
<string name="face_recalibrate_notification_title" msgid="2524791952735579082">"Issue with Face Unlock"</string>
<string name="face_recalibrate_notification_content" msgid="3064513770251355594">"Tap to delete your face model, then add your face again"</string>
- <string name="face_setup_notification_title" msgid="8843461561970741790">"Set up Face Unlock"</string>
- <string name="face_setup_notification_content" msgid="5463999831057751676">"Unlock your phone by looking at it"</string>
<string name="face_sensor_privacy_enabled" msgid="7407126963510598508">"To use Face Unlock, turn on "<b>"Camera access"</b>" in Settings > Privacy"</string>
- <string name="fingerprint_setup_notification_title" msgid="2002630611398849495">"Set up more ways to unlock"</string>
- <string name="fingerprint_setup_notification_content" msgid="205578121848324852">"Tap to add a fingerprint"</string>
<string name="fingerprint_recalibrate_notification_name" msgid="1414578431898579354">"Fingerprint Unlock"</string>
<string name="fingerprint_recalibrate_notification_title" msgid="2406561052064558497">"Can’t use fingerprint sensor"</string>
<string name="fingerprint_recalibrate_notification_content" msgid="8519935717822194943">"Visit a repair provider."</string>
@@ -2031,7 +2031,8 @@
<string name="autofill_update_title" msgid="3630695947047069136">"Update in "<b>"<xliff:g id="LABEL">%1$s</xliff:g>"</b>"?"</string>
<string name="autofill_update_title_with_type" msgid="5264152633488495704">"Update <xliff:g id="TYPE">%1$s</xliff:g> in "<b>"<xliff:g id="LABEL">%2$s</xliff:g>"</b>"?"</string>
<string name="autofill_update_title_with_2types" msgid="1797514386321086273">"Update <xliff:g id="TYPE_0">%1$s</xliff:g> and <xliff:g id="TYPE_1">%2$s</xliff:g> in "<b>"<xliff:g id="LABEL">%3$s</xliff:g>"</b>"?"</string>
- <string name="autofill_update_title_with_3types" msgid="1312232153076212291">"Update these items in "<b>"<xliff:g id="LABEL">%4$s</xliff:g>"</b>": <xliff:g id="TYPE_0">%1$s</xliff:g>, <xliff:g id="TYPE_1">%2$s</xliff:g>, and <xliff:g id="TYPE_2">%3$s</xliff:g> ?"</string>
+ <!-- no translation found for autofill_update_title_with_3types (8285767070604652626) -->
+ <skip />
<string name="autofill_save_yes" msgid="8035743017382012850">"Save"</string>
<string name="autofill_save_no" msgid="9212826374207023544">"No thanks"</string>
<string name="autofill_save_notnow" msgid="2853932672029024195">"Not now"</string>
diff --git a/core/res/res/values-en-rGB/strings.xml b/core/res/res/values-en-rGB/strings.xml
index ad9c777..275b1bc 100644
--- a/core/res/res/values-en-rGB/strings.xml
+++ b/core/res/res/values-en-rGB/strings.xml
@@ -669,14 +669,14 @@
</string-array>
<string name="fingerprint_error_vendor_unknown" msgid="4170002184907291065">"Something went wrong. Try again."</string>
<string name="fingerprint_icon_content_description" msgid="4741068463175388817">"Fingerprint icon"</string>
+ <string name="device_unlock_notification_name" msgid="2632928999862915709">"Device unlock"</string>
+ <string name="alternative_unlock_setup_notification_title" msgid="6241508547901933544">"Try another way to unlock"</string>
+ <string name="alternative_face_setup_notification_content" msgid="3384959224091897331">"Use Face Unlock when your fingerprint isn\'t recognised, like when your fingers are wet"</string>
+ <string name="alternative_fp_setup_notification_content" msgid="7454096947415721639">"Use Fingerprint Unlock when your face isn\'t recognised, like when there\'s not enough light"</string>
<string name="face_recalibrate_notification_name" msgid="7311163114750748686">"Face Unlock"</string>
<string name="face_recalibrate_notification_title" msgid="2524791952735579082">"Issue with Face Unlock"</string>
<string name="face_recalibrate_notification_content" msgid="3064513770251355594">"Tap to delete your face model, then add your face again"</string>
- <string name="face_setup_notification_title" msgid="8843461561970741790">"Set up Face Unlock"</string>
- <string name="face_setup_notification_content" msgid="5463999831057751676">"Unlock your phone by looking at it"</string>
<string name="face_sensor_privacy_enabled" msgid="7407126963510598508">"To use Face Unlock, turn on "<b>"Camera access"</b>" in Settings > Privacy"</string>
- <string name="fingerprint_setup_notification_title" msgid="2002630611398849495">"Set up more ways to unlock"</string>
- <string name="fingerprint_setup_notification_content" msgid="205578121848324852">"Tap to add a fingerprint"</string>
<string name="fingerprint_recalibrate_notification_name" msgid="1414578431898579354">"Fingerprint Unlock"</string>
<string name="fingerprint_recalibrate_notification_title" msgid="2406561052064558497">"Can’t use fingerprint sensor"</string>
<string name="fingerprint_recalibrate_notification_content" msgid="8519935717822194943">"Visit a repair provider."</string>
@@ -2031,7 +2031,8 @@
<string name="autofill_update_title" msgid="3630695947047069136">"Update in "<b>"<xliff:g id="LABEL">%1$s</xliff:g>"</b>"?"</string>
<string name="autofill_update_title_with_type" msgid="5264152633488495704">"Update <xliff:g id="TYPE">%1$s</xliff:g> in "<b>"<xliff:g id="LABEL">%2$s</xliff:g>"</b>"?"</string>
<string name="autofill_update_title_with_2types" msgid="1797514386321086273">"Update <xliff:g id="TYPE_0">%1$s</xliff:g> and <xliff:g id="TYPE_1">%2$s</xliff:g> in "<b>"<xliff:g id="LABEL">%3$s</xliff:g>"</b>"?"</string>
- <string name="autofill_update_title_with_3types" msgid="1312232153076212291">"Update these items in "<b>"<xliff:g id="LABEL">%4$s</xliff:g>"</b>": <xliff:g id="TYPE_0">%1$s</xliff:g>, <xliff:g id="TYPE_1">%2$s</xliff:g> and <xliff:g id="TYPE_2">%3$s</xliff:g>?"</string>
+ <!-- no translation found for autofill_update_title_with_3types (8285767070604652626) -->
+ <skip />
<string name="autofill_save_yes" msgid="8035743017382012850">"Save"</string>
<string name="autofill_save_no" msgid="9212826374207023544">"No, thanks"</string>
<string name="autofill_save_notnow" msgid="2853932672029024195">"Not now"</string>
diff --git a/core/res/res/values-en-rIN/strings.xml b/core/res/res/values-en-rIN/strings.xml
index c9db594..3504a53 100644
--- a/core/res/res/values-en-rIN/strings.xml
+++ b/core/res/res/values-en-rIN/strings.xml
@@ -669,14 +669,14 @@
</string-array>
<string name="fingerprint_error_vendor_unknown" msgid="4170002184907291065">"Something went wrong. Try again."</string>
<string name="fingerprint_icon_content_description" msgid="4741068463175388817">"Fingerprint icon"</string>
+ <string name="device_unlock_notification_name" msgid="2632928999862915709">"Device unlock"</string>
+ <string name="alternative_unlock_setup_notification_title" msgid="6241508547901933544">"Try another way to unlock"</string>
+ <string name="alternative_face_setup_notification_content" msgid="3384959224091897331">"Use Face Unlock when your fingerprint isn\'t recognised, like when your fingers are wet"</string>
+ <string name="alternative_fp_setup_notification_content" msgid="7454096947415721639">"Use Fingerprint Unlock when your face isn\'t recognised, like when there\'s not enough light"</string>
<string name="face_recalibrate_notification_name" msgid="7311163114750748686">"Face Unlock"</string>
<string name="face_recalibrate_notification_title" msgid="2524791952735579082">"Issue with Face Unlock"</string>
<string name="face_recalibrate_notification_content" msgid="3064513770251355594">"Tap to delete your face model, then add your face again"</string>
- <string name="face_setup_notification_title" msgid="8843461561970741790">"Set up Face Unlock"</string>
- <string name="face_setup_notification_content" msgid="5463999831057751676">"Unlock your phone by looking at it"</string>
<string name="face_sensor_privacy_enabled" msgid="7407126963510598508">"To use Face Unlock, turn on "<b>"Camera access"</b>" in Settings > Privacy"</string>
- <string name="fingerprint_setup_notification_title" msgid="2002630611398849495">"Set up more ways to unlock"</string>
- <string name="fingerprint_setup_notification_content" msgid="205578121848324852">"Tap to add a fingerprint"</string>
<string name="fingerprint_recalibrate_notification_name" msgid="1414578431898579354">"Fingerprint Unlock"</string>
<string name="fingerprint_recalibrate_notification_title" msgid="2406561052064558497">"Can’t use fingerprint sensor"</string>
<string name="fingerprint_recalibrate_notification_content" msgid="8519935717822194943">"Visit a repair provider."</string>
@@ -2031,7 +2031,8 @@
<string name="autofill_update_title" msgid="3630695947047069136">"Update in "<b>"<xliff:g id="LABEL">%1$s</xliff:g>"</b>"?"</string>
<string name="autofill_update_title_with_type" msgid="5264152633488495704">"Update <xliff:g id="TYPE">%1$s</xliff:g> in "<b>"<xliff:g id="LABEL">%2$s</xliff:g>"</b>"?"</string>
<string name="autofill_update_title_with_2types" msgid="1797514386321086273">"Update <xliff:g id="TYPE_0">%1$s</xliff:g> and <xliff:g id="TYPE_1">%2$s</xliff:g> in "<b>"<xliff:g id="LABEL">%3$s</xliff:g>"</b>"?"</string>
- <string name="autofill_update_title_with_3types" msgid="1312232153076212291">"Update these items in "<b>"<xliff:g id="LABEL">%4$s</xliff:g>"</b>": <xliff:g id="TYPE_0">%1$s</xliff:g>, <xliff:g id="TYPE_1">%2$s</xliff:g> and <xliff:g id="TYPE_2">%3$s</xliff:g>?"</string>
+ <!-- no translation found for autofill_update_title_with_3types (8285767070604652626) -->
+ <skip />
<string name="autofill_save_yes" msgid="8035743017382012850">"Save"</string>
<string name="autofill_save_no" msgid="9212826374207023544">"No, thanks"</string>
<string name="autofill_save_notnow" msgid="2853932672029024195">"Not now"</string>
diff --git a/core/res/res/values-en-rXC/strings.xml b/core/res/res/values-en-rXC/strings.xml
index 5888963..9a72749 100644
--- a/core/res/res/values-en-rXC/strings.xml
+++ b/core/res/res/values-en-rXC/strings.xml
@@ -669,14 +669,14 @@
</string-array>
<string name="fingerprint_error_vendor_unknown" msgid="4170002184907291065">"Something went wrong. Try again."</string>
<string name="fingerprint_icon_content_description" msgid="4741068463175388817">"Fingerprint icon"</string>
+ <string name="device_unlock_notification_name" msgid="2632928999862915709">"Device unlock"</string>
+ <string name="alternative_unlock_setup_notification_title" msgid="6241508547901933544">"Try another way to unlock"</string>
+ <string name="alternative_face_setup_notification_content" msgid="3384959224091897331">"Use Face Unlock when your fingerprint isn\'t recognized, like when your fingers are wet"</string>
+ <string name="alternative_fp_setup_notification_content" msgid="7454096947415721639">"Use Fingerprint Unlock when your face isn\'t recognized, like when there\'s not enough light"</string>
<string name="face_recalibrate_notification_name" msgid="7311163114750748686">"Face Unlock"</string>
<string name="face_recalibrate_notification_title" msgid="2524791952735579082">"Issue with Face Unlock"</string>
<string name="face_recalibrate_notification_content" msgid="3064513770251355594">"Tap to delete your face model, then add your face again"</string>
- <string name="face_setup_notification_title" msgid="8843461561970741790">"Set up Face Unlock"</string>
- <string name="face_setup_notification_content" msgid="5463999831057751676">"Unlock your phone by looking at it"</string>
<string name="face_sensor_privacy_enabled" msgid="7407126963510598508">"To use Face Unlock, turn on "<b>"Camera access"</b>" in Settings > Privacy"</string>
- <string name="fingerprint_setup_notification_title" msgid="2002630611398849495">"Set up more ways to unlock"</string>
- <string name="fingerprint_setup_notification_content" msgid="205578121848324852">"Tap to add a fingerprint"</string>
<string name="fingerprint_recalibrate_notification_name" msgid="1414578431898579354">"Fingerprint Unlock"</string>
<string name="fingerprint_recalibrate_notification_title" msgid="2406561052064558497">"Can’t use fingerprint sensor"</string>
<string name="fingerprint_recalibrate_notification_content" msgid="8519935717822194943">"Visit a repair provider."</string>
@@ -2031,7 +2031,8 @@
<string name="autofill_update_title" msgid="3630695947047069136">"Update in "<b>"<xliff:g id="LABEL">%1$s</xliff:g>"</b>"?"</string>
<string name="autofill_update_title_with_type" msgid="5264152633488495704">"Update <xliff:g id="TYPE">%1$s</xliff:g> in "<b>"<xliff:g id="LABEL">%2$s</xliff:g>"</b>"?"</string>
<string name="autofill_update_title_with_2types" msgid="1797514386321086273">"Update <xliff:g id="TYPE_0">%1$s</xliff:g> and <xliff:g id="TYPE_1">%2$s</xliff:g> in "<b>"<xliff:g id="LABEL">%3$s</xliff:g>"</b>"?"</string>
- <string name="autofill_update_title_with_3types" msgid="1312232153076212291">"Update these items in "<b>"<xliff:g id="LABEL">%4$s</xliff:g>"</b>": <xliff:g id="TYPE_0">%1$s</xliff:g>, <xliff:g id="TYPE_1">%2$s</xliff:g>, and <xliff:g id="TYPE_2">%3$s</xliff:g> ?"</string>
+ <!-- no translation found for autofill_update_title_with_3types (8285767070604652626) -->
+ <skip />
<string name="autofill_save_yes" msgid="8035743017382012850">"Save"</string>
<string name="autofill_save_no" msgid="9212826374207023544">"No thanks"</string>
<string name="autofill_save_notnow" msgid="2853932672029024195">"Not now"</string>
diff --git a/core/res/res/values-es-rUS/strings.xml b/core/res/res/values-es-rUS/strings.xml
index 756a198..8b651d1 100644
--- a/core/res/res/values-es-rUS/strings.xml
+++ b/core/res/res/values-es-rUS/strings.xml
@@ -670,14 +670,14 @@
</string-array>
<string name="fingerprint_error_vendor_unknown" msgid="4170002184907291065">"Se produjo un error. Vuelve a intentarlo."</string>
<string name="fingerprint_icon_content_description" msgid="4741068463175388817">"Ícono de huella dactilar"</string>
+ <string name="device_unlock_notification_name" msgid="2632928999862915709">"Desbloqueo del dispositivo"</string>
+ <string name="alternative_unlock_setup_notification_title" msgid="6241508547901933544">"Prueba otra forma para desbloquear el dispositivo"</string>
+ <string name="alternative_face_setup_notification_content" msgid="3384959224091897331">"Usa la función de Desbloqueo facial cuando no se reconoce tu huella dactilar (por ejemplo cuando tienes los dedos mojados)"</string>
+ <string name="alternative_fp_setup_notification_content" msgid="7454096947415721639">"Usa la función de Desbloqueo facial cuando no se reconoce tu rostro (por ejemplo cuando no hay suficiente luz)"</string>
<string name="face_recalibrate_notification_name" msgid="7311163114750748686">"Desbloqueo facial"</string>
<string name="face_recalibrate_notification_title" msgid="2524791952735579082">"Problema con el Desbloqueo facial"</string>
<string name="face_recalibrate_notification_content" msgid="3064513770251355594">"Presiona para borrar el modelo de rostro y, luego, vuelve a agregar tu rostro"</string>
- <string name="face_setup_notification_title" msgid="8843461561970741790">"Configura Desbloqueo facial"</string>
- <string name="face_setup_notification_content" msgid="5463999831057751676">"Desbloquea el teléfono con solo mirarlo"</string>
<string name="face_sensor_privacy_enabled" msgid="7407126963510598508">"Para usar Desbloqueo facial, activa el "<b>"Acceso a la cámara"</b>" en Configuración y privacidad"</string>
- <string name="fingerprint_setup_notification_title" msgid="2002630611398849495">"Configura más formas de desbloquear el dispositivo"</string>
- <string name="fingerprint_setup_notification_content" msgid="205578121848324852">"Presiona para agregar una huella dactilar"</string>
<string name="fingerprint_recalibrate_notification_name" msgid="1414578431898579354">"Desbloqueo con huellas dactilares"</string>
<string name="fingerprint_recalibrate_notification_title" msgid="2406561052064558497">"No se puede usar el sensor de huellas dactilares"</string>
<string name="fingerprint_recalibrate_notification_content" msgid="8519935717822194943">"Consulta a un proveedor de reparaciones."</string>
@@ -2032,7 +2032,8 @@
<string name="autofill_update_title" msgid="3630695947047069136">"¿Quieres actualizar en "<b>"<xliff:g id="LABEL">%1$s</xliff:g>"</b>"?"</string>
<string name="autofill_update_title_with_type" msgid="5264152633488495704">"¿Quieres actualizar <xliff:g id="TYPE">%1$s</xliff:g> en "<b>"<xliff:g id="LABEL">%2$s</xliff:g>"</b>"?"</string>
<string name="autofill_update_title_with_2types" msgid="1797514386321086273">"¿Quieres actualizar <xliff:g id="TYPE_0">%1$s</xliff:g> y <xliff:g id="TYPE_1">%2$s</xliff:g> en "<b>"<xliff:g id="LABEL">%3$s</xliff:g>"</b>"?"</string>
- <string name="autofill_update_title_with_3types" msgid="1312232153076212291">"¿Quieres actualizar estos elementos en "<b>"<xliff:g id="LABEL">%4$s</xliff:g>"</b>": <xliff:g id="TYPE_0">%1$s</xliff:g>, <xliff:g id="TYPE_1">%2$s</xliff:g> y <xliff:g id="TYPE_2">%3$s</xliff:g>?"</string>
+ <!-- no translation found for autofill_update_title_with_3types (8285767070604652626) -->
+ <skip />
<string name="autofill_save_yes" msgid="8035743017382012850">"Guardar"</string>
<string name="autofill_save_no" msgid="9212826374207023544">"No, gracias"</string>
<string name="autofill_save_notnow" msgid="2853932672029024195">"Ahora no"</string>
diff --git a/core/res/res/values-es/strings.xml b/core/res/res/values-es/strings.xml
index 57480bd..9c678c5 100644
--- a/core/res/res/values-es/strings.xml
+++ b/core/res/res/values-es/strings.xml
@@ -670,14 +670,14 @@
</string-array>
<string name="fingerprint_error_vendor_unknown" msgid="4170002184907291065">"Se ha producido un error. Inténtalo de nuevo."</string>
<string name="fingerprint_icon_content_description" msgid="4741068463175388817">"Icono de huella digital"</string>
+ <string name="device_unlock_notification_name" msgid="2632928999862915709">"Desbloqueo del dispositivo"</string>
+ <string name="alternative_unlock_setup_notification_title" msgid="6241508547901933544">"Prueba otro método de desbloqueo"</string>
+ <string name="alternative_face_setup_notification_content" msgid="3384959224091897331">"Usa Desbloqueo facial cuando no se reconozca tu huella digital (por ejemplo, cuando tus dedos estén húmedos)"</string>
+ <string name="alternative_fp_setup_notification_content" msgid="7454096947415721639">"Usa Desbloqueo con huella digital cuando no se reconozca tu cara (por ejemplo, cuando no haya suficiente luz)"</string>
<string name="face_recalibrate_notification_name" msgid="7311163114750748686">"Desbloqueo facial"</string>
<string name="face_recalibrate_notification_title" msgid="2524791952735579082">"Problema con Desbloqueo facial"</string>
<string name="face_recalibrate_notification_content" msgid="3064513770251355594">"Toca para eliminar tu modelo facial y luego añade de nuevo tu cara"</string>
- <string name="face_setup_notification_title" msgid="8843461561970741790">"Configura Desbloqueo facial"</string>
- <string name="face_setup_notification_content" msgid="5463999831057751676">"Desbloquea el teléfono con solo mirarlo"</string>
<string name="face_sensor_privacy_enabled" msgid="7407126963510598508">"Para usar Desbloqueo Facial, habilita el "<b>"acceso a la cámara"</b>" en Ajustes y privacidad"</string>
- <string name="fingerprint_setup_notification_title" msgid="2002630611398849495">"Configura más formas de desbloqueo"</string>
- <string name="fingerprint_setup_notification_content" msgid="205578121848324852">"Toca para añadir una huella digital"</string>
<string name="fingerprint_recalibrate_notification_name" msgid="1414578431898579354">"Desbloqueo con huella digital"</string>
<string name="fingerprint_recalibrate_notification_title" msgid="2406561052064558497">"No se puede usar el sensor de huellas digitales"</string>
<string name="fingerprint_recalibrate_notification_content" msgid="8519935717822194943">"Visita un proveedor de reparaciones."</string>
@@ -2032,7 +2032,8 @@
<string name="autofill_update_title" msgid="3630695947047069136">"¿Actualizar en "<b>"<xliff:g id="LABEL">%1$s</xliff:g>"</b>"?"</string>
<string name="autofill_update_title_with_type" msgid="5264152633488495704">"¿Actualizar <xliff:g id="TYPE">%1$s</xliff:g> en "<b>"<xliff:g id="LABEL">%2$s</xliff:g>"</b>"?"</string>
<string name="autofill_update_title_with_2types" msgid="1797514386321086273">"¿Actualizar <xliff:g id="TYPE_0">%1$s</xliff:g> y <xliff:g id="TYPE_1">%2$s</xliff:g> en "<b>"<xliff:g id="LABEL">%3$s</xliff:g>"</b>"?"</string>
- <string name="autofill_update_title_with_3types" msgid="1312232153076212291">"¿Actualizar estos elementos en "<b>"<xliff:g id="LABEL">%4$s</xliff:g>"</b>" (<xliff:g id="TYPE_0">%1$s</xliff:g>, <xliff:g id="TYPE_1">%2$s</xliff:g> y <xliff:g id="TYPE_2">%3$s</xliff:g>)?"</string>
+ <!-- no translation found for autofill_update_title_with_3types (8285767070604652626) -->
+ <skip />
<string name="autofill_save_yes" msgid="8035743017382012850">"Guardar"</string>
<string name="autofill_save_no" msgid="9212826374207023544">"No, gracias"</string>
<string name="autofill_save_notnow" msgid="2853932672029024195">"Ahora no"</string>
diff --git a/core/res/res/values-et/strings.xml b/core/res/res/values-et/strings.xml
index 3359447..c17a179 100644
--- a/core/res/res/values-et/strings.xml
+++ b/core/res/res/values-et/strings.xml
@@ -669,14 +669,14 @@
</string-array>
<string name="fingerprint_error_vendor_unknown" msgid="4170002184907291065">"Midagi läks valesti. Proovige uuesti."</string>
<string name="fingerprint_icon_content_description" msgid="4741068463175388817">"Sõrmejälje ikoon"</string>
+ <string name="device_unlock_notification_name" msgid="2632928999862915709">"Seadme avamine"</string>
+ <string name="alternative_unlock_setup_notification_title" msgid="6241508547901933544">"Proovige avamiseks teist viisi"</string>
+ <string name="alternative_face_setup_notification_content" msgid="3384959224091897331">"Kasutage näoga avamist, kui teie sõrmejälge ei tuvastata, näiteks kui sõrmed on märjad"</string>
+ <string name="alternative_fp_setup_notification_content" msgid="7454096947415721639">"Kasutage sõrmejäljega avamist, kui teie nägu ei tuvastata, näiteks kui pole piisavalt valgust"</string>
<string name="face_recalibrate_notification_name" msgid="7311163114750748686">"Näoga avamine"</string>
<string name="face_recalibrate_notification_title" msgid="2524791952735579082">"Probleem funktsiooniga Näoga avamine"</string>
<string name="face_recalibrate_notification_content" msgid="3064513770251355594">"Puudutage näomudeli kustutamiseks, seejärel lisage oma nägu uuesti"</string>
- <string name="face_setup_notification_title" msgid="8843461561970741790">"Näoga avamise seadistamine"</string>
- <string name="face_setup_notification_content" msgid="5463999831057751676">"Avage telefon seda vaadates"</string>
<string name="face_sensor_privacy_enabled" msgid="7407126963510598508">"Näoga avamise kasutamiseks lülitage menüüs Seaded > Privaatsus sisse "<b>"juurdepääs kaamerale"</b>"."</string>
- <string name="fingerprint_setup_notification_title" msgid="2002630611398849495">"Seadistage rohkem viise avamiseks"</string>
- <string name="fingerprint_setup_notification_content" msgid="205578121848324852">"Puudutage sõrmejälje lisamiseks"</string>
<string name="fingerprint_recalibrate_notification_name" msgid="1414578431898579354">"Sõrmejäljega avamine"</string>
<string name="fingerprint_recalibrate_notification_title" msgid="2406561052064558497">"Sõrmejäljeandurit ei saa kasutada"</string>
<string name="fingerprint_recalibrate_notification_content" msgid="8519935717822194943">"Külastage remonditeenuse pakkujat."</string>
@@ -2031,7 +2031,8 @@
<string name="autofill_update_title" msgid="3630695947047069136">"Kas värskendada üksust teenuses "<b>"<xliff:g id="LABEL">%1$s</xliff:g>"</b>"?"</string>
<string name="autofill_update_title_with_type" msgid="5264152633488495704">"Kas värskendada üksust <xliff:g id="TYPE">%1$s</xliff:g> teenuses "<b>"<xliff:g id="LABEL">%2$s</xliff:g>"</b>"?"</string>
<string name="autofill_update_title_with_2types" msgid="1797514386321086273">"Kas värskendada üksusi <xliff:g id="TYPE_0">%1$s</xliff:g> ja <xliff:g id="TYPE_1">%2$s</xliff:g> teenuses "<b>"<xliff:g id="LABEL">%3$s</xliff:g>"</b>"?"</string>
- <string name="autofill_update_title_with_3types" msgid="1312232153076212291">"Kas värskendada teenuses "<b>"<xliff:g id="LABEL">%4$s</xliff:g>"</b>" neid üksusi: <xliff:g id="TYPE_0">%1$s</xliff:g>, <xliff:g id="TYPE_1">%2$s</xliff:g> ja <xliff:g id="TYPE_2">%3$s</xliff:g>?"</string>
+ <!-- no translation found for autofill_update_title_with_3types (8285767070604652626) -->
+ <skip />
<string name="autofill_save_yes" msgid="8035743017382012850">"Salvesta"</string>
<string name="autofill_save_no" msgid="9212826374207023544">"Tänan, ei"</string>
<string name="autofill_save_notnow" msgid="2853932672029024195">"Hiljem"</string>
diff --git a/core/res/res/values-eu/strings.xml b/core/res/res/values-eu/strings.xml
index c2ef534..7a6fa1d 100644
--- a/core/res/res/values-eu/strings.xml
+++ b/core/res/res/values-eu/strings.xml
@@ -669,14 +669,18 @@
</string-array>
<string name="fingerprint_error_vendor_unknown" msgid="4170002184907291065">"Arazo bat izan da. Saiatu berriro."</string>
<string name="fingerprint_icon_content_description" msgid="4741068463175388817">"Hatz-markaren ikonoa"</string>
+ <!-- no translation found for device_unlock_notification_name (2632928999862915709) -->
+ <skip />
+ <!-- no translation found for alternative_unlock_setup_notification_title (6241508547901933544) -->
+ <skip />
+ <!-- no translation found for alternative_face_setup_notification_content (3384959224091897331) -->
+ <skip />
+ <!-- no translation found for alternative_fp_setup_notification_content (7454096947415721639) -->
+ <skip />
<string name="face_recalibrate_notification_name" msgid="7311163114750748686">"Aurpegi bidez desblokeatzea"</string>
<string name="face_recalibrate_notification_title" msgid="2524791952735579082">"Arazoak ditugu aurpegi bidez desblokeatzeko eginbidearekin"</string>
<string name="face_recalibrate_notification_content" msgid="3064513770251355594">"Sakatu hau aurpegi-eredua ezabatzeko eta, gero, gehitu aurpegia berriro"</string>
- <string name="face_setup_notification_title" msgid="8843461561970741790">"Konfiguratu Aurpegi bidez desblokeatzea"</string>
- <string name="face_setup_notification_content" msgid="5463999831057751676">"Telefonoa desblokeatzeko, begira iezaiozu"</string>
<string name="face_sensor_privacy_enabled" msgid="7407126963510598508">"Aurpegi bidez desblokeatzeko eginbidea erabiltzeko, aktibatu "<b>"kamera erabiltzeko baimena"</b>" Ezarpenak > Pribatutasuna atalean"</string>
- <string name="fingerprint_setup_notification_title" msgid="2002630611398849495">"Konfiguratu telefonoa desblokeatzeko modu gehiago"</string>
- <string name="fingerprint_setup_notification_content" msgid="205578121848324852">"Sakatu hau hatz-marka bat gehitzeko"</string>
<string name="fingerprint_recalibrate_notification_name" msgid="1414578431898579354">"Hatz-marka bidez desblokeatzea"</string>
<string name="fingerprint_recalibrate_notification_title" msgid="2406561052064558497">"Ezin da erabili hatz-marken sentsorea"</string>
<string name="fingerprint_recalibrate_notification_content" msgid="8519935717822194943">"Jarri harremanetan konponketak egiten dituen hornitzaile batekin."</string>
@@ -938,7 +942,7 @@
<string name="relationTypeAssistant" msgid="4057605157116589315">"Laguntzailea"</string>
<string name="relationTypeBrother" msgid="7141662427379247820">"Anaia/Neba"</string>
<string name="relationTypeChild" msgid="9076258911292693601">"Semea/Alaba"</string>
- <string name="relationTypeDomesticPartner" msgid="7825306887697559238">"Izatezko bikotea"</string>
+ <string name="relationTypeDomesticPartner" msgid="7825306887697559238">"Izatezko bikotekidea"</string>
<string name="relationTypeFather" msgid="3856225062864790596">"Aita"</string>
<string name="relationTypeFriend" msgid="3192092625893980574">"Laguna"</string>
<string name="relationTypeManager" msgid="2272860813153171857">"Kudeatzailea"</string>
@@ -2031,7 +2035,8 @@
<string name="autofill_update_title" msgid="3630695947047069136"><b>"<xliff:g id="LABEL">%1$s</xliff:g>"</b>" zerbitzuan eguneratu nahi duzu?"</string>
<string name="autofill_update_title_with_type" msgid="5264152633488495704"><b>"<xliff:g id="LABEL">%2$s</xliff:g>"</b>" zerbitzuan eguneratu nahi duzu <xliff:g id="TYPE">%1$s</xliff:g>?"</string>
<string name="autofill_update_title_with_2types" msgid="1797514386321086273"><b>"<xliff:g id="LABEL">%3$s</xliff:g>"</b>" zerbitzuan eguneratu nahi dituzu <xliff:g id="TYPE_0">%1$s</xliff:g> eta <xliff:g id="TYPE_1">%2$s</xliff:g>?"</string>
- <string name="autofill_update_title_with_3types" msgid="1312232153076212291"><b>"<xliff:g id="LABEL">%4$s</xliff:g>"</b>" zerbitzuan eguneratu nahi dituzu <xliff:g id="TYPE_0">%1$s</xliff:g>, <xliff:g id="TYPE_1">%2$s</xliff:g> eta <xliff:g id="TYPE_2">%3$s</xliff:g>?"</string>
+ <!-- no translation found for autofill_update_title_with_3types (8285767070604652626) -->
+ <skip />
<string name="autofill_save_yes" msgid="8035743017382012850">"Gorde"</string>
<string name="autofill_save_no" msgid="9212826374207023544">"Ez, eskerrik asko"</string>
<string name="autofill_save_notnow" msgid="2853932672029024195">"Orain ez"</string>
diff --git a/core/res/res/values-fa/strings.xml b/core/res/res/values-fa/strings.xml
index 3841eaa..c733bea 100644
--- a/core/res/res/values-fa/strings.xml
+++ b/core/res/res/values-fa/strings.xml
@@ -669,14 +669,14 @@
</string-array>
<string name="fingerprint_error_vendor_unknown" msgid="4170002184907291065">"مشکلی پیش آمد. دوباره امتحان کنید."</string>
<string name="fingerprint_icon_content_description" msgid="4741068463175388817">"نماد اثر انگشت"</string>
+ <string name="device_unlock_notification_name" msgid="2632928999862915709">"باز کردن قفل دستگاه"</string>
+ <string name="alternative_unlock_setup_notification_title" msgid="6241508547901933544">"روش دیگری را برای قفلگشایی امتحان کنید"</string>
+ <string name="alternative_face_setup_notification_content" msgid="3384959224091897331">"وقتی اثر انگشتتان تشخیص داده نمیشود، مثل زمانی که انگشتانتان خیس است، از «قفلگشایی با چهره» استفاده کنید"</string>
+ <string name="alternative_fp_setup_notification_content" msgid="7454096947415721639">"وقتی چهرهتان تشخیص داده نمیشود، مثل زمانی که نور کافی نیست، از «قفلگشایی با اثر انگشت» استفاده کنید"</string>
<string name="face_recalibrate_notification_name" msgid="7311163114750748686">"قفلگشایی با چهره"</string>
<string name="face_recalibrate_notification_title" msgid="2524791952735579082">"مشکل در «قفلگشایی با چهره»"</string>
<string name="face_recalibrate_notification_content" msgid="3064513770251355594">"برای حذف مدل چهرهتان ضربه بزنید، سپس چهرهتان را دوباره اضافه کنید"</string>
- <string name="face_setup_notification_title" msgid="8843461561970741790">"راهاندازی «قفلگشایی با چهره»"</string>
- <string name="face_setup_notification_content" msgid="5463999831057751676">"برای باز کردن قفل تلفن خود به آن نگاه کنید"</string>
<string name="face_sensor_privacy_enabled" msgid="7407126963510598508">"برای استفاده از «قفلگشایی با چهره»، "<b>"دسترسی به دوربین"</b>" را در «تنظیمات > حریم خصوصی» روشن کنید"</string>
- <string name="fingerprint_setup_notification_title" msgid="2002630611398849495">"راهاندازی روشهای بیشتر برای باز کردن قفل"</string>
- <string name="fingerprint_setup_notification_content" msgid="205578121848324852">"برای افزودن اثر انگشت، ضربه بزنید"</string>
<string name="fingerprint_recalibrate_notification_name" msgid="1414578431898579354">"قفلگشایی با اثر انگشت"</string>
<string name="fingerprint_recalibrate_notification_title" msgid="2406561052064558497">"امکان استفاده از حسگر اثر انگشت وجود ندارد"</string>
<string name="fingerprint_recalibrate_notification_content" msgid="8519935717822194943">"به ارائهدهنده خدمات تعمیر مراجعه کنید."</string>
@@ -1596,7 +1596,7 @@
<string name="data_usage_restricted_body" msgid="5338694433686077733">"برای برداشتن محدودیت ضربه بزنید."</string>
<string name="data_usage_rapid_title" msgid="2950192123248740375">"مصرف بالای داده تلفن همراه"</string>
<string name="data_usage_rapid_body" msgid="3886676853263693432">"برنامههای شما بیش از معمول داده مصرف کردهاند"</string>
- <string name="data_usage_rapid_app_body" msgid="5425779218506513861">"<xliff:g id="APP">%s</xliff:g> بیش از معمول داده مصرف کرده است"</string>
+ <string name="data_usage_rapid_app_body" msgid="5425779218506513861">"<xliff:g id="APP">%s</xliff:g> بیش از معمول داده مصرف کرده است"</string>
<string name="ssl_certificate" msgid="5690020361307261997">"گواهی امنیتی"</string>
<string name="ssl_certificate_is_valid" msgid="7293675884598527081">"این گواهی معتبر است."</string>
<string name="issued_to" msgid="5975877665505297662">"صادرشده برای:"</string>
@@ -2031,7 +2031,8 @@
<string name="autofill_update_title" msgid="3630695947047069136">"در "<b>"<xliff:g id="LABEL">%1$s</xliff:g>"</b>" بهروزرسانی شود؟"</string>
<string name="autofill_update_title_with_type" msgid="5264152633488495704">"<xliff:g id="TYPE">%1$s</xliff:g> در "<b>"<xliff:g id="LABEL">%2$s</xliff:g>"</b>" بهروزرسانی شود؟"</string>
<string name="autofill_update_title_with_2types" msgid="1797514386321086273">"<xliff:g id="TYPE_0">%1$s</xliff:g> و <xliff:g id="TYPE_1">%2$s</xliff:g> در "<b>"<xliff:g id="LABEL">%3$s</xliff:g>"</b>" بهروزرسانی شود؟"</string>
- <string name="autofill_update_title_with_3types" msgid="1312232153076212291">"این موارد در "<b>"<xliff:g id="LABEL">%4$s</xliff:g>"</b>": <xliff:g id="TYPE_0">%1$s</xliff:g>، <xliff:g id="TYPE_1">%2$s</xliff:g> و <xliff:g id="TYPE_2">%3$s</xliff:g> بهروزرسانی شود؟"</string>
+ <!-- no translation found for autofill_update_title_with_3types (8285767070604652626) -->
+ <skip />
<string name="autofill_save_yes" msgid="8035743017382012850">"ذخیره"</string>
<string name="autofill_save_no" msgid="9212826374207023544">"نه سپاسگزارم"</string>
<string name="autofill_save_notnow" msgid="2853932672029024195">"حالا نه"</string>
diff --git a/core/res/res/values-fi/strings.xml b/core/res/res/values-fi/strings.xml
index 5c9f0e4..5cf1c99 100644
--- a/core/res/res/values-fi/strings.xml
+++ b/core/res/res/values-fi/strings.xml
@@ -669,14 +669,14 @@
</string-array>
<string name="fingerprint_error_vendor_unknown" msgid="4170002184907291065">"Jotain meni vikaan. Yritä uudelleen."</string>
<string name="fingerprint_icon_content_description" msgid="4741068463175388817">"Sormenjälkikuvake"</string>
+ <string name="device_unlock_notification_name" msgid="2632928999862915709">"Laitteen lukituksen avaus"</string>
+ <string name="alternative_unlock_setup_notification_title" msgid="6241508547901933544">"Kokeile toista tapaa avata lukitus"</string>
+ <string name="alternative_face_setup_notification_content" msgid="3384959224091897331">"Käytä kasvojentunnistusavausta, kun sormenjälkeä ei tunnisteta, esimerkiksi sormien ollessa märät"</string>
+ <string name="alternative_fp_setup_notification_content" msgid="7454096947415721639">"Käytä sormenjälkiavausta, kun kasvoja ei tunnisteta, esimerkiksi hämärässä"</string>
<string name="face_recalibrate_notification_name" msgid="7311163114750748686">"Kasvojentunnistusavaus"</string>
<string name="face_recalibrate_notification_title" msgid="2524791952735579082">"Face Unlockiin liittyvä ongelma"</string>
<string name="face_recalibrate_notification_content" msgid="3064513770251355594">"Poista kasvomalli napauttamalla ja lisää sitten kasvosi uudelleen"</string>
- <string name="face_setup_notification_title" msgid="8843461561970741790">"Ota kasvojentunnistusavaus käyttöön"</string>
- <string name="face_setup_notification_content" msgid="5463999831057751676">"Avaa puhelimesi lukitus katsomalla laitetta"</string>
<string name="face_sensor_privacy_enabled" msgid="7407126963510598508">"Jos haluat käyttää kasvojentunnistusavausta, valitse Asetukset > Yksityisyys ja laita "<b>"Pääsy kameraan"</b>" päälle"</string>
- <string name="fingerprint_setup_notification_title" msgid="2002630611398849495">"Ota käyttöön lisää tapoja avata lukitus"</string>
- <string name="fingerprint_setup_notification_content" msgid="205578121848324852">"Napauta lisätäksesi sormenjälki"</string>
<string name="fingerprint_recalibrate_notification_name" msgid="1414578431898579354">"Sormenjälkiavaus"</string>
<string name="fingerprint_recalibrate_notification_title" msgid="2406561052064558497">"Sormenjälkitunnistinta ei voi käyttää"</string>
<string name="fingerprint_recalibrate_notification_content" msgid="8519935717822194943">"Ota yhteys korjauspalveluun."</string>
@@ -2031,7 +2031,8 @@
<string name="autofill_update_title" msgid="3630695947047069136">"Päivitetäänkö "<b>"<xliff:g id="LABEL">%1$s</xliff:g>"</b>"?"</string>
<string name="autofill_update_title_with_type" msgid="5264152633488495704">"Päivitetäänkö <xliff:g id="TYPE">%1$s</xliff:g> ("<b>"<xliff:g id="LABEL">%2$s</xliff:g>"</b>")?"</string>
<string name="autofill_update_title_with_2types" msgid="1797514386321086273">"Päivitetäänkö <xliff:g id="TYPE_0">%1$s</xliff:g> ja <xliff:g id="TYPE_1">%2$s</xliff:g> ("<b>"<xliff:g id="LABEL">%3$s</xliff:g>"</b>")?"</string>
- <string name="autofill_update_title_with_3types" msgid="1312232153076212291">"Päivitetäänkö nämä ("<b>"<xliff:g id="LABEL">%4$s</xliff:g>"</b>"): <xliff:g id="TYPE_0">%1$s</xliff:g>, <xliff:g id="TYPE_1">%2$s</xliff:g> ja <xliff:g id="TYPE_2">%3$s</xliff:g>?"</string>
+ <!-- no translation found for autofill_update_title_with_3types (8285767070604652626) -->
+ <skip />
<string name="autofill_save_yes" msgid="8035743017382012850">"Tallenna"</string>
<string name="autofill_save_no" msgid="9212826374207023544">"Ei kiitos"</string>
<string name="autofill_save_notnow" msgid="2853932672029024195">"Ei nyt"</string>
diff --git a/core/res/res/values-fr-rCA/strings.xml b/core/res/res/values-fr-rCA/strings.xml
index 9b0c536..fda1183 100644
--- a/core/res/res/values-fr-rCA/strings.xml
+++ b/core/res/res/values-fr-rCA/strings.xml
@@ -670,14 +670,14 @@
</string-array>
<string name="fingerprint_error_vendor_unknown" msgid="4170002184907291065">"Un problème est survenu. Réessayez."</string>
<string name="fingerprint_icon_content_description" msgid="4741068463175388817">"Icône d\'empreinte digitale"</string>
+ <string name="device_unlock_notification_name" msgid="2632928999862915709">"Déverrouillage de l\'appareil"</string>
+ <string name="alternative_unlock_setup_notification_title" msgid="6241508547901933544">"Essayez une autre façon de déverrouiller"</string>
+ <string name="alternative_face_setup_notification_content" msgid="3384959224091897331">"Utilisez Déverrouillage par reconnaissance faciale lorsque votre empreinte digitale n\'est pas reconnue, par exemple lorsque vos doigts sont mouillés"</string>
+ <string name="alternative_fp_setup_notification_content" msgid="7454096947415721639">"Utilisez Déverrouillage par empreinte digitale lorsque votre visage n\'est pas reconnu, par exemple lorsque la luminosité est insuffisante"</string>
<string name="face_recalibrate_notification_name" msgid="7311163114750748686">"Déverrouillage par reconnaissance faciale"</string>
<string name="face_recalibrate_notification_title" msgid="2524791952735579082">"Problème avec la fonctionnalité de déverrouillage par reconnaissance faciale"</string>
<string name="face_recalibrate_notification_content" msgid="3064513770251355594">"Touchez pour supprimer votre modèle facial, puis ajoutez votre visage de nouveau"</string>
- <string name="face_setup_notification_title" msgid="8843461561970741790">"Configurer le Déverrouillage par reconnaissance faciale"</string>
- <string name="face_setup_notification_content" msgid="5463999831057751676">"Déverrouillez votre téléphone en le regardant"</string>
<string name="face_sensor_privacy_enabled" msgid="7407126963510598508">"Pour utiliser le déverrouillage par reconnaissance faciale, activez l\'"<b>"accès à l\'appareil photo"</b>" dans Paramètres > Confidentialité"</string>
- <string name="fingerprint_setup_notification_title" msgid="2002630611398849495">"Configurer d\'autres méthodes de déverrouillage"</string>
- <string name="fingerprint_setup_notification_content" msgid="205578121848324852">"Touchez pour ajouter une empreinte digitale"</string>
<string name="fingerprint_recalibrate_notification_name" msgid="1414578431898579354">"Déverrouillage par empreinte digitale"</string>
<string name="fingerprint_recalibrate_notification_title" msgid="2406561052064558497">"Impossible d\'utiliser le capteur d\'empreintes digitales"</string>
<string name="fingerprint_recalibrate_notification_content" msgid="8519935717822194943">"Consultez un fournisseur de services de réparation."</string>
@@ -2032,7 +2032,8 @@
<string name="autofill_update_title" msgid="3630695947047069136">"Mettre à jour sous "<b>"<xliff:g id="LABEL">%1$s</xliff:g>"</b>"?"</string>
<string name="autofill_update_title_with_type" msgid="5264152633488495704">"Mettre à jour <xliff:g id="TYPE">%1$s</xliff:g> sous "<b>"<xliff:g id="LABEL">%2$s</xliff:g>"</b>"?"</string>
<string name="autofill_update_title_with_2types" msgid="1797514386321086273">"Mettre à jour <xliff:g id="TYPE_0">%1$s</xliff:g> et <xliff:g id="TYPE_1">%2$s</xliff:g> sous "<b>"<xliff:g id="LABEL">%3$s</xliff:g>"</b>"?"</string>
- <string name="autofill_update_title_with_3types" msgid="1312232153076212291">"Mettre à jour ces éléments sous "<b>"<xliff:g id="LABEL">%4$s</xliff:g>"</b>" : <xliff:g id="TYPE_0">%1$s</xliff:g>, <xliff:g id="TYPE_1">%2$s</xliff:g> et <xliff:g id="TYPE_2">%3$s</xliff:g>?"</string>
+ <!-- no translation found for autofill_update_title_with_3types (8285767070604652626) -->
+ <skip />
<string name="autofill_save_yes" msgid="8035743017382012850">"Enregistrer"</string>
<string name="autofill_save_no" msgid="9212826374207023544">"Non, merci"</string>
<string name="autofill_save_notnow" msgid="2853932672029024195">"Pas maintenant"</string>
diff --git a/core/res/res/values-fr/strings.xml b/core/res/res/values-fr/strings.xml
index f5f4316..654a3f2 100644
--- a/core/res/res/values-fr/strings.xml
+++ b/core/res/res/values-fr/strings.xml
@@ -670,14 +670,14 @@
</string-array>
<string name="fingerprint_error_vendor_unknown" msgid="4170002184907291065">"Un problème est survenu. Réessayez."</string>
<string name="fingerprint_icon_content_description" msgid="4741068463175388817">"Icône d\'empreinte digitale"</string>
+ <string name="device_unlock_notification_name" msgid="2632928999862915709">"Déverrouillage de l\'appareil"</string>
+ <string name="alternative_unlock_setup_notification_title" msgid="6241508547901933544">"Essayez une autre méthode de déverrouillage"</string>
+ <string name="alternative_face_setup_notification_content" msgid="3384959224091897331">"Utilisez le déverrouillage par reconnaissance faciale lorsque votre empreinte digitale n\'est pas reconnue, par exemple lorsque vos doigts sont mouillés"</string>
+ <string name="alternative_fp_setup_notification_content" msgid="7454096947415721639">"Utilisez le déverrouillage par empreinte digitale lorsque votre visage n\'est pas reconnu, par exemple lorsque la luminosité n\'est pas suffisante"</string>
<string name="face_recalibrate_notification_name" msgid="7311163114750748686">"Déverrouillage par reconnaissance faciale"</string>
<string name="face_recalibrate_notification_title" msgid="2524791952735579082">"Problème lié au déverrouillage par reconnaissance faciale"</string>
<string name="face_recalibrate_notification_content" msgid="3064513770251355594">"Appuyez pour supprimer votre empreinte faciale, puis ajoutez de nouveau votre visage"</string>
- <string name="face_setup_notification_title" msgid="8843461561970741790">"Configurer le déverrouillage par reconnaissance faciale"</string>
- <string name="face_setup_notification_content" msgid="5463999831057751676">"Déverrouillez votre téléphone en le regardant"</string>
<string name="face_sensor_privacy_enabled" msgid="7407126963510598508">"Pour utiliser le déverrouillage par reconnaissance faciale, activez "<b>"Accès à l\'appareil photo"</b>" dans Paramètres > Confidentialité"</string>
- <string name="fingerprint_setup_notification_title" msgid="2002630611398849495">"Configurer d\'autres méthodes de déverrouillage"</string>
- <string name="fingerprint_setup_notification_content" msgid="205578121848324852">"Appuyez pour ajouter une empreinte digitale"</string>
<string name="fingerprint_recalibrate_notification_name" msgid="1414578431898579354">"Déverrouillage par empreinte digitale"</string>
<string name="fingerprint_recalibrate_notification_title" msgid="2406561052064558497">"Impossible d\'utiliser le lecteur d\'empreinte digitale"</string>
<string name="fingerprint_recalibrate_notification_content" msgid="8519935717822194943">"Contactez un réparateur."</string>
@@ -2032,7 +2032,8 @@
<string name="autofill_update_title" msgid="3630695947047069136">"Mettre à jour cet élément dans "<b>"<xliff:g id="LABEL">%1$s</xliff:g>"</b>" ?"</string>
<string name="autofill_update_title_with_type" msgid="5264152633488495704">"Mettre à jour <xliff:g id="TYPE">%1$s</xliff:g> dans "<b>"<xliff:g id="LABEL">%2$s</xliff:g>"</b>" ?"</string>
<string name="autofill_update_title_with_2types" msgid="1797514386321086273">"Mettre à jour <xliff:g id="TYPE_0">%1$s</xliff:g> et <xliff:g id="TYPE_1">%2$s</xliff:g> dans "<b>"<xliff:g id="LABEL">%3$s</xliff:g>"</b>" ?"</string>
- <string name="autofill_update_title_with_3types" msgid="1312232153076212291">"Mettre à jour les éléments <xliff:g id="TYPE_0">%1$s</xliff:g>, <xliff:g id="TYPE_1">%2$s</xliff:g> et <xliff:g id="TYPE_2">%3$s</xliff:g> dans "<b>"<xliff:g id="LABEL">%4$s</xliff:g>"</b>" ?"</string>
+ <!-- no translation found for autofill_update_title_with_3types (8285767070604652626) -->
+ <skip />
<string name="autofill_save_yes" msgid="8035743017382012850">"Enregistrer"</string>
<string name="autofill_save_no" msgid="9212826374207023544">"Non, merci"</string>
<string name="autofill_save_notnow" msgid="2853932672029024195">"Pas maintenant"</string>
diff --git a/core/res/res/values-gl/strings.xml b/core/res/res/values-gl/strings.xml
index ef6d317..51006b9 100644
--- a/core/res/res/values-gl/strings.xml
+++ b/core/res/res/values-gl/strings.xml
@@ -669,14 +669,14 @@
</string-array>
<string name="fingerprint_error_vendor_unknown" msgid="4170002184907291065">"Produciuse un erro. Téntao de novo."</string>
<string name="fingerprint_icon_content_description" msgid="4741068463175388817">"Icona de impresión dixital"</string>
+ <string name="device_unlock_notification_name" msgid="2632928999862915709">"Desbloqueo do dispositivo"</string>
+ <string name="alternative_unlock_setup_notification_title" msgid="6241508547901933544">"Proba outro método de desbloqueo"</string>
+ <string name="alternative_face_setup_notification_content" msgid="3384959224091897331">"Usa a o desbloqueo facial cando non se dea recoñecido a túa impresión dixital (por exemplo, cando teñas os dedos húmidos)"</string>
+ <string name="alternative_fp_setup_notification_content" msgid="7454096947415721639">"Usa o desbloqueo dactilar cando non se dea recoñecido a túa cara (por exemplo, cando non haxa moita luz)"</string>
<string name="face_recalibrate_notification_name" msgid="7311163114750748686">"Desbloqueo facial"</string>
<string name="face_recalibrate_notification_title" msgid="2524791952735579082">"Produciuse un problema co desbloqueo facial"</string>
<string name="face_recalibrate_notification_content" msgid="3064513770251355594">"Toca para eliminar o teu modelo facial e despois engade de novo a cara"</string>
- <string name="face_setup_notification_title" msgid="8843461561970741790">"Configurar o desbloqueo facial"</string>
- <string name="face_setup_notification_content" msgid="5463999831057751676">"Mira o teléfono para desbloquealo"</string>
<string name="face_sensor_privacy_enabled" msgid="7407126963510598508">"Para usar o desbloqueo facial, activa "<b>"Acceso á cámara"</b>" en Configuración > Privacidade"</string>
- <string name="fingerprint_setup_notification_title" msgid="2002630611398849495">"Configura máis maneiras de desbloquear o dispositivo"</string>
- <string name="fingerprint_setup_notification_content" msgid="205578121848324852">"Toca para engadir unha impresión dixital"</string>
<string name="fingerprint_recalibrate_notification_name" msgid="1414578431898579354">"Desbloqueo dactilar"</string>
<string name="fingerprint_recalibrate_notification_title" msgid="2406561052064558497">"Non se puido usar o sensor de impresión dixital"</string>
<string name="fingerprint_recalibrate_notification_content" msgid="8519935717822194943">"Visita un provedor de reparacións."</string>
@@ -2031,7 +2031,8 @@
<string name="autofill_update_title" msgid="3630695947047069136">"Queres actualizar o contido en "<b>"<xliff:g id="LABEL">%1$s</xliff:g>"</b>"?"</string>
<string name="autofill_update_title_with_type" msgid="5264152633488495704">"Queres actualizar <xliff:g id="TYPE">%1$s</xliff:g> en "<b>"<xliff:g id="LABEL">%2$s</xliff:g>"</b>"?"</string>
<string name="autofill_update_title_with_2types" msgid="1797514386321086273">"Queres actualizar <xliff:g id="TYPE_0">%1$s</xliff:g> e <xliff:g id="TYPE_1">%2$s</xliff:g> en "<b>"<xliff:g id="LABEL">%3$s</xliff:g>"</b>"?"</string>
- <string name="autofill_update_title_with_3types" msgid="1312232153076212291">"Queres actualizar <xliff:g id="TYPE_0">%1$s</xliff:g>, <xliff:g id="TYPE_1">%2$s</xliff:g> e <xliff:g id="TYPE_2">%3$s</xliff:g> en "<b>"<xliff:g id="LABEL">%4$s</xliff:g>"</b>"?"</string>
+ <!-- no translation found for autofill_update_title_with_3types (8285767070604652626) -->
+ <skip />
<string name="autofill_save_yes" msgid="8035743017382012850">"Gardar"</string>
<string name="autofill_save_no" msgid="9212826374207023544">"Non, grazas"</string>
<string name="autofill_save_notnow" msgid="2853932672029024195">"Agora non"</string>
diff --git a/core/res/res/values-gu/strings.xml b/core/res/res/values-gu/strings.xml
index 1f1bc51..aa2a887 100644
--- a/core/res/res/values-gu/strings.xml
+++ b/core/res/res/values-gu/strings.xml
@@ -669,14 +669,14 @@
</string-array>
<string name="fingerprint_error_vendor_unknown" msgid="4170002184907291065">"કંઈક ખોટું થયું. ફરી પ્રયાસ કરો."</string>
<string name="fingerprint_icon_content_description" msgid="4741068463175388817">"ફિંગરપ્રિન્ટ આયકન"</string>
+ <string name="device_unlock_notification_name" msgid="2632928999862915709">"ડિવાઇસ અનલૉક કરવું"</string>
+ <string name="alternative_unlock_setup_notification_title" msgid="6241508547901933544">"અનલૉક કરવા માટેની અન્ય રીત અજમાવી જુઓ"</string>
+ <string name="alternative_face_setup_notification_content" msgid="3384959224091897331">"જ્યારે તમારી ફિંગરપ્રિન્ટ ઓળખાતી ન હોય, જેમ કે જ્યારે તમારી આંગળીઓ ભીની હોય ત્યારે ફેસ અનલૉકનો ઉપયોગ કરો"</string>
+ <string name="alternative_fp_setup_notification_content" msgid="7454096947415721639">"જ્યારે તમારો ચહેરો ઓળખાતો ન હોય, જેમ કે જ્યારે પૂરતો પ્રકાશ ન હોય ત્યારે ફિંગરપ્રિન્ટ અનલૉકનો ઉપયોગ કરો"</string>
<string name="face_recalibrate_notification_name" msgid="7311163114750748686">"ફેસ અનલૉક"</string>
<string name="face_recalibrate_notification_title" msgid="2524791952735579082">"ફેસ અનલૉકની સુવિધામાં સમસ્યા"</string>
<string name="face_recalibrate_notification_content" msgid="3064513770251355594">"તમારા ચહેરાનું મૉડલ ડિલીટ કરવા માટે ટૅપ કરો, પછી તમારો ચહેરો ફરીથી ઉમેરો"</string>
- <string name="face_setup_notification_title" msgid="8843461561970741790">"ફેસ અનલૉક સુવિધાનું સેટઅપ કરો"</string>
- <string name="face_setup_notification_content" msgid="5463999831057751676">"તમારા ફોનની તરફ જોઈને તેને અનલૉક કરો"</string>
<string name="face_sensor_privacy_enabled" msgid="7407126963510598508">"ફેસ અનલૉક સુવિધાનો ઉપયોગ કરવા માટે, સેટિંગ > પ્રાઇવસીમાં જઈને "<b>"કૅમેરા ઍક્સેસ"</b>" ચાલુ કરો"</string>
- <string name="fingerprint_setup_notification_title" msgid="2002630611398849495">"અનલૉક કરવાની બીજી રીતોનું સેટઅપ કરો"</string>
- <string name="fingerprint_setup_notification_content" msgid="205578121848324852">"ફિંગરપ્રિન્ટ ઉમેરવા માટે ટૅપ કરો"</string>
<string name="fingerprint_recalibrate_notification_name" msgid="1414578431898579354">"ફિંગરપ્રિન્ટ અનલૉક"</string>
<string name="fingerprint_recalibrate_notification_title" msgid="2406561052064558497">"ફિંગરપ્રિન્ટ સેન્સરનો ઉપયોગ કરી શકાતો નથી"</string>
<string name="fingerprint_recalibrate_notification_content" msgid="8519935717822194943">"રિપેર કરવાની સેવા આપતા પ્રદાતાની મુલાકાત લો."</string>
@@ -2031,7 +2031,8 @@
<string name="autofill_update_title" msgid="3630695947047069136"><b>"<xliff:g id="LABEL">%1$s</xliff:g>"</b>"માં અપડેટ કરીએ?"</string>
<string name="autofill_update_title_with_type" msgid="5264152633488495704">"<xliff:g id="TYPE">%1$s</xliff:g>ને "<b>"<xliff:g id="LABEL">%2$s</xliff:g>"</b>"માં અપડેટ કરીએ?"</string>
<string name="autofill_update_title_with_2types" msgid="1797514386321086273">"<xliff:g id="TYPE_0">%1$s</xliff:g> અને <xliff:g id="TYPE_1">%2$s</xliff:g>ને "<b>"<xliff:g id="LABEL">%3$s</xliff:g>"</b>"માં અપડેટ કરીએ?"</string>
- <string name="autofill_update_title_with_3types" msgid="1312232153076212291"><b>"<xliff:g id="LABEL">%4$s</xliff:g>"</b>"માંની: <xliff:g id="TYPE_0">%1$s</xliff:g>, <xliff:g id="TYPE_1">%2$s</xliff:g> અને <xliff:g id="TYPE_2">%3$s</xliff:g> બાબતોને અપડેટ કરીએ?"</string>
+ <!-- no translation found for autofill_update_title_with_3types (8285767070604652626) -->
+ <skip />
<string name="autofill_save_yes" msgid="8035743017382012850">"સાચવો"</string>
<string name="autofill_save_no" msgid="9212826374207023544">"ના, આભાર"</string>
<string name="autofill_save_notnow" msgid="2853932672029024195">"હમણાં નહીં"</string>
diff --git a/core/res/res/values-hi/strings.xml b/core/res/res/values-hi/strings.xml
index 01dec74..a81ada6 100644
--- a/core/res/res/values-hi/strings.xml
+++ b/core/res/res/values-hi/strings.xml
@@ -669,14 +669,14 @@
</string-array>
<string name="fingerprint_error_vendor_unknown" msgid="4170002184907291065">"कोई गड़बड़ी हुई. फिर से कोशिश करें."</string>
<string name="fingerprint_icon_content_description" msgid="4741068463175388817">"फ़िंगरप्रिंट आइकॉन"</string>
+ <string name="device_unlock_notification_name" msgid="2632928999862915709">"डिवाइस अनलॉक करने की सुविधा"</string>
+ <string name="alternative_unlock_setup_notification_title" msgid="6241508547901933544">"अनलॉक करने का दूसरा तरीका आज़माएं"</string>
+ <string name="alternative_face_setup_notification_content" msgid="3384959224091897331">"फ़िंगरप्रिंट की पहचान न होने पर, फ़ेस अनलॉक का इस्तेमाल करें. जैसे, जब आपकी उंगलियां गीली हों"</string>
+ <string name="alternative_fp_setup_notification_content" msgid="7454096947415721639">"चेहरे की पहचान न होने पर, फ़िंगरप्रिंट अनलॉक का इस्तेमाल करें. जैसे, जब रोशनी कम हो"</string>
<string name="face_recalibrate_notification_name" msgid="7311163114750748686">"फ़ेस अनलॉक"</string>
<string name="face_recalibrate_notification_title" msgid="2524791952735579082">"फ़ेस अनलॉक से जुड़ी समस्या"</string>
<string name="face_recalibrate_notification_content" msgid="3064513770251355594">"अपने चेहरे का मॉडल मिटाने के लिए टैप करें. इसके बाद, अपना चेहरा फिर से रजिस्टर करें"</string>
- <string name="face_setup_notification_title" msgid="8843461561970741790">"फे़स अनलॉक की सुविधा सेट अप करें"</string>
- <string name="face_setup_notification_content" msgid="5463999831057751676">"अपने फ़ोन की तरफ़ देखकर उसे अनलॉक करें"</string>
<string name="face_sensor_privacy_enabled" msgid="7407126963510598508">"फ़ेस अनलॉक की सुविधा का इस्तेमाल करने के लिए, सेटिंग और निजता में जाकर, "<b>"कैमरे का ऐक्सेस"</b>" चालू करें"</string>
- <string name="fingerprint_setup_notification_title" msgid="2002630611398849495">"फ़ोन को अनलॉक करने के दूसरे तरीके सेट अप करें"</string>
- <string name="fingerprint_setup_notification_content" msgid="205578121848324852">"फ़िंगरप्रिंट जोड़ने के लिए टैप करें"</string>
<string name="fingerprint_recalibrate_notification_name" msgid="1414578431898579354">"फ़िंगरप्रिंट अनलॉक"</string>
<string name="fingerprint_recalibrate_notification_title" msgid="2406561052064558497">"फ़िंगरप्रिंट सेंसर इस्तेमाल नहीं किया जा सकता"</string>
<string name="fingerprint_recalibrate_notification_content" msgid="8519935717822194943">"फ़िंगरप्रिंट सेंसर को रिपेयर करने की सेवा देने वाली कंपनी से संपर्क करें."</string>
@@ -2031,7 +2031,8 @@
<string name="autofill_update_title" msgid="3630695947047069136">"क्या आप "<b>"<xliff:g id="LABEL">%1$s</xliff:g>"</b>" में अपडेट करना चाहते हैं?"</string>
<string name="autofill_update_title_with_type" msgid="5264152633488495704">"क्या आप "<b>"<xliff:g id="LABEL">%2$s</xliff:g>"</b>" में <xliff:g id="TYPE">%1$s</xliff:g> अपडेट करना चाहते हैं?"</string>
<string name="autofill_update_title_with_2types" msgid="1797514386321086273">"क्या आप "<b>"<xliff:g id="LABEL">%3$s</xliff:g>"</b>" में <xliff:g id="TYPE_0">%1$s</xliff:g> और <xliff:g id="TYPE_1">%2$s</xliff:g> अपडेट करना चाहते हैं?"</string>
- <string name="autofill_update_title_with_3types" msgid="1312232153076212291">"क्या आप "<b>"<xliff:g id="LABEL">%4$s</xliff:g>"</b>" में इन आइटम को अपडेट करना चाहते हैं: <xliff:g id="TYPE_0">%1$s</xliff:g>, <xliff:g id="TYPE_1">%2$s</xliff:g>, और <xliff:g id="TYPE_2">%3$s</xliff:g>?"</string>
+ <!-- no translation found for autofill_update_title_with_3types (8285767070604652626) -->
+ <skip />
<string name="autofill_save_yes" msgid="8035743017382012850">"सेव करें"</string>
<string name="autofill_save_no" msgid="9212826374207023544">"नहीं, धन्यवाद"</string>
<string name="autofill_save_notnow" msgid="2853932672029024195">"अभी नहीं"</string>
diff --git a/core/res/res/values-hr/strings.xml b/core/res/res/values-hr/strings.xml
index c1d7a23..fefddbc 100644
--- a/core/res/res/values-hr/strings.xml
+++ b/core/res/res/values-hr/strings.xml
@@ -670,14 +670,14 @@
</string-array>
<string name="fingerprint_error_vendor_unknown" msgid="4170002184907291065">"Nešto nije u redu. Pokušajte ponovo."</string>
<string name="fingerprint_icon_content_description" msgid="4741068463175388817">"Ikona otiska prsta"</string>
+ <string name="device_unlock_notification_name" msgid="2632928999862915709">"Otključavanje uređaja"</string>
+ <string name="alternative_unlock_setup_notification_title" msgid="6241508547901933544">"Pokušajte s drugim načinom otključavanja"</string>
+ <string name="alternative_face_setup_notification_content" msgid="3384959224091897331">"Upotrijebite otključavanje licem ako se ne prepozna otisak prsta, primjerice ako su prsti mokri"</string>
+ <string name="alternative_fp_setup_notification_content" msgid="7454096947415721639">"Upotrijebite otključavanje otiskom prsta ako se ne prepozna lice, primjerice ako nema dovoljno svjetla"</string>
<string name="face_recalibrate_notification_name" msgid="7311163114750748686">"Otključavanje licem"</string>
<string name="face_recalibrate_notification_title" msgid="2524791952735579082">"Poteškoće s otključavanjem licem"</string>
<string name="face_recalibrate_notification_content" msgid="3064513770251355594">"Dodirnite da biste izbrisali model lica, a zatim ponovo dodajte svoje lice"</string>
- <string name="face_setup_notification_title" msgid="8843461561970741790">"Postavite otključavanje licem"</string>
- <string name="face_setup_notification_content" msgid="5463999831057751676">"Otključajte telefon gledajući u njega"</string>
<string name="face_sensor_privacy_enabled" msgid="7407126963510598508">"Da biste koristili otključavanje licem, uključite opciju "<b>"Pristup kameri"</b>" u odjeljku Postavke > Privatnost"</string>
- <string name="fingerprint_setup_notification_title" msgid="2002630611398849495">"Postavite više načina otključavanja"</string>
- <string name="fingerprint_setup_notification_content" msgid="205578121848324852">"Dodirnite da biste dodali otisak prsta"</string>
<string name="fingerprint_recalibrate_notification_name" msgid="1414578431898579354">"Otključavanje otiskom prsta"</string>
<string name="fingerprint_recalibrate_notification_title" msgid="2406561052064558497">"Senzor otiska prsta ne može se koristiti"</string>
<string name="fingerprint_recalibrate_notification_content" msgid="8519935717822194943">"Posjetite davatelja usluga popravaka."</string>
@@ -2032,7 +2032,8 @@
<string name="autofill_update_title" msgid="3630695947047069136">"Želite li ažurirati u oznaku "<b>"<xliff:g id="LABEL">%1$s</xliff:g>"</b>"?"</string>
<string name="autofill_update_title_with_type" msgid="5264152633488495704">"Želite li ažurirati podatke <xliff:g id="TYPE">%1$s</xliff:g> u oznaci "<b>"<xliff:g id="LABEL">%2$s</xliff:g>"</b>"?"</string>
<string name="autofill_update_title_with_2types" msgid="1797514386321086273">"Želite li ažurirati podatke <xliff:g id="TYPE_0">%1$s</xliff:g> i <xliff:g id="TYPE_1">%2$s</xliff:g> u oznaci "<b>"<xliff:g id="LABEL">%3$s</xliff:g>"</b>"?"</string>
- <string name="autofill_update_title_with_3types" msgid="1312232153076212291">"Želite li ažurirati ove stavke u oznaci "<b>"<xliff:g id="LABEL">%4$s</xliff:g>"</b>": <xliff:g id="TYPE_0">%1$s</xliff:g>, <xliff:g id="TYPE_1">%2$s</xliff:g> i <xliff:g id="TYPE_2">%3$s</xliff:g> ?"</string>
+ <!-- no translation found for autofill_update_title_with_3types (8285767070604652626) -->
+ <skip />
<string name="autofill_save_yes" msgid="8035743017382012850">"Spremi"</string>
<string name="autofill_save_no" msgid="9212826374207023544">"Ne, hvala"</string>
<string name="autofill_save_notnow" msgid="2853932672029024195">"Ne sad"</string>
diff --git a/core/res/res/values-hu/strings.xml b/core/res/res/values-hu/strings.xml
index 75e1ee2..8c8b38f 100644
--- a/core/res/res/values-hu/strings.xml
+++ b/core/res/res/values-hu/strings.xml
@@ -669,14 +669,14 @@
</string-array>
<string name="fingerprint_error_vendor_unknown" msgid="4170002184907291065">"Hiba történt. Próbálja újra."</string>
<string name="fingerprint_icon_content_description" msgid="4741068463175388817">"Ujjlenyomat ikon"</string>
+ <string name="device_unlock_notification_name" msgid="2632928999862915709">"Eszköz feloldása"</string>
+ <string name="alternative_unlock_setup_notification_title" msgid="6241508547901933544">"Próbálkozzon a feloldás más módjával"</string>
+ <string name="alternative_face_setup_notification_content" msgid="3384959224091897331">"Használja az Arcalapú feloldás funkciót, ha a rendszer nem ismeri fel az ujjlenyomatot, például amikor ujjai nedvesek"</string>
+ <string name="alternative_fp_setup_notification_content" msgid="7454096947415721639">"Használja a Feloldás ujjlenyomattal funkciót, ha a rendszer nem ismeri fel az arcát, például amikor nincs elég fény"</string>
<string name="face_recalibrate_notification_name" msgid="7311163114750748686">"Arcalapú feloldás"</string>
<string name="face_recalibrate_notification_title" msgid="2524791952735579082">"Arcalapú feloldással kapcsolatos problémák"</string>
<string name="face_recalibrate_notification_content" msgid="3064513770251355594">"Koppintson arcmodellje törléséhez, majd készítsen újat"</string>
- <string name="face_setup_notification_title" msgid="8843461561970741790">"Az Arcalapú feloldás beállítása"</string>
- <string name="face_setup_notification_content" msgid="5463999831057751676">"Feloldhatja a zárolást úgy, hogy ránéz a telefonra"</string>
<string name="face_sensor_privacy_enabled" msgid="7407126963510598508">"Az Arcalapú feloldás funkció használatához kapcsolja be a "<b>"Hozzáférés a kamerához"</b>" beállítást a Beállítások > Adatvédelem szakaszban."</string>
- <string name="fingerprint_setup_notification_title" msgid="2002630611398849495">"További feloldási módszerek beállítása"</string>
- <string name="fingerprint_setup_notification_content" msgid="205578121848324852">"Koppintson ide ujjlenyomat hozzáadásához"</string>
<string name="fingerprint_recalibrate_notification_name" msgid="1414578431898579354">"Feloldás ujjlenyomattal"</string>
<string name="fingerprint_recalibrate_notification_title" msgid="2406561052064558497">"Nem lehet használni az ujjlenyomat-érzékelőt"</string>
<string name="fingerprint_recalibrate_notification_content" msgid="8519935717822194943">"Keresse fel a szervizt."</string>
@@ -2031,7 +2031,8 @@
<string name="autofill_update_title" msgid="3630695947047069136">"Frissíti a(z) "<b>"<xliff:g id="LABEL">%1$s</xliff:g>"</b>" szolgáltatásban?"</string>
<string name="autofill_update_title_with_type" msgid="5264152633488495704">"Frissíti a(z) "<b>"<xliff:g id="LABEL">%2$s</xliff:g>"</b>" szolgáltatásban a következőt: <xliff:g id="TYPE">%1$s</xliff:g>?"</string>
<string name="autofill_update_title_with_2types" msgid="1797514386321086273">"Frissíti a(z) "<b>"<xliff:g id="LABEL">%3$s</xliff:g>"</b>" szolgáltatásban a következőket: <xliff:g id="TYPE_0">%1$s</xliff:g> és <xliff:g id="TYPE_1">%2$s</xliff:g>?"</string>
- <string name="autofill_update_title_with_3types" msgid="1312232153076212291">"Frissíti a(z) "<b>"<xliff:g id="LABEL">%4$s</xliff:g>"</b>" szolgáltatásban a következőket: <xliff:g id="TYPE_0">%1$s</xliff:g>, <xliff:g id="TYPE_1">%2$s</xliff:g> és <xliff:g id="TYPE_2">%3$s</xliff:g>?"</string>
+ <!-- no translation found for autofill_update_title_with_3types (8285767070604652626) -->
+ <skip />
<string name="autofill_save_yes" msgid="8035743017382012850">"Mentés"</string>
<string name="autofill_save_no" msgid="9212826374207023544">"Nem, köszönöm"</string>
<string name="autofill_save_notnow" msgid="2853932672029024195">"Ne most"</string>
diff --git a/core/res/res/values-hy/strings.xml b/core/res/res/values-hy/strings.xml
index b2e1230..e240c31 100644
--- a/core/res/res/values-hy/strings.xml
+++ b/core/res/res/values-hy/strings.xml
@@ -669,14 +669,14 @@
</string-array>
<string name="fingerprint_error_vendor_unknown" msgid="4170002184907291065">"Սխալ առաջացավ։ Նորից փորձեք։"</string>
<string name="fingerprint_icon_content_description" msgid="4741068463175388817">"Մատնահետքի պատկերակ"</string>
+ <string name="device_unlock_notification_name" msgid="2632928999862915709">"Սարքի ապակողպում"</string>
+ <string name="alternative_unlock_setup_notification_title" msgid="6241508547901933544">"Ընտրեք ապակողպման այլ եղանակ"</string>
+ <string name="alternative_face_setup_notification_content" msgid="3384959224091897331">"Օգտագործեք դեմքով ապակողպումը, երբ ձեր մատնահետքը չի հաջողվում ճանաչել, օրինակ՝ երբ ձեր ձեռքերը թաց են"</string>
+ <string name="alternative_fp_setup_notification_content" msgid="7454096947415721639">"Օգտագործեք մատնահետքով ապակողպումը, երբ ձեր դեմքը չի հաջողվում ճանաչել, օրինակ՝ երբ լուսավորությունը թույլ է"</string>
<string name="face_recalibrate_notification_name" msgid="7311163114750748686">"Դեմքով ապակողպում"</string>
<string name="face_recalibrate_notification_title" msgid="2524791952735579082">"Դեմքով ապակողպման հետ կապված խնդիր"</string>
<string name="face_recalibrate_notification_content" msgid="3064513770251355594">"Հպեք՝ ձեր դեմքի նմուշը ջնջելու համար, այնուհետև նորից ավելացրեք այն:"</string>
- <string name="face_setup_notification_title" msgid="8843461561970741790">"Կարգավորեք դեմքով ապակողպումը"</string>
- <string name="face_setup_notification_content" msgid="5463999831057751676">"Ապակողպելու համար պարզապես նայեք հեռախոսին"</string>
<string name="face_sensor_privacy_enabled" msgid="7407126963510598508">"Դեմքով ապակողպումն օգտագործելու համար անցեք Կարգավորումներ > Գաղտնիություն և տրամադրեք "<b>"տեսախցիկն օգտագործելու թույլտվություն"</b>"։"</string>
- <string name="fingerprint_setup_notification_title" msgid="2002630611398849495">"Կարգավորեք ապակողպելու այլ եղանակներ"</string>
- <string name="fingerprint_setup_notification_content" msgid="205578121848324852">"Հպեք՝ մատնահետք ավելացնելու համար"</string>
<string name="fingerprint_recalibrate_notification_name" msgid="1414578431898579354">"Մատնահետքով ապակողպում"</string>
<string name="fingerprint_recalibrate_notification_title" msgid="2406561052064558497">"Մատնահետքերի սկաները հնարավոր չէ օգտագործել"</string>
<string name="fingerprint_recalibrate_notification_content" msgid="8519935717822194943">"Այցելեք սպասարկման կենտրոն։"</string>
@@ -2031,7 +2031,8 @@
<string name="autofill_update_title" msgid="3630695947047069136">"Թարմացնե՞լ "<b>"<xliff:g id="LABEL">%1$s</xliff:g>"</b>" ծառայությունում"</string>
<string name="autofill_update_title_with_type" msgid="5264152633488495704">"Թարմացնե՞լ տվյալները (<xliff:g id="TYPE">%1$s</xliff:g>) "<b>"<xliff:g id="LABEL">%2$s</xliff:g>"</b>" ծառայությունում"</string>
<string name="autofill_update_title_with_2types" msgid="1797514386321086273">"Թարմացնե՞լ տվյալները (<xliff:g id="TYPE_0">%1$s</xliff:g>, <xliff:g id="TYPE_1">%2$s</xliff:g>) "<b>"<xliff:g id="LABEL">%3$s</xliff:g>"</b>" ծառայությունում"</string>
- <string name="autofill_update_title_with_3types" msgid="1312232153076212291">"Թարմացնե՞լ տվյալները (<xliff:g id="TYPE_0">%1$s</xliff:g>, <xliff:g id="TYPE_1">%2$s</xliff:g>, <xliff:g id="TYPE_2">%3$s</xliff:g>) "<b>"<xliff:g id="LABEL">%4$s</xliff:g>"</b>" ծառայությունում"</string>
+ <!-- no translation found for autofill_update_title_with_3types (8285767070604652626) -->
+ <skip />
<string name="autofill_save_yes" msgid="8035743017382012850">"Պահել"</string>
<string name="autofill_save_no" msgid="9212826374207023544">"Ոչ"</string>
<string name="autofill_save_notnow" msgid="2853932672029024195">"Ոչ հիմա"</string>
diff --git a/core/res/res/values-in/strings.xml b/core/res/res/values-in/strings.xml
index f0c8b10..5a9188d 100644
--- a/core/res/res/values-in/strings.xml
+++ b/core/res/res/values-in/strings.xml
@@ -669,14 +669,14 @@
</string-array>
<string name="fingerprint_error_vendor_unknown" msgid="4170002184907291065">"Terjadi error. Coba lagi."</string>
<string name="fingerprint_icon_content_description" msgid="4741068463175388817">"Ikon sidik jari"</string>
+ <string name="device_unlock_notification_name" msgid="2632928999862915709">"Buka kunci perangkat"</string>
+ <string name="alternative_unlock_setup_notification_title" msgid="6241508547901933544">"Coba cara lain untuk membuka kunci"</string>
+ <string name="alternative_face_setup_notification_content" msgid="3384959224091897331">"Gunakan Buka dengan Wajah saat sidik jari Anda tidak dikenali, seperti saat jari Anda basah"</string>
+ <string name="alternative_fp_setup_notification_content" msgid="7454096947415721639">"Gunakan Buka dengan Sidik Jari saat wajah Anda tidak dikenali, seperti saat pencahayaan tidak cukup"</string>
<string name="face_recalibrate_notification_name" msgid="7311163114750748686">"Buka dengan Wajah"</string>
<string name="face_recalibrate_notification_title" msgid="2524791952735579082">"Masalah pada Buka dengan Wajah"</string>
<string name="face_recalibrate_notification_content" msgid="3064513770251355594">"Ketuk untuk menghapus model wajah, lalu tambahkan wajah Anda lagi"</string>
- <string name="face_setup_notification_title" msgid="8843461561970741790">"Siapkan Buka dengan Wajah"</string>
- <string name="face_setup_notification_content" msgid="5463999831057751676">"Buka kunci ponsel dengan melihat ke ponsel"</string>
<string name="face_sensor_privacy_enabled" msgid="7407126963510598508">"Untuk menggunakan Buka dengan Wajah, aktifkan "<b>"Akses kamera"</b>" di Setelan > Privasi"</string>
- <string name="fingerprint_setup_notification_title" msgid="2002630611398849495">"Siapkan lebih banyak cara untuk membuka kunci"</string>
- <string name="fingerprint_setup_notification_content" msgid="205578121848324852">"Ketuk untuk menambahkan sidik jari"</string>
<string name="fingerprint_recalibrate_notification_name" msgid="1414578431898579354">"Buka dengan Sidik Jari"</string>
<string name="fingerprint_recalibrate_notification_title" msgid="2406561052064558497">"Tidak dapat menggunakan sensor sidik jari"</string>
<string name="fingerprint_recalibrate_notification_content" msgid="8519935717822194943">"Kunjungi penyedia reparasi."</string>
@@ -1596,7 +1596,7 @@
<string name="data_usage_restricted_body" msgid="5338694433686077733">"Ketuk untuk menghapus batasan."</string>
<string name="data_usage_rapid_title" msgid="2950192123248740375">"Penggunaan data seluler tinggi"</string>
<string name="data_usage_rapid_body" msgid="3886676853263693432">"Aplikasi Anda menggunakan lebih banyak kuota daripada biasanya"</string>
- <string name="data_usage_rapid_app_body" msgid="5425779218506513861">"<xliff:g id="APP">%s</xliff:g> menggunakan lebih banyak kuota daripada biasanya"</string>
+ <string name="data_usage_rapid_app_body" msgid="5425779218506513861">"<xliff:g id="APP">%s</xliff:g> menggunakan lebih banyak data daripada biasanya"</string>
<string name="ssl_certificate" msgid="5690020361307261997">"Sertifikat keamanan"</string>
<string name="ssl_certificate_is_valid" msgid="7293675884598527081">"Sertifikat ini valid."</string>
<string name="issued_to" msgid="5975877665505297662">"Diterbitkan ke:"</string>
@@ -2031,7 +2031,8 @@
<string name="autofill_update_title" msgid="3630695947047069136">"Perbarui di "<b>"<xliff:g id="LABEL">%1$s</xliff:g>"</b>"?"</string>
<string name="autofill_update_title_with_type" msgid="5264152633488495704">"Perbarui <xliff:g id="TYPE">%1$s</xliff:g> di "<b>"<xliff:g id="LABEL">%2$s</xliff:g>"</b>"?"</string>
<string name="autofill_update_title_with_2types" msgid="1797514386321086273">"Perbarui <xliff:g id="TYPE_0">%1$s</xliff:g> dan <xliff:g id="TYPE_1">%2$s</xliff:g> di "<b>"<xliff:g id="LABEL">%3$s</xliff:g>"</b>"?"</string>
- <string name="autofill_update_title_with_3types" msgid="1312232153076212291">"Perbarui item-item berikut di "<b>"<xliff:g id="LABEL">%4$s</xliff:g>"</b>": <xliff:g id="TYPE_0">%1$s</xliff:g>, <xliff:g id="TYPE_1">%2$s</xliff:g>, dan <xliff:g id="TYPE_2">%3$s</xliff:g> ?"</string>
+ <!-- no translation found for autofill_update_title_with_3types (8285767070604652626) -->
+ <skip />
<string name="autofill_save_yes" msgid="8035743017382012850">"Simpan"</string>
<string name="autofill_save_no" msgid="9212826374207023544">"Lain kali"</string>
<string name="autofill_save_notnow" msgid="2853932672029024195">"Lain kali"</string>
diff --git a/core/res/res/values-is/strings.xml b/core/res/res/values-is/strings.xml
index 7584d1c..8ea6f6a 100644
--- a/core/res/res/values-is/strings.xml
+++ b/core/res/res/values-is/strings.xml
@@ -669,14 +669,14 @@
</string-array>
<string name="fingerprint_error_vendor_unknown" msgid="4170002184907291065">"Eitthvað fór úrskeiðis. Reyndu aftur."</string>
<string name="fingerprint_icon_content_description" msgid="4741068463175388817">"Fingrafaratákn"</string>
+ <string name="device_unlock_notification_name" msgid="2632928999862915709">"Taka tæki úr lás"</string>
+ <string name="alternative_unlock_setup_notification_title" msgid="6241508547901933544">"Prófaðu aðra leið til að opna"</string>
+ <string name="alternative_face_setup_notification_content" msgid="3384959224091897331">"Notaðu andlitskenni þegar ekki er hægt að greina fingrafarið, t.d. þegar fingurnir eru blautir"</string>
+ <string name="alternative_fp_setup_notification_content" msgid="7454096947415721639">"Notaðu fingrafarskenni þegar ekki er hægt að greina andlitið þitt, t.d. í lítilli birtu"</string>
<string name="face_recalibrate_notification_name" msgid="7311163114750748686">"Andlitskenni"</string>
<string name="face_recalibrate_notification_title" msgid="2524791952735579082">"Vandamál varðandi andlitskenni"</string>
<string name="face_recalibrate_notification_content" msgid="3064513770251355594">"Ýttu til að eyða andlitslíkaninu og skráðu svo andlitið aftur"</string>
- <string name="face_setup_notification_title" msgid="8843461561970741790">"Setja upp andlitskenni"</string>
- <string name="face_setup_notification_content" msgid="5463999831057751676">"Taktu símann úr lás með því að horfa á hann"</string>
<string name="face_sensor_privacy_enabled" msgid="7407126963510598508">"Þú verður að kveikja á "<b>"aðgangi að myndavél"</b>" í „Stillingar > persónuvernd“ til að nota andlitskenni"</string>
- <string name="fingerprint_setup_notification_title" msgid="2002630611398849495">"Settu upp fleiri leiðir til að taka úr lás"</string>
- <string name="fingerprint_setup_notification_content" msgid="205578121848324852">"Ýttu til að bæta við fingrafari"</string>
<string name="fingerprint_recalibrate_notification_name" msgid="1414578431898579354">"Fingrafarskenni"</string>
<string name="fingerprint_recalibrate_notification_title" msgid="2406561052064558497">"Ekki er hægt að nota fingrafaralesara"</string>
<string name="fingerprint_recalibrate_notification_content" msgid="8519935717822194943">"Þú verður að fara á verkstæði."</string>
@@ -2031,7 +2031,8 @@
<string name="autofill_update_title" msgid="3630695947047069136">"Uppfæra í "<b>"<xliff:g id="LABEL">%1$s</xliff:g>"</b>"?"</string>
<string name="autofill_update_title_with_type" msgid="5264152633488495704">"Uppfæra <xliff:g id="TYPE">%1$s</xliff:g> í "<b>"<xliff:g id="LABEL">%2$s</xliff:g>"</b>"?"</string>
<string name="autofill_update_title_with_2types" msgid="1797514386321086273">"Uppfæra <xliff:g id="TYPE_0">%1$s</xliff:g> og <xliff:g id="TYPE_1">%2$s</xliff:g> í "<b>"<xliff:g id="LABEL">%3$s</xliff:g>"</b>"?"</string>
- <string name="autofill_update_title_with_3types" msgid="1312232153076212291">"Uppfæra þessi atriði í "<b>"<xliff:g id="LABEL">%4$s</xliff:g>"</b>": <xliff:g id="TYPE_0">%1$s</xliff:g>, <xliff:g id="TYPE_1">%2$s</xliff:g> og <xliff:g id="TYPE_2">%3$s</xliff:g>?"</string>
+ <!-- no translation found for autofill_update_title_with_3types (8285767070604652626) -->
+ <skip />
<string name="autofill_save_yes" msgid="8035743017382012850">"Vista"</string>
<string name="autofill_save_no" msgid="9212826374207023544">"Nei, takk"</string>
<string name="autofill_save_notnow" msgid="2853932672029024195">"Ekki núna"</string>
diff --git a/core/res/res/values-it/strings.xml b/core/res/res/values-it/strings.xml
index ac0c7df..eb0b569 100644
--- a/core/res/res/values-it/strings.xml
+++ b/core/res/res/values-it/strings.xml
@@ -310,11 +310,11 @@
<string name="permgrouplab_sms" msgid="795737735126084874">"SMS"</string>
<string name="permgroupdesc_sms" msgid="5726462398070064542">"Possono inviare e visualizzare SMS"</string>
<string name="permgrouplab_storage" msgid="17339216290379241">"File"</string>
- <string name="permgroupdesc_storage" msgid="5378659041354582769">"Consente di accedere ai file sul tuo dispositivo"</string>
+ <string name="permgroupdesc_storage" msgid="5378659041354582769">"accedere ai file sul tuo dispositivo"</string>
<string name="permgrouplab_readMediaAural" msgid="1858331312624942053">"Musica e audio"</string>
- <string name="permgroupdesc_readMediaAural" msgid="7565467343667089595">"accesso a musica e audio sul tuo dispositivo"</string>
+ <string name="permgroupdesc_readMediaAural" msgid="7565467343667089595">"accedere a musica e audio sul tuo dispositivo"</string>
<string name="permgrouplab_readMediaVisual" msgid="4724874717811908660">"Foto e video"</string>
- <string name="permgroupdesc_readMediaVisual" msgid="4080463241903508688">"accesso a foto e video sul tuo dispositivo"</string>
+ <string name="permgroupdesc_readMediaVisual" msgid="4080463241903508688">"accedere a foto e video sul tuo dispositivo"</string>
<string name="permgrouplab_microphone" msgid="2480597427667420076">"Microfono"</string>
<string name="permgroupdesc_microphone" msgid="1047786732792487722">"Possono registrare audio"</string>
<string name="permgrouplab_activityRecognition" msgid="3324466667921775766">"Attività fisica"</string>
@@ -322,7 +322,7 @@
<string name="permgrouplab_camera" msgid="9090413408963547706">"Fotocamera"</string>
<string name="permgroupdesc_camera" msgid="7585150538459320326">"Possono scattare foto e registrare video"</string>
<string name="permgrouplab_nearby_devices" msgid="5529147543651181991">"Dispositivi nelle vicinanze"</string>
- <string name="permgroupdesc_nearby_devices" msgid="3213561597116913508">"Consente di rilevare dispositivi nelle vicinanze e di connettersi a tali dispositivi"</string>
+ <string name="permgroupdesc_nearby_devices" msgid="3213561597116913508">"rilevare e connettersi a dispositivi nelle vicinanze"</string>
<string name="permgrouplab_calllog" msgid="7926834372073550288">"Registri chiamate"</string>
<string name="permgroupdesc_calllog" msgid="2026996642917801803">"Possono leggere e modificare il registro chiamate del telefono"</string>
<string name="permgrouplab_phone" msgid="570318944091926620">"Telefono"</string>
@@ -670,14 +670,14 @@
</string-array>
<string name="fingerprint_error_vendor_unknown" msgid="4170002184907291065">"Si è verificato un errore. Riprova."</string>
<string name="fingerprint_icon_content_description" msgid="4741068463175388817">"Icona dell\'impronta"</string>
+ <string name="device_unlock_notification_name" msgid="2632928999862915709">"Sblocco dispositivo"</string>
+ <string name="alternative_unlock_setup_notification_title" msgid="6241508547901933544">"Prova un\'altra modalità di sblocco"</string>
+ <string name="alternative_face_setup_notification_content" msgid="3384959224091897331">"Usa lo sblocco con il volto se la tua impronta non viene riconosciuta, ad esempio quando hai le dita bagnate"</string>
+ <string name="alternative_fp_setup_notification_content" msgid="7454096947415721639">"Usa lo sblocco con l\'impronta se il tuo volto non viene riconosciuto, ad esempio quando non c\'è abbastanza luce"</string>
<string name="face_recalibrate_notification_name" msgid="7311163114750748686">"Sblocco con il volto"</string>
<string name="face_recalibrate_notification_title" msgid="2524791952735579082">"Problema con Sblocco con il volto"</string>
<string name="face_recalibrate_notification_content" msgid="3064513770251355594">"Tocca per eliminare il tuo modello del volto e poi riaggiungi il tuo volto"</string>
- <string name="face_setup_notification_title" msgid="8843461561970741790">"Configura lo sblocco con il volto"</string>
- <string name="face_setup_notification_content" msgid="5463999831057751676">"Sblocca il telefono guardandolo"</string>
<string name="face_sensor_privacy_enabled" msgid="7407126963510598508">"Per utilizzare lo sblocco con il volto, attiva "<b>"l\'accesso alla fotocamera"</b>" in Impostazioni > Privacy"</string>
- <string name="fingerprint_setup_notification_title" msgid="2002630611398849495">"Configura altri modi per sbloccare"</string>
- <string name="fingerprint_setup_notification_content" msgid="205578121848324852">"Tocca per aggiungere un\'impronta"</string>
<string name="fingerprint_recalibrate_notification_name" msgid="1414578431898579354">"Sblocco con l\'impronta"</string>
<string name="fingerprint_recalibrate_notification_title" msgid="2406561052064558497">"Impossibile utilizzare il sensore di impronte digitali"</string>
<string name="fingerprint_recalibrate_notification_content" msgid="8519935717822194943">"Contatta un fornitore di servizi di riparazione."</string>
@@ -1963,7 +1963,7 @@
<string name="app_streaming_blocked_title" msgid="6090945835898766139">"<xliff:g id="ACTIVITY">%1$s</xliff:g> non disponibile"</string>
<string name="app_streaming_blocked_title_for_permission_dialog" msgid="3805704317624448487">"Richiesta di autorizzazione rifiutata"</string>
<string name="app_streaming_blocked_title_for_camera_dialog" msgid="3935701653713853065">"Fotocamera non disponibile"</string>
- <string name="app_streaming_blocked_title_for_fingerprint_dialog" msgid="3516853717714141951">"Continua sul telefono"</string>
+ <string name="app_streaming_blocked_title_for_fingerprint_dialog" msgid="3516853717714141951">"Continua sullo smartphone"</string>
<string name="app_streaming_blocked_title_for_microphone_dialog" msgid="544822455127171206">"Microfono non disponibile"</string>
<string name="app_streaming_blocked_title_for_playstore_dialog" msgid="8149823099822897538">"Play Store non disponibile"</string>
<string name="app_streaming_blocked_title_for_settings_dialog" product="tv" msgid="196994247017450357">"Impostazioni di Android TV non disponibili"</string>
@@ -2032,7 +2032,8 @@
<string name="autofill_update_title" msgid="3630695947047069136">"Vuoi aggiornare su "<b>"<xliff:g id="LABEL">%1$s</xliff:g>"</b>"?"</string>
<string name="autofill_update_title_with_type" msgid="5264152633488495704">"Vuoi aggiornare <xliff:g id="TYPE">%1$s</xliff:g> su "<b>"<xliff:g id="LABEL">%2$s</xliff:g>"</b>"?"</string>
<string name="autofill_update_title_with_2types" msgid="1797514386321086273">"Vuoi aggiornare <xliff:g id="TYPE_0">%1$s</xliff:g> e <xliff:g id="TYPE_1">%2$s</xliff:g> su "<b>"<xliff:g id="LABEL">%3$s</xliff:g>"</b>"?"</string>
- <string name="autofill_update_title_with_3types" msgid="1312232153076212291">"Vuoi aggiornare questi elementi su "<b>"<xliff:g id="LABEL">%4$s</xliff:g>"</b>": <xliff:g id="TYPE_0">%1$s</xliff:g>, <xliff:g id="TYPE_1">%2$s</xliff:g> e <xliff:g id="TYPE_2">%3$s</xliff:g>?"</string>
+ <!-- no translation found for autofill_update_title_with_3types (8285767070604652626) -->
+ <skip />
<string name="autofill_save_yes" msgid="8035743017382012850">"Salva"</string>
<string name="autofill_save_no" msgid="9212826374207023544">"No, grazie"</string>
<string name="autofill_save_notnow" msgid="2853932672029024195">"Non ora"</string>
diff --git a/core/res/res/values-iw/strings.xml b/core/res/res/values-iw/strings.xml
index a17e989..a3ffd39 100644
--- a/core/res/res/values-iw/strings.xml
+++ b/core/res/res/values-iw/strings.xml
@@ -670,14 +670,14 @@
</string-array>
<string name="fingerprint_error_vendor_unknown" msgid="4170002184907291065">"משהו השתבש. עליך לנסות שוב."</string>
<string name="fingerprint_icon_content_description" msgid="4741068463175388817">"סמל טביעת אצבע"</string>
+ <string name="device_unlock_notification_name" msgid="2632928999862915709">"ביטול הנעילה של המכשיר"</string>
+ <string name="alternative_unlock_setup_notification_title" msgid="6241508547901933544">"כדאי לנסות דרך אחרת לביטול הנעילה"</string>
+ <string name="alternative_face_setup_notification_content" msgid="3384959224091897331">"אפשר להשתמש בפתיחה ע\"י זיהוי הפנים כשטביעת האצבע שלך לא מזוהה, למשל כשהאצבעות שלך רטובות"</string>
+ <string name="alternative_fp_setup_notification_content" msgid="7454096947415721639">"אפשר להשתמש בביטול הנעילה בטביעת אצבע כשהפנים שלך לא מזוהות, למשל כשאין מספיק אור"</string>
<string name="face_recalibrate_notification_name" msgid="7311163114750748686">"פתיחה ע\"י זיהוי הפנים"</string>
<string name="face_recalibrate_notification_title" msgid="2524791952735579082">"בעיה בפתיחה ע\"י זיהוי הפנים"</string>
<string name="face_recalibrate_notification_content" msgid="3064513770251355594">"יש להקיש כדי למחוק את התבנית לזיהוי הפנים, ואז להוסיף תבנית חדשה לזיהוי הפנים"</string>
- <string name="face_setup_notification_title" msgid="8843461561970741790">"הגדרת התכונה \'פתיחה ע\"י זיהוי הפנים\'"</string>
- <string name="face_setup_notification_content" msgid="5463999831057751676">"יש להביט בטלפון כדי לבטל את נעילתו"</string>
<string name="face_sensor_privacy_enabled" msgid="7407126963510598508">"כדי להשתמש בתכונה \'פתיחה ע\"י זיהוי הפנים\', יש להפעיל את ה"<b>"גישה למצלמה"</b>" בהגדרות > פרטיות"</string>
- <string name="fingerprint_setup_notification_title" msgid="2002630611398849495">"אפשר להגדיר דרכים נוספות לביטול נעילה"</string>
- <string name="fingerprint_setup_notification_content" msgid="205578121848324852">"יש להקיש כדי להוסיף טביעת אצבע"</string>
<string name="fingerprint_recalibrate_notification_name" msgid="1414578431898579354">"ביטול הנעילה בטביעת אצבע"</string>
<string name="fingerprint_recalibrate_notification_title" msgid="2406561052064558497">"לא ניתן להשתמש בחיישן טביעות האצבע"</string>
<string name="fingerprint_recalibrate_notification_content" msgid="8519935717822194943">"צריך ליצור קשר עם ספק תיקונים."</string>
@@ -2032,7 +2032,8 @@
<string name="autofill_update_title" msgid="3630695947047069136">"לעדכן בשירות "<b>"<xliff:g id="LABEL">%1$s</xliff:g>"</b>"?"</string>
<string name="autofill_update_title_with_type" msgid="5264152633488495704">"לעדכן <xliff:g id="TYPE">%1$s</xliff:g> בשירות "<b>"<xliff:g id="LABEL">%2$s</xliff:g>"</b>"?"</string>
<string name="autofill_update_title_with_2types" msgid="1797514386321086273">"האם לעדכן את <xliff:g id="TYPE_0">%1$s</xliff:g> ואת <xliff:g id="TYPE_1">%2$s</xliff:g> ב-"<b>"<xliff:g id="LABEL">%3$s</xliff:g>"</b>"?"</string>
- <string name="autofill_update_title_with_3types" msgid="1312232153076212291">"לעדכן את הפריטים אלה ב-"<b>"<xliff:g id="LABEL">%4$s</xliff:g>"</b>": <xliff:g id="TYPE_0">%1$s</xliff:g>, <xliff:g id="TYPE_1">%2$s</xliff:g> ו<xliff:g id="TYPE_2">%3$s</xliff:g> ?"</string>
+ <!-- no translation found for autofill_update_title_with_3types (8285767070604652626) -->
+ <skip />
<string name="autofill_save_yes" msgid="8035743017382012850">"שמירה"</string>
<string name="autofill_save_no" msgid="9212826374207023544">"לא, תודה"</string>
<string name="autofill_save_notnow" msgid="2853932672029024195">"לא עכשיו"</string>
diff --git a/core/res/res/values-ja/strings.xml b/core/res/res/values-ja/strings.xml
index 3065202..aa4ac4e 100644
--- a/core/res/res/values-ja/strings.xml
+++ b/core/res/res/values-ja/strings.xml
@@ -669,14 +669,14 @@
</string-array>
<string name="fingerprint_error_vendor_unknown" msgid="4170002184907291065">"エラーが発生しました。もう一度お試しください。"</string>
<string name="fingerprint_icon_content_description" msgid="4741068463175388817">"指紋アイコン"</string>
+ <string name="device_unlock_notification_name" msgid="2632928999862915709">"デバイスのロック解除"</string>
+ <string name="alternative_unlock_setup_notification_title" msgid="6241508547901933544">"別のロック解除方法を試す"</string>
+ <string name="alternative_face_setup_notification_content" msgid="3384959224091897331">"指が濡れている場合など、指紋が認識されないときに顔認証を使用できます"</string>
+ <string name="alternative_fp_setup_notification_content" msgid="7454096947415721639">"暗い場所など、顔が認識されないときに指紋認証を使用できます"</string>
<string name="face_recalibrate_notification_name" msgid="7311163114750748686">"顔認証"</string>
<string name="face_recalibrate_notification_title" msgid="2524791952735579082">"顔認証に関する問題"</string>
<string name="face_recalibrate_notification_content" msgid="3064513770251355594">"タップして顔モデルを削除してから、改めて顔を追加してください"</string>
- <string name="face_setup_notification_title" msgid="8843461561970741790">"顔認証の設定"</string>
- <string name="face_setup_notification_content" msgid="5463999831057751676">"スマートフォンに顔を向けるとロックが解除されます"</string>
<string name="face_sensor_privacy_enabled" msgid="7407126963510598508">"顔認証を使用するには、[設定] > [プライバシー] で"<b>"カメラへのアクセス"</b>"を有効にしてください"</string>
- <string name="fingerprint_setup_notification_title" msgid="2002630611398849495">"その他のロック解除方法の設定"</string>
- <string name="fingerprint_setup_notification_content" msgid="205578121848324852">"タップすると指紋が追加されます"</string>
<string name="fingerprint_recalibrate_notification_name" msgid="1414578431898579354">"指紋認証"</string>
<string name="fingerprint_recalibrate_notification_title" msgid="2406561052064558497">"指紋認証センサーを使用できません"</string>
<string name="fingerprint_recalibrate_notification_content" msgid="8519935717822194943">"修理業者に調整を依頼してください。"</string>
@@ -1594,7 +1594,7 @@
<string name="data_usage_limit_snoozed_body" msgid="545146591766765678">"設定した上限を <xliff:g id="SIZE">%s</xliff:g> 超えました"</string>
<string name="data_usage_restricted_title" msgid="126711424380051268">"バックグラウンドデータに上限あり"</string>
<string name="data_usage_restricted_body" msgid="5338694433686077733">"タップして制限を解除します。"</string>
- <string name="data_usage_rapid_title" msgid="2950192123248740375">"モバイルデータ使用量の増加"</string>
+ <string name="data_usage_rapid_title" msgid="2950192123248740375">"高いモバイルデータ使用量"</string>
<string name="data_usage_rapid_body" msgid="3886676853263693432">"アプリが通常より多くのデータを使用しています"</string>
<string name="data_usage_rapid_app_body" msgid="5425779218506513861">"「<xliff:g id="APP">%s</xliff:g>」が通常より多くのデータを使用しています"</string>
<string name="ssl_certificate" msgid="5690020361307261997">"セキュリティ証明書"</string>
@@ -2031,7 +2031,8 @@
<string name="autofill_update_title" msgid="3630695947047069136"><b>"<xliff:g id="LABEL">%1$s</xliff:g>"</b>" で更新しますか?"</string>
<string name="autofill_update_title_with_type" msgid="5264152633488495704">"<xliff:g id="TYPE">%1$s</xliff:g>を "<b>"<xliff:g id="LABEL">%2$s</xliff:g>"</b>" で更新しますか?"</string>
<string name="autofill_update_title_with_2types" msgid="1797514386321086273">"<xliff:g id="TYPE_0">%1$s</xliff:g>、<xliff:g id="TYPE_1">%2$s</xliff:g>を "<b>"<xliff:g id="LABEL">%3$s</xliff:g>"</b>" で更新しますか?"</string>
- <string name="autofill_update_title_with_3types" msgid="1312232153076212291">"<xliff:g id="TYPE_0">%1$s</xliff:g>、<xliff:g id="TYPE_1">%2$s</xliff:g>、<xliff:g id="TYPE_2">%3$s</xliff:g>を "<b>"<xliff:g id="LABEL">%4$s</xliff:g>"</b>" で更新しますか?"</string>
+ <!-- no translation found for autofill_update_title_with_3types (8285767070604652626) -->
+ <skip />
<string name="autofill_save_yes" msgid="8035743017382012850">"はい"</string>
<string name="autofill_save_no" msgid="9212826374207023544">"いいえ"</string>
<string name="autofill_save_notnow" msgid="2853932672029024195">"後で"</string>
diff --git a/core/res/res/values-ka/strings.xml b/core/res/res/values-ka/strings.xml
index dd7aff7..9e730df 100644
--- a/core/res/res/values-ka/strings.xml
+++ b/core/res/res/values-ka/strings.xml
@@ -669,14 +669,14 @@
</string-array>
<string name="fingerprint_error_vendor_unknown" msgid="4170002184907291065">"რაღაც შეცდომა მოხდა. ცადეთ ხელახლა."</string>
<string name="fingerprint_icon_content_description" msgid="4741068463175388817">"თითის ანაბეჭდის ხატულა"</string>
+ <string name="device_unlock_notification_name" msgid="2632928999862915709">"მოწყობილობის განბლოკვა"</string>
+ <string name="alternative_unlock_setup_notification_title" msgid="6241508547901933544">"ცადეთ სხვა გზით განბლოკვა"</string>
+ <string name="alternative_face_setup_notification_content" msgid="3384959224091897331">"გამოიყენეთ სახით განბლოკვა, როდესაც თქვენი თითის ანაბეჭდის ამოცნობა ვერ ხდება, მაგალითად, როდესაც თქვენი თითები სველია"</string>
+ <string name="alternative_fp_setup_notification_content" msgid="7454096947415721639">"გამოიყენეთ ანაბეჭდით განბლოკვა, როდესაც თქვენი სახის ამოცნობა ვერ ხდება, მაგალითად, არასაკმარისი განათების დროს"</string>
<string name="face_recalibrate_notification_name" msgid="7311163114750748686">"განბლოკვა სახით"</string>
<string name="face_recalibrate_notification_title" msgid="2524791952735579082">"პრობლემა სახით განბლოკვასთან დაკავშირებით"</string>
<string name="face_recalibrate_notification_content" msgid="3064513770251355594">"შეეხეთ თქვენი სახის მოდელის წასაშლელად, შემდეგ დაამატეთ სახე ხელახლა"</string>
- <string name="face_setup_notification_title" msgid="8843461561970741790">"სახით განბლოკვის პარამეტრების დაყენება"</string>
- <string name="face_setup_notification_content" msgid="5463999831057751676">"განბლოკეთ თქვენი ტელეფონი შეხედვით"</string>
<string name="face_sensor_privacy_enabled" msgid="7407126963510598508">"იმისთვის, რომ სახით განბლოკვით ისარგებლოთ, ჩართეთ "<b>"კამერაზე წვდომა"</b>" პარამეტრებსა და კონფიდენციალურობაში"</string>
- <string name="fingerprint_setup_notification_title" msgid="2002630611398849495">"დააყენეთ განბლოკვის სხვა ხერხები"</string>
- <string name="fingerprint_setup_notification_content" msgid="205578121848324852">"შეეხეთ თითის ანაბეჭდის დასამატებლად"</string>
<string name="fingerprint_recalibrate_notification_name" msgid="1414578431898579354">"თითის ანაბეჭდით განბლოკვა"</string>
<string name="fingerprint_recalibrate_notification_title" msgid="2406561052064558497">"თითის ანაბეჭდის სენსორის გამოყენება ვერ ხერხდება"</string>
<string name="fingerprint_recalibrate_notification_content" msgid="8519935717822194943">"ეწვიეთ შეკეთების სერვისის პროვაიდერს."</string>
@@ -2031,7 +2031,8 @@
<string name="autofill_update_title" msgid="3630695947047069136">"გსურთ "<b>"<xliff:g id="LABEL">%1$s</xliff:g>"</b>"-ში განახლება?"</string>
<string name="autofill_update_title_with_type" msgid="5264152633488495704">"გსურთ, "<b>"<xliff:g id="LABEL">%2$s</xliff:g>"</b>"-ში განაახლოთ <xliff:g id="TYPE">%1$s</xliff:g>?"</string>
<string name="autofill_update_title_with_2types" msgid="1797514386321086273">"გსურთ, "<b>"<xliff:g id="LABEL">%3$s</xliff:g>"</b>"-ში განაახლოთ <xliff:g id="TYPE_0">%1$s</xliff:g> და <xliff:g id="TYPE_1">%2$s</xliff:g>?"</string>
- <string name="autofill_update_title_with_3types" msgid="1312232153076212291">"გსურთ, "<b>"<xliff:g id="LABEL">%4$s</xliff:g>"</b>"-ში განაახლოთ <xliff:g id="TYPE_0">%1$s</xliff:g>, <xliff:g id="TYPE_1">%2$s</xliff:g> და <xliff:g id="TYPE_2">%3$s</xliff:g>?"</string>
+ <!-- no translation found for autofill_update_title_with_3types (8285767070604652626) -->
+ <skip />
<string name="autofill_save_yes" msgid="8035743017382012850">"შენახვა"</string>
<string name="autofill_save_no" msgid="9212826374207023544">"არა, გმადლობთ"</string>
<string name="autofill_save_notnow" msgid="2853932672029024195">"ახლა არა"</string>
diff --git a/core/res/res/values-kk/strings.xml b/core/res/res/values-kk/strings.xml
index 8cfc1c0..1561045 100644
--- a/core/res/res/values-kk/strings.xml
+++ b/core/res/res/values-kk/strings.xml
@@ -669,14 +669,14 @@
</string-array>
<string name="fingerprint_error_vendor_unknown" msgid="4170002184907291065">"Бірдеңе дұрыс болмады. Қайталап көріңіз."</string>
<string name="fingerprint_icon_content_description" msgid="4741068463175388817">"Саусақ ізі белгішесі"</string>
+ <string name="device_unlock_notification_name" msgid="2632928999862915709">"Құрылғының құлпын ашу"</string>
+ <string name="alternative_unlock_setup_notification_title" msgid="6241508547901933544">"Басқа ашу тәсілін қолданып көріңіз"</string>
+ <string name="alternative_face_setup_notification_content" msgid="3384959224091897331">"Саусағыңыздың ізі анықталмаған (мысалы, саусақтарыңыз дымқыл болған) кезде, бет тану функциясын қолданыңыз."</string>
+ <string name="alternative_fp_setup_notification_content" msgid="7454096947415721639">"Бетіңіз анықталмаған (мысалы, жарық әлсіз болған) кезде, cаусақ ізімен ашу функциясын қолданыңыз."</string>
<string name="face_recalibrate_notification_name" msgid="7311163114750748686">"Бет тану"</string>
<string name="face_recalibrate_notification_title" msgid="2524791952735579082">"Бет тану функциясына қатысты мәселе шықты"</string>
<string name="face_recalibrate_notification_content" msgid="3064513770251355594">"Бет үлгісін жою үшін түртіңіз, содан соң жаңа бет үлгісін қосыңыз."</string>
- <string name="face_setup_notification_title" msgid="8843461561970741790">"Бет тану функциясын реттеу"</string>
- <string name="face_setup_notification_content" msgid="5463999831057751676">"Телефоныңызға қарап, оның құлпын ашыңыз."</string>
<string name="face_sensor_privacy_enabled" msgid="7407126963510598508">"Face Unlock функциясын пайдалану үшін \"Параметрлер > Құпиялық\" бөлімінен "<b>"Камераны пайдалану рұқсатын"</b>" қосыңыз."</string>
- <string name="fingerprint_setup_notification_title" msgid="2002630611398849495">"Құлыпты ашудың басқа тәсілдерін реттеу"</string>
- <string name="fingerprint_setup_notification_content" msgid="205578121848324852">"Саусақ ізін қосу үшін түртіңіз."</string>
<string name="fingerprint_recalibrate_notification_name" msgid="1414578431898579354">"Құлыпты саусақ ізімен ашу"</string>
<string name="fingerprint_recalibrate_notification_title" msgid="2406561052064558497">"Саусақ ізін оқу сканерін пайдалану мүмкін емес"</string>
<string name="fingerprint_recalibrate_notification_content" msgid="8519935717822194943">"Жөндеу қызметіне барыңыз."</string>
@@ -2031,7 +2031,8 @@
<string name="autofill_update_title" msgid="3630695947047069136"><b>"<xliff:g id="LABEL">%1$s</xliff:g>"</b>" қызметінде жаңартылсын ба?"</string>
<string name="autofill_update_title_with_type" msgid="5264152633488495704">"<xliff:g id="TYPE">%1$s</xliff:g> деректері "<b>"<xliff:g id="LABEL">%2$s</xliff:g>"</b>" қызметінде жаңартылсын ба?"</string>
<string name="autofill_update_title_with_2types" msgid="1797514386321086273">"<xliff:g id="TYPE_0">%1$s</xliff:g> және <xliff:g id="TYPE_1">%2$s</xliff:g> деректері "<b>"<xliff:g id="LABEL">%3$s</xliff:g>"</b>" қызметінде жаңартылсын ба?"</string>
- <string name="autofill_update_title_with_3types" msgid="1312232153076212291"><b>"<xliff:g id="LABEL">%4$s</xliff:g>"</b>" қызметіндегі <xliff:g id="TYPE_0">%1$s</xliff:g>, <xliff:g id="TYPE_1">%2$s</xliff:g> және <xliff:g id="TYPE_2">%3$s</xliff:g> деректері жаңартылсын ба?"</string>
+ <!-- no translation found for autofill_update_title_with_3types (8285767070604652626) -->
+ <skip />
<string name="autofill_save_yes" msgid="8035743017382012850">"Сақтау"</string>
<string name="autofill_save_no" msgid="9212826374207023544">"Жоқ, рақмет"</string>
<string name="autofill_save_notnow" msgid="2853932672029024195">"Қазір емес"</string>
diff --git a/core/res/res/values-km/strings.xml b/core/res/res/values-km/strings.xml
index db89e91..cea9af4 100644
--- a/core/res/res/values-km/strings.xml
+++ b/core/res/res/values-km/strings.xml
@@ -669,14 +669,14 @@
</string-array>
<string name="fingerprint_error_vendor_unknown" msgid="4170002184907291065">"មានអ្វីមួយខុសប្រក្រតី។ សូមព្យាយាមម្ដងទៀត។"</string>
<string name="fingerprint_icon_content_description" msgid="4741068463175388817">"រូបស្នាមម្រាមដៃ"</string>
+ <string name="device_unlock_notification_name" msgid="2632928999862915709">"ការដោះសោឧបករណ៍"</string>
+ <string name="alternative_unlock_setup_notification_title" msgid="6241508547901933544">"សាកល្បងប្រើវិធីដោះសោផ្សេងទៀត"</string>
+ <string name="alternative_face_setup_notification_content" msgid="3384959224091897331">"ប្រើការដោះសោដោយស្កេនមុខ នៅពេលដែលមិនស្គាល់ស្នាមម្រាមដៃរបស់អ្នក ដូចជានៅពេលដែលម្រាមដៃរបស់អ្នកសើមជាដើម"</string>
+ <string name="alternative_fp_setup_notification_content" msgid="7454096947415721639">"ប្រើការដោះសោដោយស្កេនស្នាមម្រាមដៃ នៅពេលដែលមិនស្គាល់មុខរបស់អ្នក ដូចជានៅពេលដែលមិនមានពន្លឺគ្រប់គ្រាន់ជាដើម"</string>
<string name="face_recalibrate_notification_name" msgid="7311163114750748686">"ការដោះសោដោយស្កេនមុខ"</string>
<string name="face_recalibrate_notification_title" msgid="2524791952735579082">"មានបញ្ហាពាក់ព័ន្ធនឹងមុខងារដោះសោតាមទម្រង់មុខ"</string>
<string name="face_recalibrate_notification_content" msgid="3064513770251355594">"ចុចដើម្បីលុបគំរូមុខរបស់អ្នក រួចបញ្ចូលមុខរបស់អ្នកម្ដងទៀត"</string>
- <string name="face_setup_notification_title" msgid="8843461561970741790">"រៀបចំការដោះសោដោយស្កេនមុខ"</string>
- <string name="face_setup_notification_content" msgid="5463999831057751676">"ដោះសោទូរសព្ទរបស់អ្នកដោយសម្លឹងមើលវា"</string>
<string name="face_sensor_privacy_enabled" msgid="7407126963510598508">"ដើម្បីប្រើមុខងារដោះសោតាមទម្រង់មុខ សូមបើក"<b>"ការចូលប្រើកាមេរ៉ា"</b>"នៅក្នុងការកំណត់ > ឯកជនភាព"</string>
- <string name="fingerprint_setup_notification_title" msgid="2002630611398849495">"រៀបចំវិធីច្រើនទៀតដើម្បីដោះសោ"</string>
- <string name="fingerprint_setup_notification_content" msgid="205578121848324852">"ចុចដើម្បីបញ្ចូលស្នាមម្រាមដៃ"</string>
<string name="fingerprint_recalibrate_notification_name" msgid="1414578431898579354">"ការដោះសោដោយស្កេនស្នាមម្រាមដៃ"</string>
<string name="fingerprint_recalibrate_notification_title" msgid="2406561052064558497">"មិនអាចប្រើឧបករណ៍ចាប់ស្នាមម្រាមដៃបានទេ"</string>
<string name="fingerprint_recalibrate_notification_content" msgid="8519935717822194943">"ទាក់ទងក្រុមហ៊ុនផ្ដល់ការជួសជុល។"</string>
@@ -2031,7 +2031,8 @@
<string name="autofill_update_title" msgid="3630695947047069136">"ធ្វើបច្ចុប្បន្នភាពនៅក្នុង "<b>"<xliff:g id="LABEL">%1$s</xliff:g>"</b>"?"</string>
<string name="autofill_update_title_with_type" msgid="5264152633488495704">"ធ្វើបច្ចុប្បន្នភាព <xliff:g id="TYPE">%1$s</xliff:g> នៅក្នុង "<b>"<xliff:g id="LABEL">%2$s</xliff:g>"</b>"?"</string>
<string name="autofill_update_title_with_2types" msgid="1797514386321086273">"ធ្វើបច្ចុប្បន្នភាព <xliff:g id="TYPE_0">%1$s</xliff:g> និង <xliff:g id="TYPE_1">%2$s</xliff:g> នៅក្នុង "<b>"<xliff:g id="LABEL">%3$s</xliff:g>"</b>"?"</string>
- <string name="autofill_update_title_with_3types" msgid="1312232153076212291">"ធ្វើបច្ចុប្បន្នភាពធាតុទាំងនេះនៅក្នុង "<b>"<xliff:g id="LABEL">%4$s</xliff:g>"</b>": <xliff:g id="TYPE_0">%1$s</xliff:g>, <xliff:g id="TYPE_1">%2$s</xliff:g> និង <xliff:g id="TYPE_2">%3$s</xliff:g>?"</string>
+ <!-- no translation found for autofill_update_title_with_3types (8285767070604652626) -->
+ <skip />
<string name="autofill_save_yes" msgid="8035743017382012850">"រក្សាទុក"</string>
<string name="autofill_save_no" msgid="9212826374207023544">"ទេ អរគុណ"</string>
<string name="autofill_save_notnow" msgid="2853932672029024195">"កុំទាន់"</string>
diff --git a/core/res/res/values-kn/strings.xml b/core/res/res/values-kn/strings.xml
index 73060eb..4cdb755 100644
--- a/core/res/res/values-kn/strings.xml
+++ b/core/res/res/values-kn/strings.xml
@@ -669,14 +669,14 @@
</string-array>
<string name="fingerprint_error_vendor_unknown" msgid="4170002184907291065">"ಏನೋ ತಪ್ಪಾಗಿದೆ. ಪುನಃ ಪ್ರಯತ್ನಿಸಿ."</string>
<string name="fingerprint_icon_content_description" msgid="4741068463175388817">"ಫಿಂಗರ್ಪ್ರಿಂಟ್ ಐಕಾನ್"</string>
+ <string name="device_unlock_notification_name" msgid="2632928999862915709">"ಸಾಧನದ ಅನ್ಲಾಕ್"</string>
+ <string name="alternative_unlock_setup_notification_title" msgid="6241508547901933544">"ಅನ್ಲಾಕ್ ಮಾಡಲು ಮತ್ತೊಂದು ವಿಧಾನವನ್ನು ಬಳಸಿ"</string>
+ <string name="alternative_face_setup_notification_content" msgid="3384959224091897331">"ನಿಮ್ಮ ಫಿಂಗರ್ಪ್ರಿಂಟ್ ಅನ್ನು ಗುರುತಿಸದಿದ್ದಾಗ ಫೇಸ್ ಅನ್ಲಾಕ್ ಅನ್ನು ಬಳಸಿ, ಉದಾಹರಣೆಗೆ, ನಿಮ್ಮ ಬೆರಳುಗಳು ಒದ್ದೆಯಾಗಿದ್ದಾಗ"</string>
+ <string name="alternative_fp_setup_notification_content" msgid="7454096947415721639">"ನಿಮ್ಮ ಮುಖವನ್ನು ಗುರುತಿಸದಿದ್ದಾಗ ಫಿಂಗರ್ಪ್ರಿಂಟ್ ಅನ್ಲಾಕ್ ಅನ್ನು ಬಳಸಿ, ಉದಾಹರಣೆಗೆ, ಸಾಕಷ್ಟು ಬೆಳಕು ಇಲ್ಲದಿದ್ದಾಗ"</string>
<string name="face_recalibrate_notification_name" msgid="7311163114750748686">"ಫೇಸ್ ಅನ್ಲಾಕ್"</string>
<string name="face_recalibrate_notification_title" msgid="2524791952735579082">"ಫೇಸ್ ಅನ್ಲಾಕ್ ಕುರಿತು ಸಮಸ್ಯೆ ಇದೆ"</string>
<string name="face_recalibrate_notification_content" msgid="3064513770251355594">"ನಿಮ್ಮ ಫೇಸ್ ಮಾಡೆಲ್ ಅನ್ನು ಅಳಿಸಲು ಟ್ಯಾಪ್ ಮಾಡಿ, ನಂತರ ನಿಮ್ಮ ಫೇಸ್ ಮಾಡೆಲ್ ಅನ್ನು ಪುನಃ ಸೇರಿಸಿ"</string>
- <string name="face_setup_notification_title" msgid="8843461561970741790">"ಫೇಸ್ ಅನ್ಲಾಕ್ ಅನ್ನು ಸೆಟಪ್ ಮಾಡಿ"</string>
- <string name="face_setup_notification_content" msgid="5463999831057751676">"ಫೋನ್ ಅನ್ನು ನೋಡುವ ಮೂಲಕ ಅನ್ಲಾಕ್ ಮಾಡಿ"</string>
<string name="face_sensor_privacy_enabled" msgid="7407126963510598508">"ಫೇಸ್ ಅನ್ಲಾಕ್ ಬಳಸಲು, ಸೆಟ್ಟಿಂಗ್ಗಳು > ಗೌಪ್ಯತೆ ಎಂಬಲ್ಲಿ "<b>"ಕ್ಯಾಮರಾ ಪ್ರವೇಶವನ್ನು"</b>" ಆನ್ ಮಾಡಿ"</string>
- <string name="fingerprint_setup_notification_title" msgid="2002630611398849495">"ಅನ್ಲಾಕ್ ಮಾಡಲು ಹೆಚ್ಚಿನ ಮಾರ್ಗಗಳನ್ನು ಹೊಂದಿಸಿ"</string>
- <string name="fingerprint_setup_notification_content" msgid="205578121848324852">"ಫಿಂಗರ್ ಪ್ರಿಂಟ್ ಸೇರಿಸಲು ಟ್ಯಾಪ್ ಮಾಡಿ"</string>
<string name="fingerprint_recalibrate_notification_name" msgid="1414578431898579354">"ಫಿಂಗರ್ಪ್ರಿಂಟ್ ಅನ್ಲಾಕ್"</string>
<string name="fingerprint_recalibrate_notification_title" msgid="2406561052064558497">"ಫಿಂಗರ್ಪ್ರಿಂಟ್ ಸೆನ್ಸರ್ ಅನ್ನು ಬಳಸಲು ಸಾಧ್ಯವಿಲ್ಲ"</string>
<string name="fingerprint_recalibrate_notification_content" msgid="8519935717822194943">"ರಿಪೇರಿ ಮಾಡುವವರನ್ನು ಸಂಪರ್ಕಿಸಿ."</string>
@@ -2031,7 +2031,8 @@
<string name="autofill_update_title" msgid="3630695947047069136"><b>"<xliff:g id="LABEL">%1$s</xliff:g>"</b>" ನಲ್ಲಿ ಅಪ್ಡೇಟ್ ಮಾಡಬೇಕೆ?"</string>
<string name="autofill_update_title_with_type" msgid="5264152633488495704">"<xliff:g id="TYPE">%1$s</xliff:g> ಅನ್ನು "<b>"<xliff:g id="LABEL">%2$s</xliff:g>"</b>" ನಲ್ಲಿ ಅಪ್ಡೇಟ್ ಮಾಡಬೇಕೆ?"</string>
<string name="autofill_update_title_with_2types" msgid="1797514386321086273">"<xliff:g id="TYPE_0">%1$s</xliff:g> ಮತ್ತು <xliff:g id="TYPE_1">%2$s</xliff:g> ಅನ್ನು "<b>"<xliff:g id="LABEL">%3$s</xliff:g>"</b>" ನಲ್ಲಿ ಅಪ್ಡೇಟ್ ಮಾಡಬೇಕೆ?"</string>
- <string name="autofill_update_title_with_3types" msgid="1312232153076212291">"ಈ ಮುಂದಿನ ಐಟಂಗಳನ್ನು "<b>"<xliff:g id="LABEL">%4$s</xliff:g>"</b>" ನಲ್ಲಿ ಅಪ್ಡೇಟ್ ಮಾಡಬೇಕೆ: <xliff:g id="TYPE_0">%1$s</xliff:g>, <xliff:g id="TYPE_1">%2$s</xliff:g> ಮತ್ತು <xliff:g id="TYPE_2">%3$s</xliff:g> ?"</string>
+ <!-- no translation found for autofill_update_title_with_3types (8285767070604652626) -->
+ <skip />
<string name="autofill_save_yes" msgid="8035743017382012850">"ಉಳಿಸಿ"</string>
<string name="autofill_save_no" msgid="9212826374207023544">"ಬೇಡ"</string>
<string name="autofill_save_notnow" msgid="2853932672029024195">"ಸದ್ಯಕ್ಕೆ ಬೇಡ"</string>
diff --git a/core/res/res/values-ko/strings.xml b/core/res/res/values-ko/strings.xml
index 4fe36b6..ae92aa9 100644
--- a/core/res/res/values-ko/strings.xml
+++ b/core/res/res/values-ko/strings.xml
@@ -669,14 +669,14 @@
</string-array>
<string name="fingerprint_error_vendor_unknown" msgid="4170002184907291065">"문제가 발생했습니다. 다시 시도해 보세요."</string>
<string name="fingerprint_icon_content_description" msgid="4741068463175388817">"지문 아이콘"</string>
+ <string name="device_unlock_notification_name" msgid="2632928999862915709">"기기 잠금 해제"</string>
+ <string name="alternative_unlock_setup_notification_title" msgid="6241508547901933544">"다른 잠금 해제 방법 사용"</string>
+ <string name="alternative_face_setup_notification_content" msgid="3384959224091897331">"손가락에 물기가 있는 등 지문이 인식되지 않을 때는 얼굴 인식 잠금 해제를 사용하세요."</string>
+ <string name="alternative_fp_setup_notification_content" msgid="7454096947415721639">"충분히 밝지 않은 경우 등 얼굴이 인식되지 않을 때는 지문 잠금 해제를 사용하세요."</string>
<string name="face_recalibrate_notification_name" msgid="7311163114750748686">"얼굴 인식 잠금 해제"</string>
<string name="face_recalibrate_notification_title" msgid="2524791952735579082">"얼굴 인식 잠금 해제 문제"</string>
<string name="face_recalibrate_notification_content" msgid="3064513770251355594">"탭하여 얼굴 모델을 삭제한 후 다시 얼굴을 추가하세요"</string>
- <string name="face_setup_notification_title" msgid="8843461561970741790">"얼굴 인식 잠금 해제 설정"</string>
- <string name="face_setup_notification_content" msgid="5463999831057751676">"휴대전화의 화면을 응시하여 잠금 해제할 수 있습니다."</string>
<string name="face_sensor_privacy_enabled" msgid="7407126963510598508">"얼굴 인식 잠금 해제를 사용하려면 설정 > 개인 정보 보호에서 "<b>"카메라 액세스"</b>"를 사용 설정하세요."</string>
- <string name="fingerprint_setup_notification_title" msgid="2002630611398849495">"다른 잠금 해제 방법 설정"</string>
- <string name="fingerprint_setup_notification_content" msgid="205578121848324852">"지문을 추가하려면 탭하세요."</string>
<string name="fingerprint_recalibrate_notification_name" msgid="1414578431898579354">"지문 잠금 해제"</string>
<string name="fingerprint_recalibrate_notification_title" msgid="2406561052064558497">"지문 센서를 사용할 수 없음"</string>
<string name="fingerprint_recalibrate_notification_content" msgid="8519935717822194943">"수리업체에 방문하세요."</string>
@@ -2031,7 +2031,8 @@
<string name="autofill_update_title" msgid="3630695947047069136"><b>"<xliff:g id="LABEL">%1$s</xliff:g>"</b>"에서 업데이트하시겠습니까?"</string>
<string name="autofill_update_title_with_type" msgid="5264152633488495704"><b>"<xliff:g id="LABEL">%2$s</xliff:g>"</b>"에서 <xliff:g id="TYPE">%1$s</xliff:g>을(를) 업데이트하시겠습니까?"</string>
<string name="autofill_update_title_with_2types" msgid="1797514386321086273"><b>"<xliff:g id="LABEL">%3$s</xliff:g>"</b>"에서 <xliff:g id="TYPE_0">%1$s</xliff:g> 및 <xliff:g id="TYPE_1">%2$s</xliff:g>을(를) 업데이트하시겠습니까?"</string>
- <string name="autofill_update_title_with_3types" msgid="1312232153076212291"><b>"<xliff:g id="LABEL">%4$s</xliff:g>"</b>"에서 <xliff:g id="TYPE_0">%1$s</xliff:g>, <xliff:g id="TYPE_1">%2$s</xliff:g> 및 <xliff:g id="TYPE_2">%3$s</xliff:g> 업데이트"</string>
+ <!-- no translation found for autofill_update_title_with_3types (8285767070604652626) -->
+ <skip />
<string name="autofill_save_yes" msgid="8035743017382012850">"저장"</string>
<string name="autofill_save_no" msgid="9212826374207023544">"사용 안함"</string>
<string name="autofill_save_notnow" msgid="2853932672029024195">"나중에"</string>
diff --git a/core/res/res/values-ky/strings.xml b/core/res/res/values-ky/strings.xml
index 763afb1..c585841d 100644
--- a/core/res/res/values-ky/strings.xml
+++ b/core/res/res/values-ky/strings.xml
@@ -669,14 +669,14 @@
</string-array>
<string name="fingerprint_error_vendor_unknown" msgid="4170002184907291065">"Бир жерден ката кетти. Кайра аракет кылыңыз."</string>
<string name="fingerprint_icon_content_description" msgid="4741068463175388817">"Манжа изинин сүрөтчөсү"</string>
+ <string name="device_unlock_notification_name" msgid="2632928999862915709">"Түзмөктүн кулпусун ачуу"</string>
+ <string name="alternative_unlock_setup_notification_title" msgid="6241508547901933544">"Кулпуну башка жол менен ачуу"</string>
+ <string name="alternative_face_setup_notification_content" msgid="3384959224091897331">"Манжаңыздын изи таанылбай калганда, мисалы, манжаңыз ным болсо, Жүзүнөн таанып ачуу функциясын колдонуңуз"</string>
+ <string name="alternative_fp_setup_notification_content" msgid="7454096947415721639">"Жүзүңүз таанылбай калганда, мисалы, күңүрт жерде турсаңыз, Манжа изи менен ачуу функциясын колдонуңуз"</string>
<string name="face_recalibrate_notification_name" msgid="7311163114750748686">"Жүзүнөн таанып ачуу"</string>
<string name="face_recalibrate_notification_title" msgid="2524791952735579082">"Жүзүнөн таанып ачуу функциясында маселе келип чыкты"</string>
<string name="face_recalibrate_notification_content" msgid="3064513770251355594">"Жүзүңүздүн үлгүсүн өчүрүү үчүн басып, жаңы үлгүнү кошуңуз"</string>
- <string name="face_setup_notification_title" msgid="8843461561970741790">"Жүзүнөн таанып ачууну коюу"</string>
- <string name="face_setup_notification_content" msgid="5463999831057751676">"Телефонуңузду карап туруп эле кулпусун ачып алыңыз"</string>
<string name="face_sensor_privacy_enabled" msgid="7407126963510598508">"Жүзүнөн таанып ачуу функциясын колдонуу үчүн Параметрлер > Купуялык бөлүмүнө өтүп, "<b>"Камераны колдонууну"</b>" күйгүзүңүз"</string>
- <string name="fingerprint_setup_notification_title" msgid="2002630611398849495">"Кулпусун ачуунун көбүрөөк жолдорун жөндөңүз"</string>
- <string name="fingerprint_setup_notification_content" msgid="205578121848324852">"Манжа изин кошуу үчүн басыңыз"</string>
<string name="fingerprint_recalibrate_notification_name" msgid="1414578431898579354">"Кулпуланган түзмөктү манжа изи менен ачуу"</string>
<string name="fingerprint_recalibrate_notification_title" msgid="2406561052064558497">"Манжа изинин сенсорун колдонууга болбойт"</string>
<string name="fingerprint_recalibrate_notification_content" msgid="8519935717822194943">"Тейлөө кызматына кайрылыңыз."</string>
@@ -2031,7 +2031,8 @@
<string name="autofill_update_title" msgid="3630695947047069136"><b>"<xliff:g id="LABEL">%1$s</xliff:g>"</b>" кызматында жаңыртылсынбы?"</string>
<string name="autofill_update_title_with_type" msgid="5264152633488495704"><b>"<xliff:g id="LABEL">%2$s</xliff:g>"</b>" кызматындагы <xliff:g id="TYPE">%1$s</xliff:g> жаңыртылсын?"</string>
<string name="autofill_update_title_with_2types" msgid="1797514386321086273"><b>"<xliff:g id="LABEL">%3$s</xliff:g>"</b>" кызматындагы <xliff:g id="TYPE_0">%1$s</xliff:g> жана <xliff:g id="TYPE_1">%2$s</xliff:g> жаңыртылсынбы?"</string>
- <string name="autofill_update_title_with_3types" msgid="1312232153076212291"><b>"<xliff:g id="LABEL">%4$s</xliff:g>"</b>" кызматындагы төмөнкүлөр жаңыртылсынбы: <xliff:g id="TYPE_0">%1$s</xliff:g>, <xliff:g id="TYPE_1">%2$s</xliff:g> жана <xliff:g id="TYPE_2">%3$s</xliff:g>?"</string>
+ <!-- no translation found for autofill_update_title_with_3types (8285767070604652626) -->
+ <skip />
<string name="autofill_save_yes" msgid="8035743017382012850">"Сактоо"</string>
<string name="autofill_save_no" msgid="9212826374207023544">"Жок, рахмат"</string>
<string name="autofill_save_notnow" msgid="2853932672029024195">"Азыр эмес"</string>
diff --git a/core/res/res/values-lo/strings.xml b/core/res/res/values-lo/strings.xml
index c57d386..8057449 100644
--- a/core/res/res/values-lo/strings.xml
+++ b/core/res/res/values-lo/strings.xml
@@ -669,14 +669,14 @@
</string-array>
<string name="fingerprint_error_vendor_unknown" msgid="4170002184907291065">"ມີບາງຢ່າງຜິດພາດເກີດຂຶ້ນ. ກະລຸນາລອງໃໝ່."</string>
<string name="fingerprint_icon_content_description" msgid="4741068463175388817">"ໄອຄອນລາຍນິ້ວມື"</string>
+ <string name="device_unlock_notification_name" msgid="2632928999862915709">"ການປົດລັອກອຸປະກອນ"</string>
+ <string name="alternative_unlock_setup_notification_title" msgid="6241508547901933544">"ລອງໃຊ້ວິທີອື່ນເພື່ອປົດລັອກ"</string>
+ <string name="alternative_face_setup_notification_content" msgid="3384959224091897331">"ໃຊ້ການປົດລັອກດ້ວຍໜ້າເມື່ອລະບົບບໍ່ຈຳແນກລາຍນິ້ວມືຂອງທ່ານ ເຊັ່ນ: ເມື່ອນິ້ວມືຂອງທ່ານປຽກ"</string>
+ <string name="alternative_fp_setup_notification_content" msgid="7454096947415721639">"ໃຊ້ການປົດລັອກດ້ວຍລາຍນິ້ວມືເມື່ອລະບົບບໍ່ຈຳແນກໃບໜ້າຂອງທ່ານ ເຊັ່ນ: ເມື່ອບໍ່ມີແສງພຽງພໍ"</string>
<string name="face_recalibrate_notification_name" msgid="7311163114750748686">"ປົດລັອກດ້ວຍໜ້າ"</string>
<string name="face_recalibrate_notification_title" msgid="2524791952735579082">"ເກີດບັນຫາກັບການປົດລັອກດ້ວຍໜ້າ"</string>
<string name="face_recalibrate_notification_content" msgid="3064513770251355594">"ແຕະເພື່ອລຶບຮູບແບບໃບໜ້າຂອງທ່ານ, ຈາກນັ້ນເພີ່ມໃບໜ້າຂອງທ່ານໃສ່ໃໝ່"</string>
- <string name="face_setup_notification_title" msgid="8843461561970741790">"ຕັ້ງຄ່າການປົດລັອກດ້ວຍໜ້າ"</string>
- <string name="face_setup_notification_content" msgid="5463999831057751676">"ປົດລັອກໂທລະສັບຂອງທ່ານໂດຍການເບິ່ງມັນ"</string>
<string name="face_sensor_privacy_enabled" msgid="7407126963510598508">"ເພື່ອໃຊ້ການປົດລັອກດ້ວຍໜ້າ, ກະລຸນາເປີດໃຊ້ "<b>"ສິດເຂົ້າເຖິງກ້ອງຖ່າຍຮູບ"</b>" ໃນການຕັ້ງຄ່າ > ຄວາມເປັນສ່ວນຕົວ"</string>
- <string name="fingerprint_setup_notification_title" msgid="2002630611398849495">"ຕັ້ງຄ່າວິທີເພີ່ມເຕີມເພື່ອປົດລັອກ"</string>
- <string name="fingerprint_setup_notification_content" msgid="205578121848324852">"ແຕະເພື່ອເພີ່ມລາຍນິ້ວມື"</string>
<string name="fingerprint_recalibrate_notification_name" msgid="1414578431898579354">"ປົດລັອກດ້ວຍລາຍນິ້ວມື"</string>
<string name="fingerprint_recalibrate_notification_title" msgid="2406561052064558497">"ບໍ່ສາມາດໃຊ້ເຊັນເຊີລາຍນິ້ວມືໄດ້"</string>
<string name="fingerprint_recalibrate_notification_content" msgid="8519935717822194943">"ກະລຸນາໄປຫາຜູ້ໃຫ້ບໍລິການສ້ອມແປງ."</string>
@@ -2031,7 +2031,8 @@
<string name="autofill_update_title" msgid="3630695947047069136">"ອັບເດດໃນ "<b>"<xliff:g id="LABEL">%1$s</xliff:g>"</b>" ບໍ?"</string>
<string name="autofill_update_title_with_type" msgid="5264152633488495704">"ອັບເດດ <xliff:g id="TYPE">%1$s</xliff:g> ໃນ "<b>"<xliff:g id="LABEL">%2$s</xliff:g>"</b>" ບໍ?"</string>
<string name="autofill_update_title_with_2types" msgid="1797514386321086273">"ອັບເດດ <xliff:g id="TYPE_0">%1$s</xliff:g> ແລະ <xliff:g id="TYPE_1">%2$s</xliff:g> ໃນ "<b>"<xliff:g id="LABEL">%3$s</xliff:g>"</b>" ບໍ?"</string>
- <string name="autofill_update_title_with_3types" msgid="1312232153076212291">"ອັບເດດລາຍການເຫຼົ່ານີ້ໃນ "<b>"<xliff:g id="LABEL">%4$s</xliff:g>"</b>": <xliff:g id="TYPE_0">%1$s</xliff:g>, <xliff:g id="TYPE_1">%2$s</xliff:g> ແລະ <xliff:g id="TYPE_2">%3$s</xliff:g> ບໍ?"</string>
+ <!-- no translation found for autofill_update_title_with_3types (8285767070604652626) -->
+ <skip />
<string name="autofill_save_yes" msgid="8035743017382012850">"ບັນທຶກ"</string>
<string name="autofill_save_no" msgid="9212826374207023544">"ບໍ່, ຂອບໃຈ"</string>
<string name="autofill_save_notnow" msgid="2853932672029024195">"ບໍ່ຟ້າວເທື່ອ"</string>
diff --git a/core/res/res/values-lt/strings.xml b/core/res/res/values-lt/strings.xml
index ed12059..c6d36d1 100644
--- a/core/res/res/values-lt/strings.xml
+++ b/core/res/res/values-lt/strings.xml
@@ -671,14 +671,14 @@
</string-array>
<string name="fingerprint_error_vendor_unknown" msgid="4170002184907291065">"Kažkas nepavyko. Bandykite dar kartą."</string>
<string name="fingerprint_icon_content_description" msgid="4741068463175388817">"Piršto antspaudo piktograma"</string>
+ <string name="device_unlock_notification_name" msgid="2632928999862915709">"Įrenginio atrakinimo funkcija"</string>
+ <string name="alternative_unlock_setup_notification_title" msgid="6241508547901933544">"Išbandykite kitą atrakinimo metodą"</string>
+ <string name="alternative_face_setup_notification_content" msgid="3384959224091897331">"Naudokite atrakinimą pagal veidą, kai nepavyksta atpažinti piršto atspaudo, pvz., kai pirštai drėgni"</string>
+ <string name="alternative_fp_setup_notification_content" msgid="7454096947415721639">"Naudokite atrakinimą piršto atspaudu, kai nepavyksta atpažinti veido, pvz., kai netinkamas apšvietimas"</string>
<string name="face_recalibrate_notification_name" msgid="7311163114750748686">"Atrakinimas pagal veidą"</string>
<string name="face_recalibrate_notification_title" msgid="2524791952735579082">"Su atrakinimu pagal veidą susijusi problema"</string>
<string name="face_recalibrate_notification_content" msgid="3064513770251355594">"Palieskite, kad ištrintumėte veido modelį, tada iš naujo pridėkite veidą"</string>
- <string name="face_setup_notification_title" msgid="8843461561970741790">"Atrakinimo pagal veidą nustatymas"</string>
- <string name="face_setup_notification_content" msgid="5463999831057751676">"Atrakinkite telefoną pažiūrėję į jį"</string>
<string name="face_sensor_privacy_enabled" msgid="7407126963510598508">"Jei norite naudoti atrakinimą pagal veidą, įjunkite parinktį "<b>"Prieiga prie fotoaparato"</b>" skiltyje „Nustatymai“ > „Privatumas“"</string>
- <string name="fingerprint_setup_notification_title" msgid="2002630611398849495">"Daugiau atrakinimo metodų nustatymas"</string>
- <string name="fingerprint_setup_notification_content" msgid="205578121848324852">"Palieskite, kad pridėtumėte kontrolinį kodą"</string>
<string name="fingerprint_recalibrate_notification_name" msgid="1414578431898579354">"Atrakinimas piršto atspaudu"</string>
<string name="fingerprint_recalibrate_notification_title" msgid="2406561052064558497">"Negalima naudoti kontrolinio kodo jutiklio"</string>
<string name="fingerprint_recalibrate_notification_content" msgid="8519935717822194943">"Apsilankykite pas taisymo paslaugos teikėją."</string>
@@ -2033,7 +2033,8 @@
<string name="autofill_update_title" msgid="3630695947047069136">"Atnaujinti paslaugoje "<b>"<xliff:g id="LABEL">%1$s</xliff:g>"</b>"?"</string>
<string name="autofill_update_title_with_type" msgid="5264152633488495704">"Atnaujinti <xliff:g id="TYPE">%1$s</xliff:g> paslaugoje "<b>"<xliff:g id="LABEL">%2$s</xliff:g>"</b>"?"</string>
<string name="autofill_update_title_with_2types" msgid="1797514386321086273">"Atnaujinti <xliff:g id="TYPE_0">%1$s</xliff:g> ir <xliff:g id="TYPE_1">%2$s</xliff:g> paslaugoje "<b>"<xliff:g id="LABEL">%3$s</xliff:g>"</b>"?"</string>
- <string name="autofill_update_title_with_3types" msgid="1312232153076212291">"Atnaujinti šiuos elementus paslaugoje "<b>"<xliff:g id="LABEL">%4$s</xliff:g>"</b>": <xliff:g id="TYPE_0">%1$s</xliff:g>, <xliff:g id="TYPE_1">%2$s</xliff:g> ir <xliff:g id="TYPE_2">%3$s</xliff:g> ?"</string>
+ <!-- no translation found for autofill_update_title_with_3types (8285767070604652626) -->
+ <skip />
<string name="autofill_save_yes" msgid="8035743017382012850">"Išsaugoti"</string>
<string name="autofill_save_no" msgid="9212826374207023544">"Ne, ačiū"</string>
<string name="autofill_save_notnow" msgid="2853932672029024195">"Ne dabar"</string>
diff --git a/core/res/res/values-lv/strings.xml b/core/res/res/values-lv/strings.xml
index a2380d9..45b9586 100644
--- a/core/res/res/values-lv/strings.xml
+++ b/core/res/res/values-lv/strings.xml
@@ -670,14 +670,14 @@
</string-array>
<string name="fingerprint_error_vendor_unknown" msgid="4170002184907291065">"Radās kļūda. Mēģiniet vēlreiz."</string>
<string name="fingerprint_icon_content_description" msgid="4741068463175388817">"Pirksta nospieduma ikona"</string>
+ <string name="device_unlock_notification_name" msgid="2632928999862915709">"Ierīces atbloķēšana"</string>
+ <string name="alternative_unlock_setup_notification_title" msgid="6241508547901933544">"Izmēģiniet citus atbloķēšanas veidus"</string>
+ <string name="alternative_face_setup_notification_content" msgid="3384959224091897331">"Izmantojiet autorizāciju pēc sejas, ja netiek atpazīts pirksta nospiedums, piemēram, kad pirksti ir mitri."</string>
+ <string name="alternative_fp_setup_notification_content" msgid="7454096947415721639">"Izmantojiet autorizāciju ar pirksta nospiedumu, ja netiek atpazīta seja, piemēram, vājā apgaismojumā."</string>
<string name="face_recalibrate_notification_name" msgid="7311163114750748686">"Autorizācija pēc sejas"</string>
<string name="face_recalibrate_notification_title" msgid="2524791952735579082">"Problēma ar autorizāciju pēc sejas"</string>
<string name="face_recalibrate_notification_content" msgid="3064513770251355594">"Pieskarieties, lai izdzēstu sejas modeli, un pēc tam vēlreiz pievienojiet seju"</string>
- <string name="face_setup_notification_title" msgid="8843461561970741790">"Autorizācijas pēc sejas iestatīšana"</string>
- <string name="face_setup_notification_content" msgid="5463999831057751676">"Atbloķējiet tālruni, skatoties uz to"</string>
<string name="face_sensor_privacy_enabled" msgid="7407126963510598508">"Lai izmantotu autorizāciju pēc sejas, sadaļā Iestatījumi > Konfidencialitāte ieslēdziet opciju "<b>"Piekļuve kamerai"</b>"."</string>
- <string name="fingerprint_setup_notification_title" msgid="2002630611398849495">"Citi atbloķēšanas veidi"</string>
- <string name="fingerprint_setup_notification_content" msgid="205578121848324852">"Pieskarieties, lai pievienotu pirksta nospiedumu"</string>
<string name="fingerprint_recalibrate_notification_name" msgid="1414578431898579354">"Autorizācija ar pirksta nospiedumu"</string>
<string name="fingerprint_recalibrate_notification_title" msgid="2406561052064558497">"Nevar izmantot pirksta nospieduma sensoru"</string>
<string name="fingerprint_recalibrate_notification_content" msgid="8519935717822194943">"Sazinieties ar remonta pakalpojumu sniedzēju."</string>
@@ -2032,7 +2032,8 @@
<string name="autofill_update_title" msgid="3630695947047069136">"Vai atjaunināt informāciju pakalpojumā "<b>"<xliff:g id="LABEL">%1$s</xliff:g>"</b>"?"</string>
<string name="autofill_update_title_with_type" msgid="5264152633488495704">"Vai atjaunināt informāciju <xliff:g id="TYPE">%1$s</xliff:g> pakalpojumā "<b>"<xliff:g id="LABEL">%2$s</xliff:g>"</b>"?"</string>
<string name="autofill_update_title_with_2types" msgid="1797514386321086273">"Vai atjaunināt informāciju <xliff:g id="TYPE_0">%1$s</xliff:g> un <xliff:g id="TYPE_1">%2$s</xliff:g> pakalpojumā "<b>"<xliff:g id="LABEL">%3$s</xliff:g>"</b>"?"</string>
- <string name="autofill_update_title_with_3types" msgid="1312232153076212291">"Vai atjaunināt šos vienumus pakalpojumā "<b>"<xliff:g id="LABEL">%4$s</xliff:g>"</b>": <xliff:g id="TYPE_0">%1$s</xliff:g>, <xliff:g id="TYPE_1">%2$s</xliff:g> un <xliff:g id="TYPE_2">%3$s</xliff:g>?"</string>
+ <!-- no translation found for autofill_update_title_with_3types (8285767070604652626) -->
+ <skip />
<string name="autofill_save_yes" msgid="8035743017382012850">"Saglabāt"</string>
<string name="autofill_save_no" msgid="9212826374207023544">"Nē, paldies"</string>
<string name="autofill_save_notnow" msgid="2853932672029024195">"Vēlāk"</string>
diff --git a/core/res/res/values-mk/strings.xml b/core/res/res/values-mk/strings.xml
index 0eb3927..33e9a55 100644
--- a/core/res/res/values-mk/strings.xml
+++ b/core/res/res/values-mk/strings.xml
@@ -296,7 +296,7 @@
<string name="foreground_service_multiple_separator" msgid="5002287361849863168">"<xliff:g id="LEFT_SIDE">%1$s</xliff:g>, <xliff:g id="RIGHT_SIDE">%2$s</xliff:g>"</string>
<string name="safeMode" msgid="8974401416068943888">"Безбеден режим"</string>
<string name="android_system_label" msgid="5974767339591067210">"Систем Android"</string>
- <string name="user_owner_label" msgid="8628726904184471211">"Префрли на личен профил"</string>
+ <string name="user_owner_label" msgid="8628726904184471211">"Префрлете се на личен профил"</string>
<string name="managed_profile_label" msgid="7316778766973512382">"Префрли се на работен профил"</string>
<string name="user_owner_app_label" msgid="1553595155465750298">"Префрлете се на личната апликација <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
<string name="managed_profile_app_label" msgid="367401088383965725">"Префрлете се на работната апликација <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
@@ -669,14 +669,14 @@
</string-array>
<string name="fingerprint_error_vendor_unknown" msgid="4170002184907291065">"Нешто не е во ред. Обидете се повторно."</string>
<string name="fingerprint_icon_content_description" msgid="4741068463175388817">"Икона за отпечатоци"</string>
+ <string name="device_unlock_notification_name" msgid="2632928999862915709">"Отклучување на уредот"</string>
+ <string name="alternative_unlock_setup_notification_title" msgid="6241508547901933544">"Пробајте друг начин на отклучување"</string>
+ <string name="alternative_face_setup_notification_content" msgid="3384959224091897331">"Користете „Отклучување со лик“ кога не ви се препознава отпечатокот, како кога прстите ви се влажни"</string>
+ <string name="alternative_fp_setup_notification_content" msgid="7454096947415721639">"Користете „Отклучување со отпечаток“ кога не ви се препознава ликот, како кога нема доволно светлина"</string>
<string name="face_recalibrate_notification_name" msgid="7311163114750748686">"Отклучување со лик"</string>
<string name="face_recalibrate_notification_title" msgid="2524791952735579082">"Проблем со „Отклучување со лик“"</string>
<string name="face_recalibrate_notification_content" msgid="3064513770251355594">"Допрете за да го избришете вашиот модел на лик, а потоа повторно додајте го ликот"</string>
- <string name="face_setup_notification_title" msgid="8843461561970741790">"Поставете „Отклучување со лик“"</string>
- <string name="face_setup_notification_content" msgid="5463999831057751676">"Отклучете го телефонот со гледање во него"</string>
<string name="face_sensor_privacy_enabled" msgid="7407126963510598508">"За да користите „Отклучување со лик“, вклучете "<b>"Пристап до камерата"</b>" во „Поставки > Приватност“"</string>
- <string name="fingerprint_setup_notification_title" msgid="2002630611398849495">"Поставете уште начини за отклучување"</string>
- <string name="fingerprint_setup_notification_content" msgid="205578121848324852">"Допрете за да додадете отпечаток"</string>
<string name="fingerprint_recalibrate_notification_name" msgid="1414578431898579354">"Отклучување со отпечаток"</string>
<string name="fingerprint_recalibrate_notification_title" msgid="2406561052064558497">"Не може да се користи сензорот за отпечатоци"</string>
<string name="fingerprint_recalibrate_notification_content" msgid="8519935717822194943">"Однесете го на поправка."</string>
@@ -2031,7 +2031,8 @@
<string name="autofill_update_title" msgid="3630695947047069136">"Да се ажурира во "<b>"<xliff:g id="LABEL">%1$s</xliff:g>"</b>"?"</string>
<string name="autofill_update_title_with_type" msgid="5264152633488495704">"Да се ажурира <xliff:g id="TYPE">%1$s</xliff:g> во "<b>"<xliff:g id="LABEL">%2$s</xliff:g>"</b>"?"</string>
<string name="autofill_update_title_with_2types" msgid="1797514386321086273">"Да се ажурираат <xliff:g id="TYPE_0">%1$s</xliff:g> и <xliff:g id="TYPE_1">%2$s</xliff:g> во "<b>"<xliff:g id="LABEL">%3$s</xliff:g>"</b>"?"</string>
- <string name="autofill_update_title_with_3types" msgid="1312232153076212291">"Да се ажурираат овие ставки во "<b>"<xliff:g id="LABEL">%4$s</xliff:g>"</b>": <xliff:g id="TYPE_0">%1$s</xliff:g>, <xliff:g id="TYPE_1">%2$s</xliff:g> и <xliff:g id="TYPE_2">%3$s</xliff:g>?"</string>
+ <!-- no translation found for autofill_update_title_with_3types (8285767070604652626) -->
+ <skip />
<string name="autofill_save_yes" msgid="8035743017382012850">"Зачувај"</string>
<string name="autofill_save_no" msgid="9212826374207023544">"Не, фала"</string>
<string name="autofill_save_notnow" msgid="2853932672029024195">"Не сега"</string>
diff --git a/core/res/res/values-ml/strings.xml b/core/res/res/values-ml/strings.xml
index e550bdd..8a4ddf1 100644
--- a/core/res/res/values-ml/strings.xml
+++ b/core/res/res/values-ml/strings.xml
@@ -669,14 +669,14 @@
</string-array>
<string name="fingerprint_error_vendor_unknown" msgid="4170002184907291065">"എന്തോ കുഴപ്പമുണ്ടായി. വീണ്ടും ശ്രമിക്കുക."</string>
<string name="fingerprint_icon_content_description" msgid="4741068463175388817">"ഫിംഗർപ്രിന്റ് ഐക്കൺ"</string>
+ <string name="device_unlock_notification_name" msgid="2632928999862915709">"ഉപകരണ അൺലോക്ക്"</string>
+ <string name="alternative_unlock_setup_notification_title" msgid="6241508547901933544">"അൺലോക്ക് ചെയ്യാനുള്ള മറ്റൊരു മാർഗ്ഗം പരീക്ഷിച്ച് നോക്കൂ"</string>
+ <string name="alternative_face_setup_notification_content" msgid="3384959224091897331">"ഫിംഗർപ്രിന്റ് തിരിച്ചറിയാൻ കഴിയാത്തപ്പോൾ, ഉദാഹരണത്തിന്, വിരൽ നനഞ്ഞിരിക്കുമ്പോൾ ഫേസ് അൺലോക്ക് ഉപയോഗിക്കുക"</string>
+ <string name="alternative_fp_setup_notification_content" msgid="7454096947415721639">"നിങ്ങളുടെ മുഖം തിരിച്ചറിയാനാകാത്തപ്പോൾ, ഉദാഹരണത്തിന്, ആവശ്യത്തിന് ലൈറ്റ് ഇല്ലാത്തപ്പോൾ ഫിംഗർപ്രിന്റ് അൺലോക്ക് ഉപയോഗിക്കുക"</string>
<string name="face_recalibrate_notification_name" msgid="7311163114750748686">"ഫെയ്സ് അൺലോക്ക്"</string>
<string name="face_recalibrate_notification_title" msgid="2524791952735579082">"ഫെയ്സ് അൺലോക്കുമായി ബന്ധപ്പെട്ട പ്രശ്നം"</string>
<string name="face_recalibrate_notification_content" msgid="3064513770251355594">"നിങ്ങളുടെ മുഖ മോഡൽ ഇല്ലാതാക്കാൻ ടാപ്പ് ചെയ്യുക, തുടർന്ന് അത് വീണ്ടും ചേർക്കുക"</string>
- <string name="face_setup_notification_title" msgid="8843461561970741790">"ഫെയ്സ് അൺലോക്ക് സജ്ജീകരിക്കുക"</string>
- <string name="face_setup_notification_content" msgid="5463999831057751676">"ഫോണിലേക്ക് നോക്കി അത് അൺലോക്ക് ചെയ്യുക"</string>
<string name="face_sensor_privacy_enabled" msgid="7407126963510598508">"ഫെയ്സ് അൺലോക്ക് ഉപയോഗിക്കാൻ, ക്രമീകരണം > സ്വകാര്യത എന്നതിൽ "<b>"ക്യാമറാ ആക്സസ്"</b>" ഓണാക്കുക"</string>
- <string name="fingerprint_setup_notification_title" msgid="2002630611398849495">"അൺലോക്ക് ചെയ്യുന്നതിനുള്ള കൂടുതൽ വഴികൾ സജ്ജീകരിക്കുക"</string>
- <string name="fingerprint_setup_notification_content" msgid="205578121848324852">"ഫിംഗർപ്രിന്റ് ചേർക്കാൻ ടാപ്പ് ചെയ്യുക"</string>
<string name="fingerprint_recalibrate_notification_name" msgid="1414578431898579354">"ഫിംഗർപ്രിന്റ് അൺലോക്ക്"</string>
<string name="fingerprint_recalibrate_notification_title" msgid="2406561052064558497">"വിരലടയാള സെൻസർ ഉപയോഗിക്കാനാകുന്നില്ല"</string>
<string name="fingerprint_recalibrate_notification_content" msgid="8519935717822194943">"റിപ്പയർ കേന്ദ്രം സന്ദർശിക്കുക."</string>
@@ -2031,7 +2031,8 @@
<string name="autofill_update_title" msgid="3630695947047069136"><b>"<xliff:g id="LABEL">%1$s</xliff:g>"</b>" എന്നതിൽ അപ്ഡേറ്റ് ചെയ്യണോ?"</string>
<string name="autofill_update_title_with_type" msgid="5264152633488495704">"<xliff:g id="TYPE">%1$s</xliff:g>, "<b>"<xliff:g id="LABEL">%2$s</xliff:g>"</b>" എന്നതിൽ അപ്ഡേറ്റ് ചെയ്യണോ?"</string>
<string name="autofill_update_title_with_2types" msgid="1797514386321086273">"<xliff:g id="TYPE_0">%1$s</xliff:g>, <xliff:g id="TYPE_1">%2$s</xliff:g> എന്നിവ "<b>"<xliff:g id="LABEL">%3$s</xliff:g>"</b>" എന്നതിൽ അപ്ഡേറ്റ് ചെയ്യണോ?"</string>
- <string name="autofill_update_title_with_3types" msgid="1312232153076212291">"ഇനിപ്പറയുന്ന ഇനങ്ങൾ "<b>"<xliff:g id="LABEL">%4$s</xliff:g>"</b>" എന്നതിൽ അപ്ഡേറ്റ് ചെയ്യണോ: <xliff:g id="TYPE_0">%1$s</xliff:g>, <xliff:g id="TYPE_1">%2$s</xliff:g>, <xliff:g id="TYPE_2">%3$s</xliff:g> ?"</string>
+ <!-- no translation found for autofill_update_title_with_3types (8285767070604652626) -->
+ <skip />
<string name="autofill_save_yes" msgid="8035743017382012850">"സംരക്ഷിക്കുക"</string>
<string name="autofill_save_no" msgid="9212826374207023544">"വേണ്ട, നന്ദി"</string>
<string name="autofill_save_notnow" msgid="2853932672029024195">"ഇപ്പോൾ വേണ്ട"</string>
diff --git a/core/res/res/values-mn/strings.xml b/core/res/res/values-mn/strings.xml
index 5205d1e..8332bb1 100644
--- a/core/res/res/values-mn/strings.xml
+++ b/core/res/res/values-mn/strings.xml
@@ -669,14 +669,14 @@
</string-array>
<string name="fingerprint_error_vendor_unknown" msgid="4170002184907291065">"Алдаа гарлаа. Дахин оролдоно уу."</string>
<string name="fingerprint_icon_content_description" msgid="4741068463175388817">"Хурууны хээний дүрс"</string>
+ <string name="device_unlock_notification_name" msgid="2632928999862915709">"Төхөөрөмжийн түгжээг тайлна уу"</string>
+ <string name="alternative_unlock_setup_notification_title" msgid="6241508547901933544">"Түгжээг тайлах өөр аргыг оролдож үзнэ үү"</string>
+ <string name="alternative_face_setup_notification_content" msgid="3384959224091897331">"Таны хуруунууд чийгтэй байх зэрэг үед хурууны хээг тань танихгүй бол Царайгаар түгжээ тайлахыг ашиглана уу"</string>
+ <string name="alternative_fp_setup_notification_content" msgid="7454096947415721639">"Хангалттай гэрэлгүй байх зэрэг үед таны царайг танихгүй бол Хурууны хээгээр түгжээ тайлахыг ашиглана уу"</string>
<string name="face_recalibrate_notification_name" msgid="7311163114750748686">"Царайгаар түгжээ тайлах"</string>
<string name="face_recalibrate_notification_title" msgid="2524791952735579082">"Царайгаар түгжээ тайлахтай холбоотой асуудал"</string>
<string name="face_recalibrate_notification_content" msgid="3064513770251355594">"Нүүрний загвараа устгахын тулд товшоод, дараа нь царайгаа дахин нэмнэ үү"</string>
- <string name="face_setup_notification_title" msgid="8843461561970741790">"Царайгаар түгжээ тайлахыг тохируулах"</string>
- <string name="face_setup_notification_content" msgid="5463999831057751676">"Утас руугаа харж түгжээг нь тайлна уу"</string>
<string name="face_sensor_privacy_enabled" msgid="7407126963510598508">"Царайгаар түгжээ тайлахыг ашиглахын тулд Тохиргоо > Нууцлал хэсэгт "<b>" Камерын хандалтыг "</b>" асаана уу"</string>
- <string name="fingerprint_setup_notification_title" msgid="2002630611398849495">"Түгжээ тайлах илүү олон арга тохируулна уу"</string>
- <string name="fingerprint_setup_notification_content" msgid="205578121848324852">"Хурууны хээ нэмэхийн тулд товшино уу"</string>
<string name="fingerprint_recalibrate_notification_name" msgid="1414578431898579354">"Хурууны хээгээр түгжээ тайлах"</string>
<string name="fingerprint_recalibrate_notification_title" msgid="2406561052064558497">"Хурууны хээ мэдрэгч ашиглах боломжгүй"</string>
<string name="fingerprint_recalibrate_notification_content" msgid="8519935717822194943">"Засварын үйлчилгээ үзүүлэгчид зочилно уу."</string>
@@ -2031,7 +2031,8 @@
<string name="autofill_update_title" msgid="3630695947047069136"><b>"<xliff:g id="LABEL">%1$s</xliff:g>"</b>"-д шинэчлэх үү?"</string>
<string name="autofill_update_title_with_type" msgid="5264152633488495704">"<xliff:g id="TYPE">%1$s</xliff:g>-г "<b>"<xliff:g id="LABEL">%2$s</xliff:g>"</b>"-д шинэчлэх үү?"</string>
<string name="autofill_update_title_with_2types" msgid="1797514386321086273">"<xliff:g id="TYPE_0">%1$s</xliff:g> болон <xliff:g id="TYPE_1">%2$s</xliff:g>-г "<b>"<xliff:g id="LABEL">%3$s</xliff:g>"</b>"-д шинэчлэх үү?"</string>
- <string name="autofill_update_title_with_3types" msgid="1312232153076212291">"Эдгээр зүйлийг буюу <xliff:g id="TYPE_0">%1$s</xliff:g>, <xliff:g id="TYPE_1">%2$s</xliff:g> болон <xliff:g id="TYPE_2">%3$s</xliff:g>-г "<b>"<xliff:g id="LABEL">%4$s</xliff:g>"</b>"-д шинэчлэх үү?"</string>
+ <!-- no translation found for autofill_update_title_with_3types (8285767070604652626) -->
+ <skip />
<string name="autofill_save_yes" msgid="8035743017382012850">"Хадгалах"</string>
<string name="autofill_save_no" msgid="9212826374207023544">"Үгүй, баярлалаа"</string>
<string name="autofill_save_notnow" msgid="2853932672029024195">"Одоо биш"</string>
diff --git a/core/res/res/values-mr/strings.xml b/core/res/res/values-mr/strings.xml
index c469e36..effddd6 100644
--- a/core/res/res/values-mr/strings.xml
+++ b/core/res/res/values-mr/strings.xml
@@ -669,14 +669,14 @@
</string-array>
<string name="fingerprint_error_vendor_unknown" msgid="4170002184907291065">"काहीतरी चूक झाली. पुन्हा प्रयत्न करा."</string>
<string name="fingerprint_icon_content_description" msgid="4741068463175388817">"फिंगरप्रिंट आयकन"</string>
+ <string name="device_unlock_notification_name" msgid="2632928999862915709">"डिव्हाइस अनलॉक"</string>
+ <string name="alternative_unlock_setup_notification_title" msgid="6241508547901933544">"अनलॉक करण्याचा दुसरी पद्धत वापरून पहा"</string>
+ <string name="alternative_face_setup_notification_content" msgid="3384959224091897331">"तुमचे फिंगरप्रिंट ओळखले जात नाही, तेव्हा फेस अनलॉक वापरा, जसे की तुमची बोटे ओली असताना"</string>
+ <string name="alternative_fp_setup_notification_content" msgid="7454096947415721639">"तुमचा चेहरा ओळखला जात नाही, तेव्हा फिंगरप्रिंट अनलॉक वापरा, जसे की पुरेसा प्रकाश नसताना"</string>
<string name="face_recalibrate_notification_name" msgid="7311163114750748686">"फेस अनलॉक"</string>
<string name="face_recalibrate_notification_title" msgid="2524791952735579082">"फेस अनलॉकसंबंधित समस्या"</string>
<string name="face_recalibrate_notification_content" msgid="3064513770251355594">"फेस मॉडेल हटवण्यासाठी टॅप करा, त्यानंतर तुमचा चेहरा पुन्हा जोडा"</string>
- <string name="face_setup_notification_title" msgid="8843461561970741790">"फेस अनलॉक सेट करा"</string>
- <string name="face_setup_notification_content" msgid="5463999831057751676">"तुमच्या फोनकडे पाहून तो अनलॉक करा"</string>
<string name="face_sensor_privacy_enabled" msgid="7407126963510598508">"फेस अनलॉक वापरण्यासाठी, सेटिंग्ज > गोपनीयता येथे "<b>"कॅमेरा अॅक्सेस"</b>" सुरू करा"</string>
- <string name="fingerprint_setup_notification_title" msgid="2002630611398849495">"अनलॉक करण्याच्या आणखी पद्धती सेट करा"</string>
- <string name="fingerprint_setup_notification_content" msgid="205578121848324852">"फिंगरप्रिंट जोडण्यासाठी टॅप करा"</string>
<string name="fingerprint_recalibrate_notification_name" msgid="1414578431898579354">"फिंगरप्रिंट अनलॉक"</string>
<string name="fingerprint_recalibrate_notification_title" msgid="2406561052064558497">"फिंगरप्रिंट सेन्सर वापरू शकत नाही"</string>
<string name="fingerprint_recalibrate_notification_content" msgid="8519935717822194943">"दुरुस्तीच्या सेवा पुरवठादाराला भेट द्या."</string>
@@ -1596,7 +1596,7 @@
<string name="data_usage_restricted_body" msgid="5338694433686077733">"प्रतिबंध काढण्यासाठी टॅप करा."</string>
<string name="data_usage_rapid_title" msgid="2950192123248740375">"मोबाइल डेटाचा उच्च वापर"</string>
<string name="data_usage_rapid_body" msgid="3886676853263693432">"तुमच्या अॅप्सनी नेहमीपेक्षा जास्त डेटा वापरला आहे"</string>
- <string name="data_usage_rapid_app_body" msgid="5425779218506513861">"<xliff:g id="APP">%s</xliff:g> ने नेहमीपेक्षा जास्त डेटा वपरला आहे"</string>
+ <string name="data_usage_rapid_app_body" msgid="5425779218506513861">"<xliff:g id="APP">%s</xliff:g> ने नेहमीपेक्षा जास्त डेटा वापरला आहे"</string>
<string name="ssl_certificate" msgid="5690020361307261997">"सुरक्षितता प्रमाणपत्र"</string>
<string name="ssl_certificate_is_valid" msgid="7293675884598527081">"हे प्रमाणपत्र वैध आहे."</string>
<string name="issued_to" msgid="5975877665505297662">"यावर जारी केले:"</string>
@@ -2031,7 +2031,8 @@
<string name="autofill_update_title" msgid="3630695947047069136"><b>"<xliff:g id="LABEL">%1$s</xliff:g>"</b>" मध्ये अपडेट करायचे का?"</string>
<string name="autofill_update_title_with_type" msgid="5264152633488495704">"<xliff:g id="TYPE">%1$s</xliff:g>, "<b>"<xliff:g id="LABEL">%2$s</xliff:g>"</b>" मध्ये अपडेट करायचे का?"</string>
<string name="autofill_update_title_with_2types" msgid="1797514386321086273">"<xliff:g id="TYPE_0">%1$s</xliff:g> आणि <xliff:g id="TYPE_1">%2$s</xliff:g>, "<b>"<xliff:g id="LABEL">%3$s</xliff:g>"</b>" मध्ये अपडेट करायचे का?"</string>
- <string name="autofill_update_title_with_3types" msgid="1312232153076212291">"हे आयटम "<b>"<xliff:g id="LABEL">%4$s</xliff:g>"</b>": <xliff:g id="TYPE_0">%1$s</xliff:g>, <xliff:g id="TYPE_1">%2$s</xliff:g> आणि <xliff:g id="TYPE_2">%3$s</xliff:g> मध्ये अपडेट करायचे का?"</string>
+ <!-- no translation found for autofill_update_title_with_3types (8285767070604652626) -->
+ <skip />
<string name="autofill_save_yes" msgid="8035743017382012850">"सेव्ह करा"</string>
<string name="autofill_save_no" msgid="9212826374207023544">"नाही, नको"</string>
<string name="autofill_save_notnow" msgid="2853932672029024195">"आता नको"</string>
diff --git a/core/res/res/values-ms/strings.xml b/core/res/res/values-ms/strings.xml
index 6ee7721..bcaa06d 100644
--- a/core/res/res/values-ms/strings.xml
+++ b/core/res/res/values-ms/strings.xml
@@ -669,14 +669,14 @@
</string-array>
<string name="fingerprint_error_vendor_unknown" msgid="4170002184907291065">"Kesilapan telah berlaku. Cuba lagi."</string>
<string name="fingerprint_icon_content_description" msgid="4741068463175388817">"Ikon cap jari"</string>
+ <string name="device_unlock_notification_name" msgid="2632928999862915709">"Buka kunci peranti"</string>
+ <string name="alternative_unlock_setup_notification_title" msgid="6241508547901933544">"Cuba cara lain untuk membuka kunci"</string>
+ <string name="alternative_face_setup_notification_content" msgid="3384959224091897331">"Gunakan Buka Kunci Wajah apabila cap jari anda tidak dapat dicam, seperti apabila jari anda basah"</string>
+ <string name="alternative_fp_setup_notification_content" msgid="7454096947415721639">"Gunakan Buka Kunci Cap Jari apabila wajah anda tidak dapat dicam, seperti apabila cahaya tidak mencukupi"</string>
<string name="face_recalibrate_notification_name" msgid="7311163114750748686">"Buka Kunci Wajah"</string>
<string name="face_recalibrate_notification_title" msgid="2524791952735579082">"Isu dengan Buka Kunci Wajah"</string>
<string name="face_recalibrate_notification_content" msgid="3064513770251355594">"Ketik untuk memadamkan model wajah anda, kemudian tambahkan wajah anda semula"</string>
- <string name="face_setup_notification_title" msgid="8843461561970741790">"Sediakan Buka Kunci Wajah"</string>
- <string name="face_setup_notification_content" msgid="5463999831057751676">"Buka kunci telefon anda dengan melihat telefon anda"</string>
<string name="face_sensor_privacy_enabled" msgid="7407126963510598508">"Untuk menggunakan Buka Kunci Wajah, hidupkan "<b>"akses Kamera"</b>" dalam Tetapan > Privasi"</string>
- <string name="fingerprint_setup_notification_title" msgid="2002630611398849495">"Sediakan lebih banyak cara untuk membuka kunci"</string>
- <string name="fingerprint_setup_notification_content" msgid="205578121848324852">"Ketik untuk menambahkan cap jari"</string>
<string name="fingerprint_recalibrate_notification_name" msgid="1414578431898579354">"Buka Kunci Cap Jari"</string>
<string name="fingerprint_recalibrate_notification_title" msgid="2406561052064558497">"Tidak boleh menggunakan penderia cap jari"</string>
<string name="fingerprint_recalibrate_notification_content" msgid="8519935717822194943">"Lawati penyedia pembaikan."</string>
@@ -2031,7 +2031,8 @@
<string name="autofill_update_title" msgid="3630695947047069136">"Kemas kini dalam "<b>"<xliff:g id="LABEL">%1$s</xliff:g>"</b>"?"</string>
<string name="autofill_update_title_with_type" msgid="5264152633488495704">"Kemas kini <xliff:g id="TYPE">%1$s</xliff:g> dalam "<b>"<xliff:g id="LABEL">%2$s</xliff:g>"</b>"?"</string>
<string name="autofill_update_title_with_2types" msgid="1797514386321086273">"Kemas kini <xliff:g id="TYPE_0">%1$s</xliff:g> dan <xliff:g id="TYPE_1">%2$s</xliff:g> dalam "<b>"<xliff:g id="LABEL">%3$s</xliff:g>"</b>"?"</string>
- <string name="autofill_update_title_with_3types" msgid="1312232153076212291">"Kemas kini item ini dalam "<b>"<xliff:g id="LABEL">%4$s</xliff:g>"</b>": <xliff:g id="TYPE_0">%1$s</xliff:g>, <xliff:g id="TYPE_1">%2$s</xliff:g> dan <xliff:g id="TYPE_2">%3$s</xliff:g> ?"</string>
+ <!-- no translation found for autofill_update_title_with_3types (8285767070604652626) -->
+ <skip />
<string name="autofill_save_yes" msgid="8035743017382012850">"Simpan"</string>
<string name="autofill_save_no" msgid="9212826374207023544">"Tidak perlu"</string>
<string name="autofill_save_notnow" msgid="2853932672029024195">"Bukan sekarang"</string>
diff --git a/core/res/res/values-my/strings.xml b/core/res/res/values-my/strings.xml
index 51cf285..924bfe0 100644
--- a/core/res/res/values-my/strings.xml
+++ b/core/res/res/values-my/strings.xml
@@ -669,14 +669,14 @@
</string-array>
<string name="fingerprint_error_vendor_unknown" msgid="4170002184907291065">"တစ်ခုခုမှားသွားသည်။ ထပ်စမ်းကြည့်ပါ။"</string>
<string name="fingerprint_icon_content_description" msgid="4741068463175388817">"လက်ဗွေ သင်္ကေတ"</string>
+ <string name="device_unlock_notification_name" msgid="2632928999862915709">"စက်ပစ္စည်းဖွင့်ခြင်း"</string>
+ <string name="alternative_unlock_setup_notification_title" msgid="6241508547901933544">"ဖွင့်ရန် နည်းလမ်းနောက်တစ်ခု စမ်းကြည့်ပါ"</string>
+ <string name="alternative_face_setup_notification_content" msgid="3384959224091897331">"သင့်လက်ချောင်းများ ရေစိုနေချိန်ကဲ့သို့ သင်၏လက်ဗွေကို မမှတ်မိသောအခါ ‘မျက်နှာပြ လော့ခ်ဖွင့်ခြင်း’ သုံးပါ"</string>
+ <string name="alternative_fp_setup_notification_content" msgid="7454096947415721639">"အလင်းရောင် အလုံအလောက်မရချိန်ကဲ့သို့ သင့်မျက်နှာကို မမှတ်မိသောအခါ ‘လက်ဗွေသုံး လော့ခ်ဖွင့်ခြင်း’ သုံးပါ"</string>
<string name="face_recalibrate_notification_name" msgid="7311163114750748686">"မျက်နှာပြ လော့ခ်ဖွင့်ခြင်း"</string>
<string name="face_recalibrate_notification_title" msgid="2524791952735579082">"‘မျက်နှာပြ လော့ခ်ဖွင့်ခြင်း’ ဆိုင်ရာ ပြဿနာ"</string>
<string name="face_recalibrate_notification_content" msgid="3064513770251355594">"သင်၏မျက်နှာနမူနာကို ဖျက်ရန် တို့ပါ။ ထို့နောက် သင့်မျက်နှာကို ထပ်ထည့်ပါ"</string>
- <string name="face_setup_notification_title" msgid="8843461561970741790">"မျက်နှာပြ လော့ခ်ဖွင့်ခြင်းကို ထည့်သွင်းပါ"</string>
- <string name="face_setup_notification_content" msgid="5463999831057751676">"သင့်ဖုန်းကိုကြည့်၍ သော့ဖွင့်ပါ"</string>
<string name="face_sensor_privacy_enabled" msgid="7407126963510598508">"မျက်နှာပြ လော့ခ်ဖွင့်ခြင်းကို သုံးရန် "<b>"ကင်မရာ သုံးခွင့်"</b>" ကို ‘ဆက်တင်များ > ကန့်သတ်ဆက်တင်’ တွင်ဖွင့်ပါ"</string>
- <string name="fingerprint_setup_notification_title" msgid="2002630611398849495">"သော့ဖွင့်ရန် နောက်ထပ်နည်းလမ်းများကို စနစ်ထည့်သွင်းပါ"</string>
- <string name="fingerprint_setup_notification_content" msgid="205578121848324852">"လက်ဗွေထည့်ရန် တို့ပါ"</string>
<string name="fingerprint_recalibrate_notification_name" msgid="1414578431898579354">"လက်ဗွေသုံး လော့ခ်ဖွင့်ခြင်း"</string>
<string name="fingerprint_recalibrate_notification_title" msgid="2406561052064558497">"လက်ဗွေ အာရုံခံကိရိယာကို အသုံးပြု၍ မရပါ"</string>
<string name="fingerprint_recalibrate_notification_content" msgid="8519935717822194943">"ပြုပြင်ရေး ဝန်ဆောင်မှုပေးသူထံသို့ သွားပါ။"</string>
@@ -2031,7 +2031,8 @@
<string name="autofill_update_title" msgid="3630695947047069136"><b>"<xliff:g id="LABEL">%1$s</xliff:g>"</b>"တွင် အပ်ဒိတ်လုပ်လိုပါသလား။"</string>
<string name="autofill_update_title_with_type" msgid="5264152633488495704">"<xliff:g id="TYPE">%1$s</xliff:g> ကို "<b>"<xliff:g id="LABEL">%2$s</xliff:g>"</b>" တွင် အပ်ဒိတ်လုပ်လိုပါသလား။"</string>
<string name="autofill_update_title_with_2types" msgid="1797514386321086273">"<xliff:g id="TYPE_0">%1$s</xliff:g> နှင့် <xliff:g id="TYPE_1">%2$s</xliff:g> ကို "<b>"<xliff:g id="LABEL">%3$s</xliff:g>"</b>"တွင် အပ်ဒိတ်လုပ်လိုပါသလား။"</string>
- <string name="autofill_update_title_with_3types" msgid="1312232153076212291">"ဤအချက်အလက်များကို "<b>"<xliff:g id="LABEL">%4$s</xliff:g>"</b>"တွင် အပ်ဒိတ်လုပ်လိုပါသလား- <xliff:g id="TYPE_0">%1$s</xliff:g>၊ <xliff:g id="TYPE_1">%2$s</xliff:g> နှင့် <xliff:g id="TYPE_2">%3$s</xliff:g>။"</string>
+ <!-- no translation found for autofill_update_title_with_3types (8285767070604652626) -->
+ <skip />
<string name="autofill_save_yes" msgid="8035743017382012850">"သိမ်းရန်"</string>
<string name="autofill_save_no" msgid="9212826374207023544">"မလိုပါ"</string>
<string name="autofill_save_notnow" msgid="2853932672029024195">"ယခုမလုပ်ပါ"</string>
diff --git a/core/res/res/values-nb/strings.xml b/core/res/res/values-nb/strings.xml
index d48e831e..7ae201c 100644
--- a/core/res/res/values-nb/strings.xml
+++ b/core/res/res/values-nb/strings.xml
@@ -669,14 +669,14 @@
</string-array>
<string name="fingerprint_error_vendor_unknown" msgid="4170002184907291065">"Noe gikk galt. Prøv på nytt."</string>
<string name="fingerprint_icon_content_description" msgid="4741068463175388817">"Ikon for fingeravtrykk"</string>
+ <string name="device_unlock_notification_name" msgid="2632928999862915709">"Enhetsopplåsing"</string>
+ <string name="alternative_unlock_setup_notification_title" msgid="6241508547901933544">"Prøv å låse opp på en annen måte"</string>
+ <string name="alternative_face_setup_notification_content" msgid="3384959224091897331">"Bruk ansiktslåsen når fingeravtrykket ditt ikke gjenkjennes, for eksempel når du har våte fingre"</string>
+ <string name="alternative_fp_setup_notification_content" msgid="7454096947415721639">"Bruk opplåsing med fingeravtrykk når ansiktet ditt ikke gjenkjennes, for eksempel når det ikke er nok lys rundt deg"</string>
<string name="face_recalibrate_notification_name" msgid="7311163114750748686">"Ansiktslås"</string>
<string name="face_recalibrate_notification_title" msgid="2524791952735579082">"Problem med ansiktslås"</string>
<string name="face_recalibrate_notification_content" msgid="3064513770251355594">"Trykk for å slette ansiktsmodellen din, og legg deretter til ansiktet på nytt"</string>
- <string name="face_setup_notification_title" msgid="8843461561970741790">"Konfigurer ansiktslås"</string>
- <string name="face_setup_notification_content" msgid="5463999831057751676">"Lås opp telefonen ved å se på den"</string>
<string name="face_sensor_privacy_enabled" msgid="7407126963510598508">"For å bruke ansiktslås, slå på "<b>"Kameratilgang"</b>" i Innstillinger > Personvern"</string>
- <string name="fingerprint_setup_notification_title" msgid="2002630611398849495">"Konfigurer flere måter å låse opp på"</string>
- <string name="fingerprint_setup_notification_content" msgid="205578121848324852">"Trykk for å legge til et fingeravtrykk"</string>
<string name="fingerprint_recalibrate_notification_name" msgid="1414578431898579354">"Opplåsing med fingeravtrykk"</string>
<string name="fingerprint_recalibrate_notification_title" msgid="2406561052064558497">"Kan ikke bruke fingeravtrykkssensoren"</string>
<string name="fingerprint_recalibrate_notification_content" msgid="8519935717822194943">"Gå til en reparasjonsleverandør."</string>
@@ -2031,7 +2031,8 @@
<string name="autofill_update_title" msgid="3630695947047069136">"Vil du oppdatere i "<b>"<xliff:g id="LABEL">%1$s</xliff:g>"</b>"?"</string>
<string name="autofill_update_title_with_type" msgid="5264152633488495704">"Vil du oppdatere <xliff:g id="TYPE">%1$s</xliff:g> i "<b>"<xliff:g id="LABEL">%2$s</xliff:g>"</b>"?"</string>
<string name="autofill_update_title_with_2types" msgid="1797514386321086273">"Vil du oppdatere <xliff:g id="TYPE_0">%1$s</xliff:g> og <xliff:g id="TYPE_1">%2$s</xliff:g> i "<b>"<xliff:g id="LABEL">%3$s</xliff:g>"</b>"?"</string>
- <string name="autofill_update_title_with_3types" msgid="1312232153076212291">"Vil du oppdatere disse elementene i "<b>"<xliff:g id="LABEL">%4$s</xliff:g>"</b>": <xliff:g id="TYPE_0">%1$s</xliff:g>, <xliff:g id="TYPE_1">%2$s</xliff:g> og <xliff:g id="TYPE_2">%3$s</xliff:g>?"</string>
+ <!-- no translation found for autofill_update_title_with_3types (8285767070604652626) -->
+ <skip />
<string name="autofill_save_yes" msgid="8035743017382012850">"Lagre"</string>
<string name="autofill_save_no" msgid="9212826374207023544">"Nei takk"</string>
<string name="autofill_save_notnow" msgid="2853932672029024195">"Ikke nå"</string>
diff --git a/core/res/res/values-ne/strings.xml b/core/res/res/values-ne/strings.xml
index 65ea470..7d6d09f 100644
--- a/core/res/res/values-ne/strings.xml
+++ b/core/res/res/values-ne/strings.xml
@@ -669,14 +669,14 @@
</string-array>
<string name="fingerprint_error_vendor_unknown" msgid="4170002184907291065">"कुनै समस्या आयो। फेरि प्रयास गर्नुहोस्।"</string>
<string name="fingerprint_icon_content_description" msgid="4741068463175388817">"फिंगरप्रिन्ट आइकन"</string>
+ <string name="device_unlock_notification_name" msgid="2632928999862915709">"डिभाइस अनलक"</string>
+ <string name="alternative_unlock_setup_notification_title" msgid="6241508547901933544">"अनलक गर्ने अर्को तरिका प्रयोग गरी हेर्नुहोस्"</string>
+ <string name="alternative_face_setup_notification_content" msgid="3384959224091897331">"तपाईंको फिंगरप्रिन्ट पहिचान नगरिएको अवस्थामा (जस्तै, तपाईंका औँलाहरू ओसिला भएमा) फेस अनलक प्रयोग गर्नुहोस्"</string>
+ <string name="alternative_fp_setup_notification_content" msgid="7454096947415721639">"तपाईंको अनुहार पहिचान नगरिएको अवस्थामा (जस्तै, पर्याप्त उज्यालो नभएमा) फेस अनलक प्रयोग गर्नुहोस्"</string>
<string name="face_recalibrate_notification_name" msgid="7311163114750748686">"फेस अनलक"</string>
<string name="face_recalibrate_notification_title" msgid="2524791952735579082">"फेस अनलक सुविधामा अनुहार दर्ता गर्ने क्रममा त्रुटि भयो"</string>
<string name="face_recalibrate_notification_content" msgid="3064513770251355594">"आफ्नो फेस मोडेल मेटाउन ट्याप गर्नुहोस् अनि आफ्नो अनुहार फेरि दर्ता गर्नुहोस्"</string>
- <string name="face_setup_notification_title" msgid="8843461561970741790">"फेस अनलक सेटअप गर्नुहोस्"</string>
- <string name="face_setup_notification_content" msgid="5463999831057751676">"फोनमा हेरेकै भरमा फोन अनलक गर्नुहोस्"</string>
<string name="face_sensor_privacy_enabled" msgid="7407126963510598508">"फेस अनलक प्रयोग गर्न \"सेटिङ तथा गोपनीयता\" मा गई "<b>"क्यामेरा प्रयोग गर्ने अनुमति"</b>" दिनुहोस्"</string>
- <string name="fingerprint_setup_notification_title" msgid="2002630611398849495">"अनलक गर्ने अन्य तरिकाहरू सेटअप गर्नुहोस्"</string>
- <string name="fingerprint_setup_notification_content" msgid="205578121848324852">"फिंगरप्रिन्ट हाल्न ट्याप गर्नुहोस्"</string>
<string name="fingerprint_recalibrate_notification_name" msgid="1414578431898579354">"फिंगरप्रिन्ट अनलक"</string>
<string name="fingerprint_recalibrate_notification_title" msgid="2406561052064558497">"फिंगरप्रिन्ट सेन्सर प्रयोग गर्न मिल्दैन"</string>
<string name="fingerprint_recalibrate_notification_content" msgid="8519935717822194943">"फिंगरप्रिन्ट सेन्सर मर्मत गर्ने सेवा प्रदायक कम्पनीमा सम्पर्क गर्नुहोस्।"</string>
@@ -2031,7 +2031,8 @@
<string name="autofill_update_title" msgid="3630695947047069136"><b>"<xliff:g id="LABEL">%1$s</xliff:g>"</b>" मा अद्यावधिक गर्ने हो?"</string>
<string name="autofill_update_title_with_type" msgid="5264152633488495704">"<xliff:g id="TYPE">%1$s</xliff:g> लाई "<b>"<xliff:g id="LABEL">%2$s</xliff:g>"</b>" मा अद्यावधिक गर्ने हो?"</string>
<string name="autofill_update_title_with_2types" msgid="1797514386321086273">"<xliff:g id="TYPE_0">%1$s</xliff:g> र <xliff:g id="TYPE_1">%2$s</xliff:g> लाई "<b>"<xliff:g id="LABEL">%3$s</xliff:g>"</b>" मा अद्यावधिक गर्ने हो?"</string>
- <string name="autofill_update_title_with_3types" msgid="1312232153076212291">"यी वस्तुहरू "<b>"<xliff:g id="LABEL">%4$s</xliff:g>"</b>" मा अपडेट गर्नुहोस्: <xliff:g id="TYPE_0">%1$s</xliff:g>, <xliff:g id="TYPE_1">%2$s</xliff:g> र <xliff:g id="TYPE_2">%3$s</xliff:g> हो?"</string>
+ <!-- no translation found for autofill_update_title_with_3types (8285767070604652626) -->
+ <skip />
<string name="autofill_save_yes" msgid="8035743017382012850">"सेभ गर्नुहोस्"</string>
<string name="autofill_save_no" msgid="9212826374207023544">"पर्दैन, धन्यवाद"</string>
<string name="autofill_save_notnow" msgid="2853932672029024195">"अहिले होइन"</string>
diff --git a/core/res/res/values-nl/strings.xml b/core/res/res/values-nl/strings.xml
index 9a00d68..f92b394 100644
--- a/core/res/res/values-nl/strings.xml
+++ b/core/res/res/values-nl/strings.xml
@@ -669,14 +669,14 @@
</string-array>
<string name="fingerprint_error_vendor_unknown" msgid="4170002184907291065">"Er is iets misgegaan. Probeer het opnieuw."</string>
<string name="fingerprint_icon_content_description" msgid="4741068463175388817">"Vingerafdruk-icoon"</string>
+ <string name="device_unlock_notification_name" msgid="2632928999862915709">"Apparaatontgrendeling"</string>
+ <string name="alternative_unlock_setup_notification_title" msgid="6241508547901933544">"Probeer een andere manier om te ontgrendelen"</string>
+ <string name="alternative_face_setup_notification_content" msgid="3384959224091897331">"Gebruik Ontgrendelen via gezichtsherkenning als je vingerafdruk niet wordt herkend, bijvoorbeeld als je vingers nat zijn"</string>
+ <string name="alternative_fp_setup_notification_content" msgid="7454096947415721639">"Gebruik Ontgrendelen met vingerafdruk als je gezicht niet wordt herkend, bijvoorbeeld als er niet genoeg licht is"</string>
<string name="face_recalibrate_notification_name" msgid="7311163114750748686">"Ontgrendelen via gezichtsherkenning"</string>
<string name="face_recalibrate_notification_title" msgid="2524791952735579082">"Probleem met Ontgrendelen via gezichtsherkenning"</string>
<string name="face_recalibrate_notification_content" msgid="3064513770251355594">"Tik om je gezichtsmodel te verwijderen en voeg je gezicht opnieuw toe"</string>
- <string name="face_setup_notification_title" msgid="8843461561970741790">"Ontgrendelen via gezichtsherkenning instellen"</string>
- <string name="face_setup_notification_content" msgid="5463999831057751676">"Ontgrendel je telefoon door ernaar te kijken"</string>
<string name="face_sensor_privacy_enabled" msgid="7407126963510598508">"Als je Ontgrendelen via gezichtsherkenning wilt gebruiken, zet je "<b>"Cameratoegang"</b>" aan via Instellingen > Privacy"</string>
- <string name="fingerprint_setup_notification_title" msgid="2002630611398849495">"Stel meer manieren in om te ontgrendelen"</string>
- <string name="fingerprint_setup_notification_content" msgid="205578121848324852">"Tik om een vingerafdruk toe te voegen"</string>
<string name="fingerprint_recalibrate_notification_name" msgid="1414578431898579354">"Ontgrendelen met vingerafdruk"</string>
<string name="fingerprint_recalibrate_notification_title" msgid="2406561052064558497">"Kan vingerafdruksensor niet gebruiken"</string>
<string name="fingerprint_recalibrate_notification_content" msgid="8519935717822194943">"Ga naar een reparateur."</string>
@@ -2031,7 +2031,8 @@
<string name="autofill_update_title" msgid="3630695947047069136">"Updaten in "<b>"<xliff:g id="LABEL">%1$s</xliff:g>"</b>"?"</string>
<string name="autofill_update_title_with_type" msgid="5264152633488495704">"<xliff:g id="TYPE">%1$s</xliff:g> updaten in "<b>"<xliff:g id="LABEL">%2$s</xliff:g>"</b>"?"</string>
<string name="autofill_update_title_with_2types" msgid="1797514386321086273">"<xliff:g id="TYPE_0">%1$s</xliff:g> en <xliff:g id="TYPE_1">%2$s</xliff:g> updaten in "<b>"<xliff:g id="LABEL">%3$s</xliff:g>"</b>"?"</string>
- <string name="autofill_update_title_with_3types" msgid="1312232153076212291">"Deze items updaten in "<b>"<xliff:g id="LABEL">%4$s</xliff:g>"</b>": <xliff:g id="TYPE_0">%1$s</xliff:g>, <xliff:g id="TYPE_1">%2$s</xliff:g> en <xliff:g id="TYPE_2">%3$s</xliff:g>?"</string>
+ <!-- no translation found for autofill_update_title_with_3types (8285767070604652626) -->
+ <skip />
<string name="autofill_save_yes" msgid="8035743017382012850">"Opslaan"</string>
<string name="autofill_save_no" msgid="9212826374207023544">"Nee, bedankt"</string>
<string name="autofill_save_notnow" msgid="2853932672029024195">"Niet nu"</string>
diff --git a/core/res/res/values-or/strings.xml b/core/res/res/values-or/strings.xml
index 5668e25..b94ab75 100644
--- a/core/res/res/values-or/strings.xml
+++ b/core/res/res/values-or/strings.xml
@@ -669,14 +669,14 @@
</string-array>
<string name="fingerprint_error_vendor_unknown" msgid="4170002184907291065">"କିଛି ତ୍ରୁଟି ହୋଇଛି। ପୁଣି ଚେଷ୍ଟା କରନ୍ତୁ।"</string>
<string name="fingerprint_icon_content_description" msgid="4741068463175388817">"ଟିପଚିହ୍ନ ଆଇକନ୍"</string>
+ <string name="device_unlock_notification_name" msgid="2632928999862915709">"ଡିଭାଇସ ଅନଲକ"</string>
+ <string name="alternative_unlock_setup_notification_title" msgid="6241508547901933544">"ଅନଲକ କରିବା ପାଇଁ ଅନ୍ୟ ଏକ ଉପାୟ ଚେଷ୍ଟା କରନ୍ତୁ"</string>
+ <string name="alternative_face_setup_notification_content" msgid="3384959224091897331">"ଆପଣଙ୍କର ଟିପଚିହ୍ନ ଚିହ୍ନଟ ନହେଲେ, ଯେପରିକି ଆପଣଙ୍କ ଆଙ୍ଗୁଠି ଓଦା ଥିବା ସମୟରେ ଫେସ ଅନଲକ ବ୍ୟବହାର କରନ୍ତୁ"</string>
+ <string name="alternative_fp_setup_notification_content" msgid="7454096947415721639">"ଆପଣଙ୍କର ମୁହଁ ଚିହ୍ନଟ ନହେଲେ, ଯେପରିକି ପର୍ଯ୍ୟାପ୍ତ ଆଲୋକ ନଥିବା ସମୟରେ ଫିଙ୍ଗରପ୍ରିଣ୍ଟ ଅନଲକ ବ୍ୟବହାର କରନ୍ତୁ"</string>
<string name="face_recalibrate_notification_name" msgid="7311163114750748686">"ଫେସ ଅନଲକ"</string>
<string name="face_recalibrate_notification_title" msgid="2524791952735579082">"ଫେସ୍ ଅନଲକ୍ ସହ ସମସ୍ୟା"</string>
<string name="face_recalibrate_notification_content" msgid="3064513770251355594">"ଆପଣଙ୍କ ଫେସ୍ ମଡେଲକୁ ଡିଲିଟ୍ କରିବାକୁ ଟାପ୍ କରନ୍ତୁ, ତା\'ପରେ ପୁଣି ଆପଣଙ୍କ ଫେସ୍ ଯୋଗ କରନ୍ତୁ"</string>
- <string name="face_setup_notification_title" msgid="8843461561970741790">"ଫେସ ଅନଲକ ସେଟ ଅପ କରନ୍ତୁ"</string>
- <string name="face_setup_notification_content" msgid="5463999831057751676">"ଫୋନକୁ ଦେଖି ଏହାକୁ ଅନଲକ୍ କରନ୍ତୁ"</string>
<string name="face_sensor_privacy_enabled" msgid="7407126963510598508">"ଫେସ ଅନଲକ ବ୍ୟବହାର କରିବା ପାଇଁ, ସେଟିଂସ ଏବଂ ଗୋପନୀୟତାରେ "<b>"କ୍ୟାମେରା ଆକ୍ସେସ"</b>"କୁ ଚାଲୁ କରନ୍ତୁ"</string>
- <string name="fingerprint_setup_notification_title" msgid="2002630611398849495">"ଅନଲକ୍ କରିବା ପାଇଁ ଆହୁରି ଅଧିକ ଉପାୟ ସେଟ୍ ଅପ୍ କରନ୍ତୁ"</string>
- <string name="fingerprint_setup_notification_content" msgid="205578121848324852">"ଏକ ଟିପଚିହ୍ନ ଯୋଗ କରିବାକୁ ଟାପ୍ କରନ୍ତୁ"</string>
<string name="fingerprint_recalibrate_notification_name" msgid="1414578431898579354">"ଫିଙ୍ଗରପ୍ରିଣ୍ଟ ଅନଲକ୍"</string>
<string name="fingerprint_recalibrate_notification_title" msgid="2406561052064558497">"ଟିପଚିହ୍ନ ସେନ୍ସରକୁ ବ୍ୟବହାର କରାଯାଇପାରିବ ନାହିଁ"</string>
<string name="fingerprint_recalibrate_notification_content" msgid="8519935717822194943">"ଏକ ମରାମତି କେନ୍ଦ୍ରକୁ ଭିଜିଟ୍ କରନ୍ତୁ।"</string>
@@ -2031,7 +2031,8 @@
<string name="autofill_update_title" msgid="3630695947047069136"><b>"<xliff:g id="LABEL">%1$s</xliff:g>"</b>"ରେ ଅପ୍ଡେଟ୍ କରିବେ?"</string>
<string name="autofill_update_title_with_type" msgid="5264152633488495704"><b>"<xliff:g id="LABEL">%2$s</xliff:g>"</b>"ରେ <xliff:g id="TYPE">%1$s</xliff:g>କୁ ଅପ୍ଡେଟ୍ କରିବେ।"</string>
<string name="autofill_update_title_with_2types" msgid="1797514386321086273"><b>"<xliff:g id="LABEL">%3$s</xliff:g>"</b>"ରେ <xliff:g id="TYPE_0">%1$s</xliff:g> ଏବଂ <xliff:g id="TYPE_1">%2$s</xliff:g> ଅପ୍ଡେଟ୍ କରିବେ?"</string>
- <string name="autofill_update_title_with_3types" msgid="1312232153076212291"><b>"<xliff:g id="LABEL">%4$s</xliff:g>"</b>"ରେ: <xliff:g id="TYPE_0">%1$s</xliff:g>, <xliff:g id="TYPE_1">%2$s</xliff:g> ଏବଂ <xliff:g id="TYPE_2">%3$s</xliff:g> ଆଇଟମ୍ଗୁଡ଼ିକ ଅପ୍ଡେଟ୍ କରିବେ?"</string>
+ <!-- no translation found for autofill_update_title_with_3types (8285767070604652626) -->
+ <skip />
<string name="autofill_save_yes" msgid="8035743017382012850">"ସେଭ୍ କରନ୍ତୁ"</string>
<string name="autofill_save_no" msgid="9212826374207023544">"ନାଁ, ପଚାରିଥିବାରୁ ଧନ୍ୟବାଦ"</string>
<string name="autofill_save_notnow" msgid="2853932672029024195">"ଏବେ ନୁହେଁ"</string>
diff --git a/core/res/res/values-pa/strings.xml b/core/res/res/values-pa/strings.xml
index 043c296..c17b17b 100644
--- a/core/res/res/values-pa/strings.xml
+++ b/core/res/res/values-pa/strings.xml
@@ -669,14 +669,14 @@
</string-array>
<string name="fingerprint_error_vendor_unknown" msgid="4170002184907291065">"ਕੋਈ ਗੜਬੜ ਹੋ ਗਈ। ਦੁਬਾਰਾ ਕੋਸ਼ਿਸ਼ ਕਰੋ।"</string>
<string name="fingerprint_icon_content_description" msgid="4741068463175388817">"ਫਿੰਗਰਪ੍ਰਿੰਟ ਪ੍ਰਤੀਕ"</string>
+ <string name="device_unlock_notification_name" msgid="2632928999862915709">"ਡੀਵਾਈਸ ਅਣਲਾਕ"</string>
+ <string name="alternative_unlock_setup_notification_title" msgid="6241508547901933544">"ਅਣਲਾਕ ਕਰਨ ਦਾ ਕੋਈ ਹੋਰ ਤਰੀਕਾ ਅਜ਼ਮਾਓ"</string>
+ <string name="alternative_face_setup_notification_content" msgid="3384959224091897331">"ਤੁਹਾਡੇ ਫਿੰਗਰਪ੍ਰਿੰਟ ਦੀ ਪਛਾਣ ਨਾ ਹੋਣ \'ਤੇ ਫ਼ੇਸ ਅਣਲਾਕ ਦੀ ਵਰਤੋਂ ਕਰੋ, ਜਿਵੇਂ ਕਿ ਤੁਹਾਡੀਆਂ ਉਂਗਲਾਂ ਗਿੱਲੀਆਂ ਹੋਣ \'ਤੇ"</string>
+ <string name="alternative_fp_setup_notification_content" msgid="7454096947415721639">"ਤੁਹਾਡੇ ਚਿਹਰੇ ਦੀ ਪਛਾਣ ਨਾ ਹੋਣ \'ਤੇ ਫਿੰਗਰਪ੍ਰਿੰਟ ਅਣਲਾਕ ਦੀ ਵਰਤੋਂ ਕਰੋ, ਜਿਵੇਂ ਕਿ ਘੱਟ ਰੋਸ਼ਨੀ ਹੋਣ \'ਤੇ"</string>
<string name="face_recalibrate_notification_name" msgid="7311163114750748686">"ਫ਼ੇਸ ਅਣਲਾਕ"</string>
<string name="face_recalibrate_notification_title" msgid="2524791952735579082">"ਫ਼ੇਸ ਅਣਲਾਕ ਨਾਲ ਸਮੱਸਿਆ"</string>
<string name="face_recalibrate_notification_content" msgid="3064513770251355594">"ਆਪਣੇ ਚਿਹਰੇ ਦਾ ਮਾਡਲ ਮਿਟਾਉਣ ਲਈ ਟੈਪ ਕਰੋ, ਫਿਰ ਆਪਣਾ ਚਿਹਰਾ ਦੁਬਾਰਾ ਸ਼ਾਮਲ ਕਰੋ"</string>
- <string name="face_setup_notification_title" msgid="8843461561970741790">"ਫ਼ੇਸ ਅਣਲਾਕ ਦਾ ਸੈੱਟਅੱਪ ਕਰੋ"</string>
- <string name="face_setup_notification_content" msgid="5463999831057751676">"ਆਪਣੇ ਫ਼ੋਨ ਵੱਲ ਦੇਖ ਕੇ ਇਸਨੂੰ ਅਣਲਾਕ ਕਰੋ"</string>
<string name="face_sensor_privacy_enabled" msgid="7407126963510598508">"ਫ਼ੇਸ ਅਣਲਾਕ ਨੂੰ ਵਰਤਣ ਲਈ, ਸੈਟਿੰਗਾਂ > ਪਰਦੇਦਾਰੀ ਵਿੱਚ ਜਾ ਕੇ "<b>"ਕੈਮਰਾ ਪਹੁੰਚ"</b>" ਨੂੰ ਚਾਲੂ ਕਰੋ"</string>
- <string name="fingerprint_setup_notification_title" msgid="2002630611398849495">"ਅਣਲਾਕ ਕਰਨ ਦੇ ਹੋਰ ਤਰੀਕਿਆਂ ਦਾ ਸੈੱਟਅੱਪ ਕਰੋ"</string>
- <string name="fingerprint_setup_notification_content" msgid="205578121848324852">"ਫਿੰਗਰਪ੍ਰਿੰਟ ਸ਼ਾਮਲ ਕਰਨ ਲਈ ਟੈਪ ਕਰੋ"</string>
<string name="fingerprint_recalibrate_notification_name" msgid="1414578431898579354">"ਫਿੰਗਰਪ੍ਰਿੰਟ ਅਣਲਾਕ"</string>
<string name="fingerprint_recalibrate_notification_title" msgid="2406561052064558497">"ਫਿੰਗਰਪ੍ਰਿੰਟ ਸੈਂਸਰ ਦੀ ਵਰਤੋਂ ਨਹੀਂ ਕੀਤੀ ਜਾ ਸਕਦੀ"</string>
<string name="fingerprint_recalibrate_notification_content" msgid="8519935717822194943">"ਮੁਰੰਮਤ ਪ੍ਰਦਾਨਕ \'ਤੇ ਜਾਓ।"</string>
@@ -2031,7 +2031,8 @@
<string name="autofill_update_title" msgid="3630695947047069136">"ਕੀ ਤੁਸੀਂ "<b>"<xliff:g id="LABEL">%1$s</xliff:g>"</b>" ਵਿੱਚ ਅੱਪਡੇਟ ਕਰਨਾ ਚਾਹੁੰਦੇ ਹੋ?"</string>
<string name="autofill_update_title_with_type" msgid="5264152633488495704">"ਕੀ ਤੁਸੀਂ "<b>"<xliff:g id="LABEL">%2$s</xliff:g>"</b>" ਵਿੱਚ <xliff:g id="TYPE">%1$s</xliff:g> ਨੂੰ ਅੱਪਡੇਟ ਕਰਨਾ ਚਾਹੁੰਦੇ ਹੋ?"</string>
<string name="autofill_update_title_with_2types" msgid="1797514386321086273">"ਕੀ ਤੁਸੀਂ "<b>"<xliff:g id="LABEL">%3$s</xliff:g>"</b>" ਵਿੱਚ <xliff:g id="TYPE_0">%1$s</xliff:g> ਅਤੇ <xliff:g id="TYPE_1">%2$s</xliff:g> ਨੂੰ ਅੱਪਡੇਟ ਕਰਨਾ ਚਾਹੁੰਦੇ ਹੋ?"</string>
- <string name="autofill_update_title_with_3types" msgid="1312232153076212291">"ਕੀ ਤੁਸੀਂ "<b>"<xliff:g id="LABEL">%4$s</xliff:g>"</b>" ਵਿੱਚ ਇਹਨਾਂ ਆਈਟਮਾਂ ਨੂੰ ਅੱਪਡੇਟ ਕਰਨਾ ਚਾਹੁੰਦੇ ਹੋ: <xliff:g id="TYPE_0">%1$s</xliff:g>, <xliff:g id="TYPE_1">%2$s</xliff:g>, ਅਤੇ <xliff:g id="TYPE_2">%3$s</xliff:g>?"</string>
+ <!-- no translation found for autofill_update_title_with_3types (8285767070604652626) -->
+ <skip />
<string name="autofill_save_yes" msgid="8035743017382012850">"ਰੱਖਿਅਤ ਕਰੋ"</string>
<string name="autofill_save_no" msgid="9212826374207023544">"ਨਹੀਂ ਧੰਨਵਾਦ"</string>
<string name="autofill_save_notnow" msgid="2853932672029024195">"ਹੁਣੇ ਨਹੀਂ"</string>
diff --git a/core/res/res/values-pl/strings.xml b/core/res/res/values-pl/strings.xml
index 709b067..45353b1 100644
--- a/core/res/res/values-pl/strings.xml
+++ b/core/res/res/values-pl/strings.xml
@@ -671,14 +671,14 @@
</string-array>
<string name="fingerprint_error_vendor_unknown" msgid="4170002184907291065">"Coś poszło nie tak. Spróbuj ponownie."</string>
<string name="fingerprint_icon_content_description" msgid="4741068463175388817">"Ikona odcisku palca"</string>
+ <string name="device_unlock_notification_name" msgid="2632928999862915709">"Odblokowywanie urządzenia"</string>
+ <string name="alternative_unlock_setup_notification_title" msgid="6241508547901933544">"Wypróbuj inną metodę odblokowywania"</string>
+ <string name="alternative_face_setup_notification_content" msgid="3384959224091897331">"Jeśli odcisk palca nie jest rozpoznawany, ponieważ przykładowo masz mokre palce, użyj funkcji rozpoznawania twarzy"</string>
+ <string name="alternative_fp_setup_notification_content" msgid="7454096947415721639">"Jeśli twarz nie jest rozpoznawana, na przykład z powodu słabego oświetlenia, użyj funkcji odblokowywania odciskiem palca"</string>
<string name="face_recalibrate_notification_name" msgid="7311163114750748686">"Rozpoznawanie twarzy"</string>
<string name="face_recalibrate_notification_title" msgid="2524791952735579082">"Problem z rozpoznawaniem twarzy"</string>
<string name="face_recalibrate_notification_content" msgid="3064513770251355594">"Kliknij, aby usunąć model twarzy, a następnie ponownie dodaj skan twarzy"</string>
- <string name="face_setup_notification_title" msgid="8843461561970741790">"Skonfiguruj rozpoznawanie twarzy"</string>
- <string name="face_setup_notification_content" msgid="5463999831057751676">"Popatrz na ekran telefonu, aby go odblokować"</string>
<string name="face_sensor_privacy_enabled" msgid="7407126963510598508">"Aby używać rozpoznawania twarzy, włącz "<b>"dostęp do aparatu"</b>" w Ustawieniach i prywatności"</string>
- <string name="fingerprint_setup_notification_title" msgid="2002630611398849495">"Skonfiguruj więcej sposobów odblokowywania"</string>
- <string name="fingerprint_setup_notification_content" msgid="205578121848324852">"Kliknij, aby dodać odcisk palca"</string>
<string name="fingerprint_recalibrate_notification_name" msgid="1414578431898579354">"Odblokowywanie odciskiem palca"</string>
<string name="fingerprint_recalibrate_notification_title" msgid="2406561052064558497">"Nie można użyć czytnika linii papilarnych"</string>
<string name="fingerprint_recalibrate_notification_content" msgid="8519935717822194943">"Odwiedź serwis."</string>
@@ -2033,7 +2033,8 @@
<string name="autofill_update_title" msgid="3630695947047069136">"Zaktualizować w: "<b>"<xliff:g id="LABEL">%1$s</xliff:g>"</b>"?"</string>
<string name="autofill_update_title_with_type" msgid="5264152633488495704">"Zaktualizować: <xliff:g id="TYPE">%1$s</xliff:g> w: "<b>"<xliff:g id="LABEL">%2$s</xliff:g>"</b>"?"</string>
<string name="autofill_update_title_with_2types" msgid="1797514386321086273">"Zaktualizować: <xliff:g id="TYPE_0">%1$s</xliff:g> i <xliff:g id="TYPE_1">%2$s</xliff:g> w: "<b>"<xliff:g id="LABEL">%3$s</xliff:g>"</b>"?"</string>
- <string name="autofill_update_title_with_3types" msgid="1312232153076212291">"Zaktualizować w: "<b>"<xliff:g id="LABEL">%4$s</xliff:g>"</b>" te elementy: <xliff:g id="TYPE_0">%1$s</xliff:g>, <xliff:g id="TYPE_1">%2$s</xliff:g> i <xliff:g id="TYPE_2">%3$s</xliff:g>?"</string>
+ <!-- no translation found for autofill_update_title_with_3types (8285767070604652626) -->
+ <skip />
<string name="autofill_save_yes" msgid="8035743017382012850">"Zapisz"</string>
<string name="autofill_save_no" msgid="9212826374207023544">"Nie, dziękuję"</string>
<string name="autofill_save_notnow" msgid="2853932672029024195">"Nie teraz"</string>
diff --git a/core/res/res/values-pt-rBR/strings.xml b/core/res/res/values-pt-rBR/strings.xml
index eb6aaeb..d4f3529 100644
--- a/core/res/res/values-pt-rBR/strings.xml
+++ b/core/res/res/values-pt-rBR/strings.xml
@@ -670,14 +670,14 @@
</string-array>
<string name="fingerprint_error_vendor_unknown" msgid="4170002184907291065">"Algo deu errado. Tente de novo."</string>
<string name="fingerprint_icon_content_description" msgid="4741068463175388817">"Ícone de impressão digital"</string>
+ <string name="device_unlock_notification_name" msgid="2632928999862915709">"Desbloqueio do dispositivo"</string>
+ <string name="alternative_unlock_setup_notification_title" msgid="6241508547901933544">"Tente desbloquear de outra maneira"</string>
+ <string name="alternative_face_setup_notification_content" msgid="3384959224091897331">"Use o Desbloqueio facial quando sua impressão digital não for reconhecida, como quando seus dedos estiverem molhados"</string>
+ <string name="alternative_fp_setup_notification_content" msgid="7454096947415721639">"Use o Desbloqueio por impressão digital quando seu rosto não for reconhecido, como quando não houver luz suficiente"</string>
<string name="face_recalibrate_notification_name" msgid="7311163114750748686">"Desbloqueio facial"</string>
<string name="face_recalibrate_notification_title" msgid="2524791952735579082">"Problema com o Desbloqueio facial"</string>
<string name="face_recalibrate_notification_content" msgid="3064513770251355594">"Toque para excluir seu modelo de rosto e crie um novo"</string>
- <string name="face_setup_notification_title" msgid="8843461561970741790">"Configurar o Desbloqueio facial"</string>
- <string name="face_setup_notification_content" msgid="5463999831057751676">"Desbloqueie o smartphone olhando para ele"</string>
<string name="face_sensor_privacy_enabled" msgid="7407126963510598508">"Para usar o Desbloqueio facial, ative o "<b>"acesso à câmera"</b>" em Configurações > Privacidade"</string>
- <string name="fingerprint_setup_notification_title" msgid="2002630611398849495">"Configure mais formas de desbloquear a tela"</string>
- <string name="fingerprint_setup_notification_content" msgid="205578121848324852">"Toque para adicionar uma impressão digital"</string>
<string name="fingerprint_recalibrate_notification_name" msgid="1414578431898579354">"Desbloqueio por impressão digital"</string>
<string name="fingerprint_recalibrate_notification_title" msgid="2406561052064558497">"Não foi possível usar o sensor de impressão digital"</string>
<string name="fingerprint_recalibrate_notification_content" msgid="8519935717822194943">"Entre em contato com uma assistência técnica."</string>
@@ -782,7 +782,7 @@
<string name="permlab_accessNetworkConditions" msgid="1270732533356286514">"detectar observações nas condições da rede"</string>
<string name="permdesc_accessNetworkConditions" msgid="2959269186741956109">"Permite que o app detecte observações nas condições da rede. Não deve ser necessário para apps comuns."</string>
<string name="permlab_setInputCalibration" msgid="932069700285223434">"alterar calibragem do dispositivo de entrada"</string>
- <string name="permdesc_setInputCalibration" msgid="2937872391426631726">"Permite que o app modifique os parâmetros de calibragem da tela sensível ao toque. Não deve ser necessário para apps normais."</string>
+ <string name="permdesc_setInputCalibration" msgid="2937872391426631726">"Permite que o app modifique os parâmetros de calibragem da tela touch. Não deve ser necessário para apps normais."</string>
<string name="permlab_accessDrmCertificates" msgid="6473765454472436597">"acessar certificados de DRM"</string>
<string name="permdesc_accessDrmCertificates" msgid="6983139753493781941">"Permite que o app provisione e use certificados de DRM. Não deve ser necessário para apps comuns."</string>
<string name="permlab_handoverStatus" msgid="7620438488137057281">"receber status de transferência do Android Beam"</string>
@@ -1595,7 +1595,7 @@
<string name="data_usage_limit_snoozed_body" msgid="545146591766765678">"Você ultrapassou <xliff:g id="SIZE">%s</xliff:g> do limite definido"</string>
<string name="data_usage_restricted_title" msgid="126711424380051268">"Dados de segundo plano restritos"</string>
<string name="data_usage_restricted_body" msgid="5338694433686077733">"Toque para remover a restrição."</string>
- <string name="data_usage_rapid_title" msgid="2950192123248740375">"Alto uso de dados móveis"</string>
+ <string name="data_usage_rapid_title" msgid="2950192123248740375">"Uso elevado de dados móveis"</string>
<string name="data_usage_rapid_body" msgid="3886676853263693432">"Seus apps usaram mais dados do que o normal"</string>
<string name="data_usage_rapid_app_body" msgid="5425779218506513861">"O app <xliff:g id="APP">%s</xliff:g> usou mais dados do que o normal"</string>
<string name="ssl_certificate" msgid="5690020361307261997">"Certificado de segurança"</string>
@@ -2032,7 +2032,8 @@
<string name="autofill_update_title" msgid="3630695947047069136">"Atualizar em "<b>"<xliff:g id="LABEL">%1$s</xliff:g>"</b>"?"</string>
<string name="autofill_update_title_with_type" msgid="5264152633488495704">"Atualizar <xliff:g id="TYPE">%1$s</xliff:g> em "<b>"<xliff:g id="LABEL">%2$s</xliff:g>"</b>"?"</string>
<string name="autofill_update_title_with_2types" msgid="1797514386321086273">"Atualizar <xliff:g id="TYPE_0">%1$s</xliff:g> e <xliff:g id="TYPE_1">%2$s</xliff:g> em "<b>"<xliff:g id="LABEL">%3$s</xliff:g>"</b>"?"</string>
- <string name="autofill_update_title_with_3types" msgid="1312232153076212291">"Atualizar estes itens em "<b>"<xliff:g id="LABEL">%4$s</xliff:g>"</b>": <xliff:g id="TYPE_0">%1$s</xliff:g>, <xliff:g id="TYPE_1">%2$s</xliff:g> e <xliff:g id="TYPE_2">%3$s</xliff:g>?"</string>
+ <!-- no translation found for autofill_update_title_with_3types (8285767070604652626) -->
+ <skip />
<string name="autofill_save_yes" msgid="8035743017382012850">"Salvar"</string>
<string name="autofill_save_no" msgid="9212826374207023544">"Agora não"</string>
<string name="autofill_save_notnow" msgid="2853932672029024195">"Agora não"</string>
diff --git a/core/res/res/values-pt-rPT/strings.xml b/core/res/res/values-pt-rPT/strings.xml
index 25a816c..53fbfd3 100644
--- a/core/res/res/values-pt-rPT/strings.xml
+++ b/core/res/res/values-pt-rPT/strings.xml
@@ -670,14 +670,14 @@
</string-array>
<string name="fingerprint_error_vendor_unknown" msgid="4170002184907291065">"Algo correu mal. Tente novamente."</string>
<string name="fingerprint_icon_content_description" msgid="4741068463175388817">"Ícone de impressão digital"</string>
+ <string name="device_unlock_notification_name" msgid="2632928999862915709">"Desbloqueio do dispositivo"</string>
+ <string name="alternative_unlock_setup_notification_title" msgid="6241508547901933544">"Experimente outra forma de desbloquear"</string>
+ <string name="alternative_face_setup_notification_content" msgid="3384959224091897331">"Use o Desbloqueio facial quando a sua impressão digital não for reconhecida, por exemplo, quando os seus dedos estiverem molhados"</string>
+ <string name="alternative_fp_setup_notification_content" msgid="7454096947415721639">"Use o Desbloqueio por impressão digital quando o seu rosto não for reconhecido, por exemplo, quando não houver luz suficiente"</string>
<string name="face_recalibrate_notification_name" msgid="7311163114750748686">"Desbloqueio facial"</string>
<string name="face_recalibrate_notification_title" msgid="2524791952735579082">"Problema com o Desbloqueio facial"</string>
<string name="face_recalibrate_notification_content" msgid="3064513770251355594">"Toque para eliminar o seu modelo de rosto e, em seguida, adicione o seu rosto novamente"</string>
- <string name="face_setup_notification_title" msgid="8843461561970741790">"Configure o Desbloqueio facial"</string>
- <string name="face_setup_notification_content" msgid="5463999831057751676">"Desbloqueie o telemóvel ao olhar para ele"</string>
<string name="face_sensor_privacy_enabled" msgid="7407126963510598508">"Para utilizar o Desbloqueio facial, ative o "<b>"Acesso à câmara"</b>" em Definições > Privacidade"</string>
- <string name="fingerprint_setup_notification_title" msgid="2002630611398849495">"Configure mais formas de desbloquear"</string>
- <string name="fingerprint_setup_notification_content" msgid="205578121848324852">"Toque para adicionar uma impressão digital"</string>
<string name="fingerprint_recalibrate_notification_name" msgid="1414578431898579354">"Desbloqueio por impressão digital"</string>
<string name="fingerprint_recalibrate_notification_title" msgid="2406561052064558497">"Não é possível utilizar o sensor de impressões digitais"</string>
<string name="fingerprint_recalibrate_notification_content" msgid="8519935717822194943">"Visite um fornecedor de serviços de reparação."</string>
@@ -1597,7 +1597,7 @@
<string name="data_usage_restricted_body" msgid="5338694433686077733">"Toque para remover a restrição."</string>
<string name="data_usage_rapid_title" msgid="2950192123248740375">"Utilização elevada de dados móveis"</string>
<string name="data_usage_rapid_body" msgid="3886676853263693432">"As suas aplicações utilizaram mais dados do que o habitual."</string>
- <string name="data_usage_rapid_app_body" msgid="5425779218506513861">"A app <xliff:g id="APP">%s</xliff:g> utilizou mais dados do que o habitual."</string>
+ <string name="data_usage_rapid_app_body" msgid="5425779218506513861">"A app <xliff:g id="APP">%s</xliff:g> usou mais dados do que o habitual"</string>
<string name="ssl_certificate" msgid="5690020361307261997">"Certificado de segurança"</string>
<string name="ssl_certificate_is_valid" msgid="7293675884598527081">"Este certificado é válido."</string>
<string name="issued_to" msgid="5975877665505297662">"Emitido para:"</string>
@@ -2032,7 +2032,8 @@
<string name="autofill_update_title" msgid="3630695947047069136">"Quer atualizar em "<b>"<xliff:g id="LABEL">%1$s</xliff:g>"</b>"?"</string>
<string name="autofill_update_title_with_type" msgid="5264152633488495704">"Quer atualizar <xliff:g id="TYPE">%1$s</xliff:g> em "<b>"<xliff:g id="LABEL">%2$s</xliff:g>"</b>"?"</string>
<string name="autofill_update_title_with_2types" msgid="1797514386321086273">"Quer atualizar <xliff:g id="TYPE_0">%1$s</xliff:g> e <xliff:g id="TYPE_1">%2$s</xliff:g> em "<b>"<xliff:g id="LABEL">%3$s</xliff:g>"</b>"?"</string>
- <string name="autofill_update_title_with_3types" msgid="1312232153076212291">"Quer atualizar estes itens em "<b>"<xliff:g id="LABEL">%4$s</xliff:g>"</b>": <xliff:g id="TYPE_0">%1$s</xliff:g>, <xliff:g id="TYPE_1">%2$s</xliff:g> e <xliff:g id="TYPE_2">%3$s</xliff:g>?"</string>
+ <!-- no translation found for autofill_update_title_with_3types (8285767070604652626) -->
+ <skip />
<string name="autofill_save_yes" msgid="8035743017382012850">"Guardar"</string>
<string name="autofill_save_no" msgid="9212826374207023544">"Não, obrigado"</string>
<string name="autofill_save_notnow" msgid="2853932672029024195">"Agora não"</string>
diff --git a/core/res/res/values-pt/strings.xml b/core/res/res/values-pt/strings.xml
index eb6aaeb..d4f3529 100644
--- a/core/res/res/values-pt/strings.xml
+++ b/core/res/res/values-pt/strings.xml
@@ -670,14 +670,14 @@
</string-array>
<string name="fingerprint_error_vendor_unknown" msgid="4170002184907291065">"Algo deu errado. Tente de novo."</string>
<string name="fingerprint_icon_content_description" msgid="4741068463175388817">"Ícone de impressão digital"</string>
+ <string name="device_unlock_notification_name" msgid="2632928999862915709">"Desbloqueio do dispositivo"</string>
+ <string name="alternative_unlock_setup_notification_title" msgid="6241508547901933544">"Tente desbloquear de outra maneira"</string>
+ <string name="alternative_face_setup_notification_content" msgid="3384959224091897331">"Use o Desbloqueio facial quando sua impressão digital não for reconhecida, como quando seus dedos estiverem molhados"</string>
+ <string name="alternative_fp_setup_notification_content" msgid="7454096947415721639">"Use o Desbloqueio por impressão digital quando seu rosto não for reconhecido, como quando não houver luz suficiente"</string>
<string name="face_recalibrate_notification_name" msgid="7311163114750748686">"Desbloqueio facial"</string>
<string name="face_recalibrate_notification_title" msgid="2524791952735579082">"Problema com o Desbloqueio facial"</string>
<string name="face_recalibrate_notification_content" msgid="3064513770251355594">"Toque para excluir seu modelo de rosto e crie um novo"</string>
- <string name="face_setup_notification_title" msgid="8843461561970741790">"Configurar o Desbloqueio facial"</string>
- <string name="face_setup_notification_content" msgid="5463999831057751676">"Desbloqueie o smartphone olhando para ele"</string>
<string name="face_sensor_privacy_enabled" msgid="7407126963510598508">"Para usar o Desbloqueio facial, ative o "<b>"acesso à câmera"</b>" em Configurações > Privacidade"</string>
- <string name="fingerprint_setup_notification_title" msgid="2002630611398849495">"Configure mais formas de desbloquear a tela"</string>
- <string name="fingerprint_setup_notification_content" msgid="205578121848324852">"Toque para adicionar uma impressão digital"</string>
<string name="fingerprint_recalibrate_notification_name" msgid="1414578431898579354">"Desbloqueio por impressão digital"</string>
<string name="fingerprint_recalibrate_notification_title" msgid="2406561052064558497">"Não foi possível usar o sensor de impressão digital"</string>
<string name="fingerprint_recalibrate_notification_content" msgid="8519935717822194943">"Entre em contato com uma assistência técnica."</string>
@@ -782,7 +782,7 @@
<string name="permlab_accessNetworkConditions" msgid="1270732533356286514">"detectar observações nas condições da rede"</string>
<string name="permdesc_accessNetworkConditions" msgid="2959269186741956109">"Permite que o app detecte observações nas condições da rede. Não deve ser necessário para apps comuns."</string>
<string name="permlab_setInputCalibration" msgid="932069700285223434">"alterar calibragem do dispositivo de entrada"</string>
- <string name="permdesc_setInputCalibration" msgid="2937872391426631726">"Permite que o app modifique os parâmetros de calibragem da tela sensível ao toque. Não deve ser necessário para apps normais."</string>
+ <string name="permdesc_setInputCalibration" msgid="2937872391426631726">"Permite que o app modifique os parâmetros de calibragem da tela touch. Não deve ser necessário para apps normais."</string>
<string name="permlab_accessDrmCertificates" msgid="6473765454472436597">"acessar certificados de DRM"</string>
<string name="permdesc_accessDrmCertificates" msgid="6983139753493781941">"Permite que o app provisione e use certificados de DRM. Não deve ser necessário para apps comuns."</string>
<string name="permlab_handoverStatus" msgid="7620438488137057281">"receber status de transferência do Android Beam"</string>
@@ -1595,7 +1595,7 @@
<string name="data_usage_limit_snoozed_body" msgid="545146591766765678">"Você ultrapassou <xliff:g id="SIZE">%s</xliff:g> do limite definido"</string>
<string name="data_usage_restricted_title" msgid="126711424380051268">"Dados de segundo plano restritos"</string>
<string name="data_usage_restricted_body" msgid="5338694433686077733">"Toque para remover a restrição."</string>
- <string name="data_usage_rapid_title" msgid="2950192123248740375">"Alto uso de dados móveis"</string>
+ <string name="data_usage_rapid_title" msgid="2950192123248740375">"Uso elevado de dados móveis"</string>
<string name="data_usage_rapid_body" msgid="3886676853263693432">"Seus apps usaram mais dados do que o normal"</string>
<string name="data_usage_rapid_app_body" msgid="5425779218506513861">"O app <xliff:g id="APP">%s</xliff:g> usou mais dados do que o normal"</string>
<string name="ssl_certificate" msgid="5690020361307261997">"Certificado de segurança"</string>
@@ -2032,7 +2032,8 @@
<string name="autofill_update_title" msgid="3630695947047069136">"Atualizar em "<b>"<xliff:g id="LABEL">%1$s</xliff:g>"</b>"?"</string>
<string name="autofill_update_title_with_type" msgid="5264152633488495704">"Atualizar <xliff:g id="TYPE">%1$s</xliff:g> em "<b>"<xliff:g id="LABEL">%2$s</xliff:g>"</b>"?"</string>
<string name="autofill_update_title_with_2types" msgid="1797514386321086273">"Atualizar <xliff:g id="TYPE_0">%1$s</xliff:g> e <xliff:g id="TYPE_1">%2$s</xliff:g> em "<b>"<xliff:g id="LABEL">%3$s</xliff:g>"</b>"?"</string>
- <string name="autofill_update_title_with_3types" msgid="1312232153076212291">"Atualizar estes itens em "<b>"<xliff:g id="LABEL">%4$s</xliff:g>"</b>": <xliff:g id="TYPE_0">%1$s</xliff:g>, <xliff:g id="TYPE_1">%2$s</xliff:g> e <xliff:g id="TYPE_2">%3$s</xliff:g>?"</string>
+ <!-- no translation found for autofill_update_title_with_3types (8285767070604652626) -->
+ <skip />
<string name="autofill_save_yes" msgid="8035743017382012850">"Salvar"</string>
<string name="autofill_save_no" msgid="9212826374207023544">"Agora não"</string>
<string name="autofill_save_notnow" msgid="2853932672029024195">"Agora não"</string>
diff --git a/core/res/res/values-ro/strings.xml b/core/res/res/values-ro/strings.xml
index eca2eb3..ce0eb91 100644
--- a/core/res/res/values-ro/strings.xml
+++ b/core/res/res/values-ro/strings.xml
@@ -670,14 +670,18 @@
</string-array>
<string name="fingerprint_error_vendor_unknown" msgid="4170002184907291065">"A apărut o eroare. Încearcă din nou."</string>
<string name="fingerprint_icon_content_description" msgid="4741068463175388817">"Pictograma amprentă"</string>
+ <!-- no translation found for device_unlock_notification_name (2632928999862915709) -->
+ <skip />
+ <!-- no translation found for alternative_unlock_setup_notification_title (6241508547901933544) -->
+ <skip />
+ <!-- no translation found for alternative_face_setup_notification_content (3384959224091897331) -->
+ <skip />
+ <!-- no translation found for alternative_fp_setup_notification_content (7454096947415721639) -->
+ <skip />
<string name="face_recalibrate_notification_name" msgid="7311163114750748686">"Deblocare facială"</string>
<string name="face_recalibrate_notification_title" msgid="2524791952735579082">"Problemă cu Deblocarea facială"</string>
<string name="face_recalibrate_notification_content" msgid="3064513770251355594">"Atinge pentru a șterge modelul facial, apoi adaugă din nou chipul"</string>
- <string name="face_setup_notification_title" msgid="8843461561970741790">"Configurează Deblocarea facială"</string>
- <string name="face_setup_notification_content" msgid="5463999831057751676">"Deblochează-ți telefonul uitându-te la el"</string>
<string name="face_sensor_privacy_enabled" msgid="7407126963510598508">"Pentru a folosi Deblocarea facială, activează "<b>"Accesul la cameră"</b>" în Setări și confidențialitate"</string>
- <string name="fingerprint_setup_notification_title" msgid="2002630611398849495">"Configurează mai multe moduri de deblocare"</string>
- <string name="fingerprint_setup_notification_content" msgid="205578121848324852">"Atinge ca să adaugi o amprentă"</string>
<string name="fingerprint_recalibrate_notification_name" msgid="1414578431898579354">"Deblocare cu amprenta"</string>
<string name="fingerprint_recalibrate_notification_title" msgid="2406561052064558497">"Nu se poate folosi senzorul de amprentă"</string>
<string name="fingerprint_recalibrate_notification_content" msgid="8519935717822194943">"Vizitează un furnizor de servicii de reparații."</string>
@@ -2032,7 +2036,8 @@
<string name="autofill_update_title" msgid="3630695947047069136">"Actualizezi în "<b>"<xliff:g id="LABEL">%1$s</xliff:g>"</b>"?"</string>
<string name="autofill_update_title_with_type" msgid="5264152633488495704">"Actualizezi <xliff:g id="TYPE">%1$s</xliff:g> în "<b>"<xliff:g id="LABEL">%2$s</xliff:g>"</b>"?"</string>
<string name="autofill_update_title_with_2types" msgid="1797514386321086273">"Actualizezi <xliff:g id="TYPE_0">%1$s</xliff:g> și <xliff:g id="TYPE_1">%2$s</xliff:g> în "<b>"<xliff:g id="LABEL">%3$s</xliff:g>"</b>"?"</string>
- <string name="autofill_update_title_with_3types" msgid="1312232153076212291">"Actualizezi aceste articole în "<b>"<xliff:g id="LABEL">%4$s</xliff:g>"</b>": <xliff:g id="TYPE_0">%1$s</xliff:g>, <xliff:g id="TYPE_1">%2$s</xliff:g> și <xliff:g id="TYPE_2">%3$s</xliff:g>?"</string>
+ <!-- no translation found for autofill_update_title_with_3types (8285767070604652626) -->
+ <skip />
<string name="autofill_save_yes" msgid="8035743017382012850">"Salvează"</string>
<string name="autofill_save_no" msgid="9212826374207023544">"Nu, mulțumesc"</string>
<string name="autofill_save_notnow" msgid="2853932672029024195">"Nu acum"</string>
diff --git a/core/res/res/values-ru/strings.xml b/core/res/res/values-ru/strings.xml
index 8326ba8..155f654 100644
--- a/core/res/res/values-ru/strings.xml
+++ b/core/res/res/values-ru/strings.xml
@@ -671,14 +671,14 @@
</string-array>
<string name="fingerprint_error_vendor_unknown" msgid="4170002184907291065">"Произошла ошибка. Повторите попытку."</string>
<string name="fingerprint_icon_content_description" msgid="4741068463175388817">"Значок отпечатка пальца"</string>
+ <string name="device_unlock_notification_name" msgid="2632928999862915709">"Разблокировка устройства"</string>
+ <string name="alternative_unlock_setup_notification_title" msgid="6241508547901933544">"Попробуйте другой способ разблокировки"</string>
+ <string name="alternative_face_setup_notification_content" msgid="3384959224091897331">"Когда устройство не распознаёт ваш отпечаток пальца, используйте фейсконтроль. Например, это пригодится, когда палец мокрый."</string>
+ <string name="alternative_fp_setup_notification_content" msgid="7454096947415721639">"Когда устройство не распознаёт ваше лицо, используйте разблокировку по отпечатку пальца. Например, это пригодится при плохом освещении."</string>
<string name="face_recalibrate_notification_name" msgid="7311163114750748686">"Фейсконтроль"</string>
<string name="face_recalibrate_notification_title" msgid="2524791952735579082">"Ошибка фейсконтроля"</string>
<string name="face_recalibrate_notification_content" msgid="3064513770251355594">"Нажмите, чтобы удалить модель лица, а затем добавьте ее снова."</string>
- <string name="face_setup_notification_title" msgid="8843461561970741790">"Настройка фейсконтроля"</string>
- <string name="face_setup_notification_content" msgid="5463999831057751676">"Вы сможете разблокировать телефон, просто посмотрев на него."</string>
<string name="face_sensor_privacy_enabled" msgid="7407126963510598508">"Чтобы использовать фейсконтроль, разрешите "<b>"доступ к камере"</b>". Для этого перейдите в настройки и нажмите \"Конфиденциальность\"."</string>
- <string name="fingerprint_setup_notification_title" msgid="2002630611398849495">"Настройте другие способы разблокировки"</string>
- <string name="fingerprint_setup_notification_content" msgid="205578121848324852">"Нажмите, чтобы добавить отпечаток пальца."</string>
<string name="fingerprint_recalibrate_notification_name" msgid="1414578431898579354">"Разблокировка по отпечатку пальца"</string>
<string name="fingerprint_recalibrate_notification_title" msgid="2406561052064558497">"Невозможно использовать сканер отпечатков пальцев"</string>
<string name="fingerprint_recalibrate_notification_content" msgid="8519935717822194943">"Обратитесь в сервисный центр."</string>
@@ -1598,7 +1598,7 @@
<string name="data_usage_restricted_body" msgid="5338694433686077733">"Нажмите, чтобы отменить ограничение."</string>
<string name="data_usage_rapid_title" msgid="2950192123248740375">"Высокое потребление трафика"</string>
<string name="data_usage_rapid_body" msgid="3886676853263693432">"В ваших приложениях было передано больше трафика, чем обычно."</string>
- <string name="data_usage_rapid_app_body" msgid="5425779218506513861">"В приложении \"<xliff:g id="APP">%s</xliff:g>\" было передано больше трафика, чем обычно."</string>
+ <string name="data_usage_rapid_app_body" msgid="5425779218506513861">"Приложение \"<xliff:g id="APP">%s</xliff:g>\" использовало больше трафика, чем обычно."</string>
<string name="ssl_certificate" msgid="5690020361307261997">"Сертификат безопасности"</string>
<string name="ssl_certificate_is_valid" msgid="7293675884598527081">"Этот сертификат действителен."</string>
<string name="issued_to" msgid="5975877665505297662">"Кому выдан:"</string>
@@ -2033,7 +2033,8 @@
<string name="autofill_update_title" msgid="3630695947047069136">"Обновить в сервисе "<b>"<xliff:g id="LABEL">%1$s</xliff:g>"</b>"?"</string>
<string name="autofill_update_title_with_type" msgid="5264152633488495704">"Обновить данные (<xliff:g id="TYPE">%1$s</xliff:g>) в сервисе "<b>"<xliff:g id="LABEL">%2$s</xliff:g>"</b>"?"</string>
<string name="autofill_update_title_with_2types" msgid="1797514386321086273">"Обновить данные (<xliff:g id="TYPE_0">%1$s</xliff:g>, <xliff:g id="TYPE_1">%2$s</xliff:g>) в сервисе "<b>"<xliff:g id="LABEL">%3$s</xliff:g>"</b>"?"</string>
- <string name="autofill_update_title_with_3types" msgid="1312232153076212291">"Обновить данные (<xliff:g id="TYPE_0">%1$s</xliff:g>, <xliff:g id="TYPE_1">%2$s</xliff:g>, <xliff:g id="TYPE_2">%3$s</xliff:g>) в сервисе "<b>"<xliff:g id="LABEL">%4$s</xliff:g>"</b>"?"</string>
+ <!-- no translation found for autofill_update_title_with_3types (8285767070604652626) -->
+ <skip />
<string name="autofill_save_yes" msgid="8035743017382012850">"Сохранить"</string>
<string name="autofill_save_no" msgid="9212826374207023544">"Нет, спасибо"</string>
<string name="autofill_save_notnow" msgid="2853932672029024195">"Не сейчас"</string>
diff --git a/core/res/res/values-si/strings.xml b/core/res/res/values-si/strings.xml
index f81ff63..7485012 100644
--- a/core/res/res/values-si/strings.xml
+++ b/core/res/res/values-si/strings.xml
@@ -669,14 +669,14 @@
</string-array>
<string name="fingerprint_error_vendor_unknown" msgid="4170002184907291065">"යම් දෙයක් වැරදිණි. නැවත උත්සාහ කරන්න."</string>
<string name="fingerprint_icon_content_description" msgid="4741068463175388817">"ඇඟිලි සලකුණු නිරූපකය"</string>
+ <string name="device_unlock_notification_name" msgid="2632928999862915709">"උපාංගය අගුළු හැරීම"</string>
+ <string name="alternative_unlock_setup_notification_title" msgid="6241508547901933544">"අගුළු ඇරීමට වෙනත් ක්රමයක් උත්සාහ කරන්න"</string>
+ <string name="alternative_face_setup_notification_content" msgid="3384959224091897331">"ඔබේ ඇඟිලි තෙත් වූ විට වැනි ඔබේ ඇඟිලි සලකුණ හඳුනා නොගත් විට මුහුණෙන් අගුළු හැරීම භාවිත කරන්න"</string>
+ <string name="alternative_fp_setup_notification_content" msgid="7454096947415721639">"ප්රමාණවත් ආලෝකයක් නොමැති විට වැනි ඔබේ මුහුණ හඳුනා නොගත් විට ඇඟිලි සලකුණු අගුළු හැරීම භාවිත කරන්න"</string>
<string name="face_recalibrate_notification_name" msgid="7311163114750748686">"මුහුණෙන් අගුළු හැරීම"</string>
<string name="face_recalibrate_notification_title" msgid="2524791952735579082">"මුහුණෙන් අගුලු හැරීම සම්බන්ධව ගැටලුවකි"</string>
<string name="face_recalibrate_notification_content" msgid="3064513770251355594">"ඔබගේ මුහුණත ආකෘතිය මැකීමට තට්ටු කරන්න, අනතුරුව ඔබගේ මුහුණ නැවත එක් කරන්න"</string>
- <string name="face_setup_notification_title" msgid="8843461561970741790">"මුහුණෙන් අගුළු හැරීම පිහිටුවන්න"</string>
- <string name="face_setup_notification_content" msgid="5463999831057751676">"ඔබගේ දුරකථනය දෙස බැලීමෙන් එහි අගුලු හරින්න"</string>
<string name="face_sensor_privacy_enabled" msgid="7407126963510598508">"මුහුණෙන් අගුලු හැරීම භාවිත කිරීමට, සැකසීම් > පෞද්ගලිකත්වය තුළ "<b>"කැමරා ප්රවේශය"</b>" ක්රියාත්මක කරන්න"</string>
- <string name="fingerprint_setup_notification_title" msgid="2002630611398849495">"අගුලු හැරීමට තවත් ක්රම සකසන්න"</string>
- <string name="fingerprint_setup_notification_content" msgid="205578121848324852">"ඇඟිලි සලකුණක් එක් කිරීමට තට්ටු කරන්න"</string>
<string name="fingerprint_recalibrate_notification_name" msgid="1414578431898579354">"ඇඟිලි සලකුණු අගුළු හැරීම"</string>
<string name="fingerprint_recalibrate_notification_title" msgid="2406561052064558497">"ඇඟිලි සලකුණු සංවේදකය භාවිත කළ නොහැකිය"</string>
<string name="fingerprint_recalibrate_notification_content" msgid="8519935717822194943">"අළුත්වැඩියා සැපයුම්කරුවෙකු බලන්න."</string>
@@ -2031,7 +2031,8 @@
<string name="autofill_update_title" msgid="3630695947047069136"><b>"<xliff:g id="LABEL">%1$s</xliff:g>"</b>" තුළ යාවත්කාලීන කරන්නද?"</string>
<string name="autofill_update_title_with_type" msgid="5264152633488495704"><b>"<xliff:g id="LABEL">%2$s</xliff:g>"</b>" තුළ <xliff:g id="TYPE">%1$s</xliff:g> යාවත්කාලීන කරන්නද?"</string>
<string name="autofill_update_title_with_2types" msgid="1797514386321086273"><b>"<xliff:g id="LABEL">%3$s</xliff:g>"</b>" තුළ <xliff:g id="TYPE_0">%1$s</xliff:g> සහ <xliff:g id="TYPE_1">%2$s</xliff:g> යාවත්කාලීන කරන්නද?"</string>
- <string name="autofill_update_title_with_3types" msgid="1312232153076212291"><b>"<xliff:g id="LABEL">%4$s</xliff:g>"</b>" තුළ යාවත්කාලීන කරන්නද?: <xliff:g id="TYPE_0">%1$s</xliff:g>, <xliff:g id="TYPE_1">%2$s</xliff:g>, සහ <xliff:g id="TYPE_2">%3$s</xliff:g> ?"</string>
+ <!-- no translation found for autofill_update_title_with_3types (8285767070604652626) -->
+ <skip />
<string name="autofill_save_yes" msgid="8035743017382012850">"සුරකින්න"</string>
<string name="autofill_save_no" msgid="9212826374207023544">"එපා ස්තූතියි"</string>
<string name="autofill_save_notnow" msgid="2853932672029024195">"දැන් නොවේ"</string>
diff --git a/core/res/res/values-sk/strings.xml b/core/res/res/values-sk/strings.xml
index 7bcf119..c06c7cc 100644
--- a/core/res/res/values-sk/strings.xml
+++ b/core/res/res/values-sk/strings.xml
@@ -671,14 +671,14 @@
</string-array>
<string name="fingerprint_error_vendor_unknown" msgid="4170002184907291065">"Vyskytla sa chyba. Skúste to znova."</string>
<string name="fingerprint_icon_content_description" msgid="4741068463175388817">"Ikona odtlačku prsta"</string>
+ <string name="device_unlock_notification_name" msgid="2632928999862915709">"Odomknutie zariadenia"</string>
+ <string name="alternative_unlock_setup_notification_title" msgid="6241508547901933544">"Vyskúšajte iný spôsob odomknutia"</string>
+ <string name="alternative_face_setup_notification_content" msgid="3384959224091897331">"Použite odomknutie tvárou, keď odtlačok prsta nefunguje, napríklad keď máte mokré prsty"</string>
+ <string name="alternative_fp_setup_notification_content" msgid="7454096947415721639">"Použite odomknutie odtlačkom prsta, keď vaša tvár nie je rozpoznaná, napríklad pri nedostatočnom osvetlení"</string>
<string name="face_recalibrate_notification_name" msgid="7311163114750748686">"Odomknutie tvárou"</string>
<string name="face_recalibrate_notification_title" msgid="2524791952735579082">"Problém s odomknutím tvárou"</string>
<string name="face_recalibrate_notification_content" msgid="3064513770251355594">"Klepnutím odstráňte model tváre a potom znova pridajte svoju tvár"</string>
- <string name="face_setup_notification_title" msgid="8843461561970741790">"Nastavte odomknutie tvárou"</string>
- <string name="face_setup_notification_content" msgid="5463999831057751676">"Odomykajte telefón tak, že sa naň pozriete"</string>
<string name="face_sensor_privacy_enabled" msgid="7407126963510598508">"Ak chcete používať odomknutie tvárou, v sekcii Nastavenia > Ochrana súkromia zapnite "<b>"prístup ku kamere"</b></string>
- <string name="fingerprint_setup_notification_title" msgid="2002630611398849495">"Nastavte viac spôsobov odomknutia"</string>
- <string name="fingerprint_setup_notification_content" msgid="205578121848324852">"Klepnutím pridajte odtlačok prsta"</string>
<string name="fingerprint_recalibrate_notification_name" msgid="1414578431898579354">"Odomknutie odtlačkom prsta"</string>
<string name="fingerprint_recalibrate_notification_title" msgid="2406561052064558497">"Senzor odtlačkov prstov nie je možné používať"</string>
<string name="fingerprint_recalibrate_notification_content" msgid="8519935717822194943">"Navštívte poskytovateľa opráv."</string>
@@ -2033,7 +2033,8 @@
<string name="autofill_update_title" msgid="3630695947047069136">"Aktualizovať v službe "<b>"<xliff:g id="LABEL">%1$s</xliff:g>"</b>"?"</string>
<string name="autofill_update_title_with_type" msgid="5264152633488495704">"Chcete údaje <xliff:g id="TYPE">%1$s</xliff:g> aktualizovať v službe "<b>"<xliff:g id="LABEL">%2$s</xliff:g>"</b>"?"</string>
<string name="autofill_update_title_with_2types" msgid="1797514386321086273">"Chcete položky <xliff:g id="TYPE_0">%1$s</xliff:g> a <xliff:g id="TYPE_1">%2$s</xliff:g> aktualizovať v službe "<b>"<xliff:g id="LABEL">%3$s</xliff:g>"</b>"?"</string>
- <string name="autofill_update_title_with_3types" msgid="1312232153076212291">"Chcete tieto položky aktualizovať v službe "<b>"<xliff:g id="LABEL">%4$s</xliff:g>"</b>": <xliff:g id="TYPE_0">%1$s</xliff:g>, <xliff:g id="TYPE_1">%2$s</xliff:g> a <xliff:g id="TYPE_2">%3$s</xliff:g>?"</string>
+ <!-- no translation found for autofill_update_title_with_3types (8285767070604652626) -->
+ <skip />
<string name="autofill_save_yes" msgid="8035743017382012850">"Uložiť"</string>
<string name="autofill_save_no" msgid="9212826374207023544">"Nie, vďaka"</string>
<string name="autofill_save_notnow" msgid="2853932672029024195">"Teraz nie"</string>
diff --git a/core/res/res/values-sl/strings.xml b/core/res/res/values-sl/strings.xml
index 2035979..c25e052 100644
--- a/core/res/res/values-sl/strings.xml
+++ b/core/res/res/values-sl/strings.xml
@@ -671,14 +671,14 @@
</string-array>
<string name="fingerprint_error_vendor_unknown" msgid="4170002184907291065">"Prišlo je do napake. Poskusite znova."</string>
<string name="fingerprint_icon_content_description" msgid="4741068463175388817">"Ikona prstnih odtisov"</string>
+ <string name="device_unlock_notification_name" msgid="2632928999862915709">"Odklepanje naprave"</string>
+ <string name="alternative_unlock_setup_notification_title" msgid="6241508547901933544">"Preizkusite drug način odklepanja"</string>
+ <string name="alternative_face_setup_notification_content" msgid="3384959224091897331">"Kadar prstni odtis ni prepoznan, npr. ko imate mokre prste, uporabite odklepanje z obrazom."</string>
+ <string name="alternative_fp_setup_notification_content" msgid="7454096947415721639">"Kadar obraz ni prepoznan, npr. ko ni dovolj svetlobe, uporabite odklepanje s prstnim odtisom."</string>
<string name="face_recalibrate_notification_name" msgid="7311163114750748686">"Odklepanje z obrazom"</string>
<string name="face_recalibrate_notification_title" msgid="2524791952735579082">"Težava z odklepanjem z obrazom"</string>
<string name="face_recalibrate_notification_content" msgid="3064513770251355594">"Dotaknite se, da izbrišete model obraza, in nato znova dodajte obraz."</string>
- <string name="face_setup_notification_title" msgid="8843461561970741790">"Nastavitev odklepanja z obrazom"</string>
- <string name="face_setup_notification_content" msgid="5463999831057751676">"Odklenite telefon tako, da ga pogledate."</string>
<string name="face_sensor_privacy_enabled" msgid="7407126963510598508">"Če želite uporabljati odklepanje z obrazom, v meniju »Nastavitve« > »Zasebnost« vklopite možnost "<b>"Dostop do fotoaparata"</b>"."</string>
- <string name="fingerprint_setup_notification_title" msgid="2002630611398849495">"Nastavite več načinov odklepanja"</string>
- <string name="fingerprint_setup_notification_content" msgid="205578121848324852">"Dotaknite se, da dodate prstni odtis."</string>
<string name="fingerprint_recalibrate_notification_name" msgid="1414578431898579354">"Odklepanje s prstnim odtisom"</string>
<string name="fingerprint_recalibrate_notification_title" msgid="2406561052064558497">"Tipala prstnih odtisov ni mogoče uporabiti"</string>
<string name="fingerprint_recalibrate_notification_content" msgid="8519935717822194943">"Obiščite ponudnika popravil."</string>
@@ -2033,7 +2033,8 @@
<string name="autofill_update_title" msgid="3630695947047069136">"Želite posodobiti v aplikaciji "<b>"<xliff:g id="LABEL">%1$s</xliff:g>"</b>"?"</string>
<string name="autofill_update_title_with_type" msgid="5264152633488495704">"Želite posodobiti element <xliff:g id="TYPE">%1$s</xliff:g> v aplikaciji "<b>"<xliff:g id="LABEL">%2$s</xliff:g>"</b>"?"</string>
<string name="autofill_update_title_with_2types" msgid="1797514386321086273">"Želite posodobiti elementa <xliff:g id="TYPE_0">%1$s</xliff:g> in <xliff:g id="TYPE_1">%2$s</xliff:g> v aplikaciji "<b>"<xliff:g id="LABEL">%3$s</xliff:g>"</b>"?"</string>
- <string name="autofill_update_title_with_3types" msgid="1312232153076212291">"Želite posodobiti te elemente v aplikaciji "<b>"<xliff:g id="LABEL">%4$s</xliff:g>"</b>": <xliff:g id="TYPE_0">%1$s</xliff:g>, <xliff:g id="TYPE_1">%2$s</xliff:g> in <xliff:g id="TYPE_2">%3$s</xliff:g>?"</string>
+ <!-- no translation found for autofill_update_title_with_3types (8285767070604652626) -->
+ <skip />
<string name="autofill_save_yes" msgid="8035743017382012850">"Shrani"</string>
<string name="autofill_save_no" msgid="9212826374207023544">"Ne, hvala"</string>
<string name="autofill_save_notnow" msgid="2853932672029024195">"Ne zdaj"</string>
diff --git a/core/res/res/values-sq/strings.xml b/core/res/res/values-sq/strings.xml
index a2e851a..c93b699 100644
--- a/core/res/res/values-sq/strings.xml
+++ b/core/res/res/values-sq/strings.xml
@@ -669,14 +669,14 @@
</string-array>
<string name="fingerprint_error_vendor_unknown" msgid="4170002184907291065">"Ndodhi një gabim. Provo sërish."</string>
<string name="fingerprint_icon_content_description" msgid="4741068463175388817">"Ikona e gjurmës së gishtit"</string>
+ <string name="device_unlock_notification_name" msgid="2632928999862915709">"Shkyçja e pajisjes"</string>
+ <string name="alternative_unlock_setup_notification_title" msgid="6241508547901933544">"Provo një mënyrë tjetër për ta shkyçur"</string>
+ <string name="alternative_face_setup_notification_content" msgid="3384959224091897331">"Përdor \"Shkyçjen me fytyrë\" kur nuk të njihet gjurma e gishtit, si p.sh. kur i ke gishtat të lagur"</string>
+ <string name="alternative_fp_setup_notification_content" msgid="7454096947415721639">"Përdor \"Shkyçjen me gjurmën e gishtit\" kur nuk të njihet fytyra, si p.sh. kur nuk ka dritë të mjaftueshme"</string>
<string name="face_recalibrate_notification_name" msgid="7311163114750748686">"Shkyçja me fytyrë"</string>
<string name="face_recalibrate_notification_title" msgid="2524791952735579082">"Problem me \"Shkyçjen me fytyrë\""</string>
<string name="face_recalibrate_notification_content" msgid="3064513770251355594">"Trokit për të fshirë modelin tënd të fytyrës, pastaj shtoje përsëri fytyrën tënde"</string>
- <string name="face_setup_notification_title" msgid="8843461561970741790">"Konfiguro \"Shkyçjen me fytyrë\""</string>
- <string name="face_setup_notification_content" msgid="5463999831057751676">"Shkyçe telefonin duke parë tek ai"</string>
<string name="face_sensor_privacy_enabled" msgid="7407126963510598508">"Për të përdorur \"Shkyçjen me fytyrë\", aktivizo "<b>"Qasjen te kamera"</b>" te \"Cilësimet\" > \"Privatësia\""</string>
- <string name="fingerprint_setup_notification_title" msgid="2002630611398849495">"Konfiguri më shumë mënyra për të shkyçur"</string>
- <string name="fingerprint_setup_notification_content" msgid="205578121848324852">"Trokit për të shtuar një gjurmë gishti"</string>
<string name="fingerprint_recalibrate_notification_name" msgid="1414578431898579354">"Shkyçja me gjurmën e gishtit"</string>
<string name="fingerprint_recalibrate_notification_title" msgid="2406561052064558497">"Sensori i gjurmës së gishtit nuk mund të përdoret"</string>
<string name="fingerprint_recalibrate_notification_content" msgid="8519935717822194943">"Vizito një ofrues të shërbimit të riparimit."</string>
@@ -2031,7 +2031,8 @@
<string name="autofill_update_title" msgid="3630695947047069136">"Të përditësohet në "<b>"<xliff:g id="LABEL">%1$s</xliff:g>"</b>"?"</string>
<string name="autofill_update_title_with_type" msgid="5264152633488495704">"Të përditësohet <xliff:g id="TYPE">%1$s</xliff:g> në "<b>"<xliff:g id="LABEL">%2$s</xliff:g>"</b>"?"</string>
<string name="autofill_update_title_with_2types" msgid="1797514386321086273">"Të përditësohet <xliff:g id="TYPE_0">%1$s</xliff:g> dhe <xliff:g id="TYPE_1">%2$s</xliff:g> në "<b>"<xliff:g id="LABEL">%3$s</xliff:g>"</b>"?"</string>
- <string name="autofill_update_title_with_3types" msgid="1312232153076212291">"Të përditësohen këta artikuj në "<b>"<xliff:g id="LABEL">%4$s</xliff:g>"</b>": <xliff:g id="TYPE_0">%1$s</xliff:g>, <xliff:g id="TYPE_1">%2$s</xliff:g> dhe <xliff:g id="TYPE_2">%3$s</xliff:g> ?"</string>
+ <!-- no translation found for autofill_update_title_with_3types (8285767070604652626) -->
+ <skip />
<string name="autofill_save_yes" msgid="8035743017382012850">"Ruaj"</string>
<string name="autofill_save_no" msgid="9212826374207023544">"Jo, faleminderit"</string>
<string name="autofill_save_notnow" msgid="2853932672029024195">"Jo tani"</string>
diff --git a/core/res/res/values-sr/strings.xml b/core/res/res/values-sr/strings.xml
index a13d56d..14dfac5 100644
--- a/core/res/res/values-sr/strings.xml
+++ b/core/res/res/values-sr/strings.xml
@@ -670,14 +670,14 @@
</string-array>
<string name="fingerprint_error_vendor_unknown" msgid="4170002184907291065">"Дошло је до проблема. Пробајте поново."</string>
<string name="fingerprint_icon_content_description" msgid="4741068463175388817">"Икона отиска прста"</string>
+ <string name="device_unlock_notification_name" msgid="2632928999862915709">"Откључавање уређаја"</string>
+ <string name="alternative_unlock_setup_notification_title" msgid="6241508547901933544">"Пробајте други начин откључавања"</string>
+ <string name="alternative_face_setup_notification_content" msgid="3384959224091897331">"Користите откључавање лицем када вам се отисак прста не препозна, на пример, када су вам прсти влажни"</string>
+ <string name="alternative_fp_setup_notification_content" msgid="7454096947415721639">"Користите откључавање отиском прста када вам се лице не препозна, на пример, када је осветљење слабо"</string>
<string name="face_recalibrate_notification_name" msgid="7311163114750748686">"Откључавање лицем"</string>
<string name="face_recalibrate_notification_title" msgid="2524791952735579082">"Проблем са откључавање лицем"</string>
<string name="face_recalibrate_notification_content" msgid="3064513770251355594">"Додирните да бисте избрисали модел лица, па поново додајте своје лице"</string>
- <string name="face_setup_notification_title" msgid="8843461561970741790">"Подесите откључавање лицем"</string>
- <string name="face_setup_notification_content" msgid="5463999831057751676">"Откључајте телефон тако што ћете га погледати"</string>
<string name="face_sensor_privacy_enabled" msgid="7407126963510598508">"Да бисте користили откључавање лицем, укључите "<b>"приступ камери"</b>" у одељку Подешавања > Приватност"</string>
- <string name="fingerprint_setup_notification_title" msgid="2002630611398849495">"Подесите још начина за откључавање"</string>
- <string name="fingerprint_setup_notification_content" msgid="205578121848324852">"Додирните да бисте додали отисак прста"</string>
<string name="fingerprint_recalibrate_notification_name" msgid="1414578431898579354">"Откључавање отиском прста"</string>
<string name="fingerprint_recalibrate_notification_title" msgid="2406561052064558497">"Не можете да користите сензор за отисак прста"</string>
<string name="fingerprint_recalibrate_notification_content" msgid="8519935717822194943">"Посетите добављача за поправке."</string>
@@ -2032,7 +2032,8 @@
<string name="autofill_update_title" msgid="3630695947047069136">"Желите ли да ажурирате у услузи "<b>"<xliff:g id="LABEL">%1$s</xliff:g>"</b>"?"</string>
<string name="autofill_update_title_with_type" msgid="5264152633488495704">"Желите ли да ажурирате ставку <xliff:g id="TYPE">%1$s</xliff:g> у услузи "<b>"<xliff:g id="LABEL">%2$s</xliff:g>"</b>"?"</string>
<string name="autofill_update_title_with_2types" msgid="1797514386321086273">"Желите ли да ажурирате ставке <xliff:g id="TYPE_0">%1$s</xliff:g> и <xliff:g id="TYPE_1">%2$s</xliff:g> у услузи "<b>"<xliff:g id="LABEL">%3$s</xliff:g>"</b>"?"</string>
- <string name="autofill_update_title_with_3types" msgid="1312232153076212291">"Желите ли да ажурирате ове ставке у услузи "<b>"<xliff:g id="LABEL">%4$s</xliff:g>"</b>": <xliff:g id="TYPE_0">%1$s</xliff:g>, <xliff:g id="TYPE_1">%2$s</xliff:g> и <xliff:g id="TYPE_2">%3$s</xliff:g>?"</string>
+ <!-- no translation found for autofill_update_title_with_3types (8285767070604652626) -->
+ <skip />
<string name="autofill_save_yes" msgid="8035743017382012850">"Сачувај"</string>
<string name="autofill_save_no" msgid="9212826374207023544">"Не, хвала"</string>
<string name="autofill_save_notnow" msgid="2853932672029024195">"Не сада"</string>
diff --git a/core/res/res/values-sv/strings.xml b/core/res/res/values-sv/strings.xml
index 0a43e46..8d0a41f 100644
--- a/core/res/res/values-sv/strings.xml
+++ b/core/res/res/values-sv/strings.xml
@@ -669,14 +669,14 @@
</string-array>
<string name="fingerprint_error_vendor_unknown" msgid="4170002184907291065">"Något gick fel. Försök igen."</string>
<string name="fingerprint_icon_content_description" msgid="4741068463175388817">"Ikon för fingeravtryck"</string>
+ <string name="device_unlock_notification_name" msgid="2632928999862915709">"Enhetsupplåsning"</string>
+ <string name="alternative_unlock_setup_notification_title" msgid="6241508547901933544">"Testa ett annat sätt att låsa upp"</string>
+ <string name="alternative_face_setup_notification_content" msgid="3384959224091897331">"Använd ansiktslås när fingeravtrycket inte känns igen, som när dina fingrar är blöta"</string>
+ <string name="alternative_fp_setup_notification_content" msgid="7454096947415721639">"Använd fingeravtryckslås när ansiktet inte känns igen, som när det är för mörkt"</string>
<string name="face_recalibrate_notification_name" msgid="7311163114750748686">"Ansiktslås"</string>
<string name="face_recalibrate_notification_title" msgid="2524791952735579082">"Problem med ansiktslås"</string>
<string name="face_recalibrate_notification_content" msgid="3064513770251355594">"Tryck för att radera ansiktsmodellen och lägg sedan till ansiktet igen"</string>
- <string name="face_setup_notification_title" msgid="8843461561970741790">"Konfigurera ansiktslås"</string>
- <string name="face_setup_notification_content" msgid="5463999831057751676">"Lås upp telefonen genom att titta på den"</string>
<string name="face_sensor_privacy_enabled" msgid="7407126963510598508">"Om du vill använda ansiktslås aktiverar du "<b>"Kameraåtkomst"</b>" i Inställningar > Integritet"</string>
- <string name="fingerprint_setup_notification_title" msgid="2002630611398849495">"Konfigurera fler sätt att låsa upp"</string>
- <string name="fingerprint_setup_notification_content" msgid="205578121848324852">"Tryck för att lägga till ett fingeravtryck"</string>
<string name="fingerprint_recalibrate_notification_name" msgid="1414578431898579354">"Fingeravtryckslås"</string>
<string name="fingerprint_recalibrate_notification_title" msgid="2406561052064558497">"Det går inte att använda fingeravtryckssensorn"</string>
<string name="fingerprint_recalibrate_notification_content" msgid="8519935717822194943">"Besök ett reparationsställe."</string>
@@ -2031,7 +2031,8 @@
<string name="autofill_update_title" msgid="3630695947047069136">"Vill du uppdatera detta i "<b>"<xliff:g id="LABEL">%1$s</xliff:g>"</b>"?"</string>
<string name="autofill_update_title_with_type" msgid="5264152633488495704">"Vill du uppdatera <xliff:g id="TYPE">%1$s</xliff:g> i "<b>"<xliff:g id="LABEL">%2$s</xliff:g>"</b>"?"</string>
<string name="autofill_update_title_with_2types" msgid="1797514386321086273">"Vill du uppdatera <xliff:g id="TYPE_0">%1$s</xliff:g> och <xliff:g id="TYPE_1">%2$s</xliff:g> i "<b>"<xliff:g id="LABEL">%3$s</xliff:g>"</b>"?"</string>
- <string name="autofill_update_title_with_3types" msgid="1312232153076212291">"Vill du uppdatera dessa objekt i "<b>"<xliff:g id="LABEL">%4$s</xliff:g>"</b>": <xliff:g id="TYPE_0">%1$s</xliff:g>, <xliff:g id="TYPE_1">%2$s</xliff:g> och <xliff:g id="TYPE_2">%3$s</xliff:g>?"</string>
+ <!-- no translation found for autofill_update_title_with_3types (8285767070604652626) -->
+ <skip />
<string name="autofill_save_yes" msgid="8035743017382012850">"Spara"</string>
<string name="autofill_save_no" msgid="9212826374207023544">"Nej tack"</string>
<string name="autofill_save_notnow" msgid="2853932672029024195">"Inte nu"</string>
diff --git a/core/res/res/values-sw/strings.xml b/core/res/res/values-sw/strings.xml
index 466df4f..afc1fc3 100644
--- a/core/res/res/values-sw/strings.xml
+++ b/core/res/res/values-sw/strings.xml
@@ -298,7 +298,7 @@
<string name="android_system_label" msgid="5974767339591067210">"Mfumo wa Android"</string>
<string name="user_owner_label" msgid="8628726904184471211">"Tumia wasifu wa binafsi"</string>
<string name="managed_profile_label" msgid="7316778766973512382">"Tumia wasifu wa kazini"</string>
- <string name="user_owner_app_label" msgid="1553595155465750298">"Badili uweke wasifu wa binafsi <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
+ <string name="user_owner_app_label" msgid="1553595155465750298">"Badili utumie wasifu wa binafsi wa <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
<string name="managed_profile_app_label" msgid="367401088383965725">"Badili uweke wasifu wa kazini <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
<string name="permgrouplab_contacts" msgid="4254143639307316920">"Anwani"</string>
<string name="permgroupdesc_contacts" msgid="9163927941244182567">"ifikie anwani zako"</string>
@@ -669,14 +669,14 @@
</string-array>
<string name="fingerprint_error_vendor_unknown" msgid="4170002184907291065">"Hitilafu fulani imetokea. Jaribu tena."</string>
<string name="fingerprint_icon_content_description" msgid="4741068463175388817">"Aikoni ya alama ya kidole"</string>
+ <string name="device_unlock_notification_name" msgid="2632928999862915709">"Kufungua kifaa"</string>
+ <string name="alternative_unlock_setup_notification_title" msgid="6241508547901933544">"Jaribu njia nyingine ya kufungua"</string>
+ <string name="alternative_face_setup_notification_content" msgid="3384959224091897331">"Tumia kipengele cha Kufungua kwa Uso wakati alama ya kidole chako haitambuliwi, kama vile wakati vidole vyako vina unyevu"</string>
+ <string name="alternative_fp_setup_notification_content" msgid="7454096947415721639">"Tumia kipengele cha Kufungua kwa Alama ya Kidole wakati uso wako hautambuliwi, kama vile wakati hakuna mwangaza wa kutosha"</string>
<string name="face_recalibrate_notification_name" msgid="7311163114750748686">"Kufungua kwa Uso"</string>
<string name="face_recalibrate_notification_title" msgid="2524791952735579082">"Hitilafu imetokea kwenye kipengele cha Kufungua kwa Uso"</string>
<string name="face_recalibrate_notification_content" msgid="3064513770251355594">"Gusa ili ufute muundo wa uso wako, kisha uweke uso wako tena"</string>
- <string name="face_setup_notification_title" msgid="8843461561970741790">"Weka mipangilio ya Kufungua kwa Uso"</string>
- <string name="face_setup_notification_content" msgid="5463999831057751676">"Fungua simu yako kwa kuiangalia"</string>
<string name="face_sensor_privacy_enabled" msgid="7407126963510598508">"Ili utumie kipengele cha kufungua kwa uso, washa kipengele cha "<b>"ufikiaji wa Kamera"</b>" katika Mipangilio na Faragha"</string>
- <string name="fingerprint_setup_notification_title" msgid="2002630611398849495">"Weka mipangilio ya mbinu zaidi za kufungua"</string>
- <string name="fingerprint_setup_notification_content" msgid="205578121848324852">"Gusa ili uweke alama ya kidole"</string>
<string name="fingerprint_recalibrate_notification_name" msgid="1414578431898579354">"Kufungua kwa Alama ya Kidole"</string>
<string name="fingerprint_recalibrate_notification_title" msgid="2406561052064558497">"Imeshindwa kutumia kitambua alama ya kidole"</string>
<string name="fingerprint_recalibrate_notification_content" msgid="8519935717822194943">"Tembelea mtoa huduma za urekebishaji."</string>
@@ -2031,7 +2031,8 @@
<string name="autofill_update_title" msgid="3630695947047069136">"Ungependa kusasisha katika "<b>"<xliff:g id="LABEL">%1$s</xliff:g>"</b>"?"</string>
<string name="autofill_update_title_with_type" msgid="5264152633488495704">"Ungependa kusasisha <xliff:g id="TYPE">%1$s</xliff:g> katika "<b>"<xliff:g id="LABEL">%2$s</xliff:g>"</b>"?"</string>
<string name="autofill_update_title_with_2types" msgid="1797514386321086273">"Ungependa kusasisha <xliff:g id="TYPE_0">%1$s</xliff:g> na <xliff:g id="TYPE_1">%2$s</xliff:g> katika "<b>"<xliff:g id="LABEL">%3$s</xliff:g>"</b>"?"</string>
- <string name="autofill_update_title_with_3types" msgid="1312232153076212291">"Ungependa kusasisha vipengee hivi katika "<b>"<xliff:g id="LABEL">%4$s</xliff:g>"</b>": <xliff:g id="TYPE_0">%1$s</xliff:g>, <xliff:g id="TYPE_1">%2$s</xliff:g> na <xliff:g id="TYPE_2">%3$s</xliff:g>?"</string>
+ <!-- no translation found for autofill_update_title_with_3types (8285767070604652626) -->
+ <skip />
<string name="autofill_save_yes" msgid="8035743017382012850">"Hifadhi"</string>
<string name="autofill_save_no" msgid="9212826374207023544">"Hapana"</string>
<string name="autofill_save_notnow" msgid="2853932672029024195">"Si sasa"</string>
diff --git a/core/res/res/values-ta/strings.xml b/core/res/res/values-ta/strings.xml
index b2551e7..6052b4f 100644
--- a/core/res/res/values-ta/strings.xml
+++ b/core/res/res/values-ta/strings.xml
@@ -669,14 +669,14 @@
</string-array>
<string name="fingerprint_error_vendor_unknown" msgid="4170002184907291065">"ஏதோ தவறாகிவிட்டது. மீண்டும் முயலவும்."</string>
<string name="fingerprint_icon_content_description" msgid="4741068463175388817">"கைரேகை ஐகான்"</string>
+ <string name="device_unlock_notification_name" msgid="2632928999862915709">"சாதனத்தை அன்லாக் செய்தல்"</string>
+ <string name="alternative_unlock_setup_notification_title" msgid="6241508547901933544">"அன்லாக் செய்ய வேறொரு வழியை முயன்று பாருங்கள்"</string>
+ <string name="alternative_face_setup_notification_content" msgid="3384959224091897331">"உங்கள் கைரேகையை அடையாளங்காண முடியாதபோது முகம் காட்டித் திறத்தல் அம்சத்தைப் பயன்படுத்தலாம் (உதாரணமாக, உங்கள் விரல்கள் ஈரமாக இருக்கும்போது)"</string>
+ <string name="alternative_fp_setup_notification_content" msgid="7454096947415721639">"உங்கள் முகத்தை அடையாளங்காண முடியாதபோது கைரேகை அன்லாக் அம்சத்தைப் பயன்படுத்தலாம் (உதாரணமாக, போதுமான வெளிச்சம் இல்லாதபோது)"</string>
<string name="face_recalibrate_notification_name" msgid="7311163114750748686">"முகம் காட்டித் திறத்தல்"</string>
<string name="face_recalibrate_notification_title" msgid="2524791952735579082">"முகம் காட்டித் திறத்தல் அம்சத்தில் சிக்கல்"</string>
<string name="face_recalibrate_notification_content" msgid="3064513770251355594">"முகத் தோற்றப் பதிவைத் தட்டி நீக்கிவிட்டு உங்கள் முகத்தை மீண்டும் சேர்க்கவும்"</string>
- <string name="face_setup_notification_title" msgid="8843461561970741790">"\'முகம் காட்டித் திறத்தல்\' அம்சத்தை அமைத்தல்"</string>
- <string name="face_setup_notification_content" msgid="5463999831057751676">"மொபைலைப் பார்ப்பதன் மூலம் அதை அன்லாக் செய்யலாம்"</string>
<string name="face_sensor_privacy_enabled" msgid="7407126963510598508">"முகம் காட்டித் திறத்தல் அம்சத்தைப் பயன்படுத்த, அமைப்புகள் > தனியுரிமை என்பதற்குச் சென்று "<b>"கேமரா அணுகலை"</b>" இயக்கவும்"</string>
- <string name="fingerprint_setup_notification_title" msgid="2002630611398849495">"அன்லாக் செய்ய மேலும் பல வழிகளை அமையுங்கள்"</string>
- <string name="fingerprint_setup_notification_content" msgid="205578121848324852">"கைரேகையைச் சேர்க்கத் தட்டுங்கள்"</string>
<string name="fingerprint_recalibrate_notification_name" msgid="1414578431898579354">"கைரேகை அன்லாக்"</string>
<string name="fingerprint_recalibrate_notification_title" msgid="2406561052064558497">"கைரேகை சென்சாரைப் பயன்படுத்த முடியவில்லை"</string>
<string name="fingerprint_recalibrate_notification_content" msgid="8519935717822194943">"பழுதுபார்ப்புச் சேவை வழங்குநரைத் தொடர்புகொள்ளவும்."</string>
@@ -2031,7 +2031,8 @@
<string name="autofill_update_title" msgid="3630695947047069136"><b>"<xliff:g id="LABEL">%1$s</xliff:g>"</b>" இல் மாற்றவா?"</string>
<string name="autofill_update_title_with_type" msgid="5264152633488495704"><b>"<xliff:g id="LABEL">%2$s</xliff:g>"</b>" இல் <xliff:g id="TYPE">%1$s</xliff:g>ஐ மாற்றவா?"</string>
<string name="autofill_update_title_with_2types" msgid="1797514386321086273"><b>"<xliff:g id="LABEL">%3$s</xliff:g>"</b>" இல் <xliff:g id="TYPE_0">%1$s</xliff:g> மற்றும் <xliff:g id="TYPE_1">%2$s</xliff:g>ஐ மாற்றவா?"</string>
- <string name="autofill_update_title_with_3types" msgid="1312232153076212291"><b>"<xliff:g id="LABEL">%4$s</xliff:g>"</b>" இல் <xliff:g id="TYPE_0">%1$s</xliff:g>, <xliff:g id="TYPE_1">%2$s</xliff:g> மற்றும் <xliff:g id="TYPE_2">%3$s</xliff:g>ஐ மாற்றவா?"</string>
+ <!-- no translation found for autofill_update_title_with_3types (8285767070604652626) -->
+ <skip />
<string name="autofill_save_yes" msgid="8035743017382012850">"சேமி"</string>
<string name="autofill_save_no" msgid="9212826374207023544">"வேண்டாம்"</string>
<string name="autofill_save_notnow" msgid="2853932672029024195">"இப்போது வேண்டாம்"</string>
diff --git a/core/res/res/values-te/strings.xml b/core/res/res/values-te/strings.xml
index a90b2bf3..9da39c3 100644
--- a/core/res/res/values-te/strings.xml
+++ b/core/res/res/values-te/strings.xml
@@ -669,14 +669,14 @@
</string-array>
<string name="fingerprint_error_vendor_unknown" msgid="4170002184907291065">"ఏదో తప్పు జరిగింది. మళ్లీ ట్రై చేయండి."</string>
<string name="fingerprint_icon_content_description" msgid="4741068463175388817">"వేలిముద్ర చిహ్నం"</string>
+ <string name="device_unlock_notification_name" msgid="2632928999862915709">"పరికర అన్లాక్"</string>
+ <string name="alternative_unlock_setup_notification_title" msgid="6241508547901933544">"అన్లాక్ చేయడానికి మరొక మార్గాన్ని ట్రై చేయండి"</string>
+ <string name="alternative_face_setup_notification_content" msgid="3384959224091897331">"మీ వేలిముద్ర గుర్తించబడనప్పుడు, మీ వేళ్లు తడిగా ఉన్నప్పుడు ఫేస్ అన్లాక్ను ఉపయోగించండి"</string>
+ <string name="alternative_fp_setup_notification_content" msgid="7454096947415721639">"మీ ఫేస్ గుర్తించబడనప్పుడు, తగినంత వెలుతురు లేనప్పుడు వేలిముద్ర అన్లాక్ను ఉపయోగించండి"</string>
<string name="face_recalibrate_notification_name" msgid="7311163114750748686">"ఫేస్ అన్లాక్"</string>
<string name="face_recalibrate_notification_title" msgid="2524791952735579082">"ఫేస్ అన్లాక్తో సమస్య"</string>
<string name="face_recalibrate_notification_content" msgid="3064513770251355594">"ఫేస్ మోడల్ను తొలగించడానికి నొక్కండి, ఆపై మీ ముఖాన్ని మళ్లీ జోడించండి"</string>
- <string name="face_setup_notification_title" msgid="8843461561970741790">"ఫేస్ అన్లాక్ను సెటప్ చేయండి"</string>
- <string name="face_setup_notification_content" msgid="5463999831057751676">"మీ ఫోన్ను చూడటం ద్వారా దాన్ని అన్లాక్ చేయండి"</string>
<string name="face_sensor_privacy_enabled" msgid="7407126963510598508">"ఫేస్ అన్లాక్ను ఉపయోగించడానికి, సెట్టింగ్లు > గోప్యతలో "<b>"కెమెరా యాక్సెస్"</b>"ను ఆన్ చేయండి"</string>
- <string name="fingerprint_setup_notification_title" msgid="2002630611398849495">"అన్లాక్ చేయడానికి మరిన్ని మార్గాలను సెటప్ చేయండి"</string>
- <string name="fingerprint_setup_notification_content" msgid="205578121848324852">"వేలిముద్రను జోడించడానికి ట్యాప్ చేయండి"</string>
<string name="fingerprint_recalibrate_notification_name" msgid="1414578431898579354">"వేలిముద్ర అన్లాక్"</string>
<string name="fingerprint_recalibrate_notification_title" msgid="2406561052064558497">"వేలిముద్ర సెన్సార్ను ఉపయోగించడం సాధ్యం కాదు"</string>
<string name="fingerprint_recalibrate_notification_content" msgid="8519935717822194943">"రిపెయిర్ ప్రొవైడర్ను సందర్శించండి."</string>
@@ -2031,7 +2031,8 @@
<string name="autofill_update_title" msgid="3630695947047069136"><b>"<xliff:g id="LABEL">%1$s</xliff:g>"</b>"లో అప్డేట్ చేయాలా?"</string>
<string name="autofill_update_title_with_type" msgid="5264152633488495704">"<xliff:g id="TYPE">%1$s</xliff:g>ని "<b>"<xliff:g id="LABEL">%2$s</xliff:g>"</b>"లో అప్డేట్ చేయాలా?"</string>
<string name="autofill_update_title_with_2types" msgid="1797514386321086273">"<xliff:g id="TYPE_0">%1$s</xliff:g> మరియు <xliff:g id="TYPE_1">%2$s</xliff:g>ని "<b>"<xliff:g id="LABEL">%3$s</xliff:g>"</b>"లో అప్డేట్ చేయాలా?"</string>
- <string name="autofill_update_title_with_3types" msgid="1312232153076212291">"ఈ అంశాలను "<b>"<xliff:g id="LABEL">%4$s</xliff:g>"</b>"లో అప్డేట్ చేయాలా: <xliff:g id="TYPE_0">%1$s</xliff:g>, <xliff:g id="TYPE_1">%2$s</xliff:g> మరియు <xliff:g id="TYPE_2">%3$s</xliff:g> ?"</string>
+ <!-- no translation found for autofill_update_title_with_3types (8285767070604652626) -->
+ <skip />
<string name="autofill_save_yes" msgid="8035743017382012850">"సేవ్ చేయండి"</string>
<string name="autofill_save_no" msgid="9212826374207023544">"వద్దు, ధన్యవాదాలు"</string>
<string name="autofill_save_notnow" msgid="2853932672029024195">"ఇప్పుడు కాదు"</string>
diff --git a/core/res/res/values-th/strings.xml b/core/res/res/values-th/strings.xml
index 74b744f..fa66380 100644
--- a/core/res/res/values-th/strings.xml
+++ b/core/res/res/values-th/strings.xml
@@ -669,14 +669,14 @@
</string-array>
<string name="fingerprint_error_vendor_unknown" msgid="4170002184907291065">"เกิดข้อผิดพลาด ลองอีกครั้ง"</string>
<string name="fingerprint_icon_content_description" msgid="4741068463175388817">"ไอคอนลายนิ้วมือ"</string>
+ <string name="device_unlock_notification_name" msgid="2632928999862915709">"การปลดล็อกอุปกรณ์"</string>
+ <string name="alternative_unlock_setup_notification_title" msgid="6241508547901933544">"ลองใช้วิธีอื่นในการปลดล็อก"</string>
+ <string name="alternative_face_setup_notification_content" msgid="3384959224091897331">"ใช้การปลดล็อกด้วยใบหน้าเมื่อระบบจำลายนิ้วมือของคุณไม่ได้ เช่น เมื่อนิ้วมือเปียก"</string>
+ <string name="alternative_fp_setup_notification_content" msgid="7454096947415721639">"ใช้การปลดล็อกด้วยลายนิ้วมือเมื่อระบบจำใบหน้าของคุณไม่ได้ เช่น เมื่อมีแสงสว่างไม่เพียงพอ"</string>
<string name="face_recalibrate_notification_name" msgid="7311163114750748686">"การปลดล็อกด้วยใบหน้า"</string>
<string name="face_recalibrate_notification_title" msgid="2524791952735579082">"มีปัญหาเกี่ยวกับฟีเจอร์ปลดล็อกด้วยใบหน้า"</string>
<string name="face_recalibrate_notification_content" msgid="3064513770251355594">"แตะเพื่อลบรูปแบบใบหน้า แล้วเพิ่มใบหน้าอีกครั้ง"</string>
- <string name="face_setup_notification_title" msgid="8843461561970741790">"ตั้งค่าการปลดล็อกด้วยใบหน้า"</string>
- <string name="face_setup_notification_content" msgid="5463999831057751676">"ปลดล็อกโทรศัพท์โดยมองไปที่โทรศัพท์"</string>
<string name="face_sensor_privacy_enabled" msgid="7407126963510598508">"หากต้องการใช้ปลดล็อกด้วยใบหน้า ให้เปิด"<b>"การเข้าถึงกล้อง"</b>"ในการตั้งค่าและความเป็นส่วนตัว"</string>
- <string name="fingerprint_setup_notification_title" msgid="2002630611398849495">"ตั้งค่าการปลดล็อกด้วยวิธีอื่น"</string>
- <string name="fingerprint_setup_notification_content" msgid="205578121848324852">"แตะเพื่อเพิ่มลายนิ้วมือ"</string>
<string name="fingerprint_recalibrate_notification_name" msgid="1414578431898579354">"ปลดล็อกด้วยลายนิ้วมือ"</string>
<string name="fingerprint_recalibrate_notification_title" msgid="2406561052064558497">"ใช้เซ็นเซอร์ลายนิ้วมือไม่ได้"</string>
<string name="fingerprint_recalibrate_notification_content" msgid="8519935717822194943">"โปรดติดต่อผู้ให้บริการซ่อม"</string>
@@ -2031,7 +2031,8 @@
<string name="autofill_update_title" msgid="3630695947047069136">"อัปเดตใน "<b>"<xliff:g id="LABEL">%1$s</xliff:g>"</b>" ไหม"</string>
<string name="autofill_update_title_with_type" msgid="5264152633488495704">"อัปเดต<xliff:g id="TYPE">%1$s</xliff:g>ใน "<b>"<xliff:g id="LABEL">%2$s</xliff:g>"</b>" ไหม"</string>
<string name="autofill_update_title_with_2types" msgid="1797514386321086273">"อัปเดต<xliff:g id="TYPE_0">%1$s</xliff:g>และ<xliff:g id="TYPE_1">%2$s</xliff:g>ใน "<b>"<xliff:g id="LABEL">%3$s</xliff:g>"</b>" ไหม"</string>
- <string name="autofill_update_title_with_3types" msgid="1312232153076212291">"อัปเดตข้อมูล<xliff:g id="TYPE_0">%1$s</xliff:g>, <xliff:g id="TYPE_1">%2$s</xliff:g> และ<xliff:g id="TYPE_2">%3$s</xliff:g> ใน "<b>"<xliff:g id="LABEL">%4$s</xliff:g>"</b>" ไหม"</string>
+ <!-- no translation found for autofill_update_title_with_3types (8285767070604652626) -->
+ <skip />
<string name="autofill_save_yes" msgid="8035743017382012850">"บันทึก"</string>
<string name="autofill_save_no" msgid="9212826374207023544">"ไม่เป็นไร"</string>
<string name="autofill_save_notnow" msgid="2853932672029024195">"ไว้ทีหลัง"</string>
diff --git a/core/res/res/values-tl/strings.xml b/core/res/res/values-tl/strings.xml
index 633282e..840810c 100644
--- a/core/res/res/values-tl/strings.xml
+++ b/core/res/res/values-tl/strings.xml
@@ -669,14 +669,14 @@
</string-array>
<string name="fingerprint_error_vendor_unknown" msgid="4170002184907291065">"Nagkaproblema. Subukan ulit."</string>
<string name="fingerprint_icon_content_description" msgid="4741068463175388817">"Icon ng fingerprint"</string>
+ <string name="device_unlock_notification_name" msgid="2632928999862915709">"Pag-unlock ng device"</string>
+ <string name="alternative_unlock_setup_notification_title" msgid="6241508547901933544">"Sumubok ng ibang paraan ng pag-unlock."</string>
+ <string name="alternative_face_setup_notification_content" msgid="3384959224091897331">"Gamitin ang Pag-unlock Gamit ang Mukha kapag hindi nakikilala ang iyong fingerprint, tulad kapag basa ang iyong mga daliri"</string>
+ <string name="alternative_fp_setup_notification_content" msgid="7454096947415721639">"Gamitin ang Pag-unlock Gamit ang Fingerprint kapag hindi nakikilala ang iyong mukha, tulad kapag madilim"</string>
<string name="face_recalibrate_notification_name" msgid="7311163114750748686">"Pag-unlock Gamit ang Mukha"</string>
<string name="face_recalibrate_notification_title" msgid="2524791952735579082">"Isyu sa Pag-unlock Gamit ang Mukha"</string>
<string name="face_recalibrate_notification_content" msgid="3064513770251355594">"I-tap para i-delete ang iyong face model, pagkatapos ay idagdag ulit ang mukha mo"</string>
- <string name="face_setup_notification_title" msgid="8843461561970741790">"I-set up ang Pag-unlock Gamit ang Mukha"</string>
- <string name="face_setup_notification_content" msgid="5463999831057751676">"I-unlock ang iyong telepono sa pamamagitan ng pagtingin dito"</string>
<string name="face_sensor_privacy_enabled" msgid="7407126963510598508">"Para magamit ang Pag-unlock Gamit ang Mukha, i-on ang "<b>"Access sa camera"</b>" sa Mga Setting > Privacy"</string>
- <string name="fingerprint_setup_notification_title" msgid="2002630611398849495">"Mag-set up ng higit pang paraan para mag-unlock"</string>
- <string name="fingerprint_setup_notification_content" msgid="205578121848324852">"I-tap para magdagdag ng fingerprint"</string>
<string name="fingerprint_recalibrate_notification_name" msgid="1414578431898579354">"Pag-unlock Gamit ang Fingerprint"</string>
<string name="fingerprint_recalibrate_notification_title" msgid="2406561052064558497">"Hindi magamit ang sensor para sa fingerprint"</string>
<string name="fingerprint_recalibrate_notification_content" msgid="8519935717822194943">"Bumisita sa provider ng pag-aayos."</string>
@@ -2031,7 +2031,8 @@
<string name="autofill_update_title" msgid="3630695947047069136">"I-update sa "<b>"<xliff:g id="LABEL">%1$s</xliff:g>"</b>"?"</string>
<string name="autofill_update_title_with_type" msgid="5264152633488495704">"I-update ang <xliff:g id="TYPE">%1$s</xliff:g> sa "<b>"<xliff:g id="LABEL">%2$s</xliff:g>"</b>"?"</string>
<string name="autofill_update_title_with_2types" msgid="1797514386321086273">"I-update ang <xliff:g id="TYPE_0">%1$s</xliff:g> at <xliff:g id="TYPE_1">%2$s</xliff:g> sa "<b>"<xliff:g id="LABEL">%3$s</xliff:g>"</b>"?"</string>
- <string name="autofill_update_title_with_3types" msgid="1312232153076212291">"I-update ang mga item na ito sa "<b>"<xliff:g id="LABEL">%4$s</xliff:g>"</b>": <xliff:g id="TYPE_0">%1$s</xliff:g>, <xliff:g id="TYPE_1">%2$s</xliff:g>, at <xliff:g id="TYPE_2">%3$s</xliff:g> ?"</string>
+ <!-- no translation found for autofill_update_title_with_3types (8285767070604652626) -->
+ <skip />
<string name="autofill_save_yes" msgid="8035743017382012850">"I-save"</string>
<string name="autofill_save_no" msgid="9212826374207023544">"Hindi, salamat na lang"</string>
<string name="autofill_save_notnow" msgid="2853932672029024195">"Hindi ngayon"</string>
diff --git a/core/res/res/values-tr/strings.xml b/core/res/res/values-tr/strings.xml
index 17db4f4..90a5731 100644
--- a/core/res/res/values-tr/strings.xml
+++ b/core/res/res/values-tr/strings.xml
@@ -669,14 +669,14 @@
</string-array>
<string name="fingerprint_error_vendor_unknown" msgid="4170002184907291065">"Bir hata oluştu. Tekrar deneyin."</string>
<string name="fingerprint_icon_content_description" msgid="4741068463175388817">"Parmak izi simgesi"</string>
+ <string name="device_unlock_notification_name" msgid="2632928999862915709">"Cihazda kilit açma"</string>
+ <string name="alternative_unlock_setup_notification_title" msgid="6241508547901933544">"Kilidi açmak için başka bir yöntem kullanın"</string>
+ <string name="alternative_face_setup_notification_content" msgid="3384959224091897331">"Parmak iziniz tanınmadığında (ör. parmaklarınız ıslak olduğu için) Yüz Tanıma Kilidi\'ni kullanın"</string>
+ <string name="alternative_fp_setup_notification_content" msgid="7454096947415721639">"Yüzünüz tanınmadığında (ör. yeterince ışık olmadığı için) Parmak İzi Kilidi\'ni kullanın"</string>
<string name="face_recalibrate_notification_name" msgid="7311163114750748686">"Yüz Tanıma Kilidi"</string>
<string name="face_recalibrate_notification_title" msgid="2524791952735579082">"Yüz Tanıma Kilidi sorunu"</string>
<string name="face_recalibrate_notification_content" msgid="3064513770251355594">"Yüz modelinizi silmek için dokunup ardından yüzünüzü yeniden ekleyin"</string>
- <string name="face_setup_notification_title" msgid="8843461561970741790">"Yüz Tanıma Kilidi\'ni kurun"</string>
- <string name="face_setup_notification_content" msgid="5463999831057751676">"Telefonunuza bakarak kilidini açın"</string>
<string name="face_sensor_privacy_enabled" msgid="7407126963510598508">"Yüz Tanıma Kilidi\'ni kullanmak için Ayarlar > Gizlilik bölümünden "<b>"Kamera erişimi"</b>"\'ni açın"</string>
- <string name="fingerprint_setup_notification_title" msgid="2002630611398849495">"Kilidi açmak için daha fazla yöntem ayarlayın"</string>
- <string name="fingerprint_setup_notification_content" msgid="205578121848324852">"Parmak izi eklemek için dokunun"</string>
<string name="fingerprint_recalibrate_notification_name" msgid="1414578431898579354">"Parmak İzi Kilidi"</string>
<string name="fingerprint_recalibrate_notification_title" msgid="2406561052064558497">"Parmak izi sensörü kullanılamıyor"</string>
<string name="fingerprint_recalibrate_notification_content" msgid="8519935717822194943">"Bir onarım hizmeti sağlayıcıyı ziyaret edin."</string>
@@ -2031,7 +2031,8 @@
<string name="autofill_update_title" msgid="3630695947047069136"><b>"<xliff:g id="LABEL">%1$s</xliff:g>"</b>" hizmetinde güncellensin mi?"</string>
<string name="autofill_update_title_with_type" msgid="5264152633488495704">"<xliff:g id="TYPE">%1$s</xliff:g> "<b>"<xliff:g id="LABEL">%2$s</xliff:g>"</b>" hizmetinde güncellensin mi?"</string>
<string name="autofill_update_title_with_2types" msgid="1797514386321086273">"<xliff:g id="TYPE_0">%1$s</xliff:g> ve <xliff:g id="TYPE_1">%2$s</xliff:g> "<b>"<xliff:g id="LABEL">%3$s</xliff:g>"</b>" hizmetinde güncellensin mi?"</string>
- <string name="autofill_update_title_with_3types" msgid="1312232153076212291">"Şu öğeler "<b>"<xliff:g id="LABEL">%4$s</xliff:g>"</b>" hizmetinde güncellensin mi: <xliff:g id="TYPE_0">%1$s</xliff:g>, <xliff:g id="TYPE_1">%2$s</xliff:g> ve <xliff:g id="TYPE_2">%3$s</xliff:g>?"</string>
+ <!-- no translation found for autofill_update_title_with_3types (8285767070604652626) -->
+ <skip />
<string name="autofill_save_yes" msgid="8035743017382012850">"Kaydet"</string>
<string name="autofill_save_no" msgid="9212826374207023544">"Hayır, teşekkürler"</string>
<string name="autofill_save_notnow" msgid="2853932672029024195">"Şimdi değil"</string>
diff --git a/core/res/res/values-uk/strings.xml b/core/res/res/values-uk/strings.xml
index 0606c7a..f8a725d 100644
--- a/core/res/res/values-uk/strings.xml
+++ b/core/res/res/values-uk/strings.xml
@@ -671,14 +671,14 @@
</string-array>
<string name="fingerprint_error_vendor_unknown" msgid="4170002184907291065">"Сталася помилка. Повторіть спробу."</string>
<string name="fingerprint_icon_content_description" msgid="4741068463175388817">"Значок відбитка пальця"</string>
+ <string name="device_unlock_notification_name" msgid="2632928999862915709">"Розблокування пристрою"</string>
+ <string name="alternative_unlock_setup_notification_title" msgid="6241508547901933544">"Спробуйте інший спосіб розблокування"</string>
+ <string name="alternative_face_setup_notification_content" msgid="3384959224091897331">"Розблоковуйте пристрій за допомогою фейс-контролю, коли не вдається розпізнати ваш відбиток пальця (наприклад, коли у вас мокрі пальці)"</string>
+ <string name="alternative_fp_setup_notification_content" msgid="7454096947415721639">"Розблоковуйте пристрій відбитком пальця, коли не вдається розпізнати ваше обличчя (наприклад, коли недостатньо світла)"</string>
<string name="face_recalibrate_notification_name" msgid="7311163114750748686">"Фейс-контроль"</string>
<string name="face_recalibrate_notification_title" msgid="2524791952735579082">"Сталася помилка з фейсконтролем"</string>
<string name="face_recalibrate_notification_content" msgid="3064513770251355594">"Натисніть, щоб видалити свою модель обличчя, а потім знову додайте її"</string>
- <string name="face_setup_notification_title" msgid="8843461561970741790">"Налаштування фейс-контролю"</string>
- <string name="face_setup_notification_content" msgid="5463999831057751676">"Ви зможете розблоковувати телефон, подивившись на нього"</string>
<string name="face_sensor_privacy_enabled" msgid="7407126963510598508">"Щоб використовувати фейс-контроль, увімкніть "<b>"Доступ до камери"</b>" в розділі \"Налаштування\" > \"Конфіденційність\""</string>
- <string name="fingerprint_setup_notification_title" msgid="2002630611398849495">"Налаштуйте більше способів розблокування"</string>
- <string name="fingerprint_setup_notification_content" msgid="205578121848324852">"Натисніть, щоб додати відбиток пальця"</string>
<string name="fingerprint_recalibrate_notification_name" msgid="1414578431898579354">"Розблокування відбитком пальця"</string>
<string name="fingerprint_recalibrate_notification_title" msgid="2406561052064558497">"Не вдається скористатися сканером відбитків пальців"</string>
<string name="fingerprint_recalibrate_notification_content" msgid="8519935717822194943">"Зверніться до постачальника послуг із ремонту."</string>
@@ -2033,7 +2033,8 @@
<string name="autofill_update_title" msgid="3630695947047069136">"Оновити в сервісі "<b>"<xliff:g id="LABEL">%1$s</xliff:g>"</b>"?"</string>
<string name="autofill_update_title_with_type" msgid="5264152633488495704">"Оновити дані (<xliff:g id="TYPE">%1$s</xliff:g>) у сервісі "<b>"<xliff:g id="LABEL">%2$s</xliff:g>"</b>"?"</string>
<string name="autofill_update_title_with_2types" msgid="1797514386321086273">"Оновити дані (<xliff:g id="TYPE_0">%1$s</xliff:g> і <xliff:g id="TYPE_1">%2$s</xliff:g>) у сервісі "<b>"<xliff:g id="LABEL">%3$s</xliff:g>"</b>"?"</string>
- <string name="autofill_update_title_with_3types" msgid="1312232153076212291">"Оновити в сервісі "<b>"<xliff:g id="LABEL">%4$s</xliff:g>"</b>" такі дані: <xliff:g id="TYPE_0">%1$s</xliff:g>, <xliff:g id="TYPE_1">%2$s</xliff:g> і <xliff:g id="TYPE_2">%3$s</xliff:g>?"</string>
+ <!-- no translation found for autofill_update_title_with_3types (8285767070604652626) -->
+ <skip />
<string name="autofill_save_yes" msgid="8035743017382012850">"Зберегти"</string>
<string name="autofill_save_no" msgid="9212826374207023544">"Ні, дякую"</string>
<string name="autofill_save_notnow" msgid="2853932672029024195">"Не зараз"</string>
diff --git a/core/res/res/values-ur/strings.xml b/core/res/res/values-ur/strings.xml
index dc4ea46..227f25d 100644
--- a/core/res/res/values-ur/strings.xml
+++ b/core/res/res/values-ur/strings.xml
@@ -669,14 +669,14 @@
</string-array>
<string name="fingerprint_error_vendor_unknown" msgid="4170002184907291065">"کچھ غلط ہو گیا۔ دوبارہ کوشش کریں۔"</string>
<string name="fingerprint_icon_content_description" msgid="4741068463175388817">"فنگر پرنٹ آئیکن"</string>
+ <string name="device_unlock_notification_name" msgid="2632928999862915709">"آلے کو غیر مقفل کرنا"</string>
+ <string name="alternative_unlock_setup_notification_title" msgid="6241508547901933544">"غیر مقفل کرنے کا کوئی دوسرا طریقہ آزمائیں"</string>
+ <string name="alternative_face_setup_notification_content" msgid="3384959224091897331">"آپ کے فنگر پرنٹ کی شناخت نہ ہو پانے کی صورت میں، جیسے کی جب آپ کی انگلیاں گیلی ہوں، تو فیس اَنلاک کا استعمال کریں"</string>
+ <string name="alternative_fp_setup_notification_content" msgid="7454096947415721639">"آپ کے چہرے کی شناخت نہ ہو پانے کی صورت میں، جیسے کی جب خاطر خواہ روشنی موجود نہ ہو، تو فنگر پرنٹ اَن لاک کا استعمال کریں"</string>
<string name="face_recalibrate_notification_name" msgid="7311163114750748686">"فیس اَنلاک"</string>
<string name="face_recalibrate_notification_title" msgid="2524791952735579082">"فیس اَنلاک میں مسئلہ"</string>
<string name="face_recalibrate_notification_content" msgid="3064513770251355594">"اپنے چہرے کا ماڈل حذف کرنے کے لیے تھپتھپائیں پھر اپنا چہرہ دوبارہ شامل کریں"</string>
- <string name="face_setup_notification_title" msgid="8843461561970741790">"فیس اَنلاک سیٹ اپ کریں"</string>
- <string name="face_setup_notification_content" msgid="5463999831057751676">"اپنے فون کی طرف دیکھ کر اسے غیر مقفل کریں"</string>
<string name="face_sensor_privacy_enabled" msgid="7407126963510598508">"فیس اَنلاک کا استعمال کرنے کے لیے، ترتیبات اور رازداری میں "<b>"کیمرے تک رسائی"</b>" کو آن کریں"</string>
- <string name="fingerprint_setup_notification_title" msgid="2002630611398849495">"غیر مقفل کرنے کے مزید طریقے سیٹ اپ کریں"</string>
- <string name="fingerprint_setup_notification_content" msgid="205578121848324852">"فنگر پرنٹ شامل کرنے کیلئے تھپتھپائیں"</string>
<string name="fingerprint_recalibrate_notification_name" msgid="1414578431898579354">"فنگر پرنٹ اَن لاک"</string>
<string name="fingerprint_recalibrate_notification_title" msgid="2406561052064558497">"فنگر پرنٹ سینسر کا استعمال نہیں کر سکتے"</string>
<string name="fingerprint_recalibrate_notification_content" msgid="8519935717822194943">"ایک مرمت فراہم کنندہ کو ملاحظہ کریں۔"</string>
@@ -2031,7 +2031,8 @@
<string name="autofill_update_title" msgid="3630695947047069136"><b>"<xliff:g id="LABEL">%1$s</xliff:g>"</b>" میں اپ ڈیٹ کریں؟"</string>
<string name="autofill_update_title_with_type" msgid="5264152633488495704"><b>"<xliff:g id="LABEL">%2$s</xliff:g>"</b>" میں <xliff:g id="TYPE">%1$s</xliff:g> اپ ڈیٹ کریں؟"</string>
<string name="autofill_update_title_with_2types" msgid="1797514386321086273">"<xliff:g id="TYPE_0">%1$s</xliff:g> اور <xliff:g id="TYPE_1">%2$s</xliff:g> کو "<b>"<xliff:g id="LABEL">%3$s</xliff:g>"</b>" میں اپ ڈیٹ کریں؟"</string>
- <string name="autofill_update_title_with_3types" msgid="1312232153076212291">"ان آئٹمز کو "<b>"<xliff:g id="LABEL">%4$s</xliff:g> "</b>" میں اپ ڈیٹ کریں: <xliff:g id="TYPE_0">%1$s</xliff:g>، <xliff:g id="TYPE_1">%2$s</xliff:g> اور <xliff:g id="TYPE_2">%3$s</xliff:g> ؟"</string>
+ <!-- no translation found for autofill_update_title_with_3types (8285767070604652626) -->
+ <skip />
<string name="autofill_save_yes" msgid="8035743017382012850">"محفوظ کریں"</string>
<string name="autofill_save_no" msgid="9212826374207023544">"نہیں، شکریہ"</string>
<string name="autofill_save_notnow" msgid="2853932672029024195">"ابھی نہیں"</string>
diff --git a/core/res/res/values-uz/strings.xml b/core/res/res/values-uz/strings.xml
index 979c578..a0b43c7 100644
--- a/core/res/res/values-uz/strings.xml
+++ b/core/res/res/values-uz/strings.xml
@@ -669,14 +669,14 @@
</string-array>
<string name="fingerprint_error_vendor_unknown" msgid="4170002184907291065">"Xatolik yuz berdi. Qayta urining."</string>
<string name="fingerprint_icon_content_description" msgid="4741068463175388817">"Barmoq izi belgisi"</string>
+ <string name="device_unlock_notification_name" msgid="2632928999862915709">"Qurilma qulfini ochish"</string>
+ <string name="alternative_unlock_setup_notification_title" msgid="6241508547901933544">"Boshqa usulda oching"</string>
+ <string name="alternative_face_setup_notification_content" msgid="3384959224091897331">"Barmoq izi aniqlanmasa, masalan, barmoqlar hoʻl boʻlsa, Yuz bilan ochish funksiyasidan foydalaning"</string>
+ <string name="alternative_fp_setup_notification_content" msgid="7454096947415721639">"Yuz tanilmasa, masalan, yorugʻlik kamligida Barmoq izi bilan ochish funksiyasidan foydalaning"</string>
<string name="face_recalibrate_notification_name" msgid="7311163114750748686">"Yuz bilan ochish"</string>
<string name="face_recalibrate_notification_title" msgid="2524791952735579082">"Yuz bilan ochishda muammo bor"</string>
<string name="face_recalibrate_notification_content" msgid="3064513770251355594">"Yuz modelini oʻchirish uchun bosing va keyin yana yuzni qoʻshing"</string>
- <string name="face_setup_notification_title" msgid="8843461561970741790">"Yuz bilan ochishni sozlash"</string>
- <string name="face_setup_notification_content" msgid="5463999831057751676">"Telefoningizni yuz tekshiruvi yordamida qulfdan chiqaring"</string>
<string name="face_sensor_privacy_enabled" msgid="7407126963510598508">"Yuz bilan ochish uchun Sozlamalar va maxfiylik orqali "<b>"kameraga kirishga ruxsat bering"</b></string>
- <string name="fingerprint_setup_notification_title" msgid="2002630611398849495">"Qulfdan chiqarishning boshqa usullarini sozlang"</string>
- <string name="fingerprint_setup_notification_content" msgid="205578121848324852">"Barmoq izi kiritish uchun bosing"</string>
<string name="fingerprint_recalibrate_notification_name" msgid="1414578431898579354">"Barmoq izi bilan ochish"</string>
<string name="fingerprint_recalibrate_notification_title" msgid="2406561052064558497">"Barmoq izi skaneridan foydalanish imkonsiz"</string>
<string name="fingerprint_recalibrate_notification_content" msgid="8519935717822194943">"Xizmat koʻrsatish markaziga murojaat qiling."</string>
@@ -2031,7 +2031,8 @@
<string name="autofill_update_title" msgid="3630695947047069136"><b>"<xliff:g id="LABEL">%1$s</xliff:g>"</b>" xizmatida yangilansinmi?"</string>
<string name="autofill_update_title_with_type" msgid="5264152633488495704">"<xliff:g id="TYPE">%1$s</xliff:g> "<b>"<xliff:g id="LABEL">%2$s</xliff:g>"</b>" xizmatida yangilansinmi?"</string>
<string name="autofill_update_title_with_2types" msgid="1797514386321086273">"<xliff:g id="TYPE_0">%1$s</xliff:g> va <xliff:g id="TYPE_1">%2$s</xliff:g> "<b>"<xliff:g id="LABEL">%3$s</xliff:g>"</b>" xizmatida yangilansinmi?"</string>
- <string name="autofill_update_title_with_3types" msgid="1312232153076212291"><b>"<xliff:g id="LABEL">%4$s</xliff:g>"</b>" xizmatidagi <xliff:g id="TYPE_0">%1$s</xliff:g>, <xliff:g id="TYPE_1">%2$s</xliff:g> va <xliff:g id="TYPE_2">%3$s</xliff:g> yangilansinmi?"</string>
+ <!-- no translation found for autofill_update_title_with_3types (8285767070604652626) -->
+ <skip />
<string name="autofill_save_yes" msgid="8035743017382012850">"Saqlash"</string>
<string name="autofill_save_no" msgid="9212826374207023544">"Kerak emas"</string>
<string name="autofill_save_notnow" msgid="2853932672029024195">"Keyinroq"</string>
diff --git a/core/res/res/values-vi/strings.xml b/core/res/res/values-vi/strings.xml
index 58cec01..f6abefb 100644
--- a/core/res/res/values-vi/strings.xml
+++ b/core/res/res/values-vi/strings.xml
@@ -669,14 +669,14 @@
</string-array>
<string name="fingerprint_error_vendor_unknown" msgid="4170002184907291065">"Đã xảy ra lỗi. Hãy thử lại."</string>
<string name="fingerprint_icon_content_description" msgid="4741068463175388817">"Biểu tượng vân tay"</string>
+ <string name="device_unlock_notification_name" msgid="2632928999862915709">"Mở khoá thiết bị"</string>
+ <string name="alternative_unlock_setup_notification_title" msgid="6241508547901933544">"Thử mở khoá bằng cách khác"</string>
+ <string name="alternative_face_setup_notification_content" msgid="3384959224091897331">"Hãy dùng tính năng Mở khoá bằng khuôn mặt khi thiết bị không nhận diện được vân tay của bạn, chẳng hạn như khi ngón tay bị ướt"</string>
+ <string name="alternative_fp_setup_notification_content" msgid="7454096947415721639">"Hãy dùng tính năng Mở khoá bằng vân tay khi thiết bị không nhận diện được khuôn mặt của bạn, chẳng hạn như khi thiếu ánh sáng"</string>
<string name="face_recalibrate_notification_name" msgid="7311163114750748686">"Mở khóa bằng khuôn mặt"</string>
<string name="face_recalibrate_notification_title" msgid="2524791952735579082">"Vấn đề với tính năng Mở khóa bằng khuôn mặt"</string>
<string name="face_recalibrate_notification_content" msgid="3064513770251355594">"Nhấn để xóa mẫu khuôn mặt, sau đó thêm lại khuôn mặt của bạn"</string>
- <string name="face_setup_notification_title" msgid="8843461561970741790">"Thiết lập tính năng Mở khóa bằng khuôn mặt"</string>
- <string name="face_setup_notification_content" msgid="5463999831057751676">"Mở khóa điện thoại bằng cách nhìn vào điện thoại"</string>
<string name="face_sensor_privacy_enabled" msgid="7407126963510598508">"Để dùng tính năng Mở khoá bằng khuôn mặt, hãy bật tuỳ chọn "<b>"Truy cập máy ảnh"</b>" trong phần Cài đặt > Quyền riêng tư"</string>
- <string name="fingerprint_setup_notification_title" msgid="2002630611398849495">"Thiết lập thêm những cách mở khóa khác"</string>
- <string name="fingerprint_setup_notification_content" msgid="205578121848324852">"Nhấn để thêm vân tay"</string>
<string name="fingerprint_recalibrate_notification_name" msgid="1414578431898579354">"Mở khóa bằng vân tay"</string>
<string name="fingerprint_recalibrate_notification_title" msgid="2406561052064558497">"Không thể dùng cảm biến vân tay"</string>
<string name="fingerprint_recalibrate_notification_content" msgid="8519935717822194943">"Hãy liên hệ với một nhà cung cấp dịch vụ sửa chữa."</string>
@@ -2031,7 +2031,8 @@
<string name="autofill_update_title" msgid="3630695947047069136">"Cập nhật trong "<b>"<xliff:g id="LABEL">%1$s</xliff:g>"</b>"?"</string>
<string name="autofill_update_title_with_type" msgid="5264152633488495704">"Cập nhật <xliff:g id="TYPE">%1$s</xliff:g> trong "<b>"<xliff:g id="LABEL">%2$s</xliff:g>"</b>"?"</string>
<string name="autofill_update_title_with_2types" msgid="1797514386321086273">"Cập nhật <xliff:g id="TYPE_0">%1$s</xliff:g> và <xliff:g id="TYPE_1">%2$s</xliff:g> trong "<b>"<xliff:g id="LABEL">%3$s</xliff:g>"</b>"?"</string>
- <string name="autofill_update_title_with_3types" msgid="1312232153076212291">"Cập nhật các mục này: <xliff:g id="TYPE_0">%1$s</xliff:g>, <xliff:g id="TYPE_1">%2$s</xliff:g> và <xliff:g id="TYPE_2">%3$s</xliff:g> trong "<b>"<xliff:g id="LABEL">%4$s</xliff:g>"</b>"?"</string>
+ <!-- no translation found for autofill_update_title_with_3types (8285767070604652626) -->
+ <skip />
<string name="autofill_save_yes" msgid="8035743017382012850">"Lưu"</string>
<string name="autofill_save_no" msgid="9212826374207023544">"Không, cảm ơn"</string>
<string name="autofill_save_notnow" msgid="2853932672029024195">"Để sau"</string>
diff --git a/core/res/res/values-zh-rCN/strings.xml b/core/res/res/values-zh-rCN/strings.xml
index e5535af..10d865b 100644
--- a/core/res/res/values-zh-rCN/strings.xml
+++ b/core/res/res/values-zh-rCN/strings.xml
@@ -669,14 +669,14 @@
</string-array>
<string name="fingerprint_error_vendor_unknown" msgid="4170002184907291065">"出了点问题。请重试。"</string>
<string name="fingerprint_icon_content_description" msgid="4741068463175388817">"指纹图标"</string>
+ <string name="device_unlock_notification_name" msgid="2632928999862915709">"设备解锁"</string>
+ <string name="alternative_unlock_setup_notification_title" msgid="6241508547901933544">"试试其他解锁方式"</string>
+ <string name="alternative_face_setup_notification_content" msgid="3384959224091897331">"当系统无法识别您的指纹时,例如当您的手指潮湿时,可以使用人脸解锁功能"</string>
+ <string name="alternative_fp_setup_notification_content" msgid="7454096947415721639">"当系统无法识别您的面孔时,例如当光线不足时,可以使用指纹解锁功能"</string>
<string name="face_recalibrate_notification_name" msgid="7311163114750748686">"人脸解锁"</string>
<string name="face_recalibrate_notification_title" msgid="2524791952735579082">"人脸解锁存在问题"</string>
<string name="face_recalibrate_notification_content" msgid="3064513770251355594">"请点按以删除您的脸部模型,然后再添加您的脸部模型"</string>
- <string name="face_setup_notification_title" msgid="8843461561970741790">"设置人脸解锁"</string>
- <string name="face_setup_notification_content" msgid="5463999831057751676">"脸部对准手机即可将其解锁"</string>
<string name="face_sensor_privacy_enabled" msgid="7407126963510598508">"如需使用人脸解锁功能,请在“设置”>“隐私权”中开启"<b>"摄像头使用权限"</b></string>
- <string name="fingerprint_setup_notification_title" msgid="2002630611398849495">"设置更多解锁方式"</string>
- <string name="fingerprint_setup_notification_content" msgid="205578121848324852">"点按即可添加指纹"</string>
<string name="fingerprint_recalibrate_notification_name" msgid="1414578431898579354">"指纹解锁"</string>
<string name="fingerprint_recalibrate_notification_title" msgid="2406561052064558497">"无法使用指纹传感器"</string>
<string name="fingerprint_recalibrate_notification_content" msgid="8519935717822194943">"请联系维修服务提供商。"</string>
@@ -2031,7 +2031,8 @@
<string name="autofill_update_title" msgid="3630695947047069136">"要在"<b>"<xliff:g id="LABEL">%1$s</xliff:g>"</b>"中更新吗?"</string>
<string name="autofill_update_title_with_type" msgid="5264152633488495704">"要在"<b>"<xliff:g id="LABEL">%2$s</xliff:g>"</b>"中更新<xliff:g id="TYPE">%1$s</xliff:g>吗?"</string>
<string name="autofill_update_title_with_2types" msgid="1797514386321086273">"要在"<b>"<xliff:g id="LABEL">%3$s</xliff:g>"</b>"中更新<xliff:g id="TYPE_0">%1$s</xliff:g>和<xliff:g id="TYPE_1">%2$s</xliff:g>吗?"</string>
- <string name="autofill_update_title_with_3types" msgid="1312232153076212291">"要在"<b>"<xliff:g id="LABEL">%4$s</xliff:g>"</b>"中更新<xliff:g id="TYPE_0">%1$s</xliff:g>、<xliff:g id="TYPE_1">%2$s</xliff:g>和<xliff:g id="TYPE_2">%3$s</xliff:g>这些内容吗?"</string>
+ <!-- no translation found for autofill_update_title_with_3types (8285767070604652626) -->
+ <skip />
<string name="autofill_save_yes" msgid="8035743017382012850">"保存"</string>
<string name="autofill_save_no" msgid="9212826374207023544">"不用了"</string>
<string name="autofill_save_notnow" msgid="2853932672029024195">"以后再说"</string>
diff --git a/core/res/res/values-zh-rHK/strings.xml b/core/res/res/values-zh-rHK/strings.xml
index 935e757..8de6931 100644
--- a/core/res/res/values-zh-rHK/strings.xml
+++ b/core/res/res/values-zh-rHK/strings.xml
@@ -669,14 +669,14 @@
</string-array>
<string name="fingerprint_error_vendor_unknown" msgid="4170002184907291065">"發生錯誤,請再試一次。"</string>
<string name="fingerprint_icon_content_description" msgid="4741068463175388817">"指紋圖示"</string>
+ <string name="device_unlock_notification_name" msgid="2632928999862915709">"裝置解鎖"</string>
+ <string name="alternative_unlock_setup_notification_title" msgid="6241508547901933544">"改用其他解鎖方式"</string>
+ <string name="alternative_face_setup_notification_content" msgid="3384959224091897331">"當指紋無法識別 (如手指弄濕) 時使用面孔解鎖"</string>
+ <string name="alternative_fp_setup_notification_content" msgid="7454096947415721639">"當面孔無法識別 (如光線不足) 時使用指紋解鎖"</string>
<string name="face_recalibrate_notification_name" msgid="7311163114750748686">"面孔解鎖"</string>
<string name="face_recalibrate_notification_title" msgid="2524791952735579082">"「面孔解鎖」功能發生問題"</string>
<string name="face_recalibrate_notification_content" msgid="3064513770251355594">"請輕按這裡刪除面部模型,然後再重新新增"</string>
- <string name="face_setup_notification_title" msgid="8843461561970741790">"設定「面孔解鎖」"</string>
- <string name="face_setup_notification_content" msgid="5463999831057751676">"直望手機即可解鎖"</string>
<string name="face_sensor_privacy_enabled" msgid="7407126963510598508">"如要使用「面孔解鎖」,請在 [設定] > [私隱] 開啟"<b>"相機存取權"</b></string>
- <string name="fingerprint_setup_notification_title" msgid="2002630611398849495">"設定更多解鎖方法"</string>
- <string name="fingerprint_setup_notification_content" msgid="205578121848324852">"輕按即可新增指紋"</string>
<string name="fingerprint_recalibrate_notification_name" msgid="1414578431898579354">"指紋解鎖"</string>
<string name="fingerprint_recalibrate_notification_title" msgid="2406561052064558497">"無法使用指紋感應器"</string>
<string name="fingerprint_recalibrate_notification_content" msgid="8519935717822194943">"請諮詢維修服務供應商。"</string>
@@ -2031,7 +2031,8 @@
<string name="autofill_update_title" msgid="3630695947047069136">"要在 "<b>"<xliff:g id="LABEL">%1$s</xliff:g>"</b>" 中更新嗎?"</string>
<string name="autofill_update_title_with_type" msgid="5264152633488495704">"要在 "<b>"<xliff:g id="LABEL">%2$s</xliff:g>"</b>" 中更新<xliff:g id="TYPE">%1$s</xliff:g>嗎?"</string>
<string name="autofill_update_title_with_2types" msgid="1797514386321086273">"要在 "<b>"<xliff:g id="LABEL">%3$s</xliff:g>"</b>" 中更新<xliff:g id="TYPE_0">%1$s</xliff:g>和<xliff:g id="TYPE_1">%2$s</xliff:g>嗎?"</string>
- <string name="autofill_update_title_with_3types" msgid="1312232153076212291">"要在 "<b>"<xliff:g id="LABEL">%4$s</xliff:g>"</b>" 中更新<xliff:g id="TYPE_0">%1$s</xliff:g>、<xliff:g id="TYPE_1">%2$s</xliff:g>和<xliff:g id="TYPE_2">%3$s</xliff:g>嗎?"</string>
+ <!-- no translation found for autofill_update_title_with_3types (8285767070604652626) -->
+ <skip />
<string name="autofill_save_yes" msgid="8035743017382012850">"儲存"</string>
<string name="autofill_save_no" msgid="9212826374207023544">"不用了,謝謝"</string>
<string name="autofill_save_notnow" msgid="2853932672029024195">"暫時不要"</string>
diff --git a/core/res/res/values-zh-rTW/strings.xml b/core/res/res/values-zh-rTW/strings.xml
index 3f93ecd..c0631e9 100644
--- a/core/res/res/values-zh-rTW/strings.xml
+++ b/core/res/res/values-zh-rTW/strings.xml
@@ -669,14 +669,14 @@
</string-array>
<string name="fingerprint_error_vendor_unknown" msgid="4170002184907291065">"發生錯誤,請再試一次。"</string>
<string name="fingerprint_icon_content_description" msgid="4741068463175388817">"指紋圖示"</string>
+ <string name="device_unlock_notification_name" msgid="2632928999862915709">"裝置解鎖"</string>
+ <string name="alternative_unlock_setup_notification_title" msgid="6241508547901933544">"試試其他解鎖方式"</string>
+ <string name="alternative_face_setup_notification_content" msgid="3384959224091897331">"當指紋無法辨識時 (例如手指溼溼的),可使用人臉解鎖功能"</string>
+ <string name="alternative_fp_setup_notification_content" msgid="7454096947415721639">"當人臉無法辨識時 (例如光線不足),可使用指紋解鎖功能"</string>
<string name="face_recalibrate_notification_name" msgid="7311163114750748686">"人臉解鎖"</string>
<string name="face_recalibrate_notification_title" msgid="2524791952735579082">"人臉解鎖功能發生問題"</string>
<string name="face_recalibrate_notification_content" msgid="3064513770251355594">"請輕觸這裡刪除臉部模型,然後再重新新增"</string>
- <string name="face_setup_notification_title" msgid="8843461561970741790">"設定人臉解鎖功能"</string>
- <string name="face_setup_notification_content" msgid="5463999831057751676">"看著手機就能解鎖"</string>
<string name="face_sensor_privacy_enabled" msgid="7407126963510598508">"如要使用人臉解鎖功能,請前往「設定」>「隱私權」開啟"<b>"攝影機存取權"</b></string>
- <string name="fingerprint_setup_notification_title" msgid="2002630611398849495">"設定更多解鎖方式"</string>
- <string name="fingerprint_setup_notification_content" msgid="205578121848324852">"輕觸即可新增指紋"</string>
<string name="fingerprint_recalibrate_notification_name" msgid="1414578431898579354">"指紋解鎖"</string>
<string name="fingerprint_recalibrate_notification_title" msgid="2406561052064558497">"指紋感應器無法使用"</string>
<string name="fingerprint_recalibrate_notification_content" msgid="8519935717822194943">"請洽詢維修供應商。"</string>
@@ -2031,7 +2031,8 @@
<string name="autofill_update_title" msgid="3630695947047069136">"要更新 "<b>"<xliff:g id="LABEL">%1$s</xliff:g>"</b>" 中的內容嗎?"</string>
<string name="autofill_update_title_with_type" msgid="5264152633488495704">"要更新 "<b>"<xliff:g id="LABEL">%2$s</xliff:g>"</b>" 中的<xliff:g id="TYPE">%1$s</xliff:g>嗎?"</string>
<string name="autofill_update_title_with_2types" msgid="1797514386321086273">"要更新 "<b>"<xliff:g id="LABEL">%3$s</xliff:g>"</b>" 中的<xliff:g id="TYPE_0">%1$s</xliff:g>和<xliff:g id="TYPE_1">%2$s</xliff:g>嗎?"</string>
- <string name="autofill_update_title_with_3types" msgid="1312232153076212291">"要更新 "<b>"<xliff:g id="LABEL">%4$s</xliff:g>"</b>" 中的<xliff:g id="TYPE_0">%1$s</xliff:g>、<xliff:g id="TYPE_1">%2$s</xliff:g>和<xliff:g id="TYPE_2">%3$s</xliff:g>嗎?"</string>
+ <!-- no translation found for autofill_update_title_with_3types (8285767070604652626) -->
+ <skip />
<string name="autofill_save_yes" msgid="8035743017382012850">"儲存"</string>
<string name="autofill_save_no" msgid="9212826374207023544">"不用了,謝謝"</string>
<string name="autofill_save_notnow" msgid="2853932672029024195">"暫時不要"</string>
diff --git a/core/res/res/values-zu/strings.xml b/core/res/res/values-zu/strings.xml
index 929af23..e9b4080 100644
--- a/core/res/res/values-zu/strings.xml
+++ b/core/res/res/values-zu/strings.xml
@@ -669,14 +669,14 @@
</string-array>
<string name="fingerprint_error_vendor_unknown" msgid="4170002184907291065">"Kunento engahambanga kahle. Zama futhi."</string>
<string name="fingerprint_icon_content_description" msgid="4741068463175388817">"Isithonjana sezigxivizo zeminwe"</string>
+ <string name="device_unlock_notification_name" msgid="2632928999862915709">"Ukuvula idivayisi"</string>
+ <string name="alternative_unlock_setup_notification_title" msgid="6241508547901933544">"Zama enye indlela yokuvula"</string>
+ <string name="alternative_face_setup_notification_content" msgid="3384959224091897331">"Sebenzisa i-Face Unlock uma isigxivizo somunwe wakho singaqashelwa, njengalapho iminwe yakho imanzi"</string>
+ <string name="alternative_fp_setup_notification_content" msgid="7454096947415721639">"Sebenzisa i-Fingerprint Unlock uma ubuso bakho bungaqashelwa, njengalapho kungenakukhanya okwanele"</string>
<string name="face_recalibrate_notification_name" msgid="7311163114750748686">"Ukuvula ngobuso"</string>
<string name="face_recalibrate_notification_title" msgid="2524791952735579082">"Inkinga Ngokuvula ngobuso"</string>
<string name="face_recalibrate_notification_content" msgid="3064513770251355594">"Thepha ukuze usule imodeli yakho yobuso, bese wengeza futhi ubuso"</string>
- <string name="face_setup_notification_title" msgid="8843461561970741790">"Setha Ukuvula ngobuso"</string>
- <string name="face_setup_notification_content" msgid="5463999831057751676">"Vula ifoni yakho ngokuyibheka"</string>
<string name="face_sensor_privacy_enabled" msgid="7407126963510598508">"Ukuze usebenzise Ukuvula ngobuso, vula "<b>"Ukufinyelela kwekhamera"</b>" kokuthi Amasethingi > Ubumfihlo"</string>
- <string name="fingerprint_setup_notification_title" msgid="2002630611398849495">"Setha izindlela eziningi zokuvula"</string>
- <string name="fingerprint_setup_notification_content" msgid="205578121848324852">"Thepha ukuze ungeze izigxivizo zomunwe"</string>
<string name="fingerprint_recalibrate_notification_name" msgid="1414578431898579354">"Ukuvula ngesigxivizo somunwe"</string>
<string name="fingerprint_recalibrate_notification_title" msgid="2406561052064558497">"Ayikwazi ukusebenzisa inzwa yesigxivizo somunwe"</string>
<string name="fingerprint_recalibrate_notification_content" msgid="8519935717822194943">"Vakashela umhlinzeki wokulungisa."</string>
@@ -2031,7 +2031,8 @@
<string name="autofill_update_title" msgid="3630695947047069136">"Buyekeza ku-"<b>"<xliff:g id="LABEL">%1$s</xliff:g>"</b>"?"</string>
<string name="autofill_update_title_with_type" msgid="5264152633488495704">"Buyekeza i-<xliff:g id="TYPE">%1$s</xliff:g> ku-"<b>"<xliff:g id="LABEL">%2$s</xliff:g>"</b>"?"</string>
<string name="autofill_update_title_with_2types" msgid="1797514386321086273">"Buyekeza i-<xliff:g id="TYPE_0">%1$s</xliff:g> ne-<xliff:g id="TYPE_1">%2$s</xliff:g> ku-"<b>"<xliff:g id="LABEL">%3$s</xliff:g>"</b>"?"</string>
- <string name="autofill_update_title_with_3types" msgid="1312232153076212291">"Buyekeza lezi zinto ku-"<b>"<xliff:g id="LABEL">%4$s</xliff:g>"</b>": <xliff:g id="TYPE_0">%1$s</xliff:g>, <xliff:g id="TYPE_1">%2$s</xliff:g>, ne-<xliff:g id="TYPE_2">%3$s</xliff:g> ?"</string>
+ <!-- no translation found for autofill_update_title_with_3types (8285767070604652626) -->
+ <skip />
<string name="autofill_save_yes" msgid="8035743017382012850">"Londoloza"</string>
<string name="autofill_save_no" msgid="9212826374207023544">"Cha ngiyabonga"</string>
<string name="autofill_save_notnow" msgid="2853932672029024195">"Hhayi manje"</string>
diff --git a/core/res/res/values/attrs.xml b/core/res/res/values/attrs.xml
index f45499a..e54347f 100644
--- a/core/res/res/values/attrs.xml
+++ b/core/res/res/values/attrs.xml
@@ -3537,11 +3537,11 @@
<attr name="preferKeepClear" format="boolean" />
<!-- <p>Whether or not the auto handwriting initiation is enabled in this View.
- <p>For a view with active {@link android.view.inputmethod.InputConnection},
- if auto handwriting initiation is enabled stylus movement within its view boundary
+ <p>For a view with an active {@link android.view.inputmethod.InputConnection},
+ if auto handwriting initiation is enabled, stylus movement within its view boundary
will automatically trigger the handwriting mode.
- <p>This is true by default.
- See {@link android.view.View#setAutoHandwritingEnabled}. -->
+ See {@link android.view.View#setAutoHandwritingEnabled}.
+ <p>The default value of this flag is configurable by the device manufacturer. -->
<attr name="autoHandwritingEnabled" format="boolean" />
<!-- <p>The amount of offset that is applied to the left edge of the view's stylus
diff --git a/core/res/res/values/colors.xml b/core/res/res/values/colors.xml
index e67ea82..a6830a6 100644
--- a/core/res/res/values/colors.xml
+++ b/core/res/res/values/colors.xml
@@ -552,10 +552,6 @@
<color name="accessibility_magnification_thumbnail_container_background_color">#99000000</color>
<color name="accessibility_magnification_thumbnail_container_stroke_color">#FFFFFF</color>
- <!-- Color of camera light when camera is in use -->
- <color name="camera_privacy_light_day">#FFFFFF</color>
- <color name="camera_privacy_light_night">#FFFFFF</color>
-
<!-- Lily Language Picker language item view colors -->
<color name="language_picker_item_text_color">#202124</color>
<color name="language_picker_item_text_color_secondary">#5F6368</color>
diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml
index 8634ee4e..a72f779 100644
--- a/core/res/res/values/config.xml
+++ b/core/res/res/values/config.xml
@@ -3487,7 +3487,8 @@
<!-- Whether this device prefers to show snapshot or splash screen on back predict target.
When set true, there will create windowless starting surface for the preview target, so it
won't affect activity's lifecycle. This should only be disabled on low-ram device. -->
- <bool name="config_predictShowStartingSurface">true</bool>
+ <!-- TODO(b/268563842) enable once activity snapshot is ready -->
+ <bool name="config_predictShowStartingSurface">false</bool>
<!-- default window ShowCircularMask property -->
<bool name="config_windowShowCircularMask">false</bool>
@@ -5442,7 +5443,7 @@
fingerprint and face. If a dual-modality device only enrolled a single biometric and
experiences high FRR (above threshold), system notification will be sent to encourage user
to enroll the other eligible biometric. -->
- <fraction name="config_biometricNotificationFrrThreshold">30%</fraction>
+ <fraction name="config_biometricNotificationFrrThreshold">25%</fraction>
<!-- The component name for the default profile supervisor, which can be set as a profile owner
even after user setup is complete. The defined component should be used for supervision purposes
@@ -6534,8 +6535,19 @@
<!-- Interval in milliseconds to average light sensor values for camera light brightness -->
<integer name="config_cameraPrivacyLightAlsAveragingIntervalMillis">3000</integer>
- <!-- Light sensor's lux value to use as the threshold between using day or night brightness -->
- <integer name="config_cameraPrivacyLightAlsNightThreshold">4</integer>
+ <!-- Ambient Light sensor's lux values to use as the threshold between brightness colors defined
+ by config_cameraPrivacyLightColors. If the ambient brightness less than the first element
+ in this array then lights of type "camera" will be set to the color in position 0 of
+ config_cameraPrivacyLightColors. This array must be strictly increasing and have a length
+ of zero means there is only one brightness -->
+ <integer-array name="config_cameraPrivacyLightAlsLuxThresholds">
+ </integer-array>
+ <!-- Colors to configure the camera privacy light at different brightnesses. This array must
+ have exactly one more entry than config_cameraPrivacyLightAlsLuxThresholds,
+ or a length of zero if the feature isn't supported. If nonempty and the device doesn't have
+ an ambient light sensor the last element in this array will be the only one used -->
+ <array name="config_cameraPrivacyLightColors">
+ </array>
<!-- List of system components which are allowed to receive ServiceState entries in an
un-sanitized form, even if the location toggle is off. This is intended ONLY for system
diff --git a/core/res/res/values/config_tv_external_input_logging.xml b/core/res/res/values/config_tv_external_input_logging.xml
new file mode 100644
index 0000000..72e30be
--- /dev/null
+++ b/core/res/res/values/config_tv_external_input_logging.xml
@@ -0,0 +1,50 @@
+<?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.
+*/
+-->
+
+<!-- These resources are around just to allow their values to be customized
+ for different hardware and product builds. Do not translate.
+
+ NOTE: The naming convention is "config_camelCaseValue". Some legacy
+ entries do not follow the convention, but all new entries should. -->
+
+<resources>
+ <bool name="config_tvExternalInputLoggingDisplayNameFilterEnabled">false</bool>
+
+ <string-array name="config_tvExternalInputLoggingDeviceOnScreenDisplayNames">
+ <item>Chromecast</item>
+ <item>Chromecast HD</item>
+ <item>SHIELD</item>
+ <item>Roku</item>
+ <item>Roku Express 4</item>
+ <item>Home Theater</item>
+ <item>Fire TV Stick</item>
+ <item>PlayStation 5</item>
+ <item>NintendoSwitch</item>
+ </string-array>
+
+ <string-array name="config_tvExternalInputLoggingDeviceBrandNames">
+ <item>Chromecast</item>
+ <item>SHIELD</item>
+ <item>Roku</item>
+ <item>Apple</item>
+ <item>Fire TV</item>
+ <item>PlayStation</item>
+ <item>Nintendo</item>
+ </string-array>
+</resources>
diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml
index 207927a..591e505 100644
--- a/core/res/res/values/strings.xml
+++ b/core/res/res/values/strings.xml
@@ -5090,6 +5090,13 @@
-->
<string name="clone_profile_label_badge">Clone <xliff:g id="label" example="Messenger">%1$s</xliff:g></string>
+ <!--
+ Used to wrap a label for content description for a Private profile, e.g. "Private Messenger"
+ instead of Messenger when the Messenger app is installed in Private Profile.
+ [CHAR LIMIT=20]
+ -->
+ <string name="private_profile_label_badge">Private <xliff:g id="label" example="Messenger">%1$s</xliff:g></string>
+
<!-- DO NOT TRANSLATE -->
<string name="time_placeholder">--</string>
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index 343818e..ee0563b 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -1080,6 +1080,7 @@
<java-symbol type="string" name="managed_profile_label_badge_2" />
<java-symbol type="string" name="managed_profile_label_badge_3" />
<java-symbol type="string" name="clone_profile_label_badge" />
+ <java-symbol type="string" name="private_profile_label_badge" />
<java-symbol type="string" name="mediasize_unknown_portrait" />
<java-symbol type="string" name="mediasize_unknown_landscape" />
<java-symbol type="string" name="mediasize_iso_a0" />
@@ -1411,6 +1412,9 @@
<java-symbol type="drawable" name="ic_qs_one_handed_mode" />
<java-symbol type="drawable" name="ic_clone_icon_badge" />
<java-symbol type="drawable" name="ic_clone_badge" />
+ <java-symbol type="drawable" name="ic_private_profile_icon_badge" />
+ <java-symbol type="drawable" name="ic_private_profile_badge" />
+ <java-symbol type="drawable" name="stat_sys_private_profile_status" />
<java-symbol type="drawable" name="sim_light_blue" />
<java-symbol type="drawable" name="sim_light_green" />
@@ -5005,10 +5009,9 @@
<java-symbol type="string" name="vdm_camera_access_denied" />
<java-symbol type="string" name="vdm_secure_window" />
- <java-symbol type="color" name="camera_privacy_light_day"/>
- <java-symbol type="color" name="camera_privacy_light_night"/>
<java-symbol type="integer" name="config_cameraPrivacyLightAlsAveragingIntervalMillis"/>
- <java-symbol type="integer" name="config_cameraPrivacyLightAlsNightThreshold"/>
+ <java-symbol type="array" name="config_cameraPrivacyLightAlsLuxThresholds"/>
+ <java-symbol type="array" name="config_cameraPrivacyLightColors"/>
<java-symbol type="bool" name="config_bg_current_drain_monitor_enabled" />
<java-symbol type="array" name="config_bg_current_drain_threshold_to_restricted_bucket" />
@@ -5217,4 +5220,9 @@
<!-- Whether we order unlocking and waking -->
<java-symbol type="bool" name="config_orderUnlockAndWake" />
+
+ <!-- External TV Input Logging Configs -->
+ <java-symbol type="bool" name="config_tvExternalInputLoggingDisplayNameFilterEnabled" />
+ <java-symbol type="array" name="config_tvExternalInputLoggingDeviceOnScreenDisplayNames" />
+ <java-symbol type="array" name="config_tvExternalInputLoggingDeviceBrandNames" />
</resources>
diff --git a/core/tests/BroadcastRadioTests/src/android/hardware/radio/ProgramSelectorTest.java b/core/tests/BroadcastRadioTests/src/android/hardware/radio/ProgramSelectorTest.java
index ae43a1c..b1cf9c2 100644
--- a/core/tests/BroadcastRadioTests/src/android/hardware/radio/ProgramSelectorTest.java
+++ b/core/tests/BroadcastRadioTests/src/android/hardware/radio/ProgramSelectorTest.java
@@ -437,8 +437,8 @@
@Test
public void writeToParcel_forProgramSelector() {
- ProgramSelector selectorExpected =
- getFmSelector(/* secondaryIds= */ null, /* vendorIds= */ null);
+ ProgramSelector selectorExpected = getDabSelector(new ProgramSelector.Identifier[]{
+ DAB_ENSEMBLE_IDENTIFIER, DAB_FREQUENCY_IDENTIFIER}, /* vendorIds= */ null);
Parcel parcel = Parcel.obtain();
selectorExpected.writeToParcel(parcel, /* flags= */ 0);
diff --git a/core/tests/BroadcastRadioTests/src/android/hardware/radio/UniqueProgramIdentifierTest.java b/core/tests/BroadcastRadioTests/src/android/hardware/radio/UniqueProgramIdentifierTest.java
new file mode 100644
index 0000000..b36367b
--- /dev/null
+++ b/core/tests/BroadcastRadioTests/src/android/hardware/radio/UniqueProgramIdentifierTest.java
@@ -0,0 +1,187 @@
+/**
+ * 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 android.hardware.radio;
+
+import android.annotation.Nullable;
+import android.os.Parcel;
+
+import com.google.common.truth.Expect;
+
+import org.junit.Rule;
+import org.junit.Test;
+
+public final class UniqueProgramIdentifierTest {
+ private static final ProgramSelector.Identifier FM_IDENTIFIER = new ProgramSelector.Identifier(
+ ProgramSelector.IDENTIFIER_TYPE_AMFM_FREQUENCY, /* value= */ 88_500);
+
+ private static final ProgramSelector.Identifier DAB_DMB_SID_EXT_IDENTIFIER_1 =
+ new ProgramSelector.Identifier(ProgramSelector.IDENTIFIER_TYPE_DAB_DMB_SID_EXT,
+ /* value= */ 0xA000000111L);
+ private static final ProgramSelector.Identifier DAB_ENSEMBLE_IDENTIFIER =
+ new ProgramSelector.Identifier(ProgramSelector.IDENTIFIER_TYPE_DAB_ENSEMBLE,
+ /* value= */ 0x1001);
+ private static final ProgramSelector.Identifier DAB_FREQUENCY_IDENTIFIER =
+ new ProgramSelector.Identifier(ProgramSelector.IDENTIFIER_TYPE_DAB_FREQUENCY,
+ /* value= */ 220352);
+ private static final ProgramSelector.Identifier DAB_SCID_IDENTIFIER =
+ new ProgramSelector.Identifier(ProgramSelector.IDENTIFIER_TYPE_DAB_SCID,
+ /* value= */ 0x101);
+
+ @Rule
+ public final Expect expect = Expect.create();
+
+ @Test
+ public void getPrimaryId_forUniqueProgramIdentifier() {
+ ProgramSelector dabSelector = getDabSelector(new ProgramSelector.Identifier[]{
+ DAB_ENSEMBLE_IDENTIFIER, DAB_FREQUENCY_IDENTIFIER}, /* vendorIds= */ null);
+ UniqueProgramIdentifier dabIdentifier = new UniqueProgramIdentifier(dabSelector);
+
+ expect.withMessage("Primary id of DAB unique identifier")
+ .that(dabIdentifier.getPrimaryId()).isEqualTo(DAB_DMB_SID_EXT_IDENTIFIER_1);
+ }
+
+ @Test
+ public void getCriticalSecondaryIds_forDabUniqueProgramIdentifier() {
+ ProgramSelector dabSelector = getDabSelector(new ProgramSelector.Identifier[]{
+ DAB_ENSEMBLE_IDENTIFIER, DAB_FREQUENCY_IDENTIFIER, DAB_SCID_IDENTIFIER},
+ /* vendorIds= */ null);
+ UniqueProgramIdentifier dabIdentifier = new UniqueProgramIdentifier(dabSelector);
+
+ expect.withMessage("Critical secondary ids of DAB unique identifier")
+ .that(dabIdentifier.getCriticalSecondaryIds()).containsExactly(
+ DAB_ENSEMBLE_IDENTIFIER, DAB_FREQUENCY_IDENTIFIER);
+ }
+
+ @Test
+ public void getCriticalSecondaryIds_forFmUniqueProgramIdentifier() {
+ UniqueProgramIdentifier fmUniqueIdentifier = new UniqueProgramIdentifier(
+ new ProgramSelector(ProgramSelector.PROGRAM_TYPE_FM, FM_IDENTIFIER,
+ new ProgramSelector.Identifier[]{new ProgramSelector.Identifier(
+ ProgramSelector.IDENTIFIER_TYPE_RDS_PI, /* value= */ 0x1003)},
+ /* vendorIds= */ null));
+
+ expect.withMessage("Empty critical secondary id list of FM unique identifier")
+ .that(fmUniqueIdentifier.getCriticalSecondaryIds()).isEmpty();
+ }
+
+ @Test
+ public void toString_forUniqueProgramIdentifier() {
+ ProgramSelector dabSelector = getDabSelector(new ProgramSelector.Identifier[]{
+ DAB_ENSEMBLE_IDENTIFIER, DAB_FREQUENCY_IDENTIFIER}, /* vendorIds= */ null);
+ UniqueProgramIdentifier dabIdentifier = new UniqueProgramIdentifier(dabSelector);
+
+ String identifierString = dabIdentifier.toString();
+
+ expect.withMessage("Primary id in DAB unique identifier")
+ .that(identifierString).contains(DAB_DMB_SID_EXT_IDENTIFIER_1.toString());
+ expect.withMessage("Ensemble id in DAB unique identifier")
+ .that(identifierString).contains(DAB_ENSEMBLE_IDENTIFIER.toString());
+ expect.withMessage("Frequency id in DAB unique identifier")
+ .that(identifierString).contains(DAB_FREQUENCY_IDENTIFIER.toString());
+ }
+
+ @Test
+ public void hashCode_withTheSameUniqueProgramIdentifier_equals() {
+ ProgramSelector dabSelector1 = getDabSelector(new ProgramSelector.Identifier[]{
+ DAB_ENSEMBLE_IDENTIFIER, DAB_FREQUENCY_IDENTIFIER}, /* vendorIds= */ null);
+ ProgramSelector dabSelector2 = getDabSelector(new ProgramSelector.Identifier[]{
+ DAB_FREQUENCY_IDENTIFIER, DAB_ENSEMBLE_IDENTIFIER}, /* vendorIds= */ null);
+ UniqueProgramIdentifier dabIdentifier1 = new UniqueProgramIdentifier(dabSelector1);
+ UniqueProgramIdentifier dabIdentifier2 = new UniqueProgramIdentifier(dabSelector2);
+
+ expect.withMessage("Hash code of the same DAB unique identifiers")
+ .that(dabIdentifier1.hashCode()).isEqualTo(dabIdentifier2.hashCode());
+ }
+
+ @Test
+ public void equals_withIdsForUniqueProgramIdentifier_returnsTrue() {
+ ProgramSelector dabSelector1 = getDabSelector(new ProgramSelector.Identifier[]{
+ DAB_ENSEMBLE_IDENTIFIER, DAB_FREQUENCY_IDENTIFIER}, /* vendorIds= */ null);
+ ProgramSelector dabSelector2 = getDabSelector(new ProgramSelector.Identifier[]{
+ DAB_FREQUENCY_IDENTIFIER, DAB_ENSEMBLE_IDENTIFIER}, /* vendorIds= */ null);
+ UniqueProgramIdentifier dabIdentifier1 = new UniqueProgramIdentifier(dabSelector1);
+ UniqueProgramIdentifier dabIdentifier2 = new UniqueProgramIdentifier(dabSelector2);
+
+ expect.withMessage("The same DAB unique identifiers")
+ .that(dabIdentifier1).isEqualTo(dabIdentifier2);
+ }
+
+ @Test
+ public void equals_withDifferentPrimaryIdsForUniqueProgramIdentifier_returnsFalse() {
+ ProgramSelector dabSelector1 = getDabSelector(new ProgramSelector.Identifier[]{
+ DAB_ENSEMBLE_IDENTIFIER, DAB_FREQUENCY_IDENTIFIER}, /* vendorIds= */ null);
+ UniqueProgramIdentifier dabIdentifier1 = new UniqueProgramIdentifier(dabSelector1);
+ UniqueProgramIdentifier fmUniqueIdentifier = new UniqueProgramIdentifier(FM_IDENTIFIER);
+
+ expect.withMessage("Unique identifier with different primary ids")
+ .that(dabIdentifier1).isNotEqualTo(fmUniqueIdentifier);
+ }
+
+ @Test
+ public void equals_withDifferentSecondaryIdsForUniqueProgramIdentifier_returnsFalse() {
+ ProgramSelector dabSelector1 = getDabSelector(new ProgramSelector.Identifier[]{
+ DAB_ENSEMBLE_IDENTIFIER, DAB_FREQUENCY_IDENTIFIER}, /* vendorIds= */ null);
+ ProgramSelector.Identifier dabFreqIdentifier2 = new ProgramSelector.Identifier(
+ ProgramSelector.IDENTIFIER_TYPE_DAB_FREQUENCY, /* value= */ 222064);
+ ProgramSelector dabSelector2 = getDabSelector(new ProgramSelector.Identifier[]{
+ DAB_ENSEMBLE_IDENTIFIER, dabFreqIdentifier2}, /* vendorIds= */ null);
+ UniqueProgramIdentifier dabIdentifier1 = new UniqueProgramIdentifier(dabSelector1);
+ UniqueProgramIdentifier dabIdentifier2 = new UniqueProgramIdentifier(dabSelector2);
+
+ expect.withMessage("DAB unique identifier with different secondary ids")
+ .that(dabIdentifier1).isNotEqualTo(dabIdentifier2);
+ }
+
+ @Test
+ public void describeContents_forUniqueProgramIdentifier() {
+ UniqueProgramIdentifier fmUniqueIdentifier = new UniqueProgramIdentifier(FM_IDENTIFIER);
+
+ expect.withMessage("FM unique identifier contents")
+ .that(fmUniqueIdentifier.describeContents()).isEqualTo(0);
+ }
+
+ @Test
+ public void newArray_forUniqueProgramIdentifier() {
+ int createArraySize = 3;
+ UniqueProgramIdentifier[] identifiers = UniqueProgramIdentifier.CREATOR.newArray(
+ createArraySize);
+
+ expect.withMessage("Unique identifiers").that(identifiers).hasLength(createArraySize);
+ }
+
+ @Test
+ public void writeToParcel_forUniqueProgramIdentifier() {
+ ProgramSelector dabSelector = getDabSelector(new ProgramSelector.Identifier[]{
+ DAB_ENSEMBLE_IDENTIFIER, DAB_FREQUENCY_IDENTIFIER}, /* vendorIds= */ null);
+ UniqueProgramIdentifier dabIdentifier = new UniqueProgramIdentifier(dabSelector);
+ Parcel parcel = Parcel.obtain();
+
+ dabIdentifier.writeToParcel(parcel, /* flags= */ 0);
+ parcel.setDataPosition(0);
+
+ UniqueProgramIdentifier identifierFromParcel = UniqueProgramIdentifier.CREATOR
+ .createFromParcel(parcel);
+ expect.withMessage("Unique identifier created from parcel")
+ .that(identifierFromParcel).isEqualTo(dabIdentifier);
+ }
+
+ private ProgramSelector getDabSelector(@Nullable ProgramSelector.Identifier[] secondaryIds,
+ @Nullable long[] vendorIds) {
+ return new ProgramSelector(ProgramSelector.PROGRAM_TYPE_DAB, DAB_DMB_SID_EXT_IDENTIFIER_1,
+ secondaryIds, vendorIds);
+ }
+}
diff --git a/core/tests/coretests/AndroidManifest.xml b/core/tests/coretests/AndroidManifest.xml
index a358c4f..20d8d91 100644
--- a/core/tests/coretests/AndroidManifest.xml
+++ b/core/tests/coretests/AndroidManifest.xml
@@ -86,8 +86,8 @@
<uses-permission android:name="android.permission.TEST_GRANTED" />
<uses-permission android:name="android.permission.CHANGE_COMPONENT_ENABLED_STATE" />
<uses-permission android:name="com.google.android.googleapps.permission.ACCESS_GOOGLE_PASSWORD" />
- <uses-permission android:name="com.google.android.googleapps.permission.GOOGLE_AUTH" />
- <uses-permission android:name="com.google.android.googleapps.permission.GOOGLE_AUTH.ALL_SERVICES" />
+ <uses-permission android:name="com.google.android.googleapps.permission.GOOGLE_AUTH" android:maxSdkVersion="34"/>
+ <uses-permission android:name="com.google.android.googleapps.permission.GOOGLE_AUTH.ALL_SERVICES" android:maxSdkVersion="34"/>
<uses-permission android:name="android.permission.MANAGE_USERS" />
<uses-permission android:name="android.permission.INTERACT_ACROSS_USERS" />
diff --git a/core/tests/coretests/src/android/provider/NameValueCacheTest.java b/core/tests/coretests/src/android/provider/NameValueCacheTest.java
index 54a3817..87e4a42 100644
--- a/core/tests/coretests/src/android/provider/NameValueCacheTest.java
+++ b/core/tests/coretests/src/android/provider/NameValueCacheTest.java
@@ -39,6 +39,7 @@
import org.junit.After;
import org.junit.Before;
+import org.junit.Ignore;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
@@ -54,6 +55,7 @@
* Due to how the classes are structured, we have to test it in a somewhat roundabout way. We're
* mocking out the contentProvider and are handcrafting very specific Bundles to answer the queries.
*/
+@Ignore("b/297724333")
@Presubmit
@RunWith(AndroidJUnit4.class)
@SmallTest
@@ -233,6 +235,7 @@
mConfigsCacheGenerationStore.close();
}
+ @Ignore("b/297724333")
@Test
public void testCaching_singleNamespace() throws Exception {
HashMap<String, String> keyValues = new HashMap<>();
@@ -270,6 +273,7 @@
assertThat(cachedKeyValues2).containsExactlyEntriesIn(keyValues);
}
+ @Ignore("b/297724333")
@Test
public void testCaching_multipleNamespaces() throws Exception {
HashMap<String, String> keyValues = new HashMap<>();
@@ -309,6 +313,7 @@
assertThat(cachedKeyValues2).containsExactlyEntriesIn(keyValues2);
}
+ @Ignore("b/297724333")
@Test
public void testCaching_emptyNamespace() throws Exception {
Map<String, String> returnedValues = Settings.Config.getStrings(mMockContentResolver,
@@ -325,6 +330,7 @@
assertThat(cachedKeyValues).isEmpty();
}
+ @Ignore("b/297724333")
@Test
public void testCaching_singleSetting() throws Exception {
Settings.Secure.putString(mMockContentResolver, SETTING, "a");
@@ -355,6 +361,7 @@
assertThat(cachedValue2).isEqualTo("b");
}
+ @Ignore("b/297724333")
@Test
public void testCaching_multipleSettings() throws Exception {
Settings.Secure.putString(mMockContentResolver, SETTING, "a");
@@ -385,6 +392,7 @@
assertThat(cachedValue2).isEqualTo("b");
}
+ @Ignore("b/297724333")
@Test
public void testCaching_unsetSetting() throws Exception {
String returnedValue = Settings.Secure.getString(mMockContentResolver, SETTING);
diff --git a/core/tests/coretests/src/com/android/internal/app/procstats/ProcessStatsTest.java b/core/tests/coretests/src/com/android/internal/app/procstats/ProcessStatsTest.java
index 6189914..fe30d81 100644
--- a/core/tests/coretests/src/com/android/internal/app/procstats/ProcessStatsTest.java
+++ b/core/tests/coretests/src/com/android/internal/app/procstats/ProcessStatsTest.java
@@ -16,8 +16,6 @@
package com.android.internal.app.procstats;
-import static com.android.internal.app.procstats.ProcessStats.STATE_TOP;
-
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.verify;
@@ -108,7 +106,8 @@
ProcessState processState =
processStats.getProcessStateLocked(
APP_1_PACKAGE_NAME, APP_1_UID, APP_1_VERSION, APP_1_PROCESS_NAME);
- processState.setCombinedState(STATE_TOP, NOW_MS);
+ processState.setState(ActivityManager.PROCESS_STATE_TOP, ProcessStats.ADJ_MEM_FACTOR_NORMAL,
+ NOW_MS, /* pkgList */ null);
processState.commitStateTime(NOW_MS + TimeUnit.SECONDS.toMillis(DURATION_SECS));
processStats.dumpProcessState(FrameworkStatsLog.PROCESS_STATE, mStatsEventOutput);
verify(mStatsEventOutput)
@@ -158,6 +157,38 @@
}
@SmallTest
+ public void testDumpFrozenDuration() throws Exception {
+ ProcessStats processStats = new ProcessStats();
+ ProcessState processState =
+ processStats.getProcessStateLocked(
+ APP_1_PACKAGE_NAME, APP_1_UID, APP_1_VERSION, APP_1_PROCESS_NAME);
+ processState.setState(ActivityManager.PROCESS_STATE_BOUND_FOREGROUND_SERVICE,
+ ProcessStats.ADJ_MEM_FACTOR_NORMAL, NOW_MS, /* pkgList */ null);
+ processState.onProcessFrozen(NOW_MS + 1 * TimeUnit.SECONDS.toMillis(DURATION_SECS),
+ /* pkgList */ null);
+ processState.onProcessUnfrozen(NOW_MS + 2 * TimeUnit.SECONDS.toMillis(DURATION_SECS),
+ /* pkgList */ null);
+ processState.commitStateTime(NOW_MS + 3 * TimeUnit.SECONDS.toMillis(DURATION_SECS));
+ processStats.dumpProcessState(FrameworkStatsLog.PROCESS_STATE, mStatsEventOutput);
+ verify(mStatsEventOutput)
+ .write(
+ eq(FrameworkStatsLog.PROCESS_STATE),
+ eq(APP_1_UID),
+ eq(APP_1_PROCESS_NAME),
+ anyInt(),
+ anyInt(),
+ eq(0),
+ eq(0),
+ eq(0),
+ eq(0),
+ eq(2 * DURATION_SECS), // bound_fgs
+ eq(0),
+ eq(0),
+ eq(DURATION_SECS), // frozen
+ eq(0));
+ }
+
+ @SmallTest
public void testDumpProcessAssociation() throws Exception {
ProcessStats processStats = new ProcessStats();
AssociationState associationState =
diff --git a/core/tests/coretests/src/com/android/internal/content/res/OverlayConfigTest.java b/core/tests/coretests/src/com/android/internal/content/res/OverlayConfigTest.java
index 246a1e7..a0e9947 100644
--- a/core/tests/coretests/src/com/android/internal/content/res/OverlayConfigTest.java
+++ b/core/tests/coretests/src/com/android/internal/content/res/OverlayConfigTest.java
@@ -48,7 +48,9 @@
import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;
+import java.util.HashMap;
import java.util.List;
+import java.util.Map;
@Presubmit
@RunWith(AndroidJUnit4.class)
@@ -102,6 +104,25 @@
return partitionOrder.toString();
}
+ // configIndex should come from real time partition order cause partitions could get
+ // reordered by /product/overlay/partition_order.xml
+ private Map<String, Integer> createConfigIndexes(OverlayConfig overlayConfig,
+ String... configPartitions) {
+ Map<String, Integer> configIndexes = new HashMap<>();
+ for (int i = 0; i < configPartitions.length; i++) {
+ configIndexes.put(configPartitions[i], -1);
+ }
+
+ String[] partitions = overlayConfig.getPartitionOrder().split(", ");
+ int index = 0;
+ for (int i = 0; i < partitions.length; i++) {
+ if (configIndexes.containsKey(partitions[i])) {
+ configIndexes.put(partitions[i], index++);
+ }
+ }
+ return configIndexes;
+ }
+
@Test
public void testImmutableAfterNonImmutableFails() throws IOException {
mExpectedException.expect(IllegalStateException.class);
@@ -292,11 +313,13 @@
mScannerRule.addOverlay(createFile("/system_ext/overlay/five.apk"), "five");
final OverlayConfig overlayConfig = createConfigImpl();
- assertConfig(overlayConfig, "one", true, true, 0);
- assertConfig(overlayConfig, "two", true, true, 1);
- assertConfig(overlayConfig, "three", true, true, 2);
- assertConfig(overlayConfig, "four", true, true, 3);
- assertConfig(overlayConfig, "five", true, true, 4);
+ Map<String, Integer> configIndexes = createConfigIndexes(overlayConfig,
+ "vendor", "odm", "oem", "product", "system_ext");
+ assertConfig(overlayConfig, "one", true, true, configIndexes.get("vendor"));
+ assertConfig(overlayConfig, "two", true, true, configIndexes.get("odm"));
+ assertConfig(overlayConfig, "three", true, true, configIndexes.get("oem"));
+ assertConfig(overlayConfig, "four", true, true, configIndexes.get("product"));
+ assertConfig(overlayConfig, "five", true, true, configIndexes.get("system_ext"));
}
@Test
@@ -313,9 +336,11 @@
true, 0);
final OverlayConfig overlayConfig = createConfigImpl();
- assertConfig(overlayConfig, "one", false, true, 0);
- assertConfig(overlayConfig, "two", true, true, 1);
- assertConfig(overlayConfig, "three", false, true, 2);
+ Map<String, Integer> configIndexes = createConfigIndexes(overlayConfig,
+ "vendor", "odm", "product");
+ assertConfig(overlayConfig, "one", false, true, configIndexes.get("vendor"));
+ assertConfig(overlayConfig, "two", true, true, configIndexes.get("odm"));
+ assertConfig(overlayConfig, "three", false, true, configIndexes.get("product"));
}
@Test
@@ -327,9 +352,11 @@
true, 0);
final OverlayConfig overlayConfig = createConfigImpl();
- assertConfig(overlayConfig, "one", false, true, 0);
- assertConfig(overlayConfig, "two", false, true, 1);
- assertConfig(overlayConfig, "three", false, true, 2);
+ Map<String, Integer> configIndexes = createConfigIndexes(overlayConfig,
+ "vendor", "odm", "product");
+ assertConfig(overlayConfig, "one", false, true, configIndexes.get("vendor"));
+ assertConfig(overlayConfig, "two", false, true, configIndexes.get("odm"));
+ assertConfig(overlayConfig, "three", false, true, configIndexes.get("product"));
}
@Test
diff --git a/data/keyboards/Vendor_18d1_Product_9451.idc b/data/keyboards/Vendor_18d1_Product_9451.idc
index e13fcb2..07cfc79 100644
--- a/data/keyboards/Vendor_18d1_Product_9451.idc
+++ b/data/keyboards/Vendor_18d1_Product_9451.idc
@@ -13,11 +13,10 @@
# limitations under the License.
#
-# Input Device Configuration file for a flavor of the Google Reference RCU Remote.
+# Input Device Configuration file for a flavor of Google Remote Control.
#
#
# Basic Parameters
-keyboard.layout = Vendor_0957_Product_0001
keyboard.doNotWakeByDefault = 1
-audio.mic = 1
\ No newline at end of file
+audio.mic = 1
diff --git a/data/keyboards/Vendor_18d1_Product_9451.kl b/data/keyboards/Vendor_18d1_Product_9451.kl
new file mode 100644
index 0000000..fb425be
--- /dev/null
+++ b/data/keyboards/Vendor_18d1_Product_9451.kl
@@ -0,0 +1,39 @@
+# 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.
+
+#
+# Key Layout file for flavor of Google Remote Control.
+#
+
+key 116 POWER WAKE
+key 217 ASSIST WAKE
+
+key 103 DPAD_UP
+key 108 DPAD_DOWN
+key 105 DPAD_LEFT
+key 106 DPAD_RIGHT
+key 353 DPAD_CENTER
+
+key 158 BACK
+key 172 HOME WAKE
+
+key 113 VOLUME_MUTE
+key 114 VOLUME_DOWN
+key 115 VOLUME_UP
+
+# custom keys
+key usage 0x000c0186 MACRO_1 WAKE
+
+key usage 0x000c0077 BUTTON_3 WAKE #YouTube
+key usage 0x000c0078 BUTTON_4 WAKE #Netflix
diff --git a/drm/jni/android_drm_DrmManagerClient.cpp b/drm/jni/android_drm_DrmManagerClient.cpp
index e2bb6a6..14d5eaf 100644
--- a/drm/jni/android_drm_DrmManagerClient.cpp
+++ b/drm/jni/android_drm_DrmManagerClient.cpp
@@ -167,8 +167,8 @@
jint uniqueId = event.getUniqueId();
jint type = event.getType();
JNIEnv *env = AndroidRuntime::getJNIEnv();
- jstring message = env->NewStringUTF(event.getMessage().string());
- ALOGV("JNIOnInfoListener::onInfo => %d | %d | %s", uniqueId, type, event.getMessage().string());
+ jstring message = env->NewStringUTF(event.getMessage().c_str());
+ ALOGV("JNIOnInfoListener::onInfo => %d | %d | %s", uniqueId, type, event.getMessage().c_str());
env->CallStaticVoidMethod(
mClass,
@@ -273,15 +273,15 @@
const char* value = pConstraints->getAsByteArray(&key);
if (NULL != value) {
ScopedLocalRef<jbyteArray> dataArray(env, env->NewByteArray(strlen(value)));
- ScopedLocalRef<jstring> keyString(env, env->NewStringUTF(key.string()));
+ ScopedLocalRef<jstring> keyString(env, env->NewStringUTF(key.c_str()));
env->SetByteArrayRegion(dataArray.get(), 0, strlen(value), (jbyte*)value);
env->CallVoidMethod(constraints, ContentValues_putByteArray,
keyString.get(), dataArray.get());
}
} else {
String8 value = pConstraints->get(key);
- ScopedLocalRef<jstring> keyString(env, env->NewStringUTF(key.string()));
- ScopedLocalRef<jstring> valueString(env, env->NewStringUTF(value.string()));
+ ScopedLocalRef<jstring> keyString(env, env->NewStringUTF(key.c_str()));
+ ScopedLocalRef<jstring> valueString(env, env->NewStringUTF(value.c_str()));
env->CallVoidMethod(constraints, ContentValues_putString,
keyString.get(), valueString.get());
}
@@ -320,8 +320,8 @@
// insert the entry<constraintKey, constraintValue>
// to newly created java object
String8 value = pMetadata->get(key);
- ScopedLocalRef<jstring> keyString(env, env->NewStringUTF(key.string()));
- ScopedLocalRef<jstring> valueString(env, env->NewStringUTF(value.string()));
+ ScopedLocalRef<jstring> keyString(env, env->NewStringUTF(key.c_str()));
+ ScopedLocalRef<jstring> valueString(env, env->NewStringUTF(value.c_str()));
env->CallVoidMethod(metadata, ContentValues_putString,
keyString.get(), valueString.get());
}
@@ -357,19 +357,19 @@
env->CallVoidMethod(
drmSupportInfo, env->GetMethodID(clazz, "setDescription", "(Ljava/lang/String;)V"),
- env->NewStringUTF(info.getDescription().string()));
+ env->NewStringUTF(info.getDescription().c_str()));
DrmSupportInfo::MimeTypeIterator iterator = info.getMimeTypeIterator();
while (iterator.hasNext()) {
String8 value = iterator.next();
- env->CallVoidMethod(drmSupportInfo, addMimeTypeId, env->NewStringUTF(value.string()));
+ env->CallVoidMethod(drmSupportInfo, addMimeTypeId, env->NewStringUTF(value.c_str()));
}
DrmSupportInfo::FileSuffixIterator it = info.getFileSuffixIterator();
while (it.hasNext()) {
String8 value = it.next();
env->CallVoidMethod(
- drmSupportInfo, addFileSuffixId, env->NewStringUTF(value.string()));
+ drmSupportInfo, addFileSuffixId, env->NewStringUTF(value.c_str()));
}
env->SetObjectArrayElement(array, i, drmSupportInfo);
@@ -459,7 +459,7 @@
String8 keyString = Utility::getStringValue(env, key.get());
String8 valueString = Utility::getStringValue(env, valString.get());
- ALOGV("Key: %s | Value: %s", keyString.string(), valueString.string());
+ ALOGV("Key: %s | Value: %s", keyString.c_str(), valueString.c_str());
drmInfo.put(keyString, valueString);
}
@@ -488,15 +488,15 @@
jmethodID constructorId
= env->GetMethodID(clazz, "<init>", "([BLjava/lang/String;Ljava/lang/String;)V");
jobject processedData = env->NewObject(clazz, constructorId, dataArray,
- env->NewStringUTF((drmInfo.get(DrmInfoRequest::ACCOUNT_ID)).string()),
- env->NewStringUTF((drmInfo.get(DrmInfoRequest::SUBSCRIPTION_ID)).string()));
+ env->NewStringUTF((drmInfo.get(DrmInfoRequest::ACCOUNT_ID)).c_str()),
+ env->NewStringUTF((drmInfo.get(DrmInfoRequest::SUBSCRIPTION_ID)).c_str()));
constructorId
= env->GetMethodID(localRef,
"<init>", "(IILandroid/drm/ProcessedData;Ljava/lang/String;)V");
drmInfoStatus = env->NewObject(localRef, constructorId, statusCode, infoType,
- processedData, env->NewStringUTF(pDrmInfoStatus->mimeType.string()));
+ processedData, env->NewStringUTF(pDrmInfoStatus->mimeType.c_str()));
}
delete[] mData; mData = NULL;
@@ -533,7 +533,7 @@
String8 keyString = Utility::getStringValue(env, key.get());
String8 valueString = Utility::getStringValue(env, value.get());
- ALOGV("Key: %s | Value: %s", keyString.string(), valueString.string());
+ ALOGV("Key: %s | Value: %s", keyString.c_str(), valueString.c_str());
drmInfoReq.put(keyString, valueString);
}
@@ -554,7 +554,7 @@
drmInfoObject
= env->NewObject(localRef,
env->GetMethodID(localRef, "<init>", "(I[BLjava/lang/String;)V"),
- mInfoType, dataArray, env->NewStringUTF(pDrmInfo->getMimeType().string()));
+ mInfoType, dataArray, env->NewStringUTF(pDrmInfo->getMimeType().c_str()));
DrmInfo::KeyIterator it = pDrmInfo->keyIterator();
jmethodID putMethodId
@@ -563,8 +563,8 @@
while (it.hasNext()) {
String8 key = it.next();
String8 value = pDrmInfo->get(key);
- ScopedLocalRef<jstring> keyString(env, env->NewStringUTF(key.string()));
- ScopedLocalRef<jstring> valueString(env, env->NewStringUTF(value.string()));
+ ScopedLocalRef<jstring> keyString(env, env->NewStringUTF(key.c_str()));
+ ScopedLocalRef<jstring> valueString(env, env->NewStringUTF(value.c_str()));
env->CallVoidMethod(drmInfoObject, putMethodId,
keyString.get(), valueString.get());
}
@@ -602,7 +602,7 @@
->getOriginalMimeType(uniqueId,
Utility::getStringValue(env, path), fd);
ALOGV("getOriginalMimeType Exit");
- return env->NewStringUTF(mimeType.string());
+ return env->NewStringUTF(mimeType.c_str());
}
static jint android_drm_DrmManagerClient_checkRightsStatus(
diff --git a/libs/WindowManager/Jetpack/src/androidx/window/extensions/area/WindowAreaComponentImpl.java b/libs/WindowManager/Jetpack/src/androidx/window/extensions/area/WindowAreaComponentImpl.java
index 1e6e503..0112e32 100644
--- a/libs/WindowManager/Jetpack/src/androidx/window/extensions/area/WindowAreaComponentImpl.java
+++ b/libs/WindowManager/Jetpack/src/androidx/window/extensions/area/WindowAreaComponentImpl.java
@@ -518,8 +518,11 @@
return WindowAreaComponent.STATUS_UNSUPPORTED;
}
- if (mCurrentDeviceState == mConcurrentDisplayState
- || !ArrayUtils.contains(mCurrentSupportedDeviceStates, mConcurrentDisplayState)
+ if (mCurrentDeviceState == mConcurrentDisplayState) {
+ return WindowAreaComponent.STATUS_ACTIVE;
+ }
+
+ if (!ArrayUtils.contains(mCurrentSupportedDeviceStates, mConcurrentDisplayState)
|| isDeviceFolded()) {
return WindowAreaComponent.STATUS_UNAVAILABLE;
}
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 896fe61..d894487 100644
--- a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitPresenter.java
+++ b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitPresenter.java
@@ -651,7 +651,8 @@
if (minDimensionsPair == null) {
return splitAttributes;
}
- final FoldingFeature foldingFeature = getFoldingFeature(taskProperties);
+ final FoldingFeature foldingFeature = getFoldingFeatureForHingeType(
+ taskProperties, splitAttributes);
final Configuration taskConfiguration = taskProperties.getConfiguration();
final Rect primaryBounds = getPrimaryBounds(taskConfiguration, splitAttributes,
foldingFeature);
@@ -726,7 +727,8 @@
Rect getRelBoundsForPosition(@Position int position, @NonNull TaskProperties taskProperties,
@NonNull SplitAttributes splitAttributes) {
final Configuration taskConfiguration = taskProperties.getConfiguration();
- final FoldingFeature foldingFeature = getFoldingFeature(taskProperties);
+ final FoldingFeature foldingFeature = getFoldingFeatureForHingeType(
+ taskProperties, splitAttributes);
if (!shouldShowSplit(splitAttributes)) {
return new Rect();
}
@@ -933,6 +935,17 @@
}
@Nullable
+ private FoldingFeature getFoldingFeatureForHingeType(
+ @NonNull TaskProperties taskProperties,
+ @NonNull SplitAttributes splitAttributes) {
+ SplitType splitType = splitAttributes.getSplitType();
+ if (!(splitType instanceof HingeSplitType)) {
+ return null;
+ }
+ return getFoldingFeature(taskProperties);
+ }
+
+ @Nullable
@VisibleForTesting
FoldingFeature getFoldingFeature(@NonNull TaskProperties taskProperties) {
final int displayId = taskProperties.getDisplayId();
diff --git a/libs/WindowManager/Shell/res/color/desktop_mode_maximize_menu_button_color_selector.xml b/libs/WindowManager/Shell/res/color/desktop_mode_maximize_menu_button_color_selector.xml
new file mode 100644
index 0000000..65f5239
--- /dev/null
+++ b/libs/WindowManager/Shell/res/color/desktop_mode_maximize_menu_button_color_selector.xml
@@ -0,0 +1,28 @@
+<?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
+-->
+
+<selector xmlns:android="http://schemas.android.com/apk/res/android">
+ <item android:state_pressed="true"
+ android:color="@color/desktop_mode_maximize_menu_button_on_hover"/>
+ <item android:state_hovered="true"
+ android:color="@color/desktop_mode_maximize_menu_button_on_hover"/>
+ <item android:state_focused="true"
+ android:color="@color/desktop_mode_maximize_menu_button_on_hover"/>
+ <item android:state_selected="true"
+ android:color="@color/desktop_mode_maximize_menu_button_on_hover"/>
+ <item android:color="@color/desktop_mode_maximize_menu_button"/>
+</selector>
\ No newline at end of file
diff --git a/libs/WindowManager/Shell/res/color/desktop_mode_maximize_menu_button_outline_color_selector.xml b/libs/WindowManager/Shell/res/color/desktop_mode_maximize_menu_button_outline_color_selector.xml
new file mode 100644
index 0000000..86679af
--- /dev/null
+++ b/libs/WindowManager/Shell/res/color/desktop_mode_maximize_menu_button_outline_color_selector.xml
@@ -0,0 +1,28 @@
+<?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
+-->
+
+<selector xmlns:android="http://schemas.android.com/apk/res/android">
+ <item android:state_pressed="true"
+ android:color="@color/desktop_mode_maximize_menu_button_outline_on_hover"/>
+ <item android:state_hovered="true"
+ android:color="@color/desktop_mode_maximize_menu_button_outline_on_hover"/>
+ <item android:state_focused="true"
+ android:color="@color/desktop_mode_maximize_menu_button_outline_on_hover"/>
+ <item android:state_selected="true"
+ android:color="@color/desktop_mode_maximize_menu_button_outline_on_hover"/>
+ <item android:color="@color/desktop_mode_maximize_menu_button_outline"/>
+</selector>
\ No newline at end of file
diff --git a/libs/WindowManager/Shell/res/drawable/desktop_mode_decor_menu_background.xml b/libs/WindowManager/Shell/res/drawable/desktop_mode_decor_handle_menu_background.xml
similarity index 100%
rename from libs/WindowManager/Shell/res/drawable/desktop_mode_decor_menu_background.xml
rename to libs/WindowManager/Shell/res/drawable/desktop_mode_decor_handle_menu_background.xml
diff --git a/libs/WindowManager/Shell/res/drawable/desktop_mode_maximize_menu_background.xml b/libs/WindowManager/Shell/res/drawable/desktop_mode_maximize_menu_background.xml
new file mode 100644
index 0000000..5d9fe67
--- /dev/null
+++ b/libs/WindowManager/Shell/res/drawable/desktop_mode_maximize_menu_background.xml
@@ -0,0 +1,21 @@
+<?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.
+ -->
+<shape android:shape="rectangle"
+ xmlns:android="http://schemas.android.com/apk/res/android">
+ <solid android:color="@android:color/white" />
+ <corners android:radius="@dimen/desktop_mode_maximize_menu_corner_radius" />
+</shape>
diff --git a/libs/WindowManager/Shell/res/drawable/desktop_mode_maximize_menu_maximize_button_background.xml b/libs/WindowManager/Shell/res/drawable/desktop_mode_maximize_menu_maximize_button_background.xml
new file mode 100644
index 0000000..bfb0dd7
--- /dev/null
+++ b/libs/WindowManager/Shell/res/drawable/desktop_mode_maximize_menu_maximize_button_background.xml
@@ -0,0 +1,24 @@
+<?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
+-->
+
+<shape xmlns:android="http://schemas.android.com/apk/res/android"
+ android:shape="rectangle">
+ <solid android:color="@color/desktop_mode_maximize_menu_button_color_selector"/>
+ <corners
+ android:radius="@dimen/desktop_mode_maximize_menu_buttons_large_corner_radius"/>
+ <stroke android:width="1dp" android:color="@color/desktop_mode_maximize_menu_button_outline_color_selector"/>
+</shape>
\ No newline at end of file
diff --git a/libs/WindowManager/Shell/res/drawable/desktop_mode_maximize_menu_snap_left_button_background.xml b/libs/WindowManager/Shell/res/drawable/desktop_mode_maximize_menu_snap_left_button_background.xml
new file mode 100644
index 0000000..6630fca
--- /dev/null
+++ b/libs/WindowManager/Shell/res/drawable/desktop_mode_maximize_menu_snap_left_button_background.xml
@@ -0,0 +1,28 @@
+<?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
+-->
+
+<shape xmlns:android="http://schemas.android.com/apk/res/android"
+ android:shape="rectangle">
+ <solid android:color="@color/desktop_mode_maximize_menu_button_color_selector"/>
+ <corners
+ android:topLeftRadius="@dimen/desktop_mode_maximize_menu_buttons_large_corner_radius"
+ android:topRightRadius="@dimen/desktop_mode_maximize_menu_buttons_small_corner_radius"
+ android:bottomLeftRadius="@dimen/desktop_mode_maximize_menu_buttons_large_corner_radius"
+ android:bottomRightRadius="@dimen/desktop_mode_maximize_menu_buttons_small_corner_radius"/>
+ <stroke android:width="1dp" android:color="@color/desktop_mode_maximize_menu_button_outline_color_selector"/>
+</shape>
\ No newline at end of file
diff --git a/libs/WindowManager/Shell/res/drawable/desktop_mode_maximize_menu_snap_right_button_background.xml b/libs/WindowManager/Shell/res/drawable/desktop_mode_maximize_menu_snap_right_button_background.xml
new file mode 100644
index 0000000..7bd6e99
--- /dev/null
+++ b/libs/WindowManager/Shell/res/drawable/desktop_mode_maximize_menu_snap_right_button_background.xml
@@ -0,0 +1,28 @@
+<?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
+-->
+
+<shape xmlns:android="http://schemas.android.com/apk/res/android"
+ android:shape="rectangle">
+ <solid android:color="@color/desktop_mode_maximize_menu_button_color_selector"/>
+ <corners
+ android:topLeftRadius="@dimen/desktop_mode_maximize_menu_buttons_small_corner_radius"
+ android:topRightRadius="@dimen/desktop_mode_maximize_menu_buttons_large_corner_radius"
+ android:bottomLeftRadius="@dimen/desktop_mode_maximize_menu_buttons_small_corner_radius"
+ android:bottomRightRadius="@dimen/desktop_mode_maximize_menu_buttons_large_corner_radius"/>
+ <stroke android:width="1dp" android:color="@color/desktop_mode_maximize_menu_button_outline_color_selector"/>
+</shape>
\ No newline at end of file
diff --git a/libs/WindowManager/Shell/res/layout/desktop_mode_window_decor_handle_menu_app_info_pill.xml b/libs/WindowManager/Shell/res/layout/desktop_mode_window_decor_handle_menu_app_info_pill.xml
index 167a003..c03d240 100644
--- a/libs/WindowManager/Shell/res/layout/desktop_mode_window_decor_handle_menu_app_info_pill.xml
+++ b/libs/WindowManager/Shell/res/layout/desktop_mode_window_decor_handle_menu_app_info_pill.xml
@@ -19,7 +19,7 @@
android:layout_width="@dimen/desktop_mode_handle_menu_width"
android:layout_height="@dimen/desktop_mode_handle_menu_app_info_pill_height"
android:orientation="horizontal"
- android:background="@drawable/desktop_mode_decor_menu_background"
+ android:background="@drawable/desktop_mode_decor_handle_menu_background"
android:gravity="center_vertical">
<ImageView
diff --git a/libs/WindowManager/Shell/res/layout/desktop_mode_window_decor_handle_menu_more_actions_pill.xml b/libs/WindowManager/Shell/res/layout/desktop_mode_window_decor_handle_menu_more_actions_pill.xml
index 40a4b53..cdf4937 100644
--- a/libs/WindowManager/Shell/res/layout/desktop_mode_window_decor_handle_menu_more_actions_pill.xml
+++ b/libs/WindowManager/Shell/res/layout/desktop_mode_window_decor_handle_menu_more_actions_pill.xml
@@ -18,7 +18,7 @@
android:layout_width="@dimen/desktop_mode_handle_menu_width"
android:layout_height="@dimen/desktop_mode_handle_menu_more_actions_pill_height"
android:orientation="vertical"
- android:background="@drawable/desktop_mode_decor_menu_background">
+ android:background="@drawable/desktop_mode_decor_handle_menu_background">
<Button
android:id="@+id/screenshot_button"
diff --git a/libs/WindowManager/Shell/res/layout/desktop_mode_window_decor_handle_menu_windowing_pill.xml b/libs/WindowManager/Shell/res/layout/desktop_mode_window_decor_handle_menu_windowing_pill.xml
index 95283b9..08d9149 100644
--- a/libs/WindowManager/Shell/res/layout/desktop_mode_window_decor_handle_menu_windowing_pill.xml
+++ b/libs/WindowManager/Shell/res/layout/desktop_mode_window_decor_handle_menu_windowing_pill.xml
@@ -18,7 +18,7 @@
android:layout_width="@dimen/desktop_mode_handle_menu_width"
android:layout_height="@dimen/desktop_mode_handle_menu_windowing_pill_height"
android:orientation="horizontal"
- android:background="@drawable/desktop_mode_decor_menu_background"
+ android:background="@drawable/desktop_mode_decor_handle_menu_background"
android:gravity="center_vertical">
<ImageButton
diff --git a/libs/WindowManager/Shell/res/layout/desktop_mode_window_decor_maximize_menu.xml b/libs/WindowManager/Shell/res/layout/desktop_mode_window_decor_maximize_menu.xml
new file mode 100644
index 0000000..0db72f7
--- /dev/null
+++ b/libs/WindowManager/Shell/res/layout/desktop_mode_window_decor_maximize_menu.xml
@@ -0,0 +1,54 @@
+<?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"
+ style="?android:attr/buttonBarStyle"
+ android:layout_width="@dimen/desktop_mode_maximize_menu_width"
+ android:layout_height="@dimen/desktop_mode_maximize_menu_height"
+ android:orientation="horizontal"
+ android:gravity="center"
+ android:background="@drawable/desktop_mode_maximize_menu_background">
+
+
+ <Button
+ android:id="@+id/maximize_menu_maximize_button"
+ style="?android:attr/buttonBarButtonStyle"
+ android:layout_width="120dp"
+ android:layout_height="80dp"
+ android:layout_marginRight="15dp"
+ android:color="@color/desktop_mode_maximize_menu_button"
+ android:background="@drawable/desktop_mode_maximize_menu_maximize_button_background"
+ android:stateListAnimator="@null"/>
+
+ <Button
+ android:id="@+id/maximize_menu_snap_left_button"
+ style="?android:attr/buttonBarButtonStyle"
+ android:layout_width="58dp"
+ android:layout_height="80dp"
+ android:layout_marginRight="6dp"
+ android:color="@color/desktop_mode_maximize_menu_button"
+ android:background="@drawable/desktop_mode_maximize_menu_snap_left_button_background"
+ android:stateListAnimator="@null"/>
+
+ <Button
+ android:id="@+id/maximize_menu_snap_right_button"
+ style="?android:attr/buttonBarButtonStyle"
+ android:layout_width="58dp"
+ android:layout_height="80dp"
+ android:color="@color/desktop_mode_maximize_menu_button"
+ android:background="@drawable/desktop_mode_maximize_menu_snap_right_button_background"
+ android:stateListAnimator="@null"/>
+</LinearLayout>
\ No newline at end of file
diff --git a/libs/WindowManager/Shell/res/values-af/strings.xml b/libs/WindowManager/Shell/res/values-af/strings.xml
index de4a225..1164d37 100644
--- a/libs/WindowManager/Shell/res/values-af/strings.xml
+++ b/libs/WindowManager/Shell/res/values-af/strings.xml
@@ -76,10 +76,16 @@
<string name="bubbles_user_education_got_it" msgid="3382046149225428296">"Het dit"</string>
<string name="bubble_overflow_empty_title" msgid="2397251267073294968">"Geen onlangse borrels nie"</string>
<string name="bubble_overflow_empty_subtitle" msgid="2627417924958633713">"Onlangse borrels en borrels wat toegemaak is, sal hier verskyn"</string>
+ <!-- no translation found for bubble_bar_education_manage_title (6148404487810835924) -->
+ <skip />
+ <!-- no translation found for bubble_bar_education_manage_text (3199732148641842038) -->
+ <skip />
<string name="notification_bubble_title" msgid="6082910224488253378">"Borrel"</string>
<string name="manage_bubbles_text" msgid="7730624269650594419">"Bestuur"</string>
<string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"Borrel is toegemaak."</string>
- <string name="restart_button_description" msgid="6712141648865547958">"Tik om hierdie program te herbegin vir ’n beter aansig."</string>
+ <string name="restart_button_description" msgid="4564728020654658478">"Tik om hierdie app te herbegin vir ’n beter aansig"</string>
+ <string name="user_aspect_ratio_settings_button_hint" msgid="734835849600713016">"Verander hierdie app se aspekverhouding in Instellings"</string>
+ <string name="user_aspect_ratio_settings_button_description" msgid="4315566801697411684">"Verander aspekverhouding"</string>
<string name="camera_compat_treatment_suggested_button_description" msgid="8103916969024076767">"Kamerakwessies?\nTik om aan te pas"</string>
<string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"Nie opgelos nie?\nTik om terug te stel"</string>
<string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"Geen kamerakwessies nie? Tik om toe te maak."</string>
diff --git a/libs/WindowManager/Shell/res/values-am/strings.xml b/libs/WindowManager/Shell/res/values-am/strings.xml
index ca08d56..ffed367 100644
--- a/libs/WindowManager/Shell/res/values-am/strings.xml
+++ b/libs/WindowManager/Shell/res/values-am/strings.xml
@@ -76,10 +76,16 @@
<string name="bubbles_user_education_got_it" msgid="3382046149225428296">"ገባኝ"</string>
<string name="bubble_overflow_empty_title" msgid="2397251267073294968">"ምንም የቅርብ ጊዜ አረፋዎች የሉም"</string>
<string name="bubble_overflow_empty_subtitle" msgid="2627417924958633713">"የቅርብ ጊዜ አረፋዎች እና የተሰናበቱ አረፋዎች እዚህ ብቅ ይላሉ"</string>
+ <!-- no translation found for bubble_bar_education_manage_title (6148404487810835924) -->
+ <skip />
+ <!-- no translation found for bubble_bar_education_manage_text (3199732148641842038) -->
+ <skip />
<string name="notification_bubble_title" msgid="6082910224488253378">"አረፋ"</string>
<string name="manage_bubbles_text" msgid="7730624269650594419">"ያቀናብሩ"</string>
<string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"አረፋ ተሰናብቷል።"</string>
- <string name="restart_button_description" msgid="6712141648865547958">"ለተሻለ ዕይታ ይህን መተግበሪያ ዳግም ለማስነሳት መታ ያድርጉ።"</string>
+ <string name="restart_button_description" msgid="4564728020654658478">"ለተሻለ ዕይታ ይህን መተግበሪያ እንደገና ለመጀመር መታ ያድርጉ"</string>
+ <string name="user_aspect_ratio_settings_button_hint" msgid="734835849600713016">"የዚህን መተግበሪያ ምጥጥነ ገፅታ በቅንብሮች ውስጥ ይለውጡ"</string>
+ <string name="user_aspect_ratio_settings_button_description" msgid="4315566801697411684">"የምጥጥነ ገፅታ ለውጥ"</string>
<string name="camera_compat_treatment_suggested_button_description" msgid="8103916969024076767">"የካሜራ ችግሮች አሉ?\nዳግም ለማበጀት መታ ያድርጉ"</string>
<string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"አልተስተካከለም?\nለማህደር መታ ያድርጉ"</string>
<string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"ምንም የካሜራ ችግሮች የሉም? ለማሰናበት መታ ያድርጉ።"</string>
@@ -106,7 +112,7 @@
<string name="more_button_text" msgid="3655388105592893530">"ተጨማሪ"</string>
<string name="float_button_text" msgid="9221657008391364581">"ተንሳፋፊ"</string>
<string name="select_text" msgid="5139083974039906583">"ምረጥ"</string>
- <string name="screenshot_text" msgid="1477704010087786671">"ቅጽበታዊ ገፅ እይታ"</string>
+ <string name="screenshot_text" msgid="1477704010087786671">"ቅጽበታዊ ገፅ ዕይታ"</string>
<string name="close_text" msgid="4986518933445178928">"ዝጋ"</string>
<string name="collapse_menu_text" msgid="7515008122450342029">"ምናሌ ዝጋ"</string>
<string name="expand_menu_text" msgid="3847736164494181168">"ምናሌን ክፈት"</string>
diff --git a/libs/WindowManager/Shell/res/values-ar/strings.xml b/libs/WindowManager/Shell/res/values-ar/strings.xml
index a714b49..4e9b76b 100644
--- a/libs/WindowManager/Shell/res/values-ar/strings.xml
+++ b/libs/WindowManager/Shell/res/values-ar/strings.xml
@@ -76,10 +76,16 @@
<string name="bubbles_user_education_got_it" msgid="3382046149225428296">"حسنًا"</string>
<string name="bubble_overflow_empty_title" msgid="2397251267073294968">"ليس هناك فقاعات محادثات"</string>
<string name="bubble_overflow_empty_subtitle" msgid="2627417924958633713">"ستظهر هنا أحدث فقاعات المحادثات وفقاعات المحادثات التي تم إغلاقها."</string>
+ <!-- no translation found for bubble_bar_education_manage_title (6148404487810835924) -->
+ <skip />
+ <!-- no translation found for bubble_bar_education_manage_text (3199732148641842038) -->
+ <skip />
<string name="notification_bubble_title" msgid="6082910224488253378">"فقاعة"</string>
<string name="manage_bubbles_text" msgid="7730624269650594419">"إدارة"</string>
<string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"تم إغلاق الفقاعة."</string>
- <string name="restart_button_description" msgid="6712141648865547958">"انقر لإعادة تشغيل هذا التطبيق للحصول على عرض أفضل."</string>
+ <string name="restart_button_description" msgid="4564728020654658478">"انقر لإعادة تشغيل هذا التطبيق للحصول على تجربة عرض أفضل."</string>
+ <string name="user_aspect_ratio_settings_button_hint" msgid="734835849600713016">"يمكنك تغيير نسبة العرض إلى الارتفاع لهذا التطبيق من خلال \"الإعدادات\"."</string>
+ <string name="user_aspect_ratio_settings_button_description" msgid="4315566801697411684">"تغيير نسبة العرض إلى الارتفاع"</string>
<string name="camera_compat_treatment_suggested_button_description" msgid="8103916969024076767">"هل هناك مشاكل في الكاميرا؟\nانقر لإعادة الضبط."</string>
<string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"ألم يتم حل المشكلة؟\nانقر للعودة"</string>
<string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"أليس هناك مشاكل في الكاميرا؟ انقر للإغلاق."</string>
diff --git a/libs/WindowManager/Shell/res/values-as/strings.xml b/libs/WindowManager/Shell/res/values-as/strings.xml
index 6d86747..a583f34 100644
--- a/libs/WindowManager/Shell/res/values-as/strings.xml
+++ b/libs/WindowManager/Shell/res/values-as/strings.xml
@@ -76,10 +76,16 @@
<string name="bubbles_user_education_got_it" msgid="3382046149225428296">"বুজি পালোঁ"</string>
<string name="bubble_overflow_empty_title" msgid="2397251267073294968">"কোনো শেহতীয়া bubbles নাই"</string>
<string name="bubble_overflow_empty_subtitle" msgid="2627417924958633713">"শেহতীয়া bubbles আৰু অগ্ৰাহ্য কৰা bubbles ইয়াত প্ৰদর্শিত হ\'ব"</string>
+ <!-- no translation found for bubble_bar_education_manage_title (6148404487810835924) -->
+ <skip />
+ <!-- no translation found for bubble_bar_education_manage_text (3199732148641842038) -->
+ <skip />
<string name="notification_bubble_title" msgid="6082910224488253378">"বাবল"</string>
<string name="manage_bubbles_text" msgid="7730624269650594419">"পৰিচালনা কৰক"</string>
<string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"বাবল অগ্ৰাহ্য কৰা হৈছে"</string>
- <string name="restart_button_description" msgid="6712141648865547958">"উন্নত ভিউৰ বাবে এপ্টো ৰিষ্টাৰ্ট কৰিবলৈ টিপক।"</string>
+ <string name="restart_button_description" msgid="4564728020654658478">"উন্নত ভিউ পোৱাৰ বাবে এপ্টো ৰিষ্টাৰ্ট কৰিবলৈ টিপক"</string>
+ <string name="user_aspect_ratio_settings_button_hint" msgid="734835849600713016">"ছেটিঙলৈ গৈ এই এপ্টোৰ আকাৰৰ অনুপাত সলনি কৰক"</string>
+ <string name="user_aspect_ratio_settings_button_description" msgid="4315566801697411684">"আকাৰৰ অনুপাত সলনি কৰক"</string>
<string name="camera_compat_treatment_suggested_button_description" msgid="8103916969024076767">"কেমেৰাৰ কোনো সমস্যা হৈছে নেকি?\nপুনৰ খাপ খোৱাবলৈ টিপক"</string>
<string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"এইটো সমাধান কৰা নাই নেকি?\nপূৰ্বাৱস্থালৈ নিবলৈ টিপক"</string>
<string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"কেমেৰাৰ কোনো সমস্যা নাই নেকি? অগ্ৰাহ্য কৰিবলৈ টিপক।"</string>
diff --git a/libs/WindowManager/Shell/res/values-az/strings.xml b/libs/WindowManager/Shell/res/values-az/strings.xml
index 7c66282..fb09258 100644
--- a/libs/WindowManager/Shell/res/values-az/strings.xml
+++ b/libs/WindowManager/Shell/res/values-az/strings.xml
@@ -76,10 +76,16 @@
<string name="bubbles_user_education_got_it" msgid="3382046149225428296">"Anladım"</string>
<string name="bubble_overflow_empty_title" msgid="2397251267073294968">"Yumrucuqlar yoxdur"</string>
<string name="bubble_overflow_empty_subtitle" msgid="2627417924958633713">"Son yumrucuqlar və buraxılmış yumrucuqlar burada görünəcək"</string>
+ <!-- no translation found for bubble_bar_education_manage_title (6148404487810835924) -->
+ <skip />
+ <!-- no translation found for bubble_bar_education_manage_text (3199732148641842038) -->
+ <skip />
<string name="notification_bubble_title" msgid="6082910224488253378">"Qabarcıq"</string>
<string name="manage_bubbles_text" msgid="7730624269650594419">"İdarə edin"</string>
<string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"Qabarcıqdan imtina edilib."</string>
- <string name="restart_button_description" msgid="6712141648865547958">"Toxunaraq bu tətbiqi yenidən başladın ki, daha görüntü əldə edəsiniz."</string>
+ <string name="restart_button_description" msgid="4564728020654658478">"Yaxşı görünüş üçün toxunaraq bu tətbiqi yenidən başladın"</string>
+ <string name="user_aspect_ratio_settings_button_hint" msgid="734835849600713016">"Ayarlarda bu tətbiqin tərəflər nisbətini dəyişin"</string>
+ <string name="user_aspect_ratio_settings_button_description" msgid="4315566801697411684">"Tərəflər nisbətini dəyişin"</string>
<string name="camera_compat_treatment_suggested_button_description" msgid="8103916969024076767">"Kamera problemi var?\nBərpa etmək üçün toxunun"</string>
<string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"Düzəltməmisiniz?\nGeri qaytarmaq üçün toxunun"</string>
<string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"Kamera problemi yoxdur? Qapatmaq üçün toxunun."</string>
diff --git a/libs/WindowManager/Shell/res/values-b+sr+Latn/strings.xml b/libs/WindowManager/Shell/res/values-b+sr+Latn/strings.xml
index c415c86..a9ba12e 100644
--- a/libs/WindowManager/Shell/res/values-b+sr+Latn/strings.xml
+++ b/libs/WindowManager/Shell/res/values-b+sr+Latn/strings.xml
@@ -76,10 +76,16 @@
<string name="bubbles_user_education_got_it" msgid="3382046149225428296">"Važi"</string>
<string name="bubble_overflow_empty_title" msgid="2397251267073294968">"Nema nedavnih oblačića"</string>
<string name="bubble_overflow_empty_subtitle" msgid="2627417924958633713">"Ovde se prikazuju nedavni i odbačeni oblačići"</string>
+ <!-- no translation found for bubble_bar_education_manage_title (6148404487810835924) -->
+ <skip />
+ <!-- no translation found for bubble_bar_education_manage_text (3199732148641842038) -->
+ <skip />
<string name="notification_bubble_title" msgid="6082910224488253378">"Oblačić"</string>
<string name="manage_bubbles_text" msgid="7730624269650594419">"Upravljajte"</string>
<string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"Oblačić je odbačen."</string>
- <string name="restart_button_description" msgid="6712141648865547958">"Dodirnite da biste restartovali ovu aplikaciju radi boljeg prikaza."</string>
+ <string name="restart_button_description" msgid="4564728020654658478">"Dodirnite da biste restartovali ovu aplikaciju radi boljeg prikaza"</string>
+ <string name="user_aspect_ratio_settings_button_hint" msgid="734835849600713016">"Promenite razmeru ove aplikacije u Podešavanjima"</string>
+ <string name="user_aspect_ratio_settings_button_description" msgid="4315566801697411684">"Promeni razmeru"</string>
<string name="camera_compat_treatment_suggested_button_description" msgid="8103916969024076767">"Imate problema sa kamerom?\nDodirnite da biste ponovo uklopili"</string>
<string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"Problem nije rešen?\nDodirnite da biste vratili"</string>
<string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"Nemate problema sa kamerom? Dodirnite da biste odbacili."</string>
diff --git a/libs/WindowManager/Shell/res/values-be/strings.xml b/libs/WindowManager/Shell/res/values-be/strings.xml
index 3d99514..eef363b 100644
--- a/libs/WindowManager/Shell/res/values-be/strings.xml
+++ b/libs/WindowManager/Shell/res/values-be/strings.xml
@@ -76,10 +76,16 @@
<string name="bubbles_user_education_got_it" msgid="3382046149225428296">"Зразумела"</string>
<string name="bubble_overflow_empty_title" msgid="2397251267073294968">"Няма нядаўніх усплывальных апавяшчэнняў"</string>
<string name="bubble_overflow_empty_subtitle" msgid="2627417924958633713">"Нядаўнія і адхіленыя ўсплывальныя апавяшчэнні будуць паказаны тут"</string>
+ <!-- no translation found for bubble_bar_education_manage_title (6148404487810835924) -->
+ <skip />
+ <!-- no translation found for bubble_bar_education_manage_text (3199732148641842038) -->
+ <skip />
<string name="notification_bubble_title" msgid="6082910224488253378">"Усплывальнае апавяшчэнне"</string>
<string name="manage_bubbles_text" msgid="7730624269650594419">"Кіраваць"</string>
<string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"Усплывальнае апавяшчэнне адхілена."</string>
- <string name="restart_button_description" msgid="6712141648865547958">"Націсніце, каб перазапусціць гэту праграму для лепшага прагляду."</string>
+ <string name="restart_button_description" msgid="4564728020654658478">"Націсніце, каб перазапусціць гэту праграму для зручнейшага прагляду"</string>
+ <string name="user_aspect_ratio_settings_button_hint" msgid="734835849600713016">"Змяніць суадносіны бакоў для гэтай праграмы ў наладах"</string>
+ <string name="user_aspect_ratio_settings_button_description" msgid="4315566801697411684">"Змяніць суадносіны бакоў"</string>
<string name="camera_compat_treatment_suggested_button_description" msgid="8103916969024076767">"Праблемы з камерай?\nНацісніце, каб пераабсталяваць"</string>
<string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"Не ўдалося выправіць?\nНацісніце, каб аднавіць"</string>
<string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"Ніякіх праблем з камерай? Націсніце, каб адхіліць."</string>
diff --git a/libs/WindowManager/Shell/res/values-bg/strings.xml b/libs/WindowManager/Shell/res/values-bg/strings.xml
index 0473f27..281bd9d 100644
--- a/libs/WindowManager/Shell/res/values-bg/strings.xml
+++ b/libs/WindowManager/Shell/res/values-bg/strings.xml
@@ -76,10 +76,16 @@
<string name="bubbles_user_education_got_it" msgid="3382046149225428296">"Разбрах"</string>
<string name="bubble_overflow_empty_title" msgid="2397251267073294968">"Няма скорошни балончета"</string>
<string name="bubble_overflow_empty_subtitle" msgid="2627417924958633713">"Скорошните и отхвърлените балончета ще се показват тук"</string>
+ <!-- no translation found for bubble_bar_education_manage_title (6148404487810835924) -->
+ <skip />
+ <!-- no translation found for bubble_bar_education_manage_text (3199732148641842038) -->
+ <skip />
<string name="notification_bubble_title" msgid="6082910224488253378">"Балонче"</string>
<string name="manage_bubbles_text" msgid="7730624269650594419">"Управление"</string>
<string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"Балончето е отхвърлено."</string>
- <string name="restart_button_description" msgid="6712141648865547958">"Докоснете, за да рестартирате това приложение с цел по-добър изглед."</string>
+ <string name="restart_button_description" msgid="4564728020654658478">"Докоснете, за да рестартирате това приложение с цел по-добър изглед"</string>
+ <string name="user_aspect_ratio_settings_button_hint" msgid="734835849600713016">"Променете съотношението на това приложение в „Настройки“"</string>
+ <string name="user_aspect_ratio_settings_button_description" msgid="4315566801697411684">"Промяна на съотношението"</string>
<string name="camera_compat_treatment_suggested_button_description" msgid="8103916969024076767">"Имате проблеми с камерата?\nДокоснете за ремонтиране"</string>
<string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"Проблемът не се отстрани?\nДокоснете за връщане в предишното състояние"</string>
<string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"Нямате проблеми с камерата? Докоснете, за да отхвърлите."</string>
diff --git a/libs/WindowManager/Shell/res/values-bn/strings.xml b/libs/WindowManager/Shell/res/values-bn/strings.xml
index 4fe1be0..3dae948 100644
--- a/libs/WindowManager/Shell/res/values-bn/strings.xml
+++ b/libs/WindowManager/Shell/res/values-bn/strings.xml
@@ -76,10 +76,16 @@
<string name="bubbles_user_education_got_it" msgid="3382046149225428296">"বুঝেছি"</string>
<string name="bubble_overflow_empty_title" msgid="2397251267073294968">"কোনও সাম্প্রতিক বাবল নেই"</string>
<string name="bubble_overflow_empty_subtitle" msgid="2627417924958633713">"সাম্প্রতিক ও বাতিল করা বাবল এখানে দেখা যাবে"</string>
+ <!-- no translation found for bubble_bar_education_manage_title (6148404487810835924) -->
+ <skip />
+ <!-- no translation found for bubble_bar_education_manage_text (3199732148641842038) -->
+ <skip />
<string name="notification_bubble_title" msgid="6082910224488253378">"বাবল"</string>
<string name="manage_bubbles_text" msgid="7730624269650594419">"ম্যানেজ করুন"</string>
<string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"বাবল বাতিল করা হয়েছে।"</string>
- <string name="restart_button_description" msgid="6712141648865547958">"আরও ভাল ভিউয়ের জন্য এই অ্যাপ রিস্টার্ট করতে ট্যাপ করুন।"</string>
+ <string name="restart_button_description" msgid="4564728020654658478">"আরও ভাল ভিউয়ের জন্য এই অ্যাপ রিস্টার্ট করতে ট্যাপ করুন"</string>
+ <string name="user_aspect_ratio_settings_button_hint" msgid="734835849600713016">"সেটিংস থেকে এই অ্যাপের অ্যাস্পেক্ট রেশিও পরিবর্তন করুন"</string>
+ <string name="user_aspect_ratio_settings_button_description" msgid="4315566801697411684">"অ্যাস্পেক্ট রেশিও পরিবর্তন করুন"</string>
<string name="camera_compat_treatment_suggested_button_description" msgid="8103916969024076767">"ক্যামেরা সংক্রান্ত সমস্যা?\nরিফিট করতে ট্যাপ করুন"</string>
<string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"এখনও সমাধান হয়নি?\nরিভার্ট করার জন্য ট্যাপ করুন"</string>
<string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"ক্যামেরা সংক্রান্ত সমস্যা নেই? বাতিল করতে ট্যাপ করুন।"</string>
diff --git a/libs/WindowManager/Shell/res/values-bs/strings.xml b/libs/WindowManager/Shell/res/values-bs/strings.xml
index b39b497..8b7eb61 100644
--- a/libs/WindowManager/Shell/res/values-bs/strings.xml
+++ b/libs/WindowManager/Shell/res/values-bs/strings.xml
@@ -76,10 +76,16 @@
<string name="bubbles_user_education_got_it" msgid="3382046149225428296">"Razumijem"</string>
<string name="bubble_overflow_empty_title" msgid="2397251267073294968">"Nema nedavnih oblačića"</string>
<string name="bubble_overflow_empty_subtitle" msgid="2627417924958633713">"Nedavni i odbačeni oblačići će se pojaviti ovdje"</string>
+ <!-- no translation found for bubble_bar_education_manage_title (6148404487810835924) -->
+ <skip />
+ <!-- no translation found for bubble_bar_education_manage_text (3199732148641842038) -->
+ <skip />
<string name="notification_bubble_title" msgid="6082910224488253378">"Oblačić"</string>
<string name="manage_bubbles_text" msgid="7730624269650594419">"Upravljaj"</string>
<string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"Oblačić je odbačen."</string>
- <string name="restart_button_description" msgid="6712141648865547958">"Dodirnite da ponovo pokrenete ovu aplikaciju radi boljeg prikaza."</string>
+ <string name="restart_button_description" msgid="4564728020654658478">"Dodirnite da ponovo pokrenete ovu aplikaciju radi boljeg prikaza"</string>
+ <string name="user_aspect_ratio_settings_button_hint" msgid="734835849600713016">"Promijenite format slike aplikacije u Postavkama"</string>
+ <string name="user_aspect_ratio_settings_button_description" msgid="4315566801697411684">"Promijenite format slike"</string>
<string name="camera_compat_treatment_suggested_button_description" msgid="8103916969024076767">"Problemi s kamerom?\nDodirnite da ponovo namjestite"</string>
<string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"Nije popravljeno?\nDodirnite da vratite"</string>
<string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"Nema problema s kamerom? Dodirnite da odbacite."</string>
diff --git a/libs/WindowManager/Shell/res/values-ca/strings.xml b/libs/WindowManager/Shell/res/values-ca/strings.xml
index fe76e73..2250f9d 100644
--- a/libs/WindowManager/Shell/res/values-ca/strings.xml
+++ b/libs/WindowManager/Shell/res/values-ca/strings.xml
@@ -76,10 +76,16 @@
<string name="bubbles_user_education_got_it" msgid="3382046149225428296">"Entesos"</string>
<string name="bubble_overflow_empty_title" msgid="2397251267073294968">"No hi ha bombolles recents"</string>
<string name="bubble_overflow_empty_subtitle" msgid="2627417924958633713">"Les bombolles recents i les ignorades es mostraran aquí"</string>
+ <!-- no translation found for bubble_bar_education_manage_title (6148404487810835924) -->
+ <skip />
+ <!-- no translation found for bubble_bar_education_manage_text (3199732148641842038) -->
+ <skip />
<string name="notification_bubble_title" msgid="6082910224488253378">"Bombolla"</string>
<string name="manage_bubbles_text" msgid="7730624269650594419">"Gestiona"</string>
<string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"La bombolla s\'ha ignorat."</string>
- <string name="restart_button_description" msgid="6712141648865547958">"Toca per reiniciar aquesta aplicació i obtenir una millor visualització."</string>
+ <string name="restart_button_description" msgid="4564728020654658478">"Toca per reiniciar aquesta aplicació i obtenir una millor visualització"</string>
+ <string name="user_aspect_ratio_settings_button_hint" msgid="734835849600713016">"Canvia la relació d\'aspecte d\'aquesta aplicació a Configuració"</string>
+ <string name="user_aspect_ratio_settings_button_description" msgid="4315566801697411684">"Canvia la relació d\'aspecte"</string>
<string name="camera_compat_treatment_suggested_button_description" msgid="8103916969024076767">"Tens problemes amb la càmera?\nToca per resoldre\'ls"</string>
<string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"El problema no s\'ha resolt?\nToca per desfer els canvis"</string>
<string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"No tens cap problema amb la càmera? Toca per ignorar."</string>
diff --git a/libs/WindowManager/Shell/res/values-cs/strings.xml b/libs/WindowManager/Shell/res/values-cs/strings.xml
index ac22b85..ebee2c1 100644
--- a/libs/WindowManager/Shell/res/values-cs/strings.xml
+++ b/libs/WindowManager/Shell/res/values-cs/strings.xml
@@ -76,10 +76,16 @@
<string name="bubbles_user_education_got_it" msgid="3382046149225428296">"OK"</string>
<string name="bubble_overflow_empty_title" msgid="2397251267073294968">"Žádné nedávné bubliny"</string>
<string name="bubble_overflow_empty_subtitle" msgid="2627417924958633713">"Zde se budou zobrazovat nedávné bubliny a zavřené bubliny"</string>
+ <!-- no translation found for bubble_bar_education_manage_title (6148404487810835924) -->
+ <skip />
+ <!-- no translation found for bubble_bar_education_manage_text (3199732148641842038) -->
+ <skip />
<string name="notification_bubble_title" msgid="6082910224488253378">"Bublina"</string>
<string name="manage_bubbles_text" msgid="7730624269650594419">"Spravovat"</string>
<string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"Bublina byla zavřena."</string>
- <string name="restart_button_description" msgid="6712141648865547958">"Pokud je problém se zobrazením aplikace, klepněte na ni a restartujte ji."</string>
+ <string name="restart_button_description" msgid="4564728020654658478">"Klepnutím tuto aplikaci kvůli lepšímu zobrazení restartujete"</string>
+ <string name="user_aspect_ratio_settings_button_hint" msgid="734835849600713016">"Změnit v Nastavení poměr stran této aplikace"</string>
+ <string name="user_aspect_ratio_settings_button_description" msgid="4315566801697411684">"Změnit poměr stran"</string>
<string name="camera_compat_treatment_suggested_button_description" msgid="8103916969024076767">"Problémy s fotoaparátem?\nKlepnutím vyřešíte"</string>
<string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"Nepomohlo to?\nKlepnutím se vrátíte"</string>
<string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"Žádné problémy s fotoaparátem? Klepnutím zavřete."</string>
diff --git a/libs/WindowManager/Shell/res/values-da/strings.xml b/libs/WindowManager/Shell/res/values-da/strings.xml
index c91cd7a..4e46243 100644
--- a/libs/WindowManager/Shell/res/values-da/strings.xml
+++ b/libs/WindowManager/Shell/res/values-da/strings.xml
@@ -76,10 +76,16 @@
<string name="bubbles_user_education_got_it" msgid="3382046149225428296">"OK"</string>
<string name="bubble_overflow_empty_title" msgid="2397251267073294968">"Ingen seneste bobler"</string>
<string name="bubble_overflow_empty_subtitle" msgid="2627417924958633713">"Nye bobler og afviste bobler vises her"</string>
+ <!-- no translation found for bubble_bar_education_manage_title (6148404487810835924) -->
+ <skip />
+ <!-- no translation found for bubble_bar_education_manage_text (3199732148641842038) -->
+ <skip />
<string name="notification_bubble_title" msgid="6082910224488253378">"Boble"</string>
<string name="manage_bubbles_text" msgid="7730624269650594419">"Administrer"</string>
<string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"Boblen blev lukket."</string>
- <string name="restart_button_description" msgid="6712141648865547958">"Tryk for at genstarte denne app, så visningen forbedres."</string>
+ <string name="restart_button_description" msgid="4564728020654658478">"Tryk for at genstarte denne app, så visningen forbedres"</string>
+ <string name="user_aspect_ratio_settings_button_hint" msgid="734835849600713016">"Skift denne apps billedformat i Indstillinger"</string>
+ <string name="user_aspect_ratio_settings_button_description" msgid="4315566801697411684">"Skift billedformat"</string>
<string name="camera_compat_treatment_suggested_button_description" msgid="8103916969024076767">"Har du problemer med dit kamera?\nTryk for at gendanne det oprindelige format"</string>
<string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"Løste det ikke problemet?\nTryk for at fortryde"</string>
<string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"Har du ingen problemer med dit kamera? Tryk for at afvise."</string>
diff --git a/libs/WindowManager/Shell/res/values-de/strings.xml b/libs/WindowManager/Shell/res/values-de/strings.xml
index c17f97f..1d5182a 100644
--- a/libs/WindowManager/Shell/res/values-de/strings.xml
+++ b/libs/WindowManager/Shell/res/values-de/strings.xml
@@ -76,10 +76,16 @@
<string name="bubbles_user_education_got_it" msgid="3382046149225428296">"OK"</string>
<string name="bubble_overflow_empty_title" msgid="2397251267073294968">"Keine kürzlich geschlossenen Bubbles"</string>
<string name="bubble_overflow_empty_subtitle" msgid="2627417924958633713">"Hier werden aktuelle und geschlossene Bubbles angezeigt"</string>
+ <!-- no translation found for bubble_bar_education_manage_title (6148404487810835924) -->
+ <skip />
+ <!-- no translation found for bubble_bar_education_manage_text (3199732148641842038) -->
+ <skip />
<string name="notification_bubble_title" msgid="6082910224488253378">"Bubble"</string>
<string name="manage_bubbles_text" msgid="7730624269650594419">"Verwalten"</string>
<string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"Bubble verworfen."</string>
- <string name="restart_button_description" msgid="6712141648865547958">"Tippe, um diese App neu zu starten und die Ansicht zu verbessern."</string>
+ <string name="restart_button_description" msgid="4564728020654658478">"Tippen, um diese App neu zu starten und die Ansicht zu verbessern"</string>
+ <string name="user_aspect_ratio_settings_button_hint" msgid="734835849600713016">"Seitenverhältnis der App in den Einstellungen ändern"</string>
+ <string name="user_aspect_ratio_settings_button_description" msgid="4315566801697411684">"Seitenverhältnis ändern"</string>
<string name="camera_compat_treatment_suggested_button_description" msgid="8103916969024076767">"Probleme mit der Kamera?\nZum Anpassen tippen."</string>
<string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"Das Problem ist nicht behoben?\nZum Rückgängigmachen tippen."</string>
<string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"Keine Probleme mit der Kamera? Zum Schließen tippen."</string>
diff --git a/libs/WindowManager/Shell/res/values-el/strings.xml b/libs/WindowManager/Shell/res/values-el/strings.xml
index ab5c44e..34a6a07 100644
--- a/libs/WindowManager/Shell/res/values-el/strings.xml
+++ b/libs/WindowManager/Shell/res/values-el/strings.xml
@@ -76,10 +76,16 @@
<string name="bubbles_user_education_got_it" msgid="3382046149225428296">"Το κατάλαβα"</string>
<string name="bubble_overflow_empty_title" msgid="2397251267073294968">"Δεν υπάρχουν πρόσφατα συννεφάκια"</string>
<string name="bubble_overflow_empty_subtitle" msgid="2627417924958633713">"Τα πρόσφατα συννεφάκια και τα συννεφάκια που παραβλέψατε θα εμφανίζονται εδώ."</string>
+ <!-- no translation found for bubble_bar_education_manage_title (6148404487810835924) -->
+ <skip />
+ <!-- no translation found for bubble_bar_education_manage_text (3199732148641842038) -->
+ <skip />
<string name="notification_bubble_title" msgid="6082910224488253378">"Συννεφάκι"</string>
<string name="manage_bubbles_text" msgid="7730624269650594419">"Διαχείριση"</string>
<string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"Το συννεφάκι παραβλέφθηκε."</string>
- <string name="restart_button_description" msgid="6712141648865547958">"Πατήστε για να επανεκκινήσετε αυτή την εφαρμογή για καλύτερη προβολή."</string>
+ <string name="restart_button_description" msgid="4564728020654658478">"Πατήστε για να επανεκκινήσετε αυτή την εφαρμογή για καλύτερη προβολή"</string>
+ <string name="user_aspect_ratio_settings_button_hint" msgid="734835849600713016">"Αλλάξτε τον λόγο διαστάσεων αυτής της εφαρμογής στις Ρυθμίσεις"</string>
+ <string name="user_aspect_ratio_settings_button_description" msgid="4315566801697411684">"Αλλαγή λόγου διαστάσεων"</string>
<string name="camera_compat_treatment_suggested_button_description" msgid="8103916969024076767">"Προβλήματα με την κάμερα;\nΠατήστε για επιδιόρθωση."</string>
<string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"Δεν διορθώθηκε;\nΠατήστε για επαναφορά."</string>
<string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"Δεν αντιμετωπίζετε προβλήματα με την κάμερα; Πατήστε για παράβλεψη."</string>
diff --git a/libs/WindowManager/Shell/res/values-en-rAU/strings.xml b/libs/WindowManager/Shell/res/values-en-rAU/strings.xml
index ea91014..c6e1c5f 100644
--- a/libs/WindowManager/Shell/res/values-en-rAU/strings.xml
+++ b/libs/WindowManager/Shell/res/values-en-rAU/strings.xml
@@ -76,10 +76,16 @@
<string name="bubbles_user_education_got_it" msgid="3382046149225428296">"OK"</string>
<string name="bubble_overflow_empty_title" msgid="2397251267073294968">"No recent bubbles"</string>
<string name="bubble_overflow_empty_subtitle" msgid="2627417924958633713">"Recent bubbles and dismissed bubbles will appear here"</string>
+ <!-- no translation found for bubble_bar_education_manage_title (6148404487810835924) -->
+ <skip />
+ <!-- no translation found for bubble_bar_education_manage_text (3199732148641842038) -->
+ <skip />
<string name="notification_bubble_title" msgid="6082910224488253378">"Bubble"</string>
<string name="manage_bubbles_text" msgid="7730624269650594419">"Manage"</string>
<string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"Bubble dismissed."</string>
- <string name="restart_button_description" msgid="6712141648865547958">"Tap to restart this app for a better view."</string>
+ <string name="restart_button_description" msgid="4564728020654658478">"Tap to restart this app for a better view"</string>
+ <string name="user_aspect_ratio_settings_button_hint" msgid="734835849600713016">"Change this app\'s aspect ratio in Settings"</string>
+ <string name="user_aspect_ratio_settings_button_description" msgid="4315566801697411684">"Change aspect ratio"</string>
<string name="camera_compat_treatment_suggested_button_description" msgid="8103916969024076767">"Camera issues?\nTap to refit"</string>
<string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"Didn’t fix it?\nTap to revert"</string>
<string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"No camera issues? Tap to dismiss."</string>
diff --git a/libs/WindowManager/Shell/res/values-en-rCA/strings.xml b/libs/WindowManager/Shell/res/values-en-rCA/strings.xml
index 01bdf95..e536930 100644
--- a/libs/WindowManager/Shell/res/values-en-rCA/strings.xml
+++ b/libs/WindowManager/Shell/res/values-en-rCA/strings.xml
@@ -76,10 +76,16 @@
<string name="bubbles_user_education_got_it" msgid="3382046149225428296">"Got it"</string>
<string name="bubble_overflow_empty_title" msgid="2397251267073294968">"No recent bubbles"</string>
<string name="bubble_overflow_empty_subtitle" msgid="2627417924958633713">"Recent bubbles and dismissed bubbles will appear here"</string>
+ <!-- no translation found for bubble_bar_education_manage_title (6148404487810835924) -->
+ <skip />
+ <!-- no translation found for bubble_bar_education_manage_text (3199732148641842038) -->
+ <skip />
<string name="notification_bubble_title" msgid="6082910224488253378">"Bubble"</string>
<string name="manage_bubbles_text" msgid="7730624269650594419">"Manage"</string>
<string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"Bubble dismissed."</string>
- <string name="restart_button_description" msgid="6712141648865547958">"Tap to restart this app for a better view."</string>
+ <string name="restart_button_description" msgid="4564728020654658478">"Tap to restart this app for a better view"</string>
+ <string name="user_aspect_ratio_settings_button_hint" msgid="734835849600713016">"Change this app\'s aspect ratio in Settings"</string>
+ <string name="user_aspect_ratio_settings_button_description" msgid="4315566801697411684">"Change aspect ratio"</string>
<string name="camera_compat_treatment_suggested_button_description" msgid="8103916969024076767">"Camera issues?\nTap to refit"</string>
<string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"Didn’t fix it?\nTap to revert"</string>
<string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"No camera issues? Tap to dismiss."</string>
diff --git a/libs/WindowManager/Shell/res/values-en-rGB/strings.xml b/libs/WindowManager/Shell/res/values-en-rGB/strings.xml
index ea91014..c6e1c5f 100644
--- a/libs/WindowManager/Shell/res/values-en-rGB/strings.xml
+++ b/libs/WindowManager/Shell/res/values-en-rGB/strings.xml
@@ -76,10 +76,16 @@
<string name="bubbles_user_education_got_it" msgid="3382046149225428296">"OK"</string>
<string name="bubble_overflow_empty_title" msgid="2397251267073294968">"No recent bubbles"</string>
<string name="bubble_overflow_empty_subtitle" msgid="2627417924958633713">"Recent bubbles and dismissed bubbles will appear here"</string>
+ <!-- no translation found for bubble_bar_education_manage_title (6148404487810835924) -->
+ <skip />
+ <!-- no translation found for bubble_bar_education_manage_text (3199732148641842038) -->
+ <skip />
<string name="notification_bubble_title" msgid="6082910224488253378">"Bubble"</string>
<string name="manage_bubbles_text" msgid="7730624269650594419">"Manage"</string>
<string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"Bubble dismissed."</string>
- <string name="restart_button_description" msgid="6712141648865547958">"Tap to restart this app for a better view."</string>
+ <string name="restart_button_description" msgid="4564728020654658478">"Tap to restart this app for a better view"</string>
+ <string name="user_aspect_ratio_settings_button_hint" msgid="734835849600713016">"Change this app\'s aspect ratio in Settings"</string>
+ <string name="user_aspect_ratio_settings_button_description" msgid="4315566801697411684">"Change aspect ratio"</string>
<string name="camera_compat_treatment_suggested_button_description" msgid="8103916969024076767">"Camera issues?\nTap to refit"</string>
<string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"Didn’t fix it?\nTap to revert"</string>
<string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"No camera issues? Tap to dismiss."</string>
diff --git a/libs/WindowManager/Shell/res/values-en-rIN/strings.xml b/libs/WindowManager/Shell/res/values-en-rIN/strings.xml
index ea91014..c6e1c5f 100644
--- a/libs/WindowManager/Shell/res/values-en-rIN/strings.xml
+++ b/libs/WindowManager/Shell/res/values-en-rIN/strings.xml
@@ -76,10 +76,16 @@
<string name="bubbles_user_education_got_it" msgid="3382046149225428296">"OK"</string>
<string name="bubble_overflow_empty_title" msgid="2397251267073294968">"No recent bubbles"</string>
<string name="bubble_overflow_empty_subtitle" msgid="2627417924958633713">"Recent bubbles and dismissed bubbles will appear here"</string>
+ <!-- no translation found for bubble_bar_education_manage_title (6148404487810835924) -->
+ <skip />
+ <!-- no translation found for bubble_bar_education_manage_text (3199732148641842038) -->
+ <skip />
<string name="notification_bubble_title" msgid="6082910224488253378">"Bubble"</string>
<string name="manage_bubbles_text" msgid="7730624269650594419">"Manage"</string>
<string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"Bubble dismissed."</string>
- <string name="restart_button_description" msgid="6712141648865547958">"Tap to restart this app for a better view."</string>
+ <string name="restart_button_description" msgid="4564728020654658478">"Tap to restart this app for a better view"</string>
+ <string name="user_aspect_ratio_settings_button_hint" msgid="734835849600713016">"Change this app\'s aspect ratio in Settings"</string>
+ <string name="user_aspect_ratio_settings_button_description" msgid="4315566801697411684">"Change aspect ratio"</string>
<string name="camera_compat_treatment_suggested_button_description" msgid="8103916969024076767">"Camera issues?\nTap to refit"</string>
<string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"Didn’t fix it?\nTap to revert"</string>
<string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"No camera issues? Tap to dismiss."</string>
diff --git a/libs/WindowManager/Shell/res/values-en-rXC/strings.xml b/libs/WindowManager/Shell/res/values-en-rXC/strings.xml
index f6dac79..83631eb 100644
--- a/libs/WindowManager/Shell/res/values-en-rXC/strings.xml
+++ b/libs/WindowManager/Shell/res/values-en-rXC/strings.xml
@@ -76,10 +76,16 @@
<string name="bubbles_user_education_got_it" msgid="3382046149225428296">"Got it"</string>
<string name="bubble_overflow_empty_title" msgid="2397251267073294968">"No recent bubbles"</string>
<string name="bubble_overflow_empty_subtitle" msgid="2627417924958633713">"Recent bubbles and dismissed bubbles will appear here"</string>
+ <!-- no translation found for bubble_bar_education_manage_title (6148404487810835924) -->
+ <skip />
+ <!-- no translation found for bubble_bar_education_manage_text (3199732148641842038) -->
+ <skip />
<string name="notification_bubble_title" msgid="6082910224488253378">"Bubble"</string>
<string name="manage_bubbles_text" msgid="7730624269650594419">"Manage"</string>
<string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"Bubble dismissed."</string>
- <string name="restart_button_description" msgid="6712141648865547958">"Tap to restart this app for a better view."</string>
+ <string name="restart_button_description" msgid="4564728020654658478">"Tap to restart this app for a better view"</string>
+ <string name="user_aspect_ratio_settings_button_hint" msgid="734835849600713016">"Change this app\'s aspect ratio in Settings"</string>
+ <string name="user_aspect_ratio_settings_button_description" msgid="4315566801697411684">"Change aspect ratio"</string>
<string name="camera_compat_treatment_suggested_button_description" msgid="8103916969024076767">"Camera issues?\nTap to refit"</string>
<string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"Didn’t fix it?\nTap to revert"</string>
<string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"No camera issues? Tap to dismiss."</string>
diff --git a/libs/WindowManager/Shell/res/values-es-rUS/strings.xml b/libs/WindowManager/Shell/res/values-es-rUS/strings.xml
index 0190ea8..c0dfeef 100644
--- a/libs/WindowManager/Shell/res/values-es-rUS/strings.xml
+++ b/libs/WindowManager/Shell/res/values-es-rUS/strings.xml
@@ -76,10 +76,16 @@
<string name="bubbles_user_education_got_it" msgid="3382046149225428296">"Entendido"</string>
<string name="bubble_overflow_empty_title" msgid="2397251267073294968">"No hay burbujas recientes"</string>
<string name="bubble_overflow_empty_subtitle" msgid="2627417924958633713">"Las burbujas recientes y las que se descartaron aparecerán aquí"</string>
+ <!-- no translation found for bubble_bar_education_manage_title (6148404487810835924) -->
+ <skip />
+ <!-- no translation found for bubble_bar_education_manage_text (3199732148641842038) -->
+ <skip />
<string name="notification_bubble_title" msgid="6082910224488253378">"Cuadro"</string>
<string name="manage_bubbles_text" msgid="7730624269650594419">"Administrar"</string>
<string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"Se descartó el cuadro."</string>
- <string name="restart_button_description" msgid="6712141648865547958">"Presiona para reiniciar esta app y tener una mejor vista."</string>
+ <string name="restart_button_description" msgid="4564728020654658478">"Presiona para reiniciar esta app y tener una mejor vista"</string>
+ <string name="user_aspect_ratio_settings_button_hint" msgid="734835849600713016">"Cambiar la relación de aspecto de esta app en Configuración"</string>
+ <string name="user_aspect_ratio_settings_button_description" msgid="4315566801697411684">"Cambiar relación de aspecto"</string>
<string name="camera_compat_treatment_suggested_button_description" msgid="8103916969024076767">"¿Tienes problemas con la cámara?\nPresiona para reajustarla"</string>
<string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"¿No se resolvió?\nPresiona para revertir los cambios"</string>
<string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"¿No tienes problemas con la cámara? Presionar para descartar."</string>
diff --git a/libs/WindowManager/Shell/res/values-es/strings.xml b/libs/WindowManager/Shell/res/values-es/strings.xml
index 9c5e0c4..0e66c9b 100644
--- a/libs/WindowManager/Shell/res/values-es/strings.xml
+++ b/libs/WindowManager/Shell/res/values-es/strings.xml
@@ -76,10 +76,16 @@
<string name="bubbles_user_education_got_it" msgid="3382046149225428296">"Entendido"</string>
<string name="bubble_overflow_empty_title" msgid="2397251267073294968">"No hay burbujas recientes"</string>
<string name="bubble_overflow_empty_subtitle" msgid="2627417924958633713">"Las burbujas recientes y las cerradas aparecerán aquí"</string>
+ <!-- no translation found for bubble_bar_education_manage_title (6148404487810835924) -->
+ <skip />
+ <!-- no translation found for bubble_bar_education_manage_text (3199732148641842038) -->
+ <skip />
<string name="notification_bubble_title" msgid="6082910224488253378">"Burbuja"</string>
<string name="manage_bubbles_text" msgid="7730624269650594419">"Gestionar"</string>
<string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"Burbuja cerrada."</string>
- <string name="restart_button_description" msgid="6712141648865547958">"Toca para reiniciar esta aplicación y verlo mejor."</string>
+ <string name="restart_button_description" msgid="4564728020654658478">"Toca para reiniciar esta aplicación y obtener una mejor vista"</string>
+ <string name="user_aspect_ratio_settings_button_hint" msgid="734835849600713016">"Cambiar la relación de aspecto de esta aplicación en Ajustes"</string>
+ <string name="user_aspect_ratio_settings_button_description" msgid="4315566801697411684">"Cambiar relación de aspecto"</string>
<string name="camera_compat_treatment_suggested_button_description" msgid="8103916969024076767">"¿Problemas con la cámara?\nToca para reajustar"</string>
<string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"¿No se ha solucionado?\nToca para revertir"</string>
<string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"¿No hay problemas con la cámara? Toca para cerrar."</string>
diff --git a/libs/WindowManager/Shell/res/values-et/strings.xml b/libs/WindowManager/Shell/res/values-et/strings.xml
index fb23d11..201f336 100644
--- a/libs/WindowManager/Shell/res/values-et/strings.xml
+++ b/libs/WindowManager/Shell/res/values-et/strings.xml
@@ -76,10 +76,16 @@
<string name="bubbles_user_education_got_it" msgid="3382046149225428296">"Selge"</string>
<string name="bubble_overflow_empty_title" msgid="2397251267073294968">"Hiljutisi mulle pole"</string>
<string name="bubble_overflow_empty_subtitle" msgid="2627417924958633713">"Siin kuvatakse hiljutised ja suletud mullid."</string>
+ <!-- no translation found for bubble_bar_education_manage_title (6148404487810835924) -->
+ <skip />
+ <!-- no translation found for bubble_bar_education_manage_text (3199732148641842038) -->
+ <skip />
<string name="notification_bubble_title" msgid="6082910224488253378">"Mull"</string>
<string name="manage_bubbles_text" msgid="7730624269650594419">"Halda"</string>
<string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"Mullist loobuti."</string>
- <string name="restart_button_description" msgid="6712141648865547958">"Puudutage, et see rakendus parema vaate jaoks taaskäivitada."</string>
+ <string name="restart_button_description" msgid="4564728020654658478">"Puudutage, et see rakendus parema vaate jaoks taaskäivitada"</string>
+ <string name="user_aspect_ratio_settings_button_hint" msgid="734835849600713016">"Muutke selle rakenduse kuvasuhet jaotises Seaded"</string>
+ <string name="user_aspect_ratio_settings_button_description" msgid="4315566801697411684">"Muutke kuvasuhet"</string>
<string name="camera_compat_treatment_suggested_button_description" msgid="8103916969024076767">"Kas teil on kaameraprobleeme?\nPuudutage ümberpaigutamiseks."</string>
<string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"Kas probleemi ei lahendatud?\nPuudutage ennistamiseks."</string>
<string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"Kas kaameraprobleeme pole? Puudutage loobumiseks."</string>
diff --git a/libs/WindowManager/Shell/res/values-eu/strings.xml b/libs/WindowManager/Shell/res/values-eu/strings.xml
index de27a80..8443954 100644
--- a/libs/WindowManager/Shell/res/values-eu/strings.xml
+++ b/libs/WindowManager/Shell/res/values-eu/strings.xml
@@ -76,10 +76,16 @@
<string name="bubbles_user_education_got_it" msgid="3382046149225428296">"Ados"</string>
<string name="bubble_overflow_empty_title" msgid="2397251267073294968">"Ez dago azkenaldiko burbuilarik"</string>
<string name="bubble_overflow_empty_subtitle" msgid="2627417924958633713">"Azken burbuilak eta baztertutakoak agertuko dira hemen"</string>
+ <!-- no translation found for bubble_bar_education_manage_title (6148404487810835924) -->
+ <skip />
+ <!-- no translation found for bubble_bar_education_manage_text (3199732148641842038) -->
+ <skip />
<string name="notification_bubble_title" msgid="6082910224488253378">"Burbuila"</string>
<string name="manage_bubbles_text" msgid="7730624269650594419">"Kudeatu"</string>
<string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"Baztertu da globoa."</string>
- <string name="restart_button_description" msgid="6712141648865547958">"Hobeto ikusteko, sakatu hau aplikazioa berrabiarazteko."</string>
+ <string name="restart_button_description" msgid="4564728020654658478">"Hobeto ikusteko, sakatu hau, eta aplikazioa berrabiarazi egingo da"</string>
+ <string name="user_aspect_ratio_settings_button_hint" msgid="734835849600713016">"Aldatu aplikazioaren aspektu-erlazioa ezarpenetan"</string>
+ <string name="user_aspect_ratio_settings_button_description" msgid="4315566801697411684">"Aldatu aspektu-erlazioa"</string>
<string name="camera_compat_treatment_suggested_button_description" msgid="8103916969024076767">"Arazoak dauzkazu kamerarekin?\nBerriro doitzeko, sakatu hau."</string>
<string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"Ez al da konpondu?\nLeheneratzeko, sakatu hau."</string>
<string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"Ez daukazu arazorik kamerarekin? Baztertzeko, sakatu hau."</string>
diff --git a/libs/WindowManager/Shell/res/values-fa/strings.xml b/libs/WindowManager/Shell/res/values-fa/strings.xml
index edff47a..4f546e7 100644
--- a/libs/WindowManager/Shell/res/values-fa/strings.xml
+++ b/libs/WindowManager/Shell/res/values-fa/strings.xml
@@ -76,10 +76,16 @@
<string name="bubbles_user_education_got_it" msgid="3382046149225428296">"متوجهام"</string>
<string name="bubble_overflow_empty_title" msgid="2397251267073294968">"هیچ حبابک جدیدی وجود ندارد"</string>
<string name="bubble_overflow_empty_subtitle" msgid="2627417924958633713">"حبابکهای اخیر و حبابکهای ردشده اینجا ظاهر خواهند شد"</string>
+ <!-- no translation found for bubble_bar_education_manage_title (6148404487810835924) -->
+ <skip />
+ <!-- no translation found for bubble_bar_education_manage_text (3199732148641842038) -->
+ <skip />
<string name="notification_bubble_title" msgid="6082910224488253378">"حباب"</string>
<string name="manage_bubbles_text" msgid="7730624269650594419">"مدیریت"</string>
<string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"حبابک رد شد."</string>
- <string name="restart_button_description" msgid="6712141648865547958">"برای داشتن نمایی بهتر، ضربه بزنید تا این برنامه بازراهاندازی شود."</string>
+ <string name="restart_button_description" msgid="4564728020654658478">"برای داشتن نمایی بهتر، ضربه بزنید تا این برنامه بازراهاندازی شود"</string>
+ <string name="user_aspect_ratio_settings_button_hint" msgid="734835849600713016">"نسبت ابعادی این برنامه را در «تنظیمات» تغییر دهید"</string>
+ <string name="user_aspect_ratio_settings_button_description" msgid="4315566801697411684">"تغییر نسبت ابعادی"</string>
<string name="camera_compat_treatment_suggested_button_description" msgid="8103916969024076767">"دوربین مشکل دارد؟\nبرای تنظیم مجدد اندازه ضربه بزنید"</string>
<string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"مشکل برطرف نشد؟\nبرای برگرداندن ضربه بزنید"</string>
<string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"دوربین مشکلی ندارد؟ برای بستن ضربه بزنید."</string>
diff --git a/libs/WindowManager/Shell/res/values-fi/strings.xml b/libs/WindowManager/Shell/res/values-fi/strings.xml
index 92fa760..d8a18a0 100644
--- a/libs/WindowManager/Shell/res/values-fi/strings.xml
+++ b/libs/WindowManager/Shell/res/values-fi/strings.xml
@@ -76,10 +76,16 @@
<string name="bubbles_user_education_got_it" msgid="3382046149225428296">"Okei"</string>
<string name="bubble_overflow_empty_title" msgid="2397251267073294968">"Ei viimeaikaisia kuplia"</string>
<string name="bubble_overflow_empty_subtitle" msgid="2627417924958633713">"Viimeaikaiset ja äskettäin ohitetut kuplat näkyvät täällä"</string>
+ <!-- no translation found for bubble_bar_education_manage_title (6148404487810835924) -->
+ <skip />
+ <!-- no translation found for bubble_bar_education_manage_text (3199732148641842038) -->
+ <skip />
<string name="notification_bubble_title" msgid="6082910224488253378">"Kupla"</string>
<string name="manage_bubbles_text" msgid="7730624269650594419">"Ylläpidä"</string>
<string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"Kupla ohitettu."</string>
- <string name="restart_button_description" msgid="6712141648865547958">"Napauta, niin sovellus käynnistyy uudelleen paremmin näytölle sopivana."</string>
+ <string name="restart_button_description" msgid="4564728020654658478">"Napauta, niin sovellus käynnistyy uudelleen paremmin näytölle sopivana"</string>
+ <string name="user_aspect_ratio_settings_button_hint" msgid="734835849600713016">"Muuta tämän sovelluksen kuvasuhdetta Asetuksissa"</string>
+ <string name="user_aspect_ratio_settings_button_description" msgid="4315566801697411684">"Vaihda kuvasuhdetta"</string>
<string name="camera_compat_treatment_suggested_button_description" msgid="8103916969024076767">"Onko kameran kanssa ongelmia?\nKorjaa napauttamalla"</string>
<string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"Eikö ongelma ratkennut?\nKumoa napauttamalla"</string>
<string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"Ei ongelmia kameran kanssa? Hylkää napauttamalla."</string>
diff --git a/libs/WindowManager/Shell/res/values-fr-rCA/strings.xml b/libs/WindowManager/Shell/res/values-fr-rCA/strings.xml
index 6d19e55..b2077f9 100644
--- a/libs/WindowManager/Shell/res/values-fr-rCA/strings.xml
+++ b/libs/WindowManager/Shell/res/values-fr-rCA/strings.xml
@@ -76,10 +76,16 @@
<string name="bubbles_user_education_got_it" msgid="3382046149225428296">"OK"</string>
<string name="bubble_overflow_empty_title" msgid="2397251267073294968">"Aucune bulle récente"</string>
<string name="bubble_overflow_empty_subtitle" msgid="2627417924958633713">"Les bulles récentes et les bulles ignorées s\'afficheront ici"</string>
+ <!-- no translation found for bubble_bar_education_manage_title (6148404487810835924) -->
+ <skip />
+ <!-- no translation found for bubble_bar_education_manage_text (3199732148641842038) -->
+ <skip />
<string name="notification_bubble_title" msgid="6082910224488253378">"Bulle"</string>
<string name="manage_bubbles_text" msgid="7730624269650594419">"Gérer"</string>
<string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"Bulle ignorée."</string>
- <string name="restart_button_description" msgid="6712141648865547958">"Pour obtenir un meilleur affichage, touchez pour redémarrer cette application."</string>
+ <string name="restart_button_description" msgid="4564728020654658478">"Touchez pour redémarrer cette application afin d\'obtenir un meilleur affichage"</string>
+ <string name="user_aspect_ratio_settings_button_hint" msgid="734835849600713016">"Changer les proportions de cette application dans les paramètres"</string>
+ <string name="user_aspect_ratio_settings_button_description" msgid="4315566801697411684">"Modifier les proportions"</string>
<string name="camera_compat_treatment_suggested_button_description" msgid="8103916969024076767">"Problèmes d\'appareil photo?\nTouchez pour réajuster"</string>
<string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"Problème non résolu?\nTouchez pour rétablir"</string>
<string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"Aucun problème d\'appareil photo? Touchez pour ignorer."</string>
diff --git a/libs/WindowManager/Shell/res/values-fr/strings.xml b/libs/WindowManager/Shell/res/values-fr/strings.xml
index 5fb91f7..b1b8313 100644
--- a/libs/WindowManager/Shell/res/values-fr/strings.xml
+++ b/libs/WindowManager/Shell/res/values-fr/strings.xml
@@ -76,10 +76,16 @@
<string name="bubbles_user_education_got_it" msgid="3382046149225428296">"OK"</string>
<string name="bubble_overflow_empty_title" msgid="2397251267073294968">"Aucune bulle récente"</string>
<string name="bubble_overflow_empty_subtitle" msgid="2627417924958633713">"Les bulles récentes et ignorées s\'afficheront ici"</string>
+ <!-- no translation found for bubble_bar_education_manage_title (6148404487810835924) -->
+ <skip />
+ <!-- no translation found for bubble_bar_education_manage_text (3199732148641842038) -->
+ <skip />
<string name="notification_bubble_title" msgid="6082910224488253378">"Bulle"</string>
<string name="manage_bubbles_text" msgid="7730624269650594419">"Gérer"</string>
<string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"Bulle fermée."</string>
- <string name="restart_button_description" msgid="6712141648865547958">"Pour un meilleur affichage, appuyez pour redémarrer cette appli."</string>
+ <string name="restart_button_description" msgid="4564728020654658478">"Appuyez pour redémarrer cette appli et obtenir une meilleure vue."</string>
+ <string name="user_aspect_ratio_settings_button_hint" msgid="734835849600713016">"Modifiez le format de cette appli dans les Paramètres."</string>
+ <string name="user_aspect_ratio_settings_button_description" msgid="4315566801697411684">"Modifier le format"</string>
<string name="camera_compat_treatment_suggested_button_description" msgid="8103916969024076767">"Problèmes d\'appareil photo ?\nAppuyez pour réajuster"</string>
<string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"Problème non résolu ?\nAppuyez pour rétablir"</string>
<string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"Aucun problème d\'appareil photo ? Appuyez pour ignorer."</string>
diff --git a/libs/WindowManager/Shell/res/values-gl/strings.xml b/libs/WindowManager/Shell/res/values-gl/strings.xml
index c08cff8..fd90e31 100644
--- a/libs/WindowManager/Shell/res/values-gl/strings.xml
+++ b/libs/WindowManager/Shell/res/values-gl/strings.xml
@@ -76,10 +76,19 @@
<string name="bubbles_user_education_got_it" msgid="3382046149225428296">"Entendido"</string>
<string name="bubble_overflow_empty_title" msgid="2397251267073294968">"Non hai burbullas recentes"</string>
<string name="bubble_overflow_empty_subtitle" msgid="2627417924958633713">"As burbullas recentes e ignoradas aparecerán aquí."</string>
+ <!-- no translation found for bubble_bar_education_manage_title (6148404487810835924) -->
+ <skip />
+ <!-- no translation found for bubble_bar_education_manage_text (3199732148641842038) -->
+ <skip />
<string name="notification_bubble_title" msgid="6082910224488253378">"Burbulla"</string>
<string name="manage_bubbles_text" msgid="7730624269650594419">"Xestionar"</string>
<string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"Ignorouse a burbulla."</string>
- <string name="restart_button_description" msgid="6712141648865547958">"Toca o botón para reiniciar esta aplicación e gozar dunha mellor visualización."</string>
+ <!-- no translation found for restart_button_description (4564728020654658478) -->
+ <skip />
+ <!-- no translation found for user_aspect_ratio_settings_button_hint (734835849600713016) -->
+ <skip />
+ <!-- no translation found for user_aspect_ratio_settings_button_description (4315566801697411684) -->
+ <skip />
<string name="camera_compat_treatment_suggested_button_description" msgid="8103916969024076767">"Tes problemas coa cámara?\nToca para reaxustala"</string>
<string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"Non se solucionaron os problemas?\nToca para reverter o seu tratamento"</string>
<string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"Non hai problemas coa cámara? Tocar para ignorar."</string>
diff --git a/libs/WindowManager/Shell/res/values-gu/strings.xml b/libs/WindowManager/Shell/res/values-gu/strings.xml
index 2a52199..d7e34fb 100644
--- a/libs/WindowManager/Shell/res/values-gu/strings.xml
+++ b/libs/WindowManager/Shell/res/values-gu/strings.xml
@@ -76,10 +76,16 @@
<string name="bubbles_user_education_got_it" msgid="3382046149225428296">"સમજાઈ ગયું"</string>
<string name="bubble_overflow_empty_title" msgid="2397251267073294968">"તાજેતરના કોઈ બબલ નથી"</string>
<string name="bubble_overflow_empty_subtitle" msgid="2627417924958633713">"એકદમ નવા બબલ અને છોડી દીધેલા બબલ અહીં દેખાશે"</string>
+ <!-- no translation found for bubble_bar_education_manage_title (6148404487810835924) -->
+ <skip />
+ <!-- no translation found for bubble_bar_education_manage_text (3199732148641842038) -->
+ <skip />
<string name="notification_bubble_title" msgid="6082910224488253378">"બબલ"</string>
<string name="manage_bubbles_text" msgid="7730624269650594419">"મેનેજ કરો"</string>
<string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"બબલ છોડી દેવાયો."</string>
- <string name="restart_button_description" msgid="6712141648865547958">"વધુ સારા વ્યૂ માટે, આ ઍપને ફરી શરૂ કરવા ટૅપ કરો."</string>
+ <string name="restart_button_description" msgid="4564728020654658478">"વધુ સારા વ્યૂ માટે, આ ઍપને ફરી શરૂ કરવા ટૅપ કરો"</string>
+ <string name="user_aspect_ratio_settings_button_hint" msgid="734835849600713016">"સેટિંગમાં આ ઍપનો સાપેક્ષ ગુણોત્તર બદલો"</string>
+ <string name="user_aspect_ratio_settings_button_description" msgid="4315566801697411684">"સાપેક્ષ ગુણોત્તર બદલો"</string>
<string name="camera_compat_treatment_suggested_button_description" msgid="8103916969024076767">"કૅમેરામાં સમસ્યાઓ છે?\nફરીથી ફિટ કરવા માટે ટૅપ કરો"</string>
<string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"સુધારો નથી થયો?\nપહેલાંના પર પાછું ફેરવવા માટે ટૅપ કરો"</string>
<string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"કૅમેરામાં કોઈ સમસ્યા નથી? છોડી દેવા માટે ટૅપ કરો."</string>
diff --git a/libs/WindowManager/Shell/res/values-hi/strings.xml b/libs/WindowManager/Shell/res/values-hi/strings.xml
index b0b0e9c..679ea65 100644
--- a/libs/WindowManager/Shell/res/values-hi/strings.xml
+++ b/libs/WindowManager/Shell/res/values-hi/strings.xml
@@ -76,10 +76,19 @@
<string name="bubbles_user_education_got_it" msgid="3382046149225428296">"ठीक है"</string>
<string name="bubble_overflow_empty_title" msgid="2397251267073294968">"हाल ही के कोई बबल्स नहीं हैं"</string>
<string name="bubble_overflow_empty_subtitle" msgid="2627417924958633713">"हाल ही के बबल्स और हटाए गए बबल्स यहां दिखेंगे"</string>
+ <!-- no translation found for bubble_bar_education_manage_title (6148404487810835924) -->
+ <skip />
+ <!-- no translation found for bubble_bar_education_manage_text (3199732148641842038) -->
+ <skip />
<string name="notification_bubble_title" msgid="6082910224488253378">"बबल"</string>
<string name="manage_bubbles_text" msgid="7730624269650594419">"मैनेज करें"</string>
<string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"बबल खारिज किया गया."</string>
- <string name="restart_button_description" msgid="6712141648865547958">"टैप करके ऐप्लिकेशन को रीस्टार्ट करें और बेहतर व्यू पाएं."</string>
+ <!-- no translation found for restart_button_description (4564728020654658478) -->
+ <skip />
+ <!-- no translation found for user_aspect_ratio_settings_button_hint (734835849600713016) -->
+ <skip />
+ <!-- no translation found for user_aspect_ratio_settings_button_description (4315566801697411684) -->
+ <skip />
<string name="camera_compat_treatment_suggested_button_description" msgid="8103916969024076767">"क्या कैमरे से जुड़ी कोई समस्या है?\nफिर से फ़िट करने के लिए टैप करें"</string>
<string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"क्या समस्या ठीक नहीं हुई?\nपहले जैसा करने के लिए टैप करें"</string>
<string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"क्या कैमरे से जुड़ी कोई समस्या नहीं है? खारिज करने के लिए टैप करें."</string>
diff --git a/libs/WindowManager/Shell/res/values-hr/strings.xml b/libs/WindowManager/Shell/res/values-hr/strings.xml
index 08721f0..88aa1b2 100644
--- a/libs/WindowManager/Shell/res/values-hr/strings.xml
+++ b/libs/WindowManager/Shell/res/values-hr/strings.xml
@@ -76,10 +76,16 @@
<string name="bubbles_user_education_got_it" msgid="3382046149225428296">"Shvaćam"</string>
<string name="bubble_overflow_empty_title" msgid="2397251267073294968">"Nema nedavnih oblačića"</string>
<string name="bubble_overflow_empty_subtitle" msgid="2627417924958633713">"Ovdje će se prikazivati nedavni i odbačeni oblačići"</string>
+ <!-- no translation found for bubble_bar_education_manage_title (6148404487810835924) -->
+ <skip />
+ <!-- no translation found for bubble_bar_education_manage_text (3199732148641842038) -->
+ <skip />
<string name="notification_bubble_title" msgid="6082910224488253378">"Oblačić"</string>
<string name="manage_bubbles_text" msgid="7730624269650594419">"Upravljanje"</string>
<string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"Oblačić odbačen."</string>
- <string name="restart_button_description" msgid="6712141648865547958">"Dodirnite za ponovno pokretanje te aplikacije i bolji prikaz."</string>
+ <string name="restart_button_description" msgid="4564728020654658478">"Dodirnite da biste ponovo pokrenuli tu aplikaciju kako biste bolje vidjeli"</string>
+ <string name="user_aspect_ratio_settings_button_hint" msgid="734835849600713016">"Promijeni omjer slike ove aplikacije u postavkama"</string>
+ <string name="user_aspect_ratio_settings_button_description" msgid="4315566801697411684">"Promijeni omjer slike"</string>
<string name="camera_compat_treatment_suggested_button_description" msgid="8103916969024076767">"Problemi s fotoaparatom?\nDodirnite za popravak"</string>
<string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"Problem nije riješen?\nDodirnite za vraćanje"</string>
<string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"Nemate problema s fotoaparatom? Dodirnite za odbacivanje."</string>
diff --git a/libs/WindowManager/Shell/res/values-hu/strings.xml b/libs/WindowManager/Shell/res/values-hu/strings.xml
index 7566439..5a88bc4 100644
--- a/libs/WindowManager/Shell/res/values-hu/strings.xml
+++ b/libs/WindowManager/Shell/res/values-hu/strings.xml
@@ -76,10 +76,16 @@
<string name="bubbles_user_education_got_it" msgid="3382046149225428296">"Értem"</string>
<string name="bubble_overflow_empty_title" msgid="2397251267073294968">"Nincsenek buborékok a közelmúltból"</string>
<string name="bubble_overflow_empty_subtitle" msgid="2627417924958633713">"A legutóbbi és az elvetett buborékok itt jelennek majd meg"</string>
+ <!-- no translation found for bubble_bar_education_manage_title (6148404487810835924) -->
+ <skip />
+ <!-- no translation found for bubble_bar_education_manage_text (3199732148641842038) -->
+ <skip />
<string name="notification_bubble_title" msgid="6082910224488253378">"Buborék"</string>
<string name="manage_bubbles_text" msgid="7730624269650594419">"Kezelés"</string>
<string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"Buborék elvetve."</string>
- <string name="restart_button_description" msgid="6712141648865547958">"A jobb nézet érdekében koppintson az alkalmazás újraindításához."</string>
+ <string name="restart_button_description" msgid="4564728020654658478">"A jobb nézet érdekében koppintson az alkalmazás újraindításához."</string>
+ <string name="user_aspect_ratio_settings_button_hint" msgid="734835849600713016">"Az app méretarányát a Beállításokban módosíthatja"</string>
+ <string name="user_aspect_ratio_settings_button_description" msgid="4315566801697411684">"Méretarány módosítása"</string>
<string name="camera_compat_treatment_suggested_button_description" msgid="8103916969024076767">"Kamerával kapcsolatos problémába ütközött?\nKoppintson a megoldáshoz."</string>
<string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"Nem sikerült a hiba kijavítása?\nKoppintson a visszaállításhoz."</string>
<string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"Nincsenek problémái kamerával? Koppintson az elvetéshez."</string>
diff --git a/libs/WindowManager/Shell/res/values-hy/strings.xml b/libs/WindowManager/Shell/res/values-hy/strings.xml
index 2b20870..54b1213 100644
--- a/libs/WindowManager/Shell/res/values-hy/strings.xml
+++ b/libs/WindowManager/Shell/res/values-hy/strings.xml
@@ -76,10 +76,16 @@
<string name="bubbles_user_education_got_it" msgid="3382046149225428296">"Եղավ"</string>
<string name="bubble_overflow_empty_title" msgid="2397251267073294968">"Ամպիկներ չկան"</string>
<string name="bubble_overflow_empty_subtitle" msgid="2627417924958633713">"Այստեղ կցուցադրվեն վերջերս օգտագործված և փակված ամպիկները, որոնք կկարողանաք հեշտությամբ վերաբացել"</string>
+ <!-- no translation found for bubble_bar_education_manage_title (6148404487810835924) -->
+ <skip />
+ <!-- no translation found for bubble_bar_education_manage_text (3199732148641842038) -->
+ <skip />
<string name="notification_bubble_title" msgid="6082910224488253378">"Պղպջակ"</string>
<string name="manage_bubbles_text" msgid="7730624269650594419">"Կառավարել"</string>
<string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"Ամպիկը փակվեց։"</string>
- <string name="restart_button_description" msgid="6712141648865547958">"Հպեք՝ հավելվածը վերագործարկելու և ավելի հարմար տեսք ընտրելու համար։"</string>
+ <string name="restart_button_description" msgid="4564728020654658478">"Հպեք՝ հավելվածը վերագործարկելու և ավելի հարմար տեսք ընտրելու համար"</string>
+ <string name="user_aspect_ratio_settings_button_hint" msgid="734835849600713016">"Փոխել հավելվածի կողմերի հարաբերակցությունը Կարգավորումներում"</string>
+ <string name="user_aspect_ratio_settings_button_description" msgid="4315566801697411684">"Փոխել չափերի հարաբերակցությունը"</string>
<string name="camera_compat_treatment_suggested_button_description" msgid="8103916969024076767">"Տեսախցիկի հետ կապված խնդիրնե՞ր կան։\nՀպեք՝ վերակարգավորելու համար։"</string>
<string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"Չհաջողվե՞ց շտկել։\nՀպեք՝ փոփոխությունները չեղարկելու համար։"</string>
<string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"Տեսախցիկի հետ կապված խնդիրներ չկա՞ն։ Փակելու համար հպեք։"</string>
diff --git a/libs/WindowManager/Shell/res/values-in/strings.xml b/libs/WindowManager/Shell/res/values-in/strings.xml
index 3f6d9c55..32167b3 100644
--- a/libs/WindowManager/Shell/res/values-in/strings.xml
+++ b/libs/WindowManager/Shell/res/values-in/strings.xml
@@ -76,10 +76,16 @@
<string name="bubbles_user_education_got_it" msgid="3382046149225428296">"Oke"</string>
<string name="bubble_overflow_empty_title" msgid="2397251267073294968">"Tidak ada balon baru-baru ini"</string>
<string name="bubble_overflow_empty_subtitle" msgid="2627417924958633713">"Balon yang baru dipakai dan balon yang telah ditutup akan muncul di sini"</string>
+ <!-- no translation found for bubble_bar_education_manage_title (6148404487810835924) -->
+ <skip />
+ <!-- no translation found for bubble_bar_education_manage_text (3199732148641842038) -->
+ <skip />
<string name="notification_bubble_title" msgid="6082910224488253378">"Balon"</string>
<string name="manage_bubbles_text" msgid="7730624269650594419">"Kelola"</string>
<string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"Balon ditutup."</string>
- <string name="restart_button_description" msgid="6712141648865547958">"Ketuk untuk memulai ulang aplikasi ini agar mendapatkan tampilan yang lebih baik."</string>
+ <string name="restart_button_description" msgid="4564728020654658478">"Ketuk untuk memulai ulang aplikasi ini agar mendapatkan tampilan yang lebih baik"</string>
+ <string name="user_aspect_ratio_settings_button_hint" msgid="734835849600713016">"Ubah rasio aspek aplikasi ini di Setelan"</string>
+ <string name="user_aspect_ratio_settings_button_description" msgid="4315566801697411684">"Ubah rasio aspek"</string>
<string name="camera_compat_treatment_suggested_button_description" msgid="8103916969024076767">"Masalah kamera?\nKetuk untuk memperbaiki"</string>
<string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"Tidak dapat diperbaiki?\nKetuk untuk mengembalikan"</string>
<string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"Tidak ada masalah kamera? Ketuk untuk menutup."</string>
diff --git a/libs/WindowManager/Shell/res/values-is/strings.xml b/libs/WindowManager/Shell/res/values-is/strings.xml
index 20c16be..1304ae1 100644
--- a/libs/WindowManager/Shell/res/values-is/strings.xml
+++ b/libs/WindowManager/Shell/res/values-is/strings.xml
@@ -76,10 +76,16 @@
<string name="bubbles_user_education_got_it" msgid="3382046149225428296">"Ég skil"</string>
<string name="bubble_overflow_empty_title" msgid="2397251267073294968">"Engar nýlegar blöðrur"</string>
<string name="bubble_overflow_empty_subtitle" msgid="2627417924958633713">"Nýlegar blöðrur og blöðrur sem þú hefur lokað birtast hér"</string>
+ <!-- no translation found for bubble_bar_education_manage_title (6148404487810835924) -->
+ <skip />
+ <!-- no translation found for bubble_bar_education_manage_text (3199732148641842038) -->
+ <skip />
<string name="notification_bubble_title" msgid="6082910224488253378">"Blaðra"</string>
<string name="manage_bubbles_text" msgid="7730624269650594419">"Stjórna"</string>
<string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"Blöðru lokað."</string>
- <string name="restart_button_description" msgid="6712141648865547958">"Ýttu til að endurræsa forritið og fá betri sýn."</string>
+ <string name="restart_button_description" msgid="4564728020654658478">"Ýttu til að endurræsa forritið og fá betri sýn"</string>
+ <string name="user_aspect_ratio_settings_button_hint" msgid="734835849600713016">"Breyta myndhlutfalli þessa forrits í stillingunum"</string>
+ <string name="user_aspect_ratio_settings_button_description" msgid="4315566801697411684">"Breyta myndhlutfalli"</string>
<string name="camera_compat_treatment_suggested_button_description" msgid="8103916969024076767">"Myndavélavesen?\nÝttu til að breyta stærð"</string>
<string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"Ennþá vesen?\nÝttu til að afturkalla"</string>
<string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"Ekkert myndavélavesen? Ýttu til að hunsa."</string>
diff --git a/libs/WindowManager/Shell/res/values-it/strings.xml b/libs/WindowManager/Shell/res/values-it/strings.xml
index 025646c..dceac5c 100644
--- a/libs/WindowManager/Shell/res/values-it/strings.xml
+++ b/libs/WindowManager/Shell/res/values-it/strings.xml
@@ -76,10 +76,16 @@
<string name="bubbles_user_education_got_it" msgid="3382046149225428296">"OK"</string>
<string name="bubble_overflow_empty_title" msgid="2397251267073294968">"Nessuna bolla recente"</string>
<string name="bubble_overflow_empty_subtitle" msgid="2627417924958633713">"Le bolle recenti e ignorate appariranno qui"</string>
+ <!-- no translation found for bubble_bar_education_manage_title (6148404487810835924) -->
+ <skip />
+ <!-- no translation found for bubble_bar_education_manage_text (3199732148641842038) -->
+ <skip />
<string name="notification_bubble_title" msgid="6082910224488253378">"Fumetto"</string>
<string name="manage_bubbles_text" msgid="7730624269650594419">"Gestisci"</string>
<string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"Fumetto ignorato."</string>
- <string name="restart_button_description" msgid="6712141648865547958">"Tocca per riavviare quest\'app per una migliore visualizzazione."</string>
+ <string name="restart_button_description" msgid="4564728020654658478">"Tocca per riavviare l\'app e migliorare la visualizzazione"</string>
+ <string name="user_aspect_ratio_settings_button_hint" msgid="734835849600713016">"Cambia le proporzioni dell\'app nelle Impostazioni"</string>
+ <string name="user_aspect_ratio_settings_button_description" msgid="4315566801697411684">"Cambia proporzioni"</string>
<string name="camera_compat_treatment_suggested_button_description" msgid="8103916969024076767">"Problemi con la fotocamera?\nTocca per risolverli"</string>
<string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"Il problema non si è risolto?\nTocca per ripristinare"</string>
<string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"Nessun problema con la fotocamera? Tocca per ignorare."</string>
diff --git a/libs/WindowManager/Shell/res/values-iw/strings.xml b/libs/WindowManager/Shell/res/values-iw/strings.xml
index bb3845b..7cde568 100644
--- a/libs/WindowManager/Shell/res/values-iw/strings.xml
+++ b/libs/WindowManager/Shell/res/values-iw/strings.xml
@@ -76,10 +76,16 @@
<string name="bubbles_user_education_got_it" msgid="3382046149225428296">"הבנתי"</string>
<string name="bubble_overflow_empty_title" msgid="2397251267073294968">"אין בועות מהזמן האחרון"</string>
<string name="bubble_overflow_empty_subtitle" msgid="2627417924958633713">"בועות אחרונות ובועות שנסגרו יופיעו כאן"</string>
+ <!-- no translation found for bubble_bar_education_manage_title (6148404487810835924) -->
+ <skip />
+ <!-- no translation found for bubble_bar_education_manage_text (3199732148641842038) -->
+ <skip />
<string name="notification_bubble_title" msgid="6082910224488253378">"בועה"</string>
<string name="manage_bubbles_text" msgid="7730624269650594419">"ניהול"</string>
<string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"הבועה נסגרה."</string>
- <string name="restart_button_description" msgid="6712141648865547958">"כדי לראות טוב יותר יש להקיש ולהפעיל את האפליקציה הזו מחדש."</string>
+ <string name="restart_button_description" msgid="4564728020654658478">"כדי לראות טוב יותר יש להקיש ולהפעיל את האפליקציה הזו מחדש"</string>
+ <string name="user_aspect_ratio_settings_button_hint" msgid="734835849600713016">"אפשר לשנות את יחס הגובה-רוחב של האפליקציה הזו ב\'הגדרות\'"</string>
+ <string name="user_aspect_ratio_settings_button_description" msgid="4315566801697411684">"שינוי יחס גובה-רוחב"</string>
<string name="camera_compat_treatment_suggested_button_description" msgid="8103916969024076767">"בעיות במצלמה?\nאפשר להקיש כדי לבצע התאמה מחדש"</string>
<string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"הבעיה לא נפתרה?\nאפשר להקיש כדי לחזור לגרסה הקודמת"</string>
<string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"אין בעיות במצלמה? אפשר להקיש כדי לסגור."</string>
diff --git a/libs/WindowManager/Shell/res/values-ja/strings.xml b/libs/WindowManager/Shell/res/values-ja/strings.xml
index 8f2f6d8..3b3c4e4 100644
--- a/libs/WindowManager/Shell/res/values-ja/strings.xml
+++ b/libs/WindowManager/Shell/res/values-ja/strings.xml
@@ -76,10 +76,16 @@
<string name="bubbles_user_education_got_it" msgid="3382046149225428296">"OK"</string>
<string name="bubble_overflow_empty_title" msgid="2397251267073294968">"最近閉じたバブルはありません"</string>
<string name="bubble_overflow_empty_subtitle" msgid="2627417924958633713">"最近表示されたバブルや閉じたバブルが、ここに表示されます"</string>
+ <!-- no translation found for bubble_bar_education_manage_title (6148404487810835924) -->
+ <skip />
+ <!-- no translation found for bubble_bar_education_manage_text (3199732148641842038) -->
+ <skip />
<string name="notification_bubble_title" msgid="6082910224488253378">"バブル"</string>
<string name="manage_bubbles_text" msgid="7730624269650594419">"管理"</string>
<string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"ふきだしが非表示になっています。"</string>
- <string name="restart_button_description" msgid="6712141648865547958">"タップしてこのアプリを再起動すると、より見やすく表示されます。"</string>
+ <string name="restart_button_description" msgid="4564728020654658478">"タップしてこのアプリを再起動すると、表示が適切になります"</string>
+ <string name="user_aspect_ratio_settings_button_hint" msgid="734835849600713016">"このアプリのアスペクト比を [設定] で変更します"</string>
+ <string name="user_aspect_ratio_settings_button_description" msgid="4315566801697411684">"アスペクト比を変更"</string>
<string name="camera_compat_treatment_suggested_button_description" msgid="8103916969024076767">"カメラに関する問題の場合は、\nタップすると修正できます"</string>
<string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"修正されなかった場合は、\nタップすると元に戻ります"</string>
<string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"カメラに関する問題でない場合は、タップすると閉じます。"</string>
diff --git a/libs/WindowManager/Shell/res/values-ka/strings.xml b/libs/WindowManager/Shell/res/values-ka/strings.xml
index e58e67a..3c32e0e 100644
--- a/libs/WindowManager/Shell/res/values-ka/strings.xml
+++ b/libs/WindowManager/Shell/res/values-ka/strings.xml
@@ -76,10 +76,16 @@
<string name="bubbles_user_education_got_it" msgid="3382046149225428296">"გასაგებია"</string>
<string name="bubble_overflow_empty_title" msgid="2397251267073294968">"ბოლო დროს გამოყენებული ბუშტები არ არის"</string>
<string name="bubble_overflow_empty_subtitle" msgid="2627417924958633713">"აქ გამოჩნდება ბოლოდროინდელი ბუშტები და უარყოფილი ბუშტები"</string>
+ <!-- no translation found for bubble_bar_education_manage_title (6148404487810835924) -->
+ <skip />
+ <!-- no translation found for bubble_bar_education_manage_text (3199732148641842038) -->
+ <skip />
<string name="notification_bubble_title" msgid="6082910224488253378">"ბუშტი"</string>
<string name="manage_bubbles_text" msgid="7730624269650594419">"მართვა"</string>
<string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"ბუშტი დაიხურა."</string>
- <string name="restart_button_description" msgid="6712141648865547958">"შეეხეთ, რომ გადატვირთოთ ეს აპი უკეთესი ხედისთვის."</string>
+ <string name="restart_button_description" msgid="4564728020654658478">"შეხებით გადატვირთეთ ეს აპი უკეთესი ხედის მისაღებად"</string>
+ <string name="user_aspect_ratio_settings_button_hint" msgid="734835849600713016">"შეცვალეთ ამ აპის თანაფარდობა პარამეტრებიდან"</string>
+ <string name="user_aspect_ratio_settings_button_description" msgid="4315566801697411684">"თანაფარდობის შეცვლა"</string>
<string name="camera_compat_treatment_suggested_button_description" msgid="8103916969024076767">"კამერად პრობლემები აქვს?\nშეეხეთ გამოსასწორებლად"</string>
<string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"არ გამოსწორდა?\nშეეხეთ წინა ვერსიის დასაბრუნებლად"</string>
<string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"კამერას პრობლემები არ აქვს? შეეხეთ უარყოფისთვის."</string>
diff --git a/libs/WindowManager/Shell/res/values-kk/strings.xml b/libs/WindowManager/Shell/res/values-kk/strings.xml
index c40cd2f..ac5f4bf 100644
--- a/libs/WindowManager/Shell/res/values-kk/strings.xml
+++ b/libs/WindowManager/Shell/res/values-kk/strings.xml
@@ -76,10 +76,16 @@
<string name="bubbles_user_education_got_it" msgid="3382046149225428296">"Түсінікті"</string>
<string name="bubble_overflow_empty_title" msgid="2397251267073294968">"Жақындағы қалқыма хабарлар жоқ"</string>
<string name="bubble_overflow_empty_subtitle" msgid="2627417924958633713">"Соңғы және жабылған қалқыма хабарлар осы жерде көрсетіледі."</string>
+ <!-- no translation found for bubble_bar_education_manage_title (6148404487810835924) -->
+ <skip />
+ <!-- no translation found for bubble_bar_education_manage_text (3199732148641842038) -->
+ <skip />
<string name="notification_bubble_title" msgid="6082910224488253378">"Көпіршік"</string>
<string name="manage_bubbles_text" msgid="7730624269650594419">"Басқару"</string>
<string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"Қалқыма хабар жабылды."</string>
- <string name="restart_button_description" msgid="6712141648865547958">"Түртсеңіз, қолданба жабылып, ыңғайлы көрініспен қайта ашылады."</string>
+ <string name="restart_button_description" msgid="4564728020654658478">"Көріністі жақсарту үшін осы қолданбаны түртіп, қайта ашыңыз."</string>
+ <string name="user_aspect_ratio_settings_button_hint" msgid="734835849600713016">"Осы қолданбаның арақатынасын параметрлерден өзгертуге болады."</string>
+ <string name="user_aspect_ratio_settings_button_description" msgid="4315566801697411684">"Арақатынасты өзгерту"</string>
<string name="camera_compat_treatment_suggested_button_description" msgid="8103916969024076767">"Камерада қателер шықты ма?\nЖөндеу үшін түртіңіз."</string>
<string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"Жөнделмеді ме?\nҚайтару үшін түртіңіз."</string>
<string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"Камерада қателер шықпады ма? Жабу үшін түртіңіз."</string>
diff --git a/libs/WindowManager/Shell/res/values-km/strings.xml b/libs/WindowManager/Shell/res/values-km/strings.xml
index 4530267..adb229a 100644
--- a/libs/WindowManager/Shell/res/values-km/strings.xml
+++ b/libs/WindowManager/Shell/res/values-km/strings.xml
@@ -76,10 +76,16 @@
<string name="bubbles_user_education_got_it" msgid="3382046149225428296">"យល់ហើយ"</string>
<string name="bubble_overflow_empty_title" msgid="2397251267073294968">"មិនមានពពុះថ្មីៗទេ"</string>
<string name="bubble_overflow_empty_subtitle" msgid="2627417924958633713">"ពពុះថ្មីៗ និងពពុះដែលបានបិទនឹងបង្ហាញនៅទីនេះ"</string>
+ <!-- no translation found for bubble_bar_education_manage_title (6148404487810835924) -->
+ <skip />
+ <!-- no translation found for bubble_bar_education_manage_text (3199732148641842038) -->
+ <skip />
<string name="notification_bubble_title" msgid="6082910224488253378">"ពពុះ"</string>
<string name="manage_bubbles_text" msgid="7730624269650594419">"គ្រប់គ្រង"</string>
<string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"បានច្រានចោលសារលេចឡើង។"</string>
- <string name="restart_button_description" msgid="6712141648865547958">"ចុចដើម្បីចាប់ផ្ដើមកម្មវិធីនេះឡើងវិញសម្រាប់ទិដ្ឋភាពកាន់តែប្រសើរ។"</string>
+ <string name="restart_button_description" msgid="4564728020654658478">"ចុចដើម្បីចាប់ផ្ដើមកម្មវិធីនេះឡើងវិញសម្រាប់ទិដ្ឋភាពកាន់តែប្រសើរ"</string>
+ <string name="user_aspect_ratio_settings_button_hint" msgid="734835849600713016">"ផ្លាស់ប្ដូរសមាមាត្ររបស់កម្មវិធីនេះនៅក្នុងការកំណត់"</string>
+ <string name="user_aspect_ratio_settings_button_description" msgid="4315566801697411684">"ប្ដូរសមាមាត្រ"</string>
<string name="camera_compat_treatment_suggested_button_description" msgid="8103916969024076767">"មានបញ្ហាពាក់ព័ន្ធនឹងកាមេរ៉ាឬ?\nចុចដើម្បីដោះស្រាយ"</string>
<string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"មិនបានដោះស្រាយបញ្ហានេះទេឬ?\nចុចដើម្បីត្រឡប់"</string>
<string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"មិនមានបញ្ហាពាក់ព័ន្ធនឹងកាមេរ៉ាទេឬ? ចុចដើម្បីច្រានចោល។"</string>
diff --git a/libs/WindowManager/Shell/res/values-kn/strings.xml b/libs/WindowManager/Shell/res/values-kn/strings.xml
index 2dfbad4..33c50e7 100644
--- a/libs/WindowManager/Shell/res/values-kn/strings.xml
+++ b/libs/WindowManager/Shell/res/values-kn/strings.xml
@@ -76,10 +76,16 @@
<string name="bubbles_user_education_got_it" msgid="3382046149225428296">"ಅರ್ಥವಾಯಿತು"</string>
<string name="bubble_overflow_empty_title" msgid="2397251267073294968">"ಯಾವುದೇ ಇತ್ತೀಚಿನ ಬಬಲ್ಸ್ ಇಲ್ಲ"</string>
<string name="bubble_overflow_empty_subtitle" msgid="2627417924958633713">"ಇತ್ತೀಚಿನ ಬಬಲ್ಸ್ ಮತ್ತು ವಜಾಗೊಳಿಸಿದ ಬಬಲ್ಸ್ ಇಲ್ಲಿ ಗೋಚರಿಸುತ್ತವೆ"</string>
+ <!-- no translation found for bubble_bar_education_manage_title (6148404487810835924) -->
+ <skip />
+ <!-- no translation found for bubble_bar_education_manage_text (3199732148641842038) -->
+ <skip />
<string name="notification_bubble_title" msgid="6082910224488253378">"ಬಬಲ್"</string>
<string name="manage_bubbles_text" msgid="7730624269650594419">"ನಿರ್ವಹಿಸಿ"</string>
<string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"ಬಬಲ್ ವಜಾಗೊಳಿಸಲಾಗಿದೆ."</string>
- <string name="restart_button_description" msgid="6712141648865547958">"ಉತ್ತಮ ವೀಕ್ಷಣೆಗಾಗಿ ಈ ಆ್ಯಪ್ ಅನ್ನು ಮರುಪ್ರಾರಂಭಿಸಲು ಟ್ಯಾಪ್ ಮಾಡಿ."</string>
+ <string name="restart_button_description" msgid="4564728020654658478">"ಉತ್ತಮ ವೀಕ್ಷಣೆಗಾಗಿ ಈ ಆ್ಯಪ್ ಅನ್ನು ಮರುಪ್ರಾರಂಭಿಸಲು ಟ್ಯಾಪ್ ಮಾಡಿ"</string>
+ <string name="user_aspect_ratio_settings_button_hint" msgid="734835849600713016">"ಸೆಟ್ಟಿಂಗ್ಗಳಲ್ಲಿ ಈ ಆ್ಯಪ್ನ ದೃಶ್ಯಾನುಪಾತವನ್ನು ಬದಲಾಯಿಸಿ"</string>
+ <string name="user_aspect_ratio_settings_button_description" msgid="4315566801697411684">"ದೃಶ್ಯಾನುಪಾತವನ್ನು ಬದಲಾಯಿಸಿ"</string>
<string name="camera_compat_treatment_suggested_button_description" msgid="8103916969024076767">"ಕ್ಯಾಮರಾ ಸಮಸ್ಯೆಗಳಿವೆಯೇ?\nಮರುಹೊಂದಿಸಲು ಟ್ಯಾಪ್ ಮಾಡಿ"</string>
<string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"ಅದನ್ನು ಸರಿಪಡಿಸಲಿಲ್ಲವೇ?\nಹಿಂತಿರುಗಿಸಲು ಟ್ಯಾಪ್ ಮಾಡಿ"</string>
<string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"ಕ್ಯಾಮರಾ ಸಮಸ್ಯೆಗಳಿಲ್ಲವೇ? ವಜಾಗೊಳಿಸಲು ಟ್ಯಾಪ್ ಮಾಡಿ."</string>
diff --git a/libs/WindowManager/Shell/res/values-ko/strings.xml b/libs/WindowManager/Shell/res/values-ko/strings.xml
index 55697ca..dc76769 100644
--- a/libs/WindowManager/Shell/res/values-ko/strings.xml
+++ b/libs/WindowManager/Shell/res/values-ko/strings.xml
@@ -76,10 +76,16 @@
<string name="bubbles_user_education_got_it" msgid="3382046149225428296">"확인"</string>
<string name="bubble_overflow_empty_title" msgid="2397251267073294968">"최근 대화창 없음"</string>
<string name="bubble_overflow_empty_subtitle" msgid="2627417924958633713">"최근 대화창과 내가 닫은 대화창이 여기에 표시됩니다."</string>
+ <!-- no translation found for bubble_bar_education_manage_title (6148404487810835924) -->
+ <skip />
+ <!-- no translation found for bubble_bar_education_manage_text (3199732148641842038) -->
+ <skip />
<string name="notification_bubble_title" msgid="6082910224488253378">"버블"</string>
<string name="manage_bubbles_text" msgid="7730624269650594419">"관리"</string>
<string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"대화창을 닫았습니다."</string>
- <string name="restart_button_description" msgid="6712141648865547958">"더 편하게 보기를 원하면 탭하여 앱을 다시 시작하세요."</string>
+ <string name="restart_button_description" msgid="4564728020654658478">"탭하면 앱을 다시 시작하여 보기를 개선합니다."</string>
+ <string name="user_aspect_ratio_settings_button_hint" msgid="734835849600713016">"설정에서 앱의 가로세로 비율을 변경합니다."</string>
+ <string name="user_aspect_ratio_settings_button_description" msgid="4315566801697411684">"가로세로 비율 변경"</string>
<string name="camera_compat_treatment_suggested_button_description" msgid="8103916969024076767">"카메라 문제가 있나요?\n해결하려면 탭하세요."</string>
<string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"해결되지 않았나요?\n되돌리려면 탭하세요."</string>
<string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"카메라에 문제가 없나요? 닫으려면 탭하세요."</string>
diff --git a/libs/WindowManager/Shell/res/values-ky/strings.xml b/libs/WindowManager/Shell/res/values-ky/strings.xml
index 19df267..b1c0a67 100644
--- a/libs/WindowManager/Shell/res/values-ky/strings.xml
+++ b/libs/WindowManager/Shell/res/values-ky/strings.xml
@@ -76,10 +76,16 @@
<string name="bubbles_user_education_got_it" msgid="3382046149225428296">"Түшүндүм"</string>
<string name="bubble_overflow_empty_title" msgid="2397251267073294968">"Азырынча эч нерсе жок"</string>
<string name="bubble_overflow_empty_subtitle" msgid="2627417924958633713">"Акыркы жана жабылган калкып чыкма билдирмелер ушул жерде көрүнөт"</string>
+ <!-- no translation found for bubble_bar_education_manage_title (6148404487810835924) -->
+ <skip />
+ <!-- no translation found for bubble_bar_education_manage_text (3199732148641842038) -->
+ <skip />
<string name="notification_bubble_title" msgid="6082910224488253378">"Көбүк"</string>
<string name="manage_bubbles_text" msgid="7730624269650594419">"Башкаруу"</string>
<string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"Калкып чыкма билдирме жабылды."</string>
- <string name="restart_button_description" msgid="6712141648865547958">"Жакшыраак көрүү үчүн бул колдонмону өчүрүп күйгүзүңүз. Ал үчүн таптап коюңуз."</string>
+ <string name="restart_button_description" msgid="4564728020654658478">"Жакшыраак көрүү үчүн бул колдонмону өчүрүп күйгүзүңүз. Ал үчүн таптап коюңуз"</string>
+ <string name="user_aspect_ratio_settings_button_hint" msgid="734835849600713016">"Бул колдонмонун тараптарынын катнашын параметрлерден өзгөртүү"</string>
+ <string name="user_aspect_ratio_settings_button_description" msgid="4315566801697411684">"Тараптардын катнашын өзгөртүү"</string>
<string name="camera_compat_treatment_suggested_button_description" msgid="8103916969024076767">"Камерада маселелер келип чыктыбы?\nОңдоо үчүн таптаңыз"</string>
<string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"Оңдолгон жокпу?\nАртка кайтаруу үчүн таптаңыз"</string>
<string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"Камерада маселе жокпу? Этибарга албоо үчүн таптаңыз."</string>
diff --git a/libs/WindowManager/Shell/res/values-lo/strings.xml b/libs/WindowManager/Shell/res/values-lo/strings.xml
index a25699f..0b5da77 100644
--- a/libs/WindowManager/Shell/res/values-lo/strings.xml
+++ b/libs/WindowManager/Shell/res/values-lo/strings.xml
@@ -76,10 +76,16 @@
<string name="bubbles_user_education_got_it" msgid="3382046149225428296">"ເຂົ້າໃຈແລ້ວ"</string>
<string name="bubble_overflow_empty_title" msgid="2397251267073294968">"ບໍ່ມີຟອງຫຼ້າສຸດ"</string>
<string name="bubble_overflow_empty_subtitle" msgid="2627417924958633713">"ຟອງຫຼ້າສຸດ ແລະ ຟອງທີ່ປິດໄປຈະປາກົດຢູ່ບ່ອນນີ້"</string>
+ <!-- no translation found for bubble_bar_education_manage_title (6148404487810835924) -->
+ <skip />
+ <!-- no translation found for bubble_bar_education_manage_text (3199732148641842038) -->
+ <skip />
<string name="notification_bubble_title" msgid="6082910224488253378">"ຟອງ"</string>
<string name="manage_bubbles_text" msgid="7730624269650594419">"ຈັດການ"</string>
<string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"ປິດ Bubble ໄສ້ແລ້ວ."</string>
- <string name="restart_button_description" msgid="6712141648865547958">"ແຕະເພື່ອຣີສະຕາດແອັບນີ້ເພື່ອມຸມມອງທີ່ດີຂຶ້ນ."</string>
+ <string name="restart_button_description" msgid="4564728020654658478">"ແຕະເພື່ອຣີສະຕາດແອັບນີ້ເພື່ອມຸມມອງທີ່ດີຂຶ້ນ"</string>
+ <string name="user_aspect_ratio_settings_button_hint" msgid="734835849600713016">"ປ່ຽນອັດຕາສ່ວນຂອງແອັບນີ້ໃນການຕັ້ງຄ່າ"</string>
+ <string name="user_aspect_ratio_settings_button_description" msgid="4315566801697411684">"ປ່ຽນອັດຕາສ່ວນ"</string>
<string name="camera_compat_treatment_suggested_button_description" msgid="8103916969024076767">"ມີບັນຫາກ້ອງຖ່າຍຮູບບໍ?\nແຕະເພື່ອປັບໃໝ່"</string>
<string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"ບໍ່ໄດ້ແກ້ໄຂມັນບໍ?\nແຕະເພື່ອແປງກັບຄືນ"</string>
<string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"ບໍ່ມີບັນຫາກ້ອງຖ່າຍຮູບບໍ? ແຕະເພື່ອປິດໄວ້."</string>
diff --git a/libs/WindowManager/Shell/res/values-lt/strings.xml b/libs/WindowManager/Shell/res/values-lt/strings.xml
index d893fcf..ecec41a 100644
--- a/libs/WindowManager/Shell/res/values-lt/strings.xml
+++ b/libs/WindowManager/Shell/res/values-lt/strings.xml
@@ -76,10 +76,16 @@
<string name="bubbles_user_education_got_it" msgid="3382046149225428296">"Supratau"</string>
<string name="bubble_overflow_empty_title" msgid="2397251267073294968">"Nėra naujausių burbulų"</string>
<string name="bubble_overflow_empty_subtitle" msgid="2627417924958633713">"Naujausi ir atsisakyti burbulai bus rodomi čia"</string>
+ <!-- no translation found for bubble_bar_education_manage_title (6148404487810835924) -->
+ <skip />
+ <!-- no translation found for bubble_bar_education_manage_text (3199732148641842038) -->
+ <skip />
<string name="notification_bubble_title" msgid="6082910224488253378">"Debesėlis"</string>
<string name="manage_bubbles_text" msgid="7730624269650594419">"Tvarkyti"</string>
<string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"Debesėlio atsisakyta."</string>
- <string name="restart_button_description" msgid="6712141648865547958">"Palieskite, kad iš naujo paleistumėte šią programą ir matytumėte aiškiau."</string>
+ <string name="restart_button_description" msgid="4564728020654658478">"Palieskite, kad iš naujo paleistumėte šią programą ir matytumėte aiškiau"</string>
+ <string name="user_aspect_ratio_settings_button_hint" msgid="734835849600713016">"Pakeiskite šios programos kraštinių santykį"</string>
+ <string name="user_aspect_ratio_settings_button_description" msgid="4315566801697411684">"Keisti kraštinių santykį"</string>
<string name="camera_compat_treatment_suggested_button_description" msgid="8103916969024076767">"Iškilo problemų dėl kameros?\nPalieskite, kad pritaikytumėte iš naujo"</string>
<string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"Nepavyko pataisyti?\nPalieskite, kad grąžintumėte"</string>
<string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"Nėra jokių problemų dėl kameros? Palieskite, kad atsisakytumėte."</string>
diff --git a/libs/WindowManager/Shell/res/values-lv/strings.xml b/libs/WindowManager/Shell/res/values-lv/strings.xml
index a1fbcdd..d3a15bd 100644
--- a/libs/WindowManager/Shell/res/values-lv/strings.xml
+++ b/libs/WindowManager/Shell/res/values-lv/strings.xml
@@ -76,10 +76,16 @@
<string name="bubbles_user_education_got_it" msgid="3382046149225428296">"Labi"</string>
<string name="bubble_overflow_empty_title" msgid="2397251267073294968">"Nav nesen aizvērtu burbuļu"</string>
<string name="bubble_overflow_empty_subtitle" msgid="2627417924958633713">"Šeit būs redzami nesen rādītie burbuļi un aizvērtie burbuļi"</string>
+ <!-- no translation found for bubble_bar_education_manage_title (6148404487810835924) -->
+ <skip />
+ <!-- no translation found for bubble_bar_education_manage_text (3199732148641842038) -->
+ <skip />
<string name="notification_bubble_title" msgid="6082910224488253378">"Burbulis"</string>
<string name="manage_bubbles_text" msgid="7730624269650594419">"Pārvaldīt"</string>
<string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"Burbulis ir noraidīts."</string>
- <string name="restart_button_description" msgid="6712141648865547958">"Pieskarieties, lai restartētu šo lietotni un uzlabotu attēlojumu."</string>
+ <string name="restart_button_description" msgid="4564728020654658478">"Pieskarieties, lai restartētu šo lietotni un uzlabotu attēlojumu."</string>
+ <string name="user_aspect_ratio_settings_button_hint" msgid="734835849600713016">"Iestatījumos mainiet šīs lietotnes malu attiecību."</string>
+ <string name="user_aspect_ratio_settings_button_description" msgid="4315566801697411684">"Mainīt malu attiecību"</string>
<string name="camera_compat_treatment_suggested_button_description" msgid="8103916969024076767">"Vai ir problēmas ar kameru?\nPieskarieties, lai tās novērstu."</string>
<string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"Vai problēma netika novērsta?\nPieskarieties, lai atjaunotu."</string>
<string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"Vai nav problēmu ar kameru? Pieskarieties, lai nerādītu."</string>
diff --git a/libs/WindowManager/Shell/res/values-mk/strings.xml b/libs/WindowManager/Shell/res/values-mk/strings.xml
index 1567d61..008cff2 100644
--- a/libs/WindowManager/Shell/res/values-mk/strings.xml
+++ b/libs/WindowManager/Shell/res/values-mk/strings.xml
@@ -76,10 +76,16 @@
<string name="bubbles_user_education_got_it" msgid="3382046149225428296">"Сфатив"</string>
<string name="bubble_overflow_empty_title" msgid="2397251267073294968">"Нема неодамнешни балончиња"</string>
<string name="bubble_overflow_empty_subtitle" msgid="2627417924958633713">"Неодамнешните и отфрлените балончиња ќе се појавуваат тука"</string>
+ <!-- no translation found for bubble_bar_education_manage_title (6148404487810835924) -->
+ <skip />
+ <!-- no translation found for bubble_bar_education_manage_text (3199732148641842038) -->
+ <skip />
<string name="notification_bubble_title" msgid="6082910224488253378">"Балонче"</string>
<string name="manage_bubbles_text" msgid="7730624269650594419">"Управувајте"</string>
<string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"Балончето е отфрлено."</string>
- <string name="restart_button_description" msgid="6712141648865547958">"За подобар приказ, допрете за да ја рестартирате апликацијава."</string>
+ <string name="restart_button_description" msgid="4564728020654658478">"Допрете за да ја рестартирате апликацијава за подобар приказ"</string>
+ <string name="user_aspect_ratio_settings_button_hint" msgid="734835849600713016">"Промени го соодносот на апликацијава во „Поставки“"</string>
+ <string name="user_aspect_ratio_settings_button_description" msgid="4315566801697411684">"Променување на соодносот"</string>
<string name="camera_compat_treatment_suggested_button_description" msgid="8103916969024076767">"Проблеми со камерата?\nДопрете за да се совпадне повторно"</string>
<string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"Не се поправи?\nДопрете за враќање"</string>
<string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"Нема проблеми со камерата? Допрете за отфрлање."</string>
diff --git a/libs/WindowManager/Shell/res/values-ml/strings.xml b/libs/WindowManager/Shell/res/values-ml/strings.xml
index 5cca248..4e2f339 100644
--- a/libs/WindowManager/Shell/res/values-ml/strings.xml
+++ b/libs/WindowManager/Shell/res/values-ml/strings.xml
@@ -76,10 +76,16 @@
<string name="bubbles_user_education_got_it" msgid="3382046149225428296">"മനസ്സിലായി"</string>
<string name="bubble_overflow_empty_title" msgid="2397251267073294968">"അടുത്തിടെയുള്ള ബബിളുകൾ ഒന്നുമില്ല"</string>
<string name="bubble_overflow_empty_subtitle" msgid="2627417924958633713">"അടുത്തിടെയുള്ള ബബിളുകൾ, ഡിസ്മിസ് ചെയ്ത ബബിളുകൾ എന്നിവ ഇവിടെ ദൃശ്യമാവും"</string>
+ <!-- no translation found for bubble_bar_education_manage_title (6148404487810835924) -->
+ <skip />
+ <!-- no translation found for bubble_bar_education_manage_text (3199732148641842038) -->
+ <skip />
<string name="notification_bubble_title" msgid="6082910224488253378">"ബബിൾ"</string>
<string name="manage_bubbles_text" msgid="7730624269650594419">"മാനേജ് ചെയ്യുക"</string>
<string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"ബബിൾ ഡിസ്മിസ് ചെയ്തു."</string>
- <string name="restart_button_description" msgid="6712141648865547958">"മികച്ച കാഴ്ചയ്ക്കായി ഈ ആപ്പ് റീസ്റ്റാർട്ട് ചെയ്യാൻ ടാപ്പ് ചെയ്യുക."</string>
+ <string name="restart_button_description" msgid="4564728020654658478">"മികച്ച കാഴ്ചയ്ക്കായി ഈ ആപ്പ് റീസ്റ്റാർട്ട് ചെയ്യാൻ ടാപ്പ് ചെയ്യുക"</string>
+ <string name="user_aspect_ratio_settings_button_hint" msgid="734835849600713016">"ഈ ആപ്പിന്റെ വീക്ഷണ അനുപാതം, ക്രമീകരണത്തിൽ മാറ്റുക"</string>
+ <string name="user_aspect_ratio_settings_button_description" msgid="4315566801697411684">"വീക്ഷണ അനുപാതം മാറ്റുക"</string>
<string name="camera_compat_treatment_suggested_button_description" msgid="8103916969024076767">"ക്യാമറ പ്രശ്നങ്ങളുണ്ടോ?\nശരിയാക്കാൻ ടാപ്പ് ചെയ്യുക"</string>
<string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"അത് പരിഹരിച്ചില്ലേ?\nപുനഃസ്ഥാപിക്കാൻ ടാപ്പ് ചെയ്യുക"</string>
<string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"ക്യാമറാ പ്രശ്നങ്ങളൊന്നുമില്ലേ? നിരസിക്കാൻ ടാപ്പ് ചെയ്യുക."</string>
diff --git a/libs/WindowManager/Shell/res/values-mn/strings.xml b/libs/WindowManager/Shell/res/values-mn/strings.xml
index 72e54fc..c2081cf 100644
--- a/libs/WindowManager/Shell/res/values-mn/strings.xml
+++ b/libs/WindowManager/Shell/res/values-mn/strings.xml
@@ -76,10 +76,16 @@
<string name="bubbles_user_education_got_it" msgid="3382046149225428296">"Ойлголоо"</string>
<string name="bubble_overflow_empty_title" msgid="2397251267073294968">"Саяхны бөмбөлөг алга байна"</string>
<string name="bubble_overflow_empty_subtitle" msgid="2627417924958633713">"Саяхны бөмбөлгүүд болон үл хэрэгссэн бөмбөлгүүд энд харагдана"</string>
+ <!-- no translation found for bubble_bar_education_manage_title (6148404487810835924) -->
+ <skip />
+ <!-- no translation found for bubble_bar_education_manage_text (3199732148641842038) -->
+ <skip />
<string name="notification_bubble_title" msgid="6082910224488253378">"Бөмбөлөг"</string>
<string name="manage_bubbles_text" msgid="7730624269650594419">"Удирдах"</string>
<string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"Бөмбөлгийг үл хэрэгссэн."</string>
- <string name="restart_button_description" msgid="6712141648865547958">"Харагдах байдлыг сайжруулахын тулд энэ аппыг товшиж, дахин эхлүүлнэ үү."</string>
+ <string name="restart_button_description" msgid="4564728020654658478">"Харагдах байдлыг сайжруулахын тулд энэ аппыг товшиж, дахин эхлүүлнэ үү"</string>
+ <string name="user_aspect_ratio_settings_button_hint" msgid="734835849600713016">"Энэ аппын харьцааг Тохиргоонд өөрчилнө үү"</string>
+ <string name="user_aspect_ratio_settings_button_description" msgid="4315566801697411684">"Харьцааг өөрчлөх"</string>
<string name="camera_compat_treatment_suggested_button_description" msgid="8103916969024076767">"Камерын асуудал гарсан уу?\nДахин тааруулахын тулд товшино уу"</string>
<string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"Үүнийг засаагүй юу?\nБуцаахын тулд товшино уу"</string>
<string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"Камерын асуудал байхгүй юу? Хаахын тулд товшино уу."</string>
diff --git a/libs/WindowManager/Shell/res/values-mr/strings.xml b/libs/WindowManager/Shell/res/values-mr/strings.xml
index a9e6319a..f563ec6 100644
--- a/libs/WindowManager/Shell/res/values-mr/strings.xml
+++ b/libs/WindowManager/Shell/res/values-mr/strings.xml
@@ -76,10 +76,16 @@
<string name="bubbles_user_education_got_it" msgid="3382046149225428296">"समजले"</string>
<string name="bubble_overflow_empty_title" msgid="2397251267073294968">"अलीकडील कोणतेही बबल नाहीत"</string>
<string name="bubble_overflow_empty_subtitle" msgid="2627417924958633713">"अलीकडील बबल आणि डिसमिस केलेले बबल येथे दिसतील"</string>
+ <!-- no translation found for bubble_bar_education_manage_title (6148404487810835924) -->
+ <skip />
+ <!-- no translation found for bubble_bar_education_manage_text (3199732148641842038) -->
+ <skip />
<string name="notification_bubble_title" msgid="6082910224488253378">"बबल"</string>
<string name="manage_bubbles_text" msgid="7730624269650594419">"व्यवस्थापित करा"</string>
<string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"बबल डिसमिस केला."</string>
- <string name="restart_button_description" msgid="6712141648865547958">"अधिक चांगल्या व्ह्यूसाठी हे अॅप रीस्टार्ट करण्याकरिता टॅप करा."</string>
+ <string name="restart_button_description" msgid="4564728020654658478">"अधिक चांगल्या दृश्यासाठी हे अॅप रीस्टार्ट करण्याकरिता टॅप करा"</string>
+ <string name="user_aspect_ratio_settings_button_hint" msgid="734835849600713016">"सेटिंग्ज मध्ये या ॲपचा आस्पेक्ट रेशो बदला"</string>
+ <string name="user_aspect_ratio_settings_button_description" msgid="4315566801697411684">"आस्पेक्ट रेशो बदला"</string>
<string name="camera_compat_treatment_suggested_button_description" msgid="8103916969024076767">"कॅमेराशी संबंधित काही समस्या आहेत का?\nपुन्हा फिट करण्यासाठी टॅप करा"</string>
<string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"निराकरण झाले नाही?\nरिव्हर्ट करण्यासाठी कृपया टॅप करा"</string>
<string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"कॅमेराशी संबंधित कोणत्याही समस्या नाहीत का? डिसमिस करण्यासाठी टॅप करा."</string>
diff --git a/libs/WindowManager/Shell/res/values-ms/strings.xml b/libs/WindowManager/Shell/res/values-ms/strings.xml
index b475317..054d296 100644
--- a/libs/WindowManager/Shell/res/values-ms/strings.xml
+++ b/libs/WindowManager/Shell/res/values-ms/strings.xml
@@ -76,10 +76,16 @@
<string name="bubbles_user_education_got_it" msgid="3382046149225428296">"OK"</string>
<string name="bubble_overflow_empty_title" msgid="2397251267073294968">"Tiada gelembung terbaharu"</string>
<string name="bubble_overflow_empty_subtitle" msgid="2627417924958633713">"Gelembung baharu dan gelembung yang diketepikan akan dipaparkan di sini"</string>
+ <!-- no translation found for bubble_bar_education_manage_title (6148404487810835924) -->
+ <skip />
+ <!-- no translation found for bubble_bar_education_manage_text (3199732148641842038) -->
+ <skip />
<string name="notification_bubble_title" msgid="6082910224488253378">"Gelembung"</string>
<string name="manage_bubbles_text" msgid="7730624269650594419">"Urus"</string>
<string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"Gelembung diketepikan."</string>
- <string name="restart_button_description" msgid="6712141648865547958">"Ketik untuk memulakan semula apl ini untuk mendapatkan paparan yang lebih baik."</string>
+ <string name="restart_button_description" msgid="4564728020654658478">"Ketik untuk memulakan semula apl ini untuk mendapatkan paparan yang lebih baik"</string>
+ <string name="user_aspect_ratio_settings_button_hint" msgid="734835849600713016">"Tukar nisbah bidang apl ini dalam Tetapan"</string>
+ <string name="user_aspect_ratio_settings_button_description" msgid="4315566801697411684">"Tukar nisbah bidang"</string>
<string name="camera_compat_treatment_suggested_button_description" msgid="8103916969024076767">"Isu kamera?\nKetik untuk memuatkan semula"</string>
<string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"Isu tidak dibetulkan?\nKetik untuk kembali"</string>
<string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"Tiada isu kamera? Ketik untuk mengetepikan."</string>
diff --git a/libs/WindowManager/Shell/res/values-my/strings.xml b/libs/WindowManager/Shell/res/values-my/strings.xml
index cb6a1df..8af8bf4 100644
--- a/libs/WindowManager/Shell/res/values-my/strings.xml
+++ b/libs/WindowManager/Shell/res/values-my/strings.xml
@@ -76,10 +76,16 @@
<string name="bubbles_user_education_got_it" msgid="3382046149225428296">"နားလည်ပြီ"</string>
<string name="bubble_overflow_empty_title" msgid="2397251267073294968">"လတ်တလော ပူဖောင်းကွက်များ မရှိပါ"</string>
<string name="bubble_overflow_empty_subtitle" msgid="2627417924958633713">"လတ်တလော ပူဖောင်းကွက်များနှင့် ပိတ်လိုက်သော ပူဖောင်းကွက်များကို ဤနေရာတွင် မြင်ရပါမည်"</string>
+ <!-- no translation found for bubble_bar_education_manage_title (6148404487810835924) -->
+ <skip />
+ <!-- no translation found for bubble_bar_education_manage_text (3199732148641842038) -->
+ <skip />
<string name="notification_bubble_title" msgid="6082910224488253378">"ပူဖောင်းဖောက်သံ"</string>
<string name="manage_bubbles_text" msgid="7730624269650594419">"စီမံရန်"</string>
<string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"ပူဖောင်းကွက် ဖယ်လိုက်သည်။"</string>
- <string name="restart_button_description" msgid="6712141648865547958">"ပိုကောင်းသောမြင်ကွင်းအတွက် ဤအက်ပ်ပြန်စရန် တို့နိုင်သည်။"</string>
+ <string name="restart_button_description" msgid="4564728020654658478">"ပိုကောင်းသောမြင်ကွင်းအတွက် ဤအက်ပ်ပြန်စရန် တို့ပါ"</string>
+ <string name="user_aspect_ratio_settings_button_hint" msgid="734835849600713016">"ဆက်တင်များတွင် ဤအက်ပ်၏အချိုးအစားကို ပြောင်းရန်"</string>
+ <string name="user_aspect_ratio_settings_button_description" msgid="4315566801697411684">"အချိုးစား ပြောင်းရန်"</string>
<string name="camera_compat_treatment_suggested_button_description" msgid="8103916969024076767">"ကင်မရာပြဿနာလား။\nပြင်ဆင်ရန် တို့ပါ"</string>
<string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"ကောင်းမသွားဘူးလား။\nပြန်ပြောင်းရန် တို့ပါ"</string>
<string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"ကင်မရာပြဿနာ မရှိဘူးလား။ ပယ်ရန် တို့ပါ။"</string>
diff --git a/libs/WindowManager/Shell/res/values-nb/strings.xml b/libs/WindowManager/Shell/res/values-nb/strings.xml
index 6c80144..8ac3537 100644
--- a/libs/WindowManager/Shell/res/values-nb/strings.xml
+++ b/libs/WindowManager/Shell/res/values-nb/strings.xml
@@ -76,10 +76,16 @@
<string name="bubbles_user_education_got_it" msgid="3382046149225428296">"Greit"</string>
<string name="bubble_overflow_empty_title" msgid="2397251267073294968">"Ingen nylige bobler"</string>
<string name="bubble_overflow_empty_subtitle" msgid="2627417924958633713">"Nylige bobler og avviste bobler vises her"</string>
+ <!-- no translation found for bubble_bar_education_manage_title (6148404487810835924) -->
+ <skip />
+ <!-- no translation found for bubble_bar_education_manage_text (3199732148641842038) -->
+ <skip />
<string name="notification_bubble_title" msgid="6082910224488253378">"Boble"</string>
<string name="manage_bubbles_text" msgid="7730624269650594419">"Administrer"</string>
<string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"Boblen er avvist."</string>
- <string name="restart_button_description" msgid="6712141648865547958">"Trykk for å starte denne appen på nytt for bedre visning."</string>
+ <string name="restart_button_description" msgid="4564728020654658478">"Trykk for å starte denne appen på nytt og få en bedre visning"</string>
+ <string name="user_aspect_ratio_settings_button_hint" msgid="734835849600713016">"Endre høyde/bredde-forholdet for denne appen i innstillingene"</string>
+ <string name="user_aspect_ratio_settings_button_description" msgid="4315566801697411684">"Endre høyde/bredde-forholdet"</string>
<string name="camera_compat_treatment_suggested_button_description" msgid="8103916969024076767">"Har du kameraproblemer?\nTrykk for å tilpasse"</string>
<string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"Ble ikke problemet løst?\nTrykk for å gå tilbake"</string>
<string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"Har du ingen kameraproblemer? Trykk for å lukke."</string>
diff --git a/libs/WindowManager/Shell/res/values-ne/strings.xml b/libs/WindowManager/Shell/res/values-ne/strings.xml
index f9f5805..a46e356a 100644
--- a/libs/WindowManager/Shell/res/values-ne/strings.xml
+++ b/libs/WindowManager/Shell/res/values-ne/strings.xml
@@ -76,10 +76,16 @@
<string name="bubbles_user_education_got_it" msgid="3382046149225428296">"बुझेँ"</string>
<string name="bubble_overflow_empty_title" msgid="2397251267073294968">"हालैका बबलहरू छैनन्"</string>
<string name="bubble_overflow_empty_subtitle" msgid="2627417924958633713">"हालैका बबल र खारेज गरिएका बबलहरू यहाँ देखिने छन्"</string>
+ <!-- no translation found for bubble_bar_education_manage_title (6148404487810835924) -->
+ <skip />
+ <!-- no translation found for bubble_bar_education_manage_text (3199732148641842038) -->
+ <skip />
<string name="notification_bubble_title" msgid="6082910224488253378">"बबल"</string>
<string name="manage_bubbles_text" msgid="7730624269650594419">"व्यवस्थापन गर्नुहोस्"</string>
<string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"बबल हटाइयो।"</string>
- <string name="restart_button_description" msgid="6712141648865547958">"यो एप अझ राम्रो हेर्न मिल्ने बनाउनका लागि यसलाई रिस्टार्ट गर्न ट्याप गर्नुहोस्।"</string>
+ <string name="restart_button_description" msgid="4564728020654658478">"अझ राम्रो भ्यू प्राप्त गर्नका लागि यो एप रिस्टार्ट गर्न ट्याप गर्नुहोस्"</string>
+ <string name="user_aspect_ratio_settings_button_hint" msgid="734835849600713016">"सेटिङमा गई यो एपको एस्पेक्ट रेसियो परिवर्तन गर्नुहोस्"</string>
+ <string name="user_aspect_ratio_settings_button_description" msgid="4315566801697411684">"एस्पेक्ट रेसियो परिवर्तन गर्नुहोस्"</string>
<string name="camera_compat_treatment_suggested_button_description" msgid="8103916969024076767">"क्यामेरासम्बन्धी समस्या देखियो?\nसमस्या हल गर्न ट्याप गर्नुहोस्"</string>
<string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"समस्या हल भएन?\nपहिलेको जस्तै बनाउन ट्याप गर्नुहोस्"</string>
<string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"क्यामेरासम्बन्धी कुनै पनि समस्या छैन? खारेज गर्न ट्याप गर्नुहोस्।"</string>
diff --git a/libs/WindowManager/Shell/res/values-nl/strings.xml b/libs/WindowManager/Shell/res/values-nl/strings.xml
index 3064ccc..ed01315 100644
--- a/libs/WindowManager/Shell/res/values-nl/strings.xml
+++ b/libs/WindowManager/Shell/res/values-nl/strings.xml
@@ -76,10 +76,16 @@
<string name="bubbles_user_education_got_it" msgid="3382046149225428296">"OK"</string>
<string name="bubble_overflow_empty_title" msgid="2397251267073294968">"Geen recente bubbels"</string>
<string name="bubble_overflow_empty_subtitle" msgid="2627417924958633713">"Recente bubbels en gesloten bubbels zie je hier"</string>
+ <!-- no translation found for bubble_bar_education_manage_title (6148404487810835924) -->
+ <skip />
+ <!-- no translation found for bubble_bar_education_manage_text (3199732148641842038) -->
+ <skip />
<string name="notification_bubble_title" msgid="6082910224488253378">"Bubbel"</string>
<string name="manage_bubbles_text" msgid="7730624269650594419">"Beheren"</string>
<string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"Bubbel gesloten."</string>
- <string name="restart_button_description" msgid="6712141648865547958">"Tik om deze app opnieuw op te starten voor een betere weergave."</string>
+ <string name="restart_button_description" msgid="4564728020654658478">"Tik om deze app opnieuw op te starten voor een betere weergave"</string>
+ <string name="user_aspect_ratio_settings_button_hint" msgid="734835849600713016">"Wijzig de beeldverhouding van deze app in Instellingen"</string>
+ <string name="user_aspect_ratio_settings_button_description" msgid="4315566801697411684">"Beeldverhouding wijzigen"</string>
<string name="camera_compat_treatment_suggested_button_description" msgid="8103916969024076767">"Cameraproblemen?\nTik om opnieuw passend te maken."</string>
<string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"Is dit geen oplossing?\nTik om terug te zetten."</string>
<string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"Geen cameraproblemen? Tik om te sluiten."</string>
diff --git a/libs/WindowManager/Shell/res/values-or/strings.xml b/libs/WindowManager/Shell/res/values-or/strings.xml
index 267b8a3..8465907 100644
--- a/libs/WindowManager/Shell/res/values-or/strings.xml
+++ b/libs/WindowManager/Shell/res/values-or/strings.xml
@@ -76,10 +76,16 @@
<string name="bubbles_user_education_got_it" msgid="3382046149225428296">"ବୁଝିଗଲି"</string>
<string name="bubble_overflow_empty_title" msgid="2397251267073294968">"ବର୍ତ୍ତମାନ କୌଣସି ବବଲ୍ ନାହିଁ"</string>
<string name="bubble_overflow_empty_subtitle" msgid="2627417924958633713">"ବର୍ତ୍ତମାନର ଏବଂ ଖାରଜ କରାଯାଇଥିବା ବବଲଗୁଡ଼ିକ ଏଠାରେ ଦେଖାଯିବ"</string>
+ <!-- no translation found for bubble_bar_education_manage_title (6148404487810835924) -->
+ <skip />
+ <!-- no translation found for bubble_bar_education_manage_text (3199732148641842038) -->
+ <skip />
<string name="notification_bubble_title" msgid="6082910224488253378">"ବବଲ୍"</string>
<string name="manage_bubbles_text" msgid="7730624269650594419">"ପରିଚାଳନା କରନ୍ତୁ"</string>
<string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"ବବଲ୍ ଖାରଜ କରାଯାଇଛି।"</string>
- <string name="restart_button_description" msgid="6712141648865547958">"ଏକ ଆହୁରି ଭଲ ଭ୍ୟୁ ପାଇଁ ଏହି ଆପ ରିଷ୍ଟାର୍ଟ କରିବାକୁ ଟାପ କରନ୍ତୁ।"</string>
+ <string name="restart_button_description" msgid="4564728020654658478">"ଏକ ଆହୁରି ଭଲ ଭ୍ୟୁ ପାଇଁ ଏହି ଆପ ରିଷ୍ଟାର୍ଟ କରିବାକୁ ଟାପ କରନ୍ତୁ"</string>
+ <string name="user_aspect_ratio_settings_button_hint" msgid="734835849600713016">"ସେଟିଂସରେ ଏହି ଆପର ଚଉଡ଼ା ଓ ଉଚ୍ଚତାର ଅନୁପାତ ପରିବର୍ତ୍ତନ କରନ୍ତୁ"</string>
+ <string name="user_aspect_ratio_settings_button_description" msgid="4315566801697411684">"ଚଉଡ଼ା ଓ ଉଚ୍ଚତାର ଅନୁପାତ ପରିବର୍ତ୍ତନ କରନ୍ତୁ"</string>
<string name="camera_compat_treatment_suggested_button_description" msgid="8103916969024076767">"କ୍ୟାମେରାରେ ସମସ୍ୟା ଅଛି?\nପୁଣି ଫିଟ କରିବାକୁ ଟାପ କରନ୍ତୁ"</string>
<string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"ଏହାର ସମାଧାନ ହୋଇନାହିଁ?\nଫେରିଯିବା ପାଇଁ ଟାପ କରନ୍ତୁ"</string>
<string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"କ୍ୟାମେରାରେ କିଛି ସମସ୍ୟା ନାହିଁ? ଖାରଜ କରିବାକୁ ଟାପ କରନ୍ତୁ।"</string>
diff --git a/libs/WindowManager/Shell/res/values-pa/strings.xml b/libs/WindowManager/Shell/res/values-pa/strings.xml
index d9f7f34..243fec3 100644
--- a/libs/WindowManager/Shell/res/values-pa/strings.xml
+++ b/libs/WindowManager/Shell/res/values-pa/strings.xml
@@ -76,10 +76,16 @@
<string name="bubbles_user_education_got_it" msgid="3382046149225428296">"ਸਮਝ ਲਿਆ"</string>
<string name="bubble_overflow_empty_title" msgid="2397251267073294968">"ਕੋਈ ਹਾਲੀਆ ਬਬਲ ਨਹੀਂ"</string>
<string name="bubble_overflow_empty_subtitle" msgid="2627417924958633713">"ਹਾਲੀਆ ਬਬਲ ਅਤੇ ਖਾਰਜ ਕੀਤੇ ਬਬਲ ਇੱਥੇ ਦਿਸਣਗੇ"</string>
+ <!-- no translation found for bubble_bar_education_manage_title (6148404487810835924) -->
+ <skip />
+ <!-- no translation found for bubble_bar_education_manage_text (3199732148641842038) -->
+ <skip />
<string name="notification_bubble_title" msgid="6082910224488253378">"ਬੁਲਬੁਲਾ"</string>
<string name="manage_bubbles_text" msgid="7730624269650594419">"ਪ੍ਰਬੰਧਨ ਕਰੋ"</string>
<string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"ਬਬਲ ਨੂੰ ਖਾਰਜ ਕੀਤਾ ਗਿਆ।"</string>
- <string name="restart_button_description" msgid="6712141648865547958">"ਬਿਹਤਰ ਦ੍ਰਿਸ਼ ਲਈ ਇਸ ਐਪ ਨੂੰ ਮੁੜ-ਸ਼ੁਰੂ ਕਰਨ ਲਈ ਟੈਪ ਕਰੋ।"</string>
+ <string name="restart_button_description" msgid="4564728020654658478">"ਬਿਹਤਰ ਦ੍ਰਿਸ਼ ਵਾਸਤੇ ਇਸ ਐਪ ਨੂੰ ਮੁੜ-ਸ਼ੁਰੂ ਕਰਨ ਲਈ ਟੈਪ ਕਰੋ"</string>
+ <string name="user_aspect_ratio_settings_button_hint" msgid="734835849600713016">"ਸੈਟਿੰਗਾਂ ਵਿੱਚ ਜਾ ਕੇ ਇਸ ਐਪ ਦੇ ਆਕਾਰ ਅਨੁਪਾਤ ਨੂੰ ਬਦਲੋ"</string>
+ <string name="user_aspect_ratio_settings_button_description" msgid="4315566801697411684">"ਆਕਾਰ ਅਨੁਪਾਤ ਬਦਲੋ"</string>
<string name="camera_compat_treatment_suggested_button_description" msgid="8103916969024076767">"ਕੀ ਕੈਮਰੇ ਸੰਬੰਧੀ ਸਮੱਸਿਆਵਾਂ ਹਨ?\nਮੁੜ-ਫਿੱਟ ਕਰਨ ਲਈ ਟੈਪ ਕਰੋ"</string>
<string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"ਕੀ ਇਹ ਠੀਕ ਨਹੀਂ ਹੋਈ?\nਵਾਪਸ ਉਹੀ ਕਰਨ ਲਈ ਟੈਪ ਕਰੋ"</string>
<string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"ਕੀ ਕੈਮਰੇ ਸੰਬੰਧੀ ਕੋਈ ਸਮੱਸਿਆ ਨਹੀਂ ਹੈ? ਖਾਰਜ ਕਰਨ ਲਈ ਟੈਪ ਕਰੋ।"</string>
diff --git a/libs/WindowManager/Shell/res/values-pl/strings.xml b/libs/WindowManager/Shell/res/values-pl/strings.xml
index 0699f5d..34bc1a0 100644
--- a/libs/WindowManager/Shell/res/values-pl/strings.xml
+++ b/libs/WindowManager/Shell/res/values-pl/strings.xml
@@ -76,10 +76,16 @@
<string name="bubbles_user_education_got_it" msgid="3382046149225428296">"OK"</string>
<string name="bubble_overflow_empty_title" msgid="2397251267073294968">"Brak ostatnich dymków"</string>
<string name="bubble_overflow_empty_subtitle" msgid="2627417924958633713">"Tutaj będą pojawiać się ostatnie i odrzucone dymki"</string>
+ <!-- no translation found for bubble_bar_education_manage_title (6148404487810835924) -->
+ <skip />
+ <!-- no translation found for bubble_bar_education_manage_text (3199732148641842038) -->
+ <skip />
<string name="notification_bubble_title" msgid="6082910224488253378">"Dymek"</string>
<string name="manage_bubbles_text" msgid="7730624269650594419">"Zarządzaj"</string>
<string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"Zamknięto dymek"</string>
- <string name="restart_button_description" msgid="6712141648865547958">"Kliknij, aby zrestartować aplikację i zyskać lepszą widoczność."</string>
+ <string name="restart_button_description" msgid="4564728020654658478">"Kliknij w celu zrestartowania aplikacji, aby lepiej się wyświetlała."</string>
+ <string name="user_aspect_ratio_settings_button_hint" msgid="734835849600713016">"Zmień proporcje obrazu aplikacji w Ustawieniach"</string>
+ <string name="user_aspect_ratio_settings_button_description" msgid="4315566801697411684">"Zmień proporcje obrazu"</string>
<string name="camera_compat_treatment_suggested_button_description" msgid="8103916969024076767">"Problemy z aparatem?\nKliknij, aby dopasować"</string>
<string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"Naprawa się nie udała?\nKliknij, aby cofnąć"</string>
<string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"Brak problemów z aparatem? Kliknij, aby zamknąć"</string>
diff --git a/libs/WindowManager/Shell/res/values-pt-rBR/strings.xml b/libs/WindowManager/Shell/res/values-pt-rBR/strings.xml
index eea9be2..85f2fa4 100644
--- a/libs/WindowManager/Shell/res/values-pt-rBR/strings.xml
+++ b/libs/WindowManager/Shell/res/values-pt-rBR/strings.xml
@@ -76,10 +76,16 @@
<string name="bubbles_user_education_got_it" msgid="3382046149225428296">"Ok"</string>
<string name="bubble_overflow_empty_title" msgid="2397251267073294968">"Nenhum balão recente"</string>
<string name="bubble_overflow_empty_subtitle" msgid="2627417924958633713">"Os balões recentes e dispensados aparecerão aqui"</string>
+ <!-- no translation found for bubble_bar_education_manage_title (6148404487810835924) -->
+ <skip />
+ <!-- no translation found for bubble_bar_education_manage_text (3199732148641842038) -->
+ <skip />
<string name="notification_bubble_title" msgid="6082910224488253378">"Bolha"</string>
<string name="manage_bubbles_text" msgid="7730624269650594419">"Gerenciar"</string>
<string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"Balão dispensado."</string>
- <string name="restart_button_description" msgid="6712141648865547958">"Toque para reiniciar o app e atualizar a visualização."</string>
+ <string name="restart_button_description" msgid="4564728020654658478">"Toque para reiniciar o app e atualizar a visualização"</string>
+ <string name="user_aspect_ratio_settings_button_hint" msgid="734835849600713016">"Mude a proporção deste app nas Configurações"</string>
+ <string name="user_aspect_ratio_settings_button_description" msgid="4315566801697411684">"Mudar a proporção"</string>
<string name="camera_compat_treatment_suggested_button_description" msgid="8103916969024076767">"Problemas com a câmera?\nToque para ajustar o enquadramento"</string>
<string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"O problema não foi corrigido?\nToque para reverter"</string>
<string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"Não tem problemas com a câmera? Toque para dispensar."</string>
diff --git a/libs/WindowManager/Shell/res/values-pt-rPT/strings.xml b/libs/WindowManager/Shell/res/values-pt-rPT/strings.xml
index a3d6ce5..083e2e7 100644
--- a/libs/WindowManager/Shell/res/values-pt-rPT/strings.xml
+++ b/libs/WindowManager/Shell/res/values-pt-rPT/strings.xml
@@ -76,10 +76,16 @@
<string name="bubbles_user_education_got_it" msgid="3382046149225428296">"OK"</string>
<string name="bubble_overflow_empty_title" msgid="2397251267073294968">"Nenhum balão recente"</string>
<string name="bubble_overflow_empty_subtitle" msgid="2627417924958633713">"Os balões recentes e ignorados vão aparecer aqui."</string>
+ <!-- no translation found for bubble_bar_education_manage_title (6148404487810835924) -->
+ <skip />
+ <!-- no translation found for bubble_bar_education_manage_text (3199732148641842038) -->
+ <skip />
<string name="notification_bubble_title" msgid="6082910224488253378">"Balão"</string>
<string name="manage_bubbles_text" msgid="7730624269650594419">"Gerir"</string>
<string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"Balão ignorado."</string>
- <string name="restart_button_description" msgid="6712141648865547958">"Toque para reiniciar esta app e ver melhor."</string>
+ <string name="restart_button_description" msgid="4564728020654658478">"Toque para reiniciar esta app e ficar com uma melhor visão"</string>
+ <string name="user_aspect_ratio_settings_button_hint" msgid="734835849600713016">"Altere o formato desta app nas Definições"</string>
+ <string name="user_aspect_ratio_settings_button_description" msgid="4315566801697411684">"Altere o formato"</string>
<string name="camera_compat_treatment_suggested_button_description" msgid="8103916969024076767">"Problemas com a câmara?\nToque aqui para reajustar"</string>
<string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"Não foi corrigido?\nToque para reverter"</string>
<string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"Nenhum problema com a câmara? Toque para ignorar."</string>
diff --git a/libs/WindowManager/Shell/res/values-pt/strings.xml b/libs/WindowManager/Shell/res/values-pt/strings.xml
index eea9be2..85f2fa4 100644
--- a/libs/WindowManager/Shell/res/values-pt/strings.xml
+++ b/libs/WindowManager/Shell/res/values-pt/strings.xml
@@ -76,10 +76,16 @@
<string name="bubbles_user_education_got_it" msgid="3382046149225428296">"Ok"</string>
<string name="bubble_overflow_empty_title" msgid="2397251267073294968">"Nenhum balão recente"</string>
<string name="bubble_overflow_empty_subtitle" msgid="2627417924958633713">"Os balões recentes e dispensados aparecerão aqui"</string>
+ <!-- no translation found for bubble_bar_education_manage_title (6148404487810835924) -->
+ <skip />
+ <!-- no translation found for bubble_bar_education_manage_text (3199732148641842038) -->
+ <skip />
<string name="notification_bubble_title" msgid="6082910224488253378">"Bolha"</string>
<string name="manage_bubbles_text" msgid="7730624269650594419">"Gerenciar"</string>
<string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"Balão dispensado."</string>
- <string name="restart_button_description" msgid="6712141648865547958">"Toque para reiniciar o app e atualizar a visualização."</string>
+ <string name="restart_button_description" msgid="4564728020654658478">"Toque para reiniciar o app e atualizar a visualização"</string>
+ <string name="user_aspect_ratio_settings_button_hint" msgid="734835849600713016">"Mude a proporção deste app nas Configurações"</string>
+ <string name="user_aspect_ratio_settings_button_description" msgid="4315566801697411684">"Mudar a proporção"</string>
<string name="camera_compat_treatment_suggested_button_description" msgid="8103916969024076767">"Problemas com a câmera?\nToque para ajustar o enquadramento"</string>
<string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"O problema não foi corrigido?\nToque para reverter"</string>
<string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"Não tem problemas com a câmera? Toque para dispensar."</string>
diff --git a/libs/WindowManager/Shell/res/values-ro/strings.xml b/libs/WindowManager/Shell/res/values-ro/strings.xml
index 58ad60a..6e8d116 100644
--- a/libs/WindowManager/Shell/res/values-ro/strings.xml
+++ b/libs/WindowManager/Shell/res/values-ro/strings.xml
@@ -76,10 +76,16 @@
<string name="bubbles_user_education_got_it" msgid="3382046149225428296">"OK"</string>
<string name="bubble_overflow_empty_title" msgid="2397251267073294968">"Nu există baloane recente"</string>
<string name="bubble_overflow_empty_subtitle" msgid="2627417924958633713">"Baloanele recente și baloanele respinse vor apărea aici"</string>
+ <!-- no translation found for bubble_bar_education_manage_title (6148404487810835924) -->
+ <skip />
+ <!-- no translation found for bubble_bar_education_manage_text (3199732148641842038) -->
+ <skip />
<string name="notification_bubble_title" msgid="6082910224488253378">"Balon"</string>
<string name="manage_bubbles_text" msgid="7730624269650594419">"Gestionează"</string>
<string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"Balonul a fost respins."</string>
- <string name="restart_button_description" msgid="6712141648865547958">"Atinge ca să repornești aplicația pentru o afișare mai bună."</string>
+ <string name="restart_button_description" msgid="4564728020654658478">"Atinge ca să repornești aplicația pentru o vizualizare mai bună"</string>
+ <string name="user_aspect_ratio_settings_button_hint" msgid="734835849600713016">"Schimbă raportul de dimensiuni al aplicației din Setări"</string>
+ <string name="user_aspect_ratio_settings_button_description" msgid="4315566801697411684">"Schimbă raportul de dimensiuni"</string>
<string name="camera_compat_treatment_suggested_button_description" msgid="8103916969024076767">"Ai probleme cu camera foto?\nAtinge pentru a reîncadra"</string>
<string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"Nu ai remediat problema?\nAtinge pentru a reveni"</string>
<string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"Nu ai probleme cu camera foto? Atinge pentru a închide."</string>
diff --git a/libs/WindowManager/Shell/res/values-ru/strings.xml b/libs/WindowManager/Shell/res/values-ru/strings.xml
index a7db44d..c3cd959 100644
--- a/libs/WindowManager/Shell/res/values-ru/strings.xml
+++ b/libs/WindowManager/Shell/res/values-ru/strings.xml
@@ -76,10 +76,16 @@
<string name="bubbles_user_education_got_it" msgid="3382046149225428296">"ОК"</string>
<string name="bubble_overflow_empty_title" msgid="2397251267073294968">"Нет недавних всплывающих чатов"</string>
<string name="bubble_overflow_empty_subtitle" msgid="2627417924958633713">"Здесь будут появляться недавние и скрытые всплывающие чаты."</string>
+ <!-- no translation found for bubble_bar_education_manage_title (6148404487810835924) -->
+ <skip />
+ <!-- no translation found for bubble_bar_education_manage_text (3199732148641842038) -->
+ <skip />
<string name="notification_bubble_title" msgid="6082910224488253378">"Всплывающая подсказка"</string>
<string name="manage_bubbles_text" msgid="7730624269650594419">"Настроить"</string>
<string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"Всплывающий чат закрыт."</string>
- <string name="restart_button_description" msgid="6712141648865547958">"Нажмите, чтобы перезапустить приложение и настроить удобный для просмотра вид"</string>
+ <string name="restart_button_description" msgid="4564728020654658478">"Нажмите, чтобы перезапустить приложение и оптимизировать размер"</string>
+ <string name="user_aspect_ratio_settings_button_hint" msgid="734835849600713016">"Изменить соотношение сторон приложения в настройках"</string>
+ <string name="user_aspect_ratio_settings_button_description" msgid="4315566801697411684">"Изменить соотношение сторон"</string>
<string name="camera_compat_treatment_suggested_button_description" msgid="8103916969024076767">"Проблемы с камерой?\nНажмите, чтобы исправить."</string>
<string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"Не помогло?\nНажмите, чтобы отменить изменения."</string>
<string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"Нет проблем с камерой? Нажмите, чтобы закрыть."</string>
diff --git a/libs/WindowManager/Shell/res/values-si/strings.xml b/libs/WindowManager/Shell/res/values-si/strings.xml
index 4153ce2..a1e246a 100644
--- a/libs/WindowManager/Shell/res/values-si/strings.xml
+++ b/libs/WindowManager/Shell/res/values-si/strings.xml
@@ -76,10 +76,16 @@
<string name="bubbles_user_education_got_it" msgid="3382046149225428296">"තේරුණා"</string>
<string name="bubble_overflow_empty_title" msgid="2397251267073294968">"මෑත බුබුලු නැත"</string>
<string name="bubble_overflow_empty_subtitle" msgid="2627417924958633713">"මෑත බුබුලු සහ ඉවත ලූ බුබුලු මෙහි දිස් වනු ඇත"</string>
+ <!-- no translation found for bubble_bar_education_manage_title (6148404487810835924) -->
+ <skip />
+ <!-- no translation found for bubble_bar_education_manage_text (3199732148641842038) -->
+ <skip />
<string name="notification_bubble_title" msgid="6082910224488253378">"බුබුළු"</string>
<string name="manage_bubbles_text" msgid="7730624269650594419">"කළමනා කරන්න"</string>
<string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"බුබුල ඉවත දමා ඇත."</string>
- <string name="restart_button_description" msgid="6712141648865547958">"වඩා හොඳ දසුනක් ලබා ගැනීම සඳහා මෙම යෙදුම යළි ඇරඹීමට තට්ටු කරන්න."</string>
+ <string name="restart_button_description" msgid="4564728020654658478">"වඩා හොඳ දසුනක් සඳහා මෙම යෙදුම යළි ඇරඹීමට තට්ටු කරන්න"</string>
+ <string name="user_aspect_ratio_settings_button_hint" msgid="734835849600713016">"සැකසීම් තුළ මෙම යෙදුමේ දර්ශන අනුපාතය වෙනස් කරන්න"</string>
+ <string name="user_aspect_ratio_settings_button_description" msgid="4315566801697411684">"දර්ශන අනුපාතය වෙනස් කරන්න"</string>
<string name="camera_compat_treatment_suggested_button_description" msgid="8103916969024076767">"කැමරා ගැටලුද?\nයළි සවි කිරීමට තට්ටු කරන්න"</string>
<string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"එය විසඳුවේ නැතිද?\nප්රතිවර්තනය කිරීමට තට්ටු කරන්න"</string>
<string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"කැමරා ගැටලු නොමැතිද? ඉවත දැමීමට තට්ටු කරන්න"</string>
diff --git a/libs/WindowManager/Shell/res/values-sk/strings.xml b/libs/WindowManager/Shell/res/values-sk/strings.xml
index 4e38943..c27425d 100644
--- a/libs/WindowManager/Shell/res/values-sk/strings.xml
+++ b/libs/WindowManager/Shell/res/values-sk/strings.xml
@@ -76,10 +76,16 @@
<string name="bubbles_user_education_got_it" msgid="3382046149225428296">"Dobre"</string>
<string name="bubble_overflow_empty_title" msgid="2397251267073294968">"Žiadne nedávne bubliny"</string>
<string name="bubble_overflow_empty_subtitle" msgid="2627417924958633713">"Tu sa budú zobrazovať nedávne a zavreté bubliny"</string>
+ <!-- no translation found for bubble_bar_education_manage_title (6148404487810835924) -->
+ <skip />
+ <!-- no translation found for bubble_bar_education_manage_text (3199732148641842038) -->
+ <skip />
<string name="notification_bubble_title" msgid="6082910224488253378">"Bublina"</string>
<string name="manage_bubbles_text" msgid="7730624269650594419">"Spravovať"</string>
<string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"Bublina bola zavretá."</string>
- <string name="restart_button_description" msgid="6712141648865547958">"Ak chcete zlepšiť zobrazenie, klepnutím túto aplikáciu reštartujte."</string>
+ <string name="restart_button_description" msgid="4564728020654658478">"Ak chcete zlepšiť zobrazenie, klepnutím túto aplikáciu reštartujte"</string>
+ <string name="user_aspect_ratio_settings_button_hint" msgid="734835849600713016">"Zmeniť pomer strán tejto aplikácie v Nastaveniach"</string>
+ <string name="user_aspect_ratio_settings_button_description" msgid="4315566801697411684">"Zmeniť pomer strán"</string>
<string name="camera_compat_treatment_suggested_button_description" msgid="8103916969024076767">"Problémy s kamerou?\nKlepnutím znova upravte."</string>
<string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"Nevyriešilo sa to?\nKlepnutím sa vráťte."</string>
<string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"Nemáte problémy s kamerou? Klepnutím zatvoríte."</string>
diff --git a/libs/WindowManager/Shell/res/values-sl/strings.xml b/libs/WindowManager/Shell/res/values-sl/strings.xml
index b0e67a7..e3dcca8 100644
--- a/libs/WindowManager/Shell/res/values-sl/strings.xml
+++ b/libs/WindowManager/Shell/res/values-sl/strings.xml
@@ -76,10 +76,16 @@
<string name="bubbles_user_education_got_it" msgid="3382046149225428296">"V redu"</string>
<string name="bubble_overflow_empty_title" msgid="2397251267073294968">"Ni nedavnih oblačkov"</string>
<string name="bubble_overflow_empty_subtitle" msgid="2627417924958633713">"Tukaj bodo prikazani tako nedavni kot tudi opuščeni oblački"</string>
+ <!-- no translation found for bubble_bar_education_manage_title (6148404487810835924) -->
+ <skip />
+ <!-- no translation found for bubble_bar_education_manage_text (3199732148641842038) -->
+ <skip />
<string name="notification_bubble_title" msgid="6082910224488253378">"Mehurček"</string>
<string name="manage_bubbles_text" msgid="7730624269650594419">"Upravljanje"</string>
<string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"Oblaček je bil opuščen."</string>
- <string name="restart_button_description" msgid="6712141648865547958">"Če želite boljši prikaz, se dotaknite za vnovični zagon te aplikacije."</string>
+ <string name="restart_button_description" msgid="4564728020654658478">"Če želite boljši prikaz, se dotaknite za vnovični zagon te aplikacije."</string>
+ <string name="user_aspect_ratio_settings_button_hint" msgid="734835849600713016">"Razmerje stranic te aplikacije spremenite v nastavitvah."</string>
+ <string name="user_aspect_ratio_settings_button_description" msgid="4315566801697411684">"Sprememba razmerja stranic"</string>
<string name="camera_compat_treatment_suggested_button_description" msgid="8103916969024076767">"Težave s fotoaparatom?\nDotaknite se za vnovično prilagoditev"</string>
<string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"To ni odpravilo težave?\nDotaknite se za povrnitev"</string>
<string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"Nimate težav s fotoaparatom? Dotaknite se za opustitev."</string>
diff --git a/libs/WindowManager/Shell/res/values-sq/strings.xml b/libs/WindowManager/Shell/res/values-sq/strings.xml
index 29bfb92..b5b2d18 100644
--- a/libs/WindowManager/Shell/res/values-sq/strings.xml
+++ b/libs/WindowManager/Shell/res/values-sq/strings.xml
@@ -76,10 +76,16 @@
<string name="bubbles_user_education_got_it" msgid="3382046149225428296">"E kuptova"</string>
<string name="bubble_overflow_empty_title" msgid="2397251267073294968">"Nuk ka flluska të fundit"</string>
<string name="bubble_overflow_empty_subtitle" msgid="2627417924958633713">"Flluskat e fundit dhe flluskat e hequra do të shfaqen këtu"</string>
+ <!-- no translation found for bubble_bar_education_manage_title (6148404487810835924) -->
+ <skip />
+ <!-- no translation found for bubble_bar_education_manage_text (3199732148641842038) -->
+ <skip />
<string name="notification_bubble_title" msgid="6082910224488253378">"Flluskë"</string>
<string name="manage_bubbles_text" msgid="7730624269650594419">"Menaxho"</string>
<string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"Flluska u hoq."</string>
- <string name="restart_button_description" msgid="6712141648865547958">"Trokit për të rifilluar këtë aplikacion për një pamje më të mirë."</string>
+ <string name="restart_button_description" msgid="4564728020654658478">"Trokit për ta rinisur këtë aplikacion për një pamje më të mirë"</string>
+ <string name="user_aspect_ratio_settings_button_hint" msgid="734835849600713016">"Ndrysho raportin e pamjes së këtij aplikacioni te \"Cilësimet\""</string>
+ <string name="user_aspect_ratio_settings_button_description" msgid="4315566801697411684">"Ndrysho raportin e pamjes"</string>
<string name="camera_compat_treatment_suggested_button_description" msgid="8103916969024076767">"Ka probleme me kamerën?\nTrokit për ta ripërshtatur"</string>
<string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"Nuk u rregullua?\nTrokit për ta rikthyer"</string>
<string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"Nuk ka probleme me kamerën? Trokit për ta shpërfillur."</string>
diff --git a/libs/WindowManager/Shell/res/values-sr/strings.xml b/libs/WindowManager/Shell/res/values-sr/strings.xml
index 85798cf..3404bca 100644
--- a/libs/WindowManager/Shell/res/values-sr/strings.xml
+++ b/libs/WindowManager/Shell/res/values-sr/strings.xml
@@ -76,10 +76,16 @@
<string name="bubbles_user_education_got_it" msgid="3382046149225428296">"Важи"</string>
<string name="bubble_overflow_empty_title" msgid="2397251267073294968">"Нема недавних облачића"</string>
<string name="bubble_overflow_empty_subtitle" msgid="2627417924958633713">"Овде се приказују недавни и одбачени облачићи"</string>
+ <!-- no translation found for bubble_bar_education_manage_title (6148404487810835924) -->
+ <skip />
+ <!-- no translation found for bubble_bar_education_manage_text (3199732148641842038) -->
+ <skip />
<string name="notification_bubble_title" msgid="6082910224488253378">"Облачић"</string>
<string name="manage_bubbles_text" msgid="7730624269650594419">"Управљајте"</string>
<string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"Облачић је одбачен."</string>
- <string name="restart_button_description" msgid="6712141648865547958">"Додирните да бисте рестартовали ову апликацију ради бољег приказа."</string>
+ <string name="restart_button_description" msgid="4564728020654658478">"Додирните да бисте рестартовали ову апликацију ради бољег приказа"</string>
+ <string name="user_aspect_ratio_settings_button_hint" msgid="734835849600713016">"Промените размеру ове апликације у Подешавањима"</string>
+ <string name="user_aspect_ratio_settings_button_description" msgid="4315566801697411684">"Промени размеру"</string>
<string name="camera_compat_treatment_suggested_button_description" msgid="8103916969024076767">"Имате проблема са камером?\nДодирните да бисте поново уклопили"</string>
<string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"Проблем није решен?\nДодирните да бисте вратили"</string>
<string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"Немате проблема са камером? Додирните да бисте одбацили."</string>
diff --git a/libs/WindowManager/Shell/res/values-sv/strings.xml b/libs/WindowManager/Shell/res/values-sv/strings.xml
index 33652cd..1f81e0f 100644
--- a/libs/WindowManager/Shell/res/values-sv/strings.xml
+++ b/libs/WindowManager/Shell/res/values-sv/strings.xml
@@ -76,10 +76,16 @@
<string name="bubbles_user_education_got_it" msgid="3382046149225428296">"OK"</string>
<string name="bubble_overflow_empty_title" msgid="2397251267073294968">"Inga nya bubblor"</string>
<string name="bubble_overflow_empty_subtitle" msgid="2627417924958633713">"De senaste bubblorna och ignorerade bubblor visas här"</string>
+ <!-- no translation found for bubble_bar_education_manage_title (6148404487810835924) -->
+ <skip />
+ <!-- no translation found for bubble_bar_education_manage_text (3199732148641842038) -->
+ <skip />
<string name="notification_bubble_title" msgid="6082910224488253378">"Bubbla"</string>
<string name="manage_bubbles_text" msgid="7730624269650594419">"Hantera"</string>
<string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"Bubblan ignorerades."</string>
- <string name="restart_button_description" msgid="6712141648865547958">"Tryck för att starta om appen och få en bättre vy."</string>
+ <string name="restart_button_description" msgid="4564728020654658478">"Tryck för att starta om appen och få en bättre vy"</string>
+ <string name="user_aspect_ratio_settings_button_hint" msgid="734835849600713016">"Ändra appens bildformat i inställningarna"</string>
+ <string name="user_aspect_ratio_settings_button_description" msgid="4315566801697411684">"Ändra bildformat"</string>
<string name="camera_compat_treatment_suggested_button_description" msgid="8103916969024076767">"Problem med kameran?\nTryck för att anpassa på nytt"</string>
<string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"Löstes inte problemet?\nTryck för att återställa"</string>
<string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"Inga problem med kameran? Tryck för att ignorera."</string>
diff --git a/libs/WindowManager/Shell/res/values-sw/strings.xml b/libs/WindowManager/Shell/res/values-sw/strings.xml
index fe2ad1f..8ec4e34 100644
--- a/libs/WindowManager/Shell/res/values-sw/strings.xml
+++ b/libs/WindowManager/Shell/res/values-sw/strings.xml
@@ -76,10 +76,16 @@
<string name="bubbles_user_education_got_it" msgid="3382046149225428296">"Nimeelewa"</string>
<string name="bubble_overflow_empty_title" msgid="2397251267073294968">"Hakuna viputo vya hivi majuzi"</string>
<string name="bubble_overflow_empty_subtitle" msgid="2627417924958633713">"Viputo vya hivi karibuni na vile vilivyoondolewa vitaonekana hapa"</string>
+ <!-- no translation found for bubble_bar_education_manage_title (6148404487810835924) -->
+ <skip />
+ <!-- no translation found for bubble_bar_education_manage_text (3199732148641842038) -->
+ <skip />
<string name="notification_bubble_title" msgid="6082910224488253378">"Kiputo"</string>
<string name="manage_bubbles_text" msgid="7730624269650594419">"Dhibiti"</string>
<string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"Umeondoa kiputo."</string>
- <string name="restart_button_description" msgid="6712141648865547958">"Gusa ili uzime kisha uwashe programu hii, ili upate mwonekano bora."</string>
+ <string name="restart_button_description" msgid="4564728020654658478">"Gusa ili uzime kisha uwashe programu hii, ili upate mwonekano bora"</string>
+ <string name="user_aspect_ratio_settings_button_hint" msgid="734835849600713016">"Badilisha uwiano wa programu hii katika Mipangilio"</string>
+ <string name="user_aspect_ratio_settings_button_description" msgid="4315566801697411684">"Badilisha uwiano wa kipengele"</string>
<string name="camera_compat_treatment_suggested_button_description" msgid="8103916969024076767">"Je, kuna hitilafu za kamera?\nGusa ili urekebishe"</string>
<string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"Umeshindwa kurekebisha?\nGusa ili urejeshe nakala ya awali"</string>
<string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"Je, hakuna hitilafu za kamera? Gusa ili uondoe."</string>
diff --git a/libs/WindowManager/Shell/res/values-ta/strings.xml b/libs/WindowManager/Shell/res/values-ta/strings.xml
index 5bb4c27..6a8559d 100644
--- a/libs/WindowManager/Shell/res/values-ta/strings.xml
+++ b/libs/WindowManager/Shell/res/values-ta/strings.xml
@@ -76,10 +76,16 @@
<string name="bubbles_user_education_got_it" msgid="3382046149225428296">"சரி"</string>
<string name="bubble_overflow_empty_title" msgid="2397251267073294968">"சமீபத்திய குமிழ்கள் இல்லை"</string>
<string name="bubble_overflow_empty_subtitle" msgid="2627417924958633713">"சமீபத்திய குமிழ்களும் நிராகரிக்கப்பட்ட குமிழ்களும் இங்கே தோன்றும்"</string>
+ <!-- no translation found for bubble_bar_education_manage_title (6148404487810835924) -->
+ <skip />
+ <!-- no translation found for bubble_bar_education_manage_text (3199732148641842038) -->
+ <skip />
<string name="notification_bubble_title" msgid="6082910224488253378">"பபிள்"</string>
<string name="manage_bubbles_text" msgid="7730624269650594419">"நிர்வகி"</string>
<string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"குமிழ் நிராகரிக்கப்பட்டது."</string>
- <string name="restart_button_description" msgid="6712141648865547958">"இங்கு தட்டி ஆப்ஸை மீண்டும் தொடங்கி, ஆப்ஸ் காட்சியை இன்னும் சிறப்பாக்கலாம்."</string>
+ <string name="restart_button_description" msgid="4564728020654658478">"இங்கு தட்டுவதன் மூலம் இந்த ஆப்ஸை மீண்டும் தொடங்கி, ஆப்ஸ் காட்டப்படும் விதத்தை இன்னும் சிறப்பாக்கலாம்"</string>
+ <string name="user_aspect_ratio_settings_button_hint" msgid="734835849600713016">"அமைப்புகளில் இந்த ஆப்ஸின் தோற்ற விகிதத்தை மாற்றும்"</string>
+ <string name="user_aspect_ratio_settings_button_description" msgid="4315566801697411684">"தோற்ற விகிதத்தை மாற்றும்"</string>
<string name="camera_compat_treatment_suggested_button_description" msgid="8103916969024076767">"கேமரா தொடர்பான சிக்கல்களா?\nமீண்டும் பொருத்த தட்டவும்"</string>
<string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"சிக்கல்கள் சரிசெய்யப்படவில்லையா?\nமாற்றியமைக்க தட்டவும்"</string>
<string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"கேமரா தொடர்பான சிக்கல்கள் எதுவும் இல்லையா? நிராகரிக்க தட்டவும்."</string>
diff --git a/libs/WindowManager/Shell/res/values-te/strings.xml b/libs/WindowManager/Shell/res/values-te/strings.xml
index 6f95aa9..1c1e3cb 100644
--- a/libs/WindowManager/Shell/res/values-te/strings.xml
+++ b/libs/WindowManager/Shell/res/values-te/strings.xml
@@ -76,10 +76,16 @@
<string name="bubbles_user_education_got_it" msgid="3382046149225428296">"అర్థమైంది"</string>
<string name="bubble_overflow_empty_title" msgid="2397251267073294968">"ఇటీవలి బబుల్స్ ఏవీ లేవు"</string>
<string name="bubble_overflow_empty_subtitle" msgid="2627417924958633713">"ఇటీవలి బబుల్స్ మరియు తీసివేసిన బబుల్స్ ఇక్కడ కనిపిస్తాయి"</string>
+ <!-- no translation found for bubble_bar_education_manage_title (6148404487810835924) -->
+ <skip />
+ <!-- no translation found for bubble_bar_education_manage_text (3199732148641842038) -->
+ <skip />
<string name="notification_bubble_title" msgid="6082910224488253378">"బబుల్"</string>
<string name="manage_bubbles_text" msgid="7730624269650594419">"మేనేజ్ చేయండి"</string>
<string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"బబుల్ విస్మరించబడింది."</string>
- <string name="restart_button_description" msgid="6712141648865547958">"మెరుగైన వీక్షణ కోసం ఈ యాప్ను రీస్టార్ట్ చేయడానికి ట్యాప్ చేయండి."</string>
+ <string name="restart_button_description" msgid="4564728020654658478">"మెరుగైన వీక్షణ కోసం ఈ యాప్ను రీస్టార్ట్ చేయడానికి ట్యాప్ చేయండి"</string>
+ <string name="user_aspect_ratio_settings_button_hint" msgid="734835849600713016">"సెట్టింగ్లలో ఈ యాప్ ఆకార నిష్పత్తిని మార్చండి"</string>
+ <string name="user_aspect_ratio_settings_button_description" msgid="4315566801697411684">"ఆకార నిష్పత్తిని మార్చండి"</string>
<string name="camera_compat_treatment_suggested_button_description" msgid="8103916969024076767">"కెమెరా సమస్యలు ఉన్నాయా?\nరీఫిట్ చేయడానికి ట్యాప్ చేయండి"</string>
<string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"దాని సమస్యను పరిష్కరించలేదా?\nపూర్వస్థితికి మార్చడానికి ట్యాప్ చేయండి"</string>
<string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"కెమెరా సమస్యలు లేవా? తీసివేయడానికి ట్యాప్ చేయండి."</string>
diff --git a/libs/WindowManager/Shell/res/values-th/strings.xml b/libs/WindowManager/Shell/res/values-th/strings.xml
index 6733940..a0f0e27 100644
--- a/libs/WindowManager/Shell/res/values-th/strings.xml
+++ b/libs/WindowManager/Shell/res/values-th/strings.xml
@@ -76,10 +76,16 @@
<string name="bubbles_user_education_got_it" msgid="3382046149225428296">"รับทราบ"</string>
<string name="bubble_overflow_empty_title" msgid="2397251267073294968">"ไม่มีบับเบิลเมื่อเร็วๆ นี้"</string>
<string name="bubble_overflow_empty_subtitle" msgid="2627417924958633713">"บับเบิลที่แสดงและที่ปิดไปเมื่อเร็วๆ นี้จะปรากฏที่นี่"</string>
+ <!-- no translation found for bubble_bar_education_manage_title (6148404487810835924) -->
+ <skip />
+ <!-- no translation found for bubble_bar_education_manage_text (3199732148641842038) -->
+ <skip />
<string name="notification_bubble_title" msgid="6082910224488253378">"บับเบิล"</string>
<string name="manage_bubbles_text" msgid="7730624269650594419">"จัดการ"</string>
<string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"ปิดบับเบิลแล้ว"</string>
- <string name="restart_button_description" msgid="6712141648865547958">"แตะเพื่อรีสตาร์ทแอปนี้และรับมุมมองที่ดียิ่งขึ้น"</string>
+ <string name="restart_button_description" msgid="4564728020654658478">"แตะเพื่อรีสตาร์ทแอปนี้และรับมุมมองที่ดียิ่งขึ้น"</string>
+ <string name="user_aspect_ratio_settings_button_hint" msgid="734835849600713016">"เปลี่ยนสัดส่วนภาพของแอปนี้ในการตั้งค่า"</string>
+ <string name="user_aspect_ratio_settings_button_description" msgid="4315566801697411684">"เปลี่ยนอัตราส่วนกว้างยาว"</string>
<string name="camera_compat_treatment_suggested_button_description" msgid="8103916969024076767">"หากพบปัญหากับกล้อง\nแตะเพื่อแก้ไข"</string>
<string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"หากไม่ได้แก้ไข\nแตะเพื่อเปลี่ยนกลับ"</string>
<string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"หากไม่พบปัญหากับกล้อง แตะเพื่อปิด"</string>
diff --git a/libs/WindowManager/Shell/res/values-tl/strings.xml b/libs/WindowManager/Shell/res/values-tl/strings.xml
index 8cf4eb484..9bc17f0 100644
--- a/libs/WindowManager/Shell/res/values-tl/strings.xml
+++ b/libs/WindowManager/Shell/res/values-tl/strings.xml
@@ -76,10 +76,16 @@
<string name="bubbles_user_education_got_it" msgid="3382046149225428296">"OK"</string>
<string name="bubble_overflow_empty_title" msgid="2397251267073294968">"Walang kamakailang bubble"</string>
<string name="bubble_overflow_empty_subtitle" msgid="2627417924958633713">"Lalabas dito ang mga kamakailang bubble at na-dismiss na bubble"</string>
+ <!-- no translation found for bubble_bar_education_manage_title (6148404487810835924) -->
+ <skip />
+ <!-- no translation found for bubble_bar_education_manage_text (3199732148641842038) -->
+ <skip />
<string name="notification_bubble_title" msgid="6082910224488253378">"Bubble"</string>
<string name="manage_bubbles_text" msgid="7730624269650594419">"Pamahalaan"</string>
<string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"Na-dismiss na ang bubble."</string>
- <string name="restart_button_description" msgid="6712141648865547958">"I-tap para i-restart ang app na ito para sa mas magandang view."</string>
+ <string name="restart_button_description" msgid="4564728020654658478">"I-tap para i-restart ang app na ito para sa mas magandang view"</string>
+ <string name="user_aspect_ratio_settings_button_hint" msgid="734835849600713016">"Baguhin ang aspect ratio ng app na ito sa Mga Setting"</string>
+ <string name="user_aspect_ratio_settings_button_description" msgid="4315566801697411684">"Baguhin ang aspect ratio"</string>
<string name="camera_compat_treatment_suggested_button_description" msgid="8103916969024076767">"May mga isyu sa camera?\nI-tap para i-refit"</string>
<string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"Hindi ito naayos?\nI-tap para i-revert"</string>
<string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"Walang isyu sa camera? I-tap para i-dismiss."</string>
diff --git a/libs/WindowManager/Shell/res/values-tr/strings.xml b/libs/WindowManager/Shell/res/values-tr/strings.xml
index 1454435..059ec73 100644
--- a/libs/WindowManager/Shell/res/values-tr/strings.xml
+++ b/libs/WindowManager/Shell/res/values-tr/strings.xml
@@ -76,10 +76,16 @@
<string name="bubbles_user_education_got_it" msgid="3382046149225428296">"Anladım"</string>
<string name="bubble_overflow_empty_title" msgid="2397251267073294968">"Son kapatılan baloncuk yok"</string>
<string name="bubble_overflow_empty_subtitle" msgid="2627417924958633713">"Son baloncuklar ve kapattığınız baloncuklar burada görünür"</string>
+ <!-- no translation found for bubble_bar_education_manage_title (6148404487810835924) -->
+ <skip />
+ <!-- no translation found for bubble_bar_education_manage_text (3199732148641842038) -->
+ <skip />
<string name="notification_bubble_title" msgid="6082910224488253378">"Baloncuk"</string>
<string name="manage_bubbles_text" msgid="7730624269650594419">"Yönet"</string>
<string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"Balon kapatıldı."</string>
- <string name="restart_button_description" msgid="6712141648865547958">"Bu uygulamayı yeniden başlatarak daha iyi bir görünüm elde etmek için dokunun."</string>
+ <string name="restart_button_description" msgid="4564728020654658478">"Bu uygulamayı yeniden başlatarak daha iyi bir görünüm elde etmek için dokunun"</string>
+ <string name="user_aspect_ratio_settings_button_hint" msgid="734835849600713016">"Bu uygulamanın en boy oranını Ayarlar\'dan değiştirin"</string>
+ <string name="user_aspect_ratio_settings_button_description" msgid="4315566801697411684">"En boy oranını değiştir"</string>
<string name="camera_compat_treatment_suggested_button_description" msgid="8103916969024076767">"Kameranızda sorun mu var?\nDüzeltmek için dokunun"</string>
<string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"Bu işlem sorunu düzeltmedi mi?\nİşlemi geri almak için dokunun"</string>
<string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"Kameranızda sorun yok mu? Kapatmak için dokunun."</string>
diff --git a/libs/WindowManager/Shell/res/values-uk/strings.xml b/libs/WindowManager/Shell/res/values-uk/strings.xml
index 78df129..a4a4a78 100644
--- a/libs/WindowManager/Shell/res/values-uk/strings.xml
+++ b/libs/WindowManager/Shell/res/values-uk/strings.xml
@@ -76,10 +76,16 @@
<string name="bubbles_user_education_got_it" msgid="3382046149225428296">"Зрозуміло"</string>
<string name="bubble_overflow_empty_title" msgid="2397251267073294968">"Немає нещодавніх спливаючих чатів"</string>
<string name="bubble_overflow_empty_subtitle" msgid="2627417924958633713">"Тут з\'являтимуться нещодавні й закриті спливаючі чати"</string>
+ <!-- no translation found for bubble_bar_education_manage_title (6148404487810835924) -->
+ <skip />
+ <!-- no translation found for bubble_bar_education_manage_text (3199732148641842038) -->
+ <skip />
<string name="notification_bubble_title" msgid="6082910224488253378">"Спливаюче сповіщення"</string>
<string name="manage_bubbles_text" msgid="7730624269650594419">"Налаштувати"</string>
<string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"Спливаюче сповіщення закрито."</string>
- <string name="restart_button_description" msgid="6712141648865547958">"Натисніть, щоб перезапустити цей додаток для зручнішого перегляду."</string>
+ <string name="restart_button_description" msgid="4564728020654658478">"Натисніть, щоб перезапустити цей додаток для зручнішого перегляду"</string>
+ <string name="user_aspect_ratio_settings_button_hint" msgid="734835849600713016">"Змінити формат для цього додатка в налаштуваннях"</string>
+ <string name="user_aspect_ratio_settings_button_description" msgid="4315566801697411684">"Змінити формат"</string>
<string name="camera_compat_treatment_suggested_button_description" msgid="8103916969024076767">"Проблеми з камерою?\nНатисніть, щоб пристосувати"</string>
<string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"Проблему не вирішено?\nНатисніть, щоб скасувати зміни"</string>
<string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"Немає проблем із камерою? Торкніться, щоб закрити."</string>
diff --git a/libs/WindowManager/Shell/res/values-ur/strings.xml b/libs/WindowManager/Shell/res/values-ur/strings.xml
index ca16424..b25f13e 100644
--- a/libs/WindowManager/Shell/res/values-ur/strings.xml
+++ b/libs/WindowManager/Shell/res/values-ur/strings.xml
@@ -76,10 +76,16 @@
<string name="bubbles_user_education_got_it" msgid="3382046149225428296">"سمجھ آ گئی"</string>
<string name="bubble_overflow_empty_title" msgid="2397251267073294968">"کوئی حالیہ بلبلہ نہیں"</string>
<string name="bubble_overflow_empty_subtitle" msgid="2627417924958633713">"حالیہ بلبلے اور برخاست شدہ بلبلے یہاں ظاہر ہوں گے"</string>
+ <!-- no translation found for bubble_bar_education_manage_title (6148404487810835924) -->
+ <skip />
+ <!-- no translation found for bubble_bar_education_manage_text (3199732148641842038) -->
+ <skip />
<string name="notification_bubble_title" msgid="6082910224488253378">"بلبلہ"</string>
<string name="manage_bubbles_text" msgid="7730624269650594419">"نظم کریں"</string>
<string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"بلبلہ برخاست کر دیا گیا۔"</string>
- <string name="restart_button_description" msgid="6712141648865547958">"بہتر منظر کے لیے اس ایپ کو ری اسٹارٹ کرنے کی خاطر تھپتھپائیں۔"</string>
+ <string name="restart_button_description" msgid="4564728020654658478">"بہتر منظر کے لیے اس ایپ کو ری اسٹارٹ کرنے کی خاطر تھپتھپائیں"</string>
+ <string name="user_aspect_ratio_settings_button_hint" msgid="734835849600713016">"ترتیبات میں اس ایپ کی تناسبی شرح کو تبدیل کریں"</string>
+ <string name="user_aspect_ratio_settings_button_description" msgid="4315566801697411684">"تناسبی شرح کو تبدیل کریں"</string>
<string name="camera_compat_treatment_suggested_button_description" msgid="8103916969024076767">"کیمرے کے مسائل؟\nدوبارہ فٹ کرنے کیلئے تھپتھپائیں"</string>
<string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"یہ حل نہیں ہوا؟\nلوٹانے کیلئے تھپتھپائیں"</string>
<string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"کوئی کیمرے کا مسئلہ نہیں ہے؟ برخاست کرنے کیلئے تھپتھپائیں۔"</string>
diff --git a/libs/WindowManager/Shell/res/values-uz/strings.xml b/libs/WindowManager/Shell/res/values-uz/strings.xml
index c0dc033..5acf729 100644
--- a/libs/WindowManager/Shell/res/values-uz/strings.xml
+++ b/libs/WindowManager/Shell/res/values-uz/strings.xml
@@ -76,10 +76,16 @@
<string name="bubbles_user_education_got_it" msgid="3382046149225428296">"OK"</string>
<string name="bubble_overflow_empty_title" msgid="2397251267073294968">"Hech qanday bulutcha topilmadi"</string>
<string name="bubble_overflow_empty_subtitle" msgid="2627417924958633713">"Eng oxirgi va yopilgan bulutchali chatlar shu yerda chiqadi"</string>
+ <!-- no translation found for bubble_bar_education_manage_title (6148404487810835924) -->
+ <skip />
+ <!-- no translation found for bubble_bar_education_manage_text (3199732148641842038) -->
+ <skip />
<string name="notification_bubble_title" msgid="6082910224488253378">"Pufaklar"</string>
<string name="manage_bubbles_text" msgid="7730624269650594419">"Boshqarish"</string>
<string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"Bulutcha yopildi."</string>
- <string name="restart_button_description" msgid="6712141648865547958">"Yaxshiroq koʻrish maqsadida bu ilovani qayta ishga tushirish uchun bosing."</string>
+ <string name="restart_button_description" msgid="4564728020654658478">"Yaxshiroq koʻrish maqsadida bu ilovani qayta ishga tushirish uchun bosing"</string>
+ <string name="user_aspect_ratio_settings_button_hint" msgid="734835849600713016">"Sozlamalar orqali bu ilovaning tomonlar nisbatini oʻzgartiring"</string>
+ <string name="user_aspect_ratio_settings_button_description" msgid="4315566801697411684">"Tomonlar nisbatini oʻzgartirish"</string>
<string name="camera_compat_treatment_suggested_button_description" msgid="8103916969024076767">"Kamera nosozmi?\nQayta moslash uchun bosing"</string>
<string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"Tuzatilmadimi?\nQaytarish uchun bosing"</string>
<string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"Kamera muammosizmi? Yopish uchun bosing."</string>
diff --git a/libs/WindowManager/Shell/res/values-vi/strings.xml b/libs/WindowManager/Shell/res/values-vi/strings.xml
index 0281c1c..0777abc 100644
--- a/libs/WindowManager/Shell/res/values-vi/strings.xml
+++ b/libs/WindowManager/Shell/res/values-vi/strings.xml
@@ -76,10 +76,16 @@
<string name="bubbles_user_education_got_it" msgid="3382046149225428296">"Đã hiểu"</string>
<string name="bubble_overflow_empty_title" msgid="2397251267073294968">"Không có bong bóng trò chuyện nào gần đây"</string>
<string name="bubble_overflow_empty_subtitle" msgid="2627417924958633713">"Bong bóng trò chuyện đã đóng và bong bóng trò chuyện gần đây sẽ xuất hiện ở đây"</string>
+ <!-- no translation found for bubble_bar_education_manage_title (6148404487810835924) -->
+ <skip />
+ <!-- no translation found for bubble_bar_education_manage_text (3199732148641842038) -->
+ <skip />
<string name="notification_bubble_title" msgid="6082910224488253378">"Bong bóng"</string>
<string name="manage_bubbles_text" msgid="7730624269650594419">"Quản lý"</string>
<string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"Đã đóng bong bóng."</string>
- <string name="restart_button_description" msgid="6712141648865547958">"Nhấn để khởi động lại ứng dụng để có trải nghiệm xem tốt hơn."</string>
+ <string name="restart_button_description" msgid="4564728020654658478">"Nhấn nút khởi động lại ứng dụng này để xem dễ hơn"</string>
+ <string name="user_aspect_ratio_settings_button_hint" msgid="734835849600713016">"Thay đổi tỷ lệ khung hình của ứng dụng này thông qua phần Cài đặt"</string>
+ <string name="user_aspect_ratio_settings_button_description" msgid="4315566801697411684">"Thay đổi tỷ lệ khung hình"</string>
<string name="camera_compat_treatment_suggested_button_description" msgid="8103916969024076767">"Có vấn đề với máy ảnh?\nHãy nhấn để sửa lỗi"</string>
<string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"Bạn chưa khắc phục vấn đề?\nHãy nhấn để hủy bỏ"</string>
<string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"Không có vấn đề với máy ảnh? Hãy nhấn để đóng."</string>
diff --git a/libs/WindowManager/Shell/res/values-zh-rCN/strings.xml b/libs/WindowManager/Shell/res/values-zh-rCN/strings.xml
index d1f50db..cde5fe3 100644
--- a/libs/WindowManager/Shell/res/values-zh-rCN/strings.xml
+++ b/libs/WindowManager/Shell/res/values-zh-rCN/strings.xml
@@ -76,10 +76,16 @@
<string name="bubbles_user_education_got_it" msgid="3382046149225428296">"知道了"</string>
<string name="bubble_overflow_empty_title" msgid="2397251267073294968">"最近没有对话泡"</string>
<string name="bubble_overflow_empty_subtitle" msgid="2627417924958633713">"此处会显示最近的对话泡和已关闭的对话泡"</string>
+ <!-- no translation found for bubble_bar_education_manage_title (6148404487810835924) -->
+ <skip />
+ <!-- no translation found for bubble_bar_education_manage_text (3199732148641842038) -->
+ <skip />
<string name="notification_bubble_title" msgid="6082910224488253378">"气泡"</string>
<string name="manage_bubbles_text" msgid="7730624269650594419">"管理"</string>
<string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"已关闭对话泡。"</string>
- <string name="restart_button_description" msgid="6712141648865547958">"点按即可重启此应用,获得更好的视图体验。"</string>
+ <string name="restart_button_description" msgid="4564728020654658478">"点按即可重启此应用,获得更好的视觉体验"</string>
+ <string name="user_aspect_ratio_settings_button_hint" msgid="734835849600713016">"在“设置”中更改此应用的宽高比"</string>
+ <string name="user_aspect_ratio_settings_button_description" msgid="4315566801697411684">"更改高宽比"</string>
<string name="camera_compat_treatment_suggested_button_description" msgid="8103916969024076767">"相机有问题?\n点按即可整修"</string>
<string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"没有解决此问题?\n点按即可恢复"</string>
<string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"相机没有问题?点按即可忽略。"</string>
diff --git a/libs/WindowManager/Shell/res/values-zh-rHK/strings.xml b/libs/WindowManager/Shell/res/values-zh-rHK/strings.xml
index 3d33eca..fcb0c91 100644
--- a/libs/WindowManager/Shell/res/values-zh-rHK/strings.xml
+++ b/libs/WindowManager/Shell/res/values-zh-rHK/strings.xml
@@ -76,10 +76,16 @@
<string name="bubbles_user_education_got_it" msgid="3382046149225428296">"知道了"</string>
<string name="bubble_overflow_empty_title" msgid="2397251267073294968">"沒有最近曾使用的小視窗"</string>
<string name="bubble_overflow_empty_subtitle" msgid="2627417924958633713">"最近使用和關閉的小視窗會在這裡顯示"</string>
+ <!-- no translation found for bubble_bar_education_manage_title (6148404487810835924) -->
+ <skip />
+ <!-- no translation found for bubble_bar_education_manage_text (3199732148641842038) -->
+ <skip />
<string name="notification_bubble_title" msgid="6082910224488253378">"氣泡"</string>
<string name="manage_bubbles_text" msgid="7730624269650594419">"管理"</string>
<string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"對話氣泡已關閉。"</string>
- <string name="restart_button_description" msgid="6712141648865547958">"輕按並重新啟動此應用程式,以取得更佳的觀看體驗。"</string>
+ <string name="restart_button_description" msgid="4564728020654658478">"輕按並重新啟動此應用程式,以取得更佳的觀看體驗"</string>
+ <string name="user_aspect_ratio_settings_button_hint" msgid="734835849600713016">"前往「設定」變更此應用程式的長寬比"</string>
+ <string name="user_aspect_ratio_settings_button_description" msgid="4315566801697411684">"變更長寬比"</string>
<string name="camera_compat_treatment_suggested_button_description" msgid="8103916969024076767">"相機有問題?\n輕按即可修正"</string>
<string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"未能修正問題?\n輕按即可還原"</string>
<string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"相機冇問題?㩒一下就可以即可閂咗佢。"</string>
diff --git a/libs/WindowManager/Shell/res/values-zh-rTW/strings.xml b/libs/WindowManager/Shell/res/values-zh-rTW/strings.xml
index 4ca49e1..f272e91 100644
--- a/libs/WindowManager/Shell/res/values-zh-rTW/strings.xml
+++ b/libs/WindowManager/Shell/res/values-zh-rTW/strings.xml
@@ -76,10 +76,16 @@
<string name="bubbles_user_education_got_it" msgid="3382046149225428296">"我知道了"</string>
<string name="bubble_overflow_empty_title" msgid="2397251267073294968">"最近沒有任何對話框"</string>
<string name="bubble_overflow_empty_subtitle" msgid="2627417924958633713">"最近的對話框和已關閉的對話框會顯示在這裡"</string>
+ <!-- no translation found for bubble_bar_education_manage_title (6148404487810835924) -->
+ <skip />
+ <!-- no translation found for bubble_bar_education_manage_text (3199732148641842038) -->
+ <skip />
<string name="notification_bubble_title" msgid="6082910224488253378">"泡泡"</string>
<string name="manage_bubbles_text" msgid="7730624269650594419">"管理"</string>
<string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"已關閉泡泡。"</string>
- <string name="restart_button_description" msgid="6712141648865547958">"請輕觸並重新啟動此應用程式,取得更良好的觀看體驗。"</string>
+ <string name="restart_button_description" msgid="4564728020654658478">"輕觸此按鈕重新啟動這個應用程式,即可獲得更良好的觀看體驗"</string>
+ <string name="user_aspect_ratio_settings_button_hint" msgid="734835849600713016">"前往「設定」變更這個應用程式的顯示比例"</string>
+ <string name="user_aspect_ratio_settings_button_description" msgid="4315566801697411684">"變更顯示比例"</string>
<string name="camera_compat_treatment_suggested_button_description" msgid="8103916969024076767">"相機有問題嗎?\n輕觸即可修正"</string>
<string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"未修正問題嗎?\n輕觸即可還原"</string>
<string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"相機沒問題嗎?輕觸即可關閉。"</string>
diff --git a/libs/WindowManager/Shell/res/values-zu/strings.xml b/libs/WindowManager/Shell/res/values-zu/strings.xml
index 478b5a6..c1ba6ee 100644
--- a/libs/WindowManager/Shell/res/values-zu/strings.xml
+++ b/libs/WindowManager/Shell/res/values-zu/strings.xml
@@ -76,10 +76,16 @@
<string name="bubbles_user_education_got_it" msgid="3382046149225428296">"Ngiyezwa"</string>
<string name="bubble_overflow_empty_title" msgid="2397251267073294968">"Awekho amabhamuza akamuva"</string>
<string name="bubble_overflow_empty_subtitle" msgid="2627417924958633713">"Amabhamuza akamuva namabhamuza asusiwe azobonakala lapha."</string>
+ <!-- no translation found for bubble_bar_education_manage_title (6148404487810835924) -->
+ <skip />
+ <!-- no translation found for bubble_bar_education_manage_text (3199732148641842038) -->
+ <skip />
<string name="notification_bubble_title" msgid="6082910224488253378">"Ibhamuza"</string>
<string name="manage_bubbles_text" msgid="7730624269650594419">"Phatha"</string>
<string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"Ibhamuza licashisiwe."</string>
- <string name="restart_button_description" msgid="6712141648865547958">"Thepha ukuze uqale kabusha le app ukuze ibonakale kangcono."</string>
+ <string name="restart_button_description" msgid="4564728020654658478">"Thepha ukuze uqale kabusha le app ukuze ibonakale kangcono"</string>
+ <string name="user_aspect_ratio_settings_button_hint" msgid="734835849600713016">"Shintsha ukubukeka kwesilinganiselo kwe-app kuMasethingi"</string>
+ <string name="user_aspect_ratio_settings_button_description" msgid="4315566801697411684">"Shintsha ukubukeka kwesilinganiselo"</string>
<string name="camera_compat_treatment_suggested_button_description" msgid="8103916969024076767">"Izinkinga zekhamera?\nThepha ukuze uyilinganise kabusha"</string>
<string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"Akuyilungisanga?\nThepha ukuze ubuyele"</string>
<string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"Azikho izinkinga zekhamera? Thepha ukuze ucashise."</string>
diff --git a/libs/WindowManager/Shell/res/values/colors.xml b/libs/WindowManager/Shell/res/values/colors.xml
index b2ec98b..f76a346 100644
--- a/libs/WindowManager/Shell/res/values/colors.xml
+++ b/libs/WindowManager/Shell/res/values/colors.xml
@@ -73,4 +73,8 @@
<color name="desktop_mode_caption_menu_buttons_color_active">#00677E</color>
<color name="desktop_mode_resize_veil_light">#EFF1F2</color>
<color name="desktop_mode_resize_veil_dark">#1C1C17</color>
+ <color name="desktop_mode_maximize_menu_button">#DDDACD</color>
+ <color name="desktop_mode_maximize_menu_button_outline">#797869</color>
+ <color name="desktop_mode_maximize_menu_button_outline_on_hover">#606219</color>
+ <color name="desktop_mode_maximize_menu_button_on_hover">#E7E790</color>
</resources>
diff --git a/libs/WindowManager/Shell/res/values/dimen.xml b/libs/WindowManager/Shell/res/values/dimen.xml
index 20bf81d..7edf2fc 100644
--- a/libs/WindowManager/Shell/res/values/dimen.xml
+++ b/libs/WindowManager/Shell/res/values/dimen.xml
@@ -401,6 +401,24 @@
<!-- Height of button (32dp) + 2 * margin (5dp each). -->
<dimen name="freeform_decor_caption_height">42dp</dimen>
+ <!-- The width of the maximize menu in desktop mode. -->
+ <dimen name="desktop_mode_maximize_menu_width">287dp</dimen>
+
+ <!-- The height of the maximize menu in desktop mode. -->
+ <dimen name="desktop_mode_maximize_menu_height">112dp</dimen>
+
+ <!-- The larger of the two corner radii of the maximize menu buttons. -->
+ <dimen name="desktop_mode_maximize_menu_buttons_large_corner_radius">4dp</dimen>
+
+ <!-- The smaller of the two corner radii of the maximize menu buttons. -->
+ <dimen name="desktop_mode_maximize_menu_buttons_small_corner_radius">2dp</dimen>
+
+ <!-- The corner radius of the maximize menu. -->
+ <dimen name="desktop_mode_maximize_menu_corner_radius">8dp</dimen>
+
+ <!-- The radius of the Maximize menu shadow. -->
+ <dimen name="desktop_mode_maximize_menu_shadow_radius">8dp</dimen>
+
<!-- The width of the handle menu in desktop mode. -->
<dimen name="desktop_mode_handle_menu_width">216dp</dimen>
@@ -432,6 +450,10 @@
<dimen name="freeform_resize_corner">44dp</dimen>
+ <!-- The width of the area at the sides of the screen where a freeform task will transition to
+ split select if dragged until the touch input is within the range. -->
+ <dimen name="desktop_mode_transition_area_width">32dp</dimen>
+
<!-- The height of the area at the top of the screen where a freeform task will transition to
fullscreen if dragged until the top bound of the task is within the area. -->
<dimen name="desktop_mode_transition_area_height">16dp</dimen>
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 8400dde..645a961 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
@@ -16,7 +16,6 @@
package com.android.wm.shell.bubbles;
-import static android.app.ActivityTaskManager.INVALID_TASK_ID;
import static android.service.notification.NotificationListenerService.NOTIFICATION_CHANNEL_OR_GROUP_DELETED;
import static android.service.notification.NotificationListenerService.NOTIFICATION_CHANNEL_OR_GROUP_UPDATED;
import static android.service.notification.NotificationListenerService.REASON_CANCEL;
@@ -115,6 +114,7 @@
import com.android.wm.shell.sysui.ShellInit;
import com.android.wm.shell.taskview.TaskView;
import com.android.wm.shell.taskview.TaskViewTransitions;
+import com.android.wm.shell.transition.Transitions;
import java.io.PrintWriter;
import java.util.ArrayList;
@@ -143,6 +143,8 @@
// Should match with PhoneWindowManager
private static final String SYSTEM_DIALOG_REASON_KEY = "reason";
private static final String SYSTEM_DIALOG_REASON_GESTURE_NAV = "gestureNav";
+ private static final String SYSTEM_DIALOG_REASON_RECENT_APPS = "recentapps";
+ private static final String SYSTEM_DIALOG_REASON_HOME_KEY = "homekey";
/**
* Common interface to send updates to bubble views.
@@ -182,6 +184,7 @@
private final ShellTaskOrganizer mTaskOrganizer;
private final DisplayController mDisplayController;
private final TaskViewTransitions mTaskViewTransitions;
+ private final Transitions mTransitions;
private final SyncTransactionQueue mSyncQueue;
private final ShellController mShellController;
private final ShellCommandHandler mShellCommandHandler;
@@ -282,6 +285,7 @@
@ShellMainThread Handler mainHandler,
@ShellBackgroundThread ShellExecutor bgExecutor,
TaskViewTransitions taskViewTransitions,
+ Transitions transitions,
SyncTransactionQueue syncQueue,
IWindowManager wmService,
BubbleProperties bubbleProperties) {
@@ -317,6 +321,7 @@
com.android.internal.R.dimen.importance_ring_stroke_width));
mDisplayController = displayController;
mTaskViewTransitions = taskViewTransitions;
+ mTransitions = transitions;
mOneHandedOptional = oneHandedOptional;
mDragAndDropController = dragAndDropController;
mSyncQueue = syncQueue;
@@ -416,23 +421,9 @@
}
}, mMainHandler);
- mTaskStackListener.addListener(new TaskStackListenerCallback() {
- @Override
- public void onTaskMovedToFront(int taskId) {
- mMainExecutor.execute(() -> {
- int expandedId = INVALID_TASK_ID;
- if (mStackView != null && mStackView.getExpandedBubble() != null
- && isStackExpanded()
- && !mStackView.isExpansionAnimating()
- && !mStackView.isSwitchAnimating()) {
- expandedId = mStackView.getExpandedBubble().getTaskId();
- }
- if (expandedId != INVALID_TASK_ID && expandedId != taskId) {
- mBubbleData.setExpanded(false);
- }
- });
- }
+ mTransitions.registerObserver(new BubblesTransitionObserver(this, mBubbleData));
+ mTaskStackListener.addListener(new TaskStackListenerCallback() {
@Override
public void onActivityRestartAttempt(ActivityManager.RunningTaskInfo task,
boolean homeTaskVisible, boolean clearedTask, boolean wasVisible) {
@@ -883,8 +874,10 @@
String action = intent.getAction();
String reason = intent.getStringExtra(SYSTEM_DIALOG_REASON_KEY);
- if ((Intent.ACTION_CLOSE_SYSTEM_DIALOGS.equals(action)
- && SYSTEM_DIALOG_REASON_GESTURE_NAV.equals(reason))
+ boolean validReasonToCollapse = SYSTEM_DIALOG_REASON_RECENT_APPS.equals(reason)
+ || SYSTEM_DIALOG_REASON_HOME_KEY.equals(reason)
+ || SYSTEM_DIALOG_REASON_GESTURE_NAV.equals(reason);
+ if ((Intent.ACTION_CLOSE_SYSTEM_DIALOGS.equals(action) && validReasonToCollapse)
|| Intent.ACTION_SCREEN_OFF.equals(action)) {
mMainExecutor.execute(() -> collapseStack());
}
@@ -1961,6 +1954,15 @@
}
}
+ /**
+ * Returns whether the stack is animating or not.
+ */
+ public boolean isStackAnimating() {
+ return mStackView != null
+ && (mStackView.isExpansionAnimating()
+ || mStackView.isSwitchAnimating());
+ }
+
@VisibleForTesting
@Nullable
public BubbleStackView getStackView() {
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubblesTransitionObserver.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubblesTransitionObserver.java
new file mode 100644
index 0000000..9e8a385
--- /dev/null
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubblesTransitionObserver.java
@@ -0,0 +1,83 @@
+/*
+ * 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.bubbles;
+
+import static android.app.ActivityTaskManager.INVALID_TASK_ID;
+
+import android.app.ActivityManager;
+import android.os.IBinder;
+import android.view.SurfaceControl;
+import android.window.TransitionInfo;
+
+import androidx.annotation.NonNull;
+
+import com.android.wm.shell.transition.Transitions;
+import com.android.wm.shell.util.TransitionUtil;
+
+/**
+ * Observer used to identify tasks that are opening or moving to front. If a bubble activity is
+ * currently opened when this happens, we'll collapse the bubbles.
+ */
+public class BubblesTransitionObserver implements Transitions.TransitionObserver {
+
+ private BubbleController mBubbleController;
+ private BubbleData mBubbleData;
+
+ public BubblesTransitionObserver(BubbleController controller,
+ BubbleData bubbleData) {
+ mBubbleController = controller;
+ mBubbleData = bubbleData;
+ }
+
+ @Override
+ public void onTransitionReady(@NonNull IBinder transition, @NonNull TransitionInfo info,
+ @NonNull SurfaceControl.Transaction startTransaction,
+ @NonNull SurfaceControl.Transaction finishTransaction) {
+ for (TransitionInfo.Change change : info.getChanges()) {
+ final ActivityManager.RunningTaskInfo taskInfo = change.getTaskInfo();
+ // We only care about opens / move to fronts when bubbles are expanded & not animating.
+ if (taskInfo == null
+ || taskInfo.taskId == INVALID_TASK_ID
+ || !TransitionUtil.isOpeningType(change.getMode())
+ || mBubbleController.isStackAnimating()
+ || !mBubbleData.isExpanded()
+ || mBubbleData.getSelectedBubble() == null) {
+ continue;
+ }
+ int expandedId = mBubbleData.getSelectedBubble().getTaskId();
+ // If the task id that's opening is the same as the expanded bubble, skip collapsing
+ // because it is our bubble that is opening.
+ if (expandedId != INVALID_TASK_ID && expandedId != taskInfo.taskId) {
+ mBubbleData.setExpanded(false);
+ }
+ }
+ }
+
+ @Override
+ public void onTransitionStarting(@NonNull IBinder transition) {
+
+ }
+
+ @Override
+ public void onTransitionMerged(@NonNull IBinder merged, @NonNull IBinder playing) {
+
+ }
+
+ @Override
+ public void onTransitionFinished(@NonNull IBinder transition, boolean aborted) {
+
+ }
+}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/common/pip/LegacySizeSpecSource.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/common/pip/LegacySizeSpecSource.kt
index fd000ee..a8743fb 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/common/pip/LegacySizeSpecSource.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/common/pip/LegacySizeSpecSource.kt
@@ -21,7 +21,6 @@
import android.graphics.PointF
import android.util.Size
import com.android.wm.shell.R
-import com.android.wm.shell.pip.PipDisplayLayoutState
class LegacySizeSpecSource(
private val context: Context,
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PhonePipKeepClearAlgorithm.java b/libs/WindowManager/Shell/src/com/android/wm/shell/common/pip/PhonePipKeepClearAlgorithm.java
similarity index 96%
rename from libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PhonePipKeepClearAlgorithm.java
rename to libs/WindowManager/Shell/src/com/android/wm/shell/common/pip/PhonePipKeepClearAlgorithm.java
index 2d34035..133242d 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PhonePipKeepClearAlgorithm.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/common/pip/PhonePipKeepClearAlgorithm.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2022 The Android Open Source Project
+ * Copyright (C) 2023 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.wm.shell.pip.phone;
+package com.android.wm.shell.common.pip;
import android.content.Context;
import android.content.res.Resources;
@@ -24,9 +24,6 @@
import android.view.Gravity;
import com.android.wm.shell.R;
-import com.android.wm.shell.pip.PipBoundsAlgorithm;
-import com.android.wm.shell.pip.PipBoundsState;
-import com.android.wm.shell.pip.PipKeepClearAlgorithmInterface;
import java.util.Set;
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/common/pip/PhoneSizeSpecSource.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/common/pip/PhoneSizeSpecSource.kt
index c563068..18c7bdd 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/common/pip/PhoneSizeSpecSource.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/common/pip/PhoneSizeSpecSource.kt
@@ -21,7 +21,6 @@
import android.os.SystemProperties
import android.util.Size
import com.android.wm.shell.R
-import com.android.wm.shell.pip.PipDisplayLayoutState
import java.io.PrintWriter
class PhoneSizeSpecSource(
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipBoundsAlgorithm.java b/libs/WindowManager/Shell/src/com/android/wm/shell/common/pip/PipBoundsAlgorithm.java
similarity index 97%
rename from libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipBoundsAlgorithm.java
rename to libs/WindowManager/Shell/src/com/android/wm/shell/common/pip/PipBoundsAlgorithm.java
index 4fef672..a9f687f 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipBoundsAlgorithm.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/common/pip/PipBoundsAlgorithm.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2020 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,7 +14,7 @@
* limitations under the License.
*/
-package com.android.wm.shell.pip;
+package com.android.wm.shell.common.pip;
import android.annotation.NonNull;
import android.annotation.Nullable;
@@ -28,8 +28,6 @@
import android.view.Gravity;
import com.android.wm.shell.R;
-import com.android.wm.shell.common.pip.PipUtils;
-import com.android.wm.shell.common.pip.SizeSpecSource;
import java.io.PrintWriter;
@@ -202,7 +200,8 @@
*
* @return {@code false} if the given source is too small to use for the entering animation.
*/
- static boolean isSourceRectHintValidForEnterPip(Rect sourceRectHint, Rect destinationBounds) {
+ public static boolean isSourceRectHintValidForEnterPip(Rect sourceRectHint,
+ Rect destinationBounds) {
return sourceRectHint != null
&& sourceRectHint.width() > destinationBounds.width()
&& sourceRectHint.height() > destinationBounds.height();
@@ -224,7 +223,7 @@
}
/**
- * @return whether the given {@param aspectRatio} is valid.
+ * @return whether the given aspectRatio is valid.
*/
public boolean isValidPictureInPictureAspectRatio(float aspectRatio) {
return Float.compare(mMinAspectRatio, aspectRatio) <= 0
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipBoundsState.java b/libs/WindowManager/Shell/src/com/android/wm/shell/common/pip/PipBoundsState.java
similarity index 97%
rename from libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipBoundsState.java
rename to libs/WindowManager/Shell/src/com/android/wm/shell/common/pip/PipBoundsState.java
index 279ffc5..3b32b6c 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipBoundsState.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/common/pip/PipBoundsState.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2020 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,7 +14,7 @@
* limitations under the License.
*/
-package com.android.wm.shell.pip;
+package com.android.wm.shell.common.pip;
import android.annotation.IntDef;
import android.annotation.NonNull;
@@ -36,7 +36,6 @@
import com.android.internal.util.function.TriConsumer;
import com.android.wm.shell.R;
import com.android.wm.shell.common.DisplayLayout;
-import com.android.wm.shell.common.pip.SizeSpecSource;
import com.android.wm.shell.protolog.ShellProtoLogGroup;
import java.io.PrintWriter;
@@ -314,8 +313,11 @@
return mPipDisplayLayoutState.getDisplayLayout();
}
+ /**
+ * Clears the PiP re-entry state.
+ */
@VisibleForTesting
- void clearReentryState() {
+ public void clearReentryState() {
mPipReentryState = null;
}
@@ -400,11 +402,18 @@
mNamedUnrestrictedKeepClearAreas.remove(name);
}
+
+ /**
+ * @return restricted keep clear areas.
+ */
@NonNull
public Set<Rect> getRestrictedKeepClearAreas() {
return mRestrictedKeepClearAreas;
}
+ /**
+ * @return unrestricted keep clear areas.
+ */
@NonNull
public Set<Rect> getUnrestrictedKeepClearAreas() {
if (mNamedUnrestrictedKeepClearAreas.isEmpty()) return mUnrestrictedKeepClearAreas;
@@ -561,7 +570,11 @@
}
}
- static final class PipReentryState {
+ /**
+ * Represents the state of pip to potentially restore upon reentry.
+ */
+ @VisibleForTesting
+ public static final class PipReentryState {
private static final String TAG = PipReentryState.class.getSimpleName();
private final @Nullable Size mSize;
@@ -573,11 +586,11 @@
}
@Nullable
- Size getSize() {
+ public Size getSize() {
return mSize;
}
- float getSnapFraction() {
+ public float getSnapFraction() {
return mSnapFraction;
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipDisplayLayoutState.java b/libs/WindowManager/Shell/src/com/android/wm/shell/common/pip/PipDisplayLayoutState.java
similarity index 98%
rename from libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipDisplayLayoutState.java
rename to libs/WindowManager/Shell/src/com/android/wm/shell/common/pip/PipDisplayLayoutState.java
index 4aa260b..ed42117 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipDisplayLayoutState.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/common/pip/PipDisplayLayoutState.java
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.wm.shell.pip;
+package com.android.wm.shell.common.pip;
import static com.android.wm.shell.common.pip.PipUtils.dpToPx;
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipKeepClearAlgorithmInterface.java b/libs/WindowManager/Shell/src/com/android/wm/shell/common/pip/PipKeepClearAlgorithmInterface.java
similarity index 95%
rename from libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipKeepClearAlgorithmInterface.java
rename to libs/WindowManager/Shell/src/com/android/wm/shell/common/pip/PipKeepClearAlgorithmInterface.java
index 5045cf9..954233c 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipKeepClearAlgorithmInterface.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/common/pip/PipKeepClearAlgorithmInterface.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2022 The Android Open Source Project
+ * Copyright (C) 2023 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.wm.shell.pip;
+package com.android.wm.shell.common.pip;
import android.graphics.Rect;
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipPinchResizingAlgorithm.java b/libs/WindowManager/Shell/src/com/android/wm/shell/common/pip/PipPinchResizingAlgorithm.java
similarity index 96%
rename from libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipPinchResizingAlgorithm.java
rename to libs/WindowManager/Shell/src/com/android/wm/shell/common/pip/PipPinchResizingAlgorithm.java
index 23153be72..02b3a88 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipPinchResizingAlgorithm.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/common/pip/PipPinchResizingAlgorithm.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2020 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.
@@ -13,7 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package com.android.wm.shell.pip.phone;
+package com.android.wm.shell.common.pip;
import android.graphics.Point;
import android.graphics.PointF;
@@ -35,7 +35,7 @@
private final PointF mTmpLastCentroid = new PointF();
/**
- * Updates {@param resizeBoundsOut} with the new bounds of the PIP, and returns the angle in
+ * Updates resizeBoundsOut with the new bounds of the PIP, and returns the angle in
* degrees that the PIP should be rotated.
*/
public float calculateBoundsAndAngle(PointF downPoint, PointF downSecondPoint,
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipSnapAlgorithm.java b/libs/WindowManager/Shell/src/com/android/wm/shell/common/pip/PipSnapAlgorithm.java
similarity index 84%
rename from libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipSnapAlgorithm.java
rename to libs/WindowManager/Shell/src/com/android/wm/shell/common/pip/PipSnapAlgorithm.java
index dd30137..007052e 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipSnapAlgorithm.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/common/pip/PipSnapAlgorithm.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2020 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,11 +14,11 @@
* limitations under the License.
*/
-package com.android.wm.shell.pip;
+package com.android.wm.shell.common.pip;
-import static com.android.wm.shell.pip.PipBoundsState.STASH_TYPE_LEFT;
-import static com.android.wm.shell.pip.PipBoundsState.STASH_TYPE_NONE;
-import static com.android.wm.shell.pip.PipBoundsState.STASH_TYPE_RIGHT;
+import static com.android.wm.shell.common.pip.PipBoundsState.STASH_TYPE_LEFT;
+import static com.android.wm.shell.common.pip.PipBoundsState.STASH_TYPE_NONE;
+import static com.android.wm.shell.common.pip.PipBoundsState.STASH_TYPE_RIGHT;
import android.graphics.Rect;
@@ -39,14 +39,14 @@
}
/**
- * @return returns a fraction that describes where along the {@param movementBounds} the
- * {@param stackBounds} are. If the {@param stackBounds} are not currently on the
- * {@param movementBounds} exactly, then they will be snapped to the movement bounds.
+ * @return returns a fraction that describes where along the movementBounds the
+ * stackBounds are. If the stackBounds are not currently on the
+ * movementBounds exactly, then they will be snapped to the movement bounds.
* stashType dictates whether the PiP is stashed (off-screen) or not. If
* that's the case, we will have to do some math to calculate the snap fraction
* correctly.
*
- * The fraction is defined in a clockwise fashion against the {@param movementBounds}:
+ * The fraction is defined in a clockwise fashion against the movementBounds:
*
* 0 1
* 4 +---+ 1
@@ -58,10 +58,10 @@
@PipBoundsState.StashType int stashType) {
final Rect tmpBounds = new Rect();
snapRectToClosestEdge(stackBounds, movementBounds, tmpBounds, stashType);
- final float widthFraction = (float) (tmpBounds.left - movementBounds.left) /
- movementBounds.width();
- final float heightFraction = (float) (tmpBounds.top - movementBounds.top) /
- movementBounds.height();
+ final float widthFraction = (float) (tmpBounds.left - movementBounds.left)
+ / movementBounds.width();
+ final float heightFraction = (float) (tmpBounds.top - movementBounds.top)
+ / movementBounds.height();
if (tmpBounds.top == movementBounds.top) {
return widthFraction;
} else if (tmpBounds.left == movementBounds.right) {
@@ -74,10 +74,10 @@
}
/**
- * Moves the {@param stackBounds} along the {@param movementBounds} to the given snap fraction.
+ * Moves the stackBounds along the movementBounds to the given snap fraction.
* See {@link #getSnapFraction(Rect, Rect)}.
*
- * The fraction is define in a clockwise fashion against the {@param movementBounds}:
+ * The fraction is define in a clockwise fashion against the movementBounds:
*
* 0 1
* 4 +---+ 1
@@ -122,11 +122,11 @@
}
/**
- * Snaps the {@param stackBounds} to the closest edge of the {@param movementBounds} and writes
- * the new bounds out to {@param boundsOut}.
+ * Snaps the stackBounds to the closest edge of the movementBounds and writes
+ * the new bounds out to boundsOut.
*/
@VisibleForTesting
- void snapRectToClosestEdge(Rect stackBounds, Rect movementBounds, Rect boundsOut,
+ public void snapRectToClosestEdge(Rect stackBounds, Rect movementBounds, Rect boundsOut,
@PipBoundsState.StashType int stashType) {
int leftEdge = stackBounds.left;
if (stashType == STASH_TYPE_LEFT) {
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 e28b8d7..998cd5d 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
@@ -59,13 +59,19 @@
import com.android.wm.shell.common.annotations.ShellBackgroundThread;
import com.android.wm.shell.common.annotations.ShellMainThread;
import com.android.wm.shell.common.annotations.ShellSplashscreenThread;
+import com.android.wm.shell.common.pip.PhonePipKeepClearAlgorithm;
+import com.android.wm.shell.common.pip.PhoneSizeSpecSource;
+import com.android.wm.shell.common.pip.PipBoundsAlgorithm;
+import com.android.wm.shell.common.pip.PipBoundsState;
+import com.android.wm.shell.common.pip.PipDisplayLayoutState;
import com.android.wm.shell.common.pip.PipMediaController;
+import com.android.wm.shell.common.pip.PipSnapAlgorithm;
import com.android.wm.shell.common.pip.PipUiEventLogger;
+import com.android.wm.shell.common.pip.SizeSpecSource;
import com.android.wm.shell.compatui.CompatUIConfiguration;
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.DesktopModeController;
import com.android.wm.shell.desktopmode.DesktopModeStatus;
import com.android.wm.shell.desktopmode.DesktopModeTaskRepository;
import com.android.wm.shell.desktopmode.DesktopTasksController;
@@ -102,13 +108,13 @@
import com.android.wm.shell.unfold.UnfoldTransitionHandler;
import com.android.wm.shell.windowdecor.WindowDecorViewModel;
+import java.util.Optional;
+
import dagger.BindsOptionalOf;
import dagger.Lazy;
import dagger.Module;
import dagger.Provides;
-import java.util.Optional;
-
/**
* Provides basic dependencies from {@link com.android.wm.shell}, these dependencies are only
* accessible from components within the WM subcomponent (can be explicitly exposed to the
@@ -354,6 +360,42 @@
return new PipMediaController(context, mainHandler);
}
+ @WMSingleton
+ @Provides
+ static SizeSpecSource provideSizeSpecSource(Context context,
+ PipDisplayLayoutState pipDisplayLayoutState) {
+ return new PhoneSizeSpecSource(context, pipDisplayLayoutState);
+ }
+
+ @WMSingleton
+ @Provides
+ static PipBoundsState providePipBoundsState(Context context,
+ SizeSpecSource sizeSpecSource, PipDisplayLayoutState pipDisplayLayoutState) {
+ return new PipBoundsState(context, sizeSpecSource, pipDisplayLayoutState);
+ }
+
+
+ @WMSingleton
+ @Provides
+ static PipSnapAlgorithm providePipSnapAlgorithm() {
+ return new PipSnapAlgorithm();
+ }
+
+ @WMSingleton
+ @Provides
+ static PhonePipKeepClearAlgorithm providePhonePipKeepClearAlgorithm(Context context) {
+ return new PhonePipKeepClearAlgorithm(context);
+ }
+
+ @WMSingleton
+ @Provides
+ static PipBoundsAlgorithm providesPipBoundsAlgorithm(Context context,
+ PipBoundsState pipBoundsState, PipSnapAlgorithm pipSnapAlgorithm,
+ PhonePipKeepClearAlgorithm pipKeepClearAlgorithm,
+ PipDisplayLayoutState pipDisplayLayoutState, SizeSpecSource sizeSpecSource) {
+ return new PipBoundsAlgorithm(context, pipBoundsState, pipSnapAlgorithm,
+ pipKeepClearAlgorithm, pipDisplayLayoutState, sizeSpecSource);
+ }
//
// Bubbles (optional feature)
@@ -757,30 +799,10 @@
@WMSingleton
@Provides
static Optional<DesktopMode> provideDesktopMode(
- Optional<DesktopModeController> desktopModeController,
Optional<DesktopTasksController> desktopTasksController) {
- if (DesktopModeStatus.isProto2Enabled()) {
- return desktopTasksController.map(DesktopTasksController::asDesktopMode);
- }
- return desktopModeController.map(DesktopModeController::asDesktopMode);
+ return desktopTasksController.map(DesktopTasksController::asDesktopMode);
}
- @BindsOptionalOf
- @DynamicOverride
- abstract DesktopModeController optionalDesktopModeController();
-
- @WMSingleton
- @Provides
- static Optional<DesktopModeController> provideDesktopModeController(
- @DynamicOverride Optional<Lazy<DesktopModeController>> desktopModeController) {
- // Use optional-of-lazy for the dependency that this provider relies on.
- // Lazy ensures that this provider will not be the cause the dependency is created
- // when it will not be returned due to the condition below.
- if (DesktopModeStatus.isProto1Enabled()) {
- return desktopModeController.map(Lazy::get);
- }
- return Optional.empty();
- }
@BindsOptionalOf
@DynamicOverride
@@ -793,7 +815,7 @@
// Use optional-of-lazy for the dependency that this provider relies on.
// Lazy ensures that this provider will not be the cause the dependency is created
// when it will not be returned due to the condition below.
- if (DesktopModeStatus.isProto2Enabled()) {
+ if (DesktopModeStatus.isEnabled()) {
return desktopTasksController.map(Lazy::get);
}
return Optional.empty();
@@ -810,7 +832,7 @@
// Use optional-of-lazy for the dependency that this provider relies on.
// Lazy ensures that this provider will not be the cause the dependency is created
// when it will not be returned due to the condition below.
- if (DesktopModeStatus.isAnyEnabled()) {
+ if (DesktopModeStatus.isEnabled()) {
return desktopModeTaskRepository.map(Lazy::get);
}
return Optional.empty();
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 36d2a70..e9f3e1a 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
@@ -54,7 +54,6 @@
import com.android.wm.shell.common.annotations.ShellMainThread;
import com.android.wm.shell.dagger.back.ShellBackAnimationModule;
import com.android.wm.shell.dagger.pip.PipModule;
-import com.android.wm.shell.desktopmode.DesktopModeController;
import com.android.wm.shell.desktopmode.DesktopModeStatus;
import com.android.wm.shell.desktopmode.DesktopModeTaskRepository;
import com.android.wm.shell.desktopmode.DesktopTasksController;
@@ -167,6 +166,7 @@
@ShellMainThread Handler mainHandler,
@ShellBackgroundThread ShellExecutor bgExecutor,
TaskViewTransitions taskViewTransitions,
+ Transitions transitions,
SyncTransactionQueue syncQueue,
IWindowManager wmService) {
return new BubbleController(context, shellInit, shellCommandHandler, shellController, data,
@@ -176,7 +176,8 @@
statusBarService, windowManager, windowManagerShellWrapper, userManager,
launcherApps, logger, taskStackListener, organizer, positioner, displayController,
oneHandedOptional, dragAndDropController, mainExecutor, mainHandler, bgExecutor,
- taskViewTransitions, syncQueue, wmService, ProdBubbleProperties.INSTANCE);
+ taskViewTransitions, transitions, syncQueue, wmService,
+ ProdBubbleProperties.INSTANCE);
}
//
@@ -195,9 +196,9 @@
ShellController shellController,
SyncTransactionQueue syncQueue,
Transitions transitions,
- Optional<DesktopModeController> desktopModeController,
- Optional<DesktopTasksController> desktopTasksController) {
- if (DesktopModeStatus.isAnyEnabled()) {
+ Optional<DesktopTasksController> desktopTasksController,
+ RootTaskDisplayAreaOrganizer rootTaskDisplayAreaOrganizer) {
+ if (DesktopModeStatus.isEnabled()) {
return new DesktopModeWindowDecorViewModel(
context,
mainHandler,
@@ -208,8 +209,8 @@
shellController,
syncQueue,
transitions,
- desktopModeController,
- desktopTasksController);
+ desktopTasksController,
+ rootTaskDisplayAreaOrganizer);
}
return new CaptionWindowDecorViewModel(
context,
@@ -349,13 +350,12 @@
@Nullable PipTransitionController pipTransitionController,
Optional<RecentsTransitionHandler> recentsTransitionHandler,
KeyguardTransitionHandler keyguardTransitionHandler,
- Optional<DesktopModeController> desktopModeController,
Optional<DesktopTasksController> desktopTasksController,
Optional<UnfoldTransitionHandler> unfoldHandler,
Transitions transitions) {
return new DefaultMixedHandler(shellInit, transitions, splitScreenOptional,
pipTransitionController, recentsTransitionHandler,
- keyguardTransitionHandler, desktopModeController, desktopTasksController,
+ keyguardTransitionHandler, desktopTasksController,
unfoldHandler);
}
@@ -467,24 +467,6 @@
@WMSingleton
@Provides
@DynamicOverride
- static DesktopModeController provideDesktopModeController(Context context,
- ShellInit shellInit,
- ShellController shellController,
- ShellTaskOrganizer shellTaskOrganizer,
- RootTaskDisplayAreaOrganizer rootTaskDisplayAreaOrganizer,
- Transitions transitions,
- @DynamicOverride DesktopModeTaskRepository desktopModeTaskRepository,
- @ShellMainThread Handler mainHandler,
- @ShellMainThread ShellExecutor mainExecutor
- ) {
- return new DesktopModeController(context, shellInit, shellController, shellTaskOrganizer,
- rootTaskDisplayAreaOrganizer, transitions, desktopModeTaskRepository, mainHandler,
- mainExecutor);
- }
-
- @WMSingleton
- @Provides
- @DynamicOverride
static DesktopTasksController provideDesktopTasksController(
Context context,
ShellInit shellInit,
@@ -549,8 +531,7 @@
@ShellCreateTriggerOverride
@Provides
static Object provideIndependentShellComponentsToCreate(
- DefaultMixedHandler defaultMixedHandler,
- Optional<DesktopModeController> desktopModeController) {
+ DefaultMixedHandler defaultMixedHandler) {
return new Object();
}
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/pip/Pip1Module.java b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/pip/Pip1Module.java
index 4e92ca1..ba882c4 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/pip/Pip1Module.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/pip/Pip1Module.java
@@ -30,9 +30,13 @@
import com.android.wm.shell.common.TabletopModeController;
import com.android.wm.shell.common.TaskStackListenerImpl;
import com.android.wm.shell.common.annotations.ShellMainThread;
-import com.android.wm.shell.common.pip.PhoneSizeSpecSource;
+import com.android.wm.shell.common.pip.PhonePipKeepClearAlgorithm;
import com.android.wm.shell.common.pip.PipAppOpsListener;
+import com.android.wm.shell.common.pip.PipBoundsAlgorithm;
+import com.android.wm.shell.common.pip.PipBoundsState;
+import com.android.wm.shell.common.pip.PipDisplayLayoutState;
import com.android.wm.shell.common.pip.PipMediaController;
+import com.android.wm.shell.common.pip.PipSnapAlgorithm;
import com.android.wm.shell.common.pip.PipUiEventLogger;
import com.android.wm.shell.common.pip.PipUtils;
import com.android.wm.shell.common.pip.SizeSpecSource;
@@ -41,17 +45,12 @@
import com.android.wm.shell.onehanded.OneHandedController;
import com.android.wm.shell.pip.Pip;
import com.android.wm.shell.pip.PipAnimationController;
-import com.android.wm.shell.pip.PipBoundsAlgorithm;
-import com.android.wm.shell.pip.PipBoundsState;
-import com.android.wm.shell.pip.PipDisplayLayoutState;
import com.android.wm.shell.pip.PipParamsChangedForwarder;
-import com.android.wm.shell.pip.PipSnapAlgorithm;
import com.android.wm.shell.pip.PipSurfaceTransactionHelper;
import com.android.wm.shell.pip.PipTaskOrganizer;
import com.android.wm.shell.pip.PipTransition;
import com.android.wm.shell.pip.PipTransitionController;
import com.android.wm.shell.pip.PipTransitionState;
-import com.android.wm.shell.pip.phone.PhonePipKeepClearAlgorithm;
import com.android.wm.shell.pip.phone.PhonePipMenuController;
import com.android.wm.shell.pip.phone.PipController;
import com.android.wm.shell.pip.phone.PipMotionHelper;
@@ -119,35 +118,6 @@
}
}
- @WMSingleton
- @Provides
- static PipBoundsState providePipBoundsState(Context context,
- SizeSpecSource sizeSpecSource, PipDisplayLayoutState pipDisplayLayoutState) {
- return new PipBoundsState(context, sizeSpecSource, pipDisplayLayoutState);
- }
-
- @WMSingleton
- @Provides
- static PipSnapAlgorithm providePipSnapAlgorithm() {
- return new PipSnapAlgorithm();
- }
-
- @WMSingleton
- @Provides
- static PhonePipKeepClearAlgorithm providePhonePipKeepClearAlgorithm(Context context) {
- return new PhonePipKeepClearAlgorithm(context);
- }
-
- @WMSingleton
- @Provides
- static PipBoundsAlgorithm providesPipBoundsAlgorithm(Context context,
- PipBoundsState pipBoundsState, PipSnapAlgorithm pipSnapAlgorithm,
- PhonePipKeepClearAlgorithm pipKeepClearAlgorithm,
- PipDisplayLayoutState pipDisplayLayoutState, SizeSpecSource sizeSpecSource) {
- return new PipBoundsAlgorithm(context, pipBoundsState, pipSnapAlgorithm,
- pipKeepClearAlgorithm, pipDisplayLayoutState, sizeSpecSource);
- }
-
// Handler is used by Icon.loadDrawableAsync
@WMSingleton
@Provides
@@ -213,13 +183,6 @@
@WMSingleton
@Provides
- static PipAnimationController providePipAnimationController(PipSurfaceTransactionHelper
- pipSurfaceTransactionHelper) {
- return new PipAnimationController(pipSurfaceTransactionHelper);
- }
-
- @WMSingleton
- @Provides
static PipTransition providePipTransition(Context context,
ShellInit shellInit, ShellTaskOrganizer shellTaskOrganizer, Transitions transitions,
PipAnimationController pipAnimationController, PipBoundsAlgorithm pipBoundsAlgorithm,
@@ -235,13 +198,6 @@
@WMSingleton
@Provides
- static SizeSpecSource provideSizeSpecSource(Context context,
- PipDisplayLayoutState pipDisplayLayoutState) {
- return new PhoneSizeSpecSource(context, pipDisplayLayoutState);
- }
-
- @WMSingleton
- @Provides
static PipAppOpsListener providePipAppOpsListener(Context context,
PipTouchHandler pipTouchHandler,
@ShellMainThread ShellExecutor mainExecutor) {
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/pip/Pip1SharedModule.java b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/pip/Pip1SharedModule.java
index c4ca501..b42372b 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/pip/Pip1SharedModule.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/pip/Pip1SharedModule.java
@@ -19,6 +19,7 @@
import android.content.Context;
import com.android.wm.shell.dagger.WMSingleton;
+import com.android.wm.shell.pip.PipAnimationController;
import com.android.wm.shell.pip.PipSurfaceTransactionHelper;
import dagger.Module;
@@ -35,4 +36,11 @@
static PipSurfaceTransactionHelper providePipSurfaceTransactionHelper(Context context) {
return new PipSurfaceTransactionHelper(context);
}
+
+ @WMSingleton
+ @Provides
+ static PipAnimationController providePipAnimationController(PipSurfaceTransactionHelper
+ pipSurfaceTransactionHelper) {
+ return new PipAnimationController(pipSurfaceTransactionHelper);
+ }
}
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 8dec4ea..af97cf6 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
@@ -16,11 +16,16 @@
package com.android.wm.shell.dagger.pip;
-import android.annotation.Nullable;
+import android.annotation.NonNull;
+import com.android.wm.shell.ShellTaskOrganizer;
+import com.android.wm.shell.common.pip.PipBoundsAlgorithm;
+import com.android.wm.shell.common.pip.PipBoundsState;
import com.android.wm.shell.dagger.WMShellBaseModule;
import com.android.wm.shell.dagger.WMSingleton;
import com.android.wm.shell.pip2.PipTransition;
+import com.android.wm.shell.sysui.ShellInit;
+import com.android.wm.shell.transition.Transitions;
import dagger.Module;
import dagger.Provides;
@@ -33,8 +38,12 @@
public abstract class Pip2Module {
@WMSingleton
@Provides
- @Nullable
- static PipTransition providePipTransition() {
- return null;
+ static PipTransition providePipTransition(@NonNull ShellInit shellInit,
+ @NonNull ShellTaskOrganizer shellTaskOrganizer,
+ @NonNull Transitions transitions,
+ PipBoundsState pipBoundsState,
+ PipBoundsAlgorithm pipBoundsAlgorithm) {
+ return new PipTransition(shellInit, shellTaskOrganizer, transitions, pipBoundsState, null,
+ pipBoundsAlgorithm);
}
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/pip/PipModule.java b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/pip/PipModule.java
index 9c9364e..570f0a3 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/pip/PipModule.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/pip/PipModule.java
@@ -16,8 +16,6 @@
package com.android.wm.shell.dagger.pip;
-import android.annotation.Nullable;
-
import com.android.wm.shell.common.pip.PipUtils;
import com.android.wm.shell.dagger.WMSingleton;
import com.android.wm.shell.pip.PipTransitionController;
@@ -38,8 +36,8 @@
@Provides
static PipTransitionController providePipTransitionController(
com.android.wm.shell.pip.PipTransition legacyPipTransition,
- @Nullable com.android.wm.shell.pip2.PipTransition newPipTransition) {
- if (PipUtils.isPip2ExperimentEnabled() && newPipTransition != null) {
+ com.android.wm.shell.pip2.PipTransition newPipTransition) {
+ if (PipUtils.isPip2ExperimentEnabled()) {
return newPipTransition;
} else {
return legacyPipTransition;
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/pip/TvPipModule.java b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/pip/TvPipModule.java
index a6ff9ec..a9675f9 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/pip/TvPipModule.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/pip/TvPipModule.java
@@ -30,16 +30,15 @@
import com.android.wm.shell.common.annotations.ShellMainThread;
import com.android.wm.shell.common.pip.LegacySizeSpecSource;
import com.android.wm.shell.common.pip.PipAppOpsListener;
+import com.android.wm.shell.common.pip.PipDisplayLayoutState;
import com.android.wm.shell.common.pip.PipMediaController;
+import com.android.wm.shell.common.pip.PipSnapAlgorithm;
import com.android.wm.shell.common.pip.PipUiEventLogger;
-import com.android.wm.shell.common.pip.SizeSpecSource;
import com.android.wm.shell.dagger.WMShellBaseModule;
import com.android.wm.shell.dagger.WMSingleton;
import com.android.wm.shell.pip.Pip;
import com.android.wm.shell.pip.PipAnimationController;
-import com.android.wm.shell.pip.PipDisplayLayoutState;
import com.android.wm.shell.pip.PipParamsChangedForwarder;
-import com.android.wm.shell.pip.PipSnapAlgorithm;
import com.android.wm.shell.pip.PipSurfaceTransactionHelper;
import com.android.wm.shell.pip.PipTaskOrganizer;
import com.android.wm.shell.pip.PipTransitionController;
@@ -131,15 +130,9 @@
@WMSingleton
@Provides
- static PipSnapAlgorithm providePipSnapAlgorithm() {
- return new PipSnapAlgorithm();
- }
-
- @WMSingleton
- @Provides
static TvPipBoundsAlgorithm provideTvPipBoundsAlgorithm(Context context,
TvPipBoundsState tvPipBoundsState, PipSnapAlgorithm pipSnapAlgorithm,
- PipDisplayLayoutState pipDisplayLayoutState, SizeSpecSource sizeSpecSource) {
+ PipDisplayLayoutState pipDisplayLayoutState, LegacySizeSpecSource sizeSpecSource) {
return new TvPipBoundsAlgorithm(context, tvPipBoundsState, pipSnapAlgorithm,
pipDisplayLayoutState, sizeSpecSource);
}
@@ -147,13 +140,13 @@
@WMSingleton
@Provides
static TvPipBoundsState provideTvPipBoundsState(Context context,
- SizeSpecSource sizeSpecSource, PipDisplayLayoutState pipDisplayLayoutState) {
+ LegacySizeSpecSource sizeSpecSource, PipDisplayLayoutState pipDisplayLayoutState) {
return new TvPipBoundsState(context, sizeSpecSource, pipDisplayLayoutState);
}
@WMSingleton
@Provides
- static SizeSpecSource provideSizeSpecSource(Context context,
+ static LegacySizeSpecSource provideSizeSpecSource(Context context,
PipDisplayLayoutState pipDisplayLayoutState) {
return new LegacySizeSpecSource(context, pipDisplayLayoutState);
}
@@ -200,13 +193,6 @@
@WMSingleton
@Provides
- static PipAnimationController providePipAnimationController(PipSurfaceTransactionHelper
- pipSurfaceTransactionHelper) {
- return new PipAnimationController(pipSurfaceTransactionHelper);
- }
-
- @WMSingleton
- @Provides
static PipTransitionState providePipTransitionState() {
return new PipTransitionState();
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeController.java
deleted file mode 100644
index 5b24d7a..0000000
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeController.java
+++ /dev/null
@@ -1,557 +0,0 @@
-/*
- * Copyright (C) 2022 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 static android.app.WindowConfiguration.ACTIVITY_TYPE_HOME;
-import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD;
-import static android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM;
-import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
-import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED;
-import static android.view.WindowManager.TRANSIT_CHANGE;
-import static android.view.WindowManager.TRANSIT_NONE;
-import static android.view.WindowManager.TRANSIT_OPEN;
-import static android.view.WindowManager.TRANSIT_TO_FRONT;
-
-import static com.android.wm.shell.common.ExecutorUtils.executeRemoteCallWithTaskPermission;
-import static com.android.wm.shell.protolog.ShellProtoLogGroup.WM_SHELL_DESKTOP_MODE;
-import static com.android.wm.shell.sysui.ShellSharedConstants.KEY_EXTRA_SHELL_DESKTOP_MODE;
-
-import android.app.ActivityManager.RunningTaskInfo;
-import android.app.WindowConfiguration;
-import android.content.Context;
-import android.content.res.TypedArray;
-import android.database.ContentObserver;
-import android.graphics.Region;
-import android.net.Uri;
-import android.os.Handler;
-import android.os.IBinder;
-import android.os.RemoteException;
-import android.os.UserHandle;
-import android.provider.Settings;
-import android.util.ArraySet;
-import android.view.SurfaceControl;
-import android.view.WindowManager;
-import android.window.DisplayAreaInfo;
-import android.window.TransitionInfo;
-import android.window.TransitionRequestInfo;
-import android.window.WindowContainerTransaction;
-
-import androidx.annotation.BinderThread;
-import androidx.annotation.NonNull;
-import androidx.annotation.Nullable;
-
-import com.android.internal.annotations.VisibleForTesting;
-import com.android.internal.protolog.common.ProtoLog;
-import com.android.wm.shell.RootTaskDisplayAreaOrganizer;
-import com.android.wm.shell.ShellTaskOrganizer;
-import com.android.wm.shell.common.ExternalInterfaceBinder;
-import com.android.wm.shell.common.RemoteCallable;
-import com.android.wm.shell.common.ShellExecutor;
-import com.android.wm.shell.common.annotations.ExternalThread;
-import com.android.wm.shell.common.annotations.ShellMainThread;
-import com.android.wm.shell.sysui.ShellController;
-import com.android.wm.shell.sysui.ShellInit;
-import com.android.wm.shell.transition.Transitions;
-
-import java.util.ArrayList;
-import java.util.Comparator;
-import java.util.List;
-import java.util.concurrent.Executor;
-import java.util.function.Consumer;
-
-/**
- * Handles windowing changes when desktop mode system setting changes
- */
-public class DesktopModeController implements RemoteCallable<DesktopModeController>,
- Transitions.TransitionHandler {
-
- private final Context mContext;
- private final ShellController mShellController;
- private final ShellTaskOrganizer mShellTaskOrganizer;
- private final RootTaskDisplayAreaOrganizer mRootTaskDisplayAreaOrganizer;
- private final Transitions mTransitions;
- private final DesktopModeTaskRepository mDesktopModeTaskRepository;
- private final ShellExecutor mMainExecutor;
- private final DesktopModeImpl mDesktopModeImpl = new DesktopModeImpl();
- private final SettingsObserver mSettingsObserver;
-
- public DesktopModeController(Context context,
- ShellInit shellInit,
- ShellController shellController,
- ShellTaskOrganizer shellTaskOrganizer,
- RootTaskDisplayAreaOrganizer rootTaskDisplayAreaOrganizer,
- Transitions transitions,
- DesktopModeTaskRepository desktopModeTaskRepository,
- @ShellMainThread Handler mainHandler,
- @ShellMainThread ShellExecutor mainExecutor) {
- mContext = context;
- mShellController = shellController;
- mShellTaskOrganizer = shellTaskOrganizer;
- mRootTaskDisplayAreaOrganizer = rootTaskDisplayAreaOrganizer;
- mTransitions = transitions;
- mDesktopModeTaskRepository = desktopModeTaskRepository;
- mMainExecutor = mainExecutor;
- mSettingsObserver = new SettingsObserver(mContext, mainHandler);
- if (DesktopModeStatus.isProto1Enabled()) {
- shellInit.addInitCallback(this::onInit, this);
- }
- }
-
- private void onInit() {
- ProtoLog.d(WM_SHELL_DESKTOP_MODE, "Initialize DesktopModeController");
- mShellController.addExternalInterface(KEY_EXTRA_SHELL_DESKTOP_MODE,
- this::createExternalInterface, this);
- mSettingsObserver.observe();
- if (DesktopModeStatus.isActive(mContext)) {
- updateDesktopModeActive(true);
- }
- mTransitions.addHandler(this);
- }
-
- @Override
- public Context getContext() {
- return mContext;
- }
-
- @Override
- public ShellExecutor getRemoteCallExecutor() {
- return mMainExecutor;
- }
-
- /**
- * Get connection interface between sysui and shell
- */
- public DesktopMode asDesktopMode() {
- return mDesktopModeImpl;
- }
-
- /**
- * Creates a new instance of the external interface to pass to another process.
- */
- private ExternalInterfaceBinder createExternalInterface() {
- return new IDesktopModeImpl(this);
- }
-
- /**
- * Adds a listener to find out about changes in the visibility of freeform tasks.
- *
- * @param listener the listener to add.
- * @param callbackExecutor the executor to call the listener on.
- */
- public void addVisibleTasksListener(DesktopModeTaskRepository.VisibleTasksListener listener,
- Executor callbackExecutor) {
- mDesktopModeTaskRepository.addVisibleTasksListener(listener, callbackExecutor);
- }
-
- /**
- * Adds a listener to track changes to corners of desktop mode tasks.
- * @param listener the listener to add.
- * @param callbackExecutor the executor to call the listener on.
- */
- public void addTaskCornerListener(Consumer<Region> listener,
- Executor callbackExecutor) {
- mDesktopModeTaskRepository.setTaskCornerListener(listener, callbackExecutor);
- }
-
- @VisibleForTesting
- void updateDesktopModeActive(boolean active) {
- ProtoLog.d(WM_SHELL_DESKTOP_MODE, "updateDesktopModeActive: active=%s", active);
-
- int displayId = mContext.getDisplayId();
-
- ArrayList<RunningTaskInfo> runningTasks = mShellTaskOrganizer.getRunningTasks(displayId);
-
- WindowContainerTransaction wct = new WindowContainerTransaction();
- // Reset freeform windowing mode that is set per task level so tasks inherit it
- clearFreeformForStandardTasks(runningTasks, wct);
- if (active) {
- moveHomeBehindVisibleTasks(runningTasks, wct);
- setDisplayAreaWindowingMode(displayId, WINDOWING_MODE_FREEFORM, wct);
- } else {
- clearBoundsForStandardTasks(runningTasks, wct);
- setDisplayAreaWindowingMode(displayId, WINDOWING_MODE_FULLSCREEN, wct);
- }
- if (Transitions.ENABLE_SHELL_TRANSITIONS) {
- mTransitions.startTransition(TRANSIT_CHANGE, wct, null);
- } else {
- mRootTaskDisplayAreaOrganizer.applyTransaction(wct);
- }
- }
-
- private WindowContainerTransaction clearBoundsForStandardTasks(
- ArrayList<RunningTaskInfo> runningTasks, WindowContainerTransaction wct) {
- ProtoLog.v(WM_SHELL_DESKTOP_MODE, "prepareClearBoundsForTasks");
- for (RunningTaskInfo taskInfo : runningTasks) {
- if (taskInfo.getActivityType() == ACTIVITY_TYPE_STANDARD) {
- ProtoLog.v(WM_SHELL_DESKTOP_MODE, "clearing bounds for token=%s taskInfo=%s",
- taskInfo.token, taskInfo);
- wct.setBounds(taskInfo.token, null);
- }
- }
- return wct;
- }
-
- private void clearFreeformForStandardTasks(ArrayList<RunningTaskInfo> runningTasks,
- WindowContainerTransaction wct) {
- ProtoLog.v(WM_SHELL_DESKTOP_MODE, "prepareClearFreeformForTasks");
- for (RunningTaskInfo taskInfo : runningTasks) {
- if (taskInfo.getWindowingMode() == WINDOWING_MODE_FREEFORM
- && taskInfo.getActivityType() == ACTIVITY_TYPE_STANDARD) {
- ProtoLog.v(WM_SHELL_DESKTOP_MODE,
- "clearing windowing mode for token=%s taskInfo=%s", taskInfo.token,
- taskInfo);
- wct.setWindowingMode(taskInfo.token, WINDOWING_MODE_UNDEFINED);
- }
- }
- }
-
- private void moveHomeBehindVisibleTasks(ArrayList<RunningTaskInfo> runningTasks,
- WindowContainerTransaction wct) {
- ProtoLog.v(WM_SHELL_DESKTOP_MODE, "moveHomeBehindVisibleTasks");
- RunningTaskInfo homeTask = null;
- ArrayList<RunningTaskInfo> visibleTasks = new ArrayList<>();
- for (RunningTaskInfo taskInfo : runningTasks) {
- if (taskInfo.getActivityType() == ACTIVITY_TYPE_HOME) {
- homeTask = taskInfo;
- } else if (taskInfo.getActivityType() == ACTIVITY_TYPE_STANDARD
- && taskInfo.isVisible()) {
- visibleTasks.add(taskInfo);
- }
- }
- if (homeTask == null) {
- ProtoLog.w(WM_SHELL_DESKTOP_MODE, "moveHomeBehindVisibleTasks: home task not found");
- } else {
- ProtoLog.v(WM_SHELL_DESKTOP_MODE, "moveHomeBehindVisibleTasks: visible tasks %d",
- visibleTasks.size());
- wct.reorder(homeTask.getToken(), true /* onTop */);
- for (RunningTaskInfo task : visibleTasks) {
- wct.reorder(task.getToken(), true /* onTop */);
- }
- }
- }
-
- private void setDisplayAreaWindowingMode(int displayId,
- @WindowConfiguration.WindowingMode int windowingMode, WindowContainerTransaction wct) {
- DisplayAreaInfo displayAreaInfo = mRootTaskDisplayAreaOrganizer.getDisplayAreaInfo(
- displayId);
- if (displayAreaInfo == null) {
- ProtoLog.e(WM_SHELL_DESKTOP_MODE,
- "unable to update windowing mode for display %d display not found", displayId);
- return;
- }
-
- ProtoLog.v(WM_SHELL_DESKTOP_MODE,
- "setWindowingMode: displayId=%d current wmMode=%d new wmMode=%d", displayId,
- displayAreaInfo.configuration.windowConfiguration.getWindowingMode(),
- windowingMode);
-
- wct.setWindowingMode(displayAreaInfo.token, windowingMode);
- }
-
- /**
- * Show apps on desktop
- */
- void showDesktopApps(int displayId) {
- // Bring apps to front, ignoring their visibility status to always ensure they are on top.
- WindowContainerTransaction wct = new WindowContainerTransaction();
- bringDesktopAppsToFront(displayId, wct);
-
- if (!wct.isEmpty()) {
- if (Transitions.ENABLE_SHELL_TRANSITIONS) {
- // TODO(b/268662477): add animation for the transition
- mTransitions.startTransition(TRANSIT_NONE, wct, null /* handler */);
- } else {
- mShellTaskOrganizer.applyTransaction(wct);
- }
- }
- }
-
- /** Get number of tasks that are marked as visible */
- int getVisibleTaskCount(int displayId) {
- return mDesktopModeTaskRepository.getVisibleTaskCount(displayId);
- }
-
- private void bringDesktopAppsToFront(int displayId, WindowContainerTransaction wct) {
- final ArraySet<Integer> activeTasks = mDesktopModeTaskRepository.getActiveTasks(displayId);
- ProtoLog.d(WM_SHELL_DESKTOP_MODE, "bringDesktopAppsToFront: tasks=%s", activeTasks.size());
-
- final List<RunningTaskInfo> taskInfos = new ArrayList<>();
- for (Integer taskId : activeTasks) {
- RunningTaskInfo taskInfo = mShellTaskOrganizer.getRunningTaskInfo(taskId);
- if (taskInfo != null) {
- taskInfos.add(taskInfo);
- }
- }
-
- if (taskInfos.isEmpty()) {
- return;
- }
-
- moveHomeTaskToFront(wct);
-
- ProtoLog.d(WM_SHELL_DESKTOP_MODE,
- "bringDesktopAppsToFront: reordering all active tasks to the front");
- final List<Integer> allTasksInZOrder =
- mDesktopModeTaskRepository.getFreeformTasksInZOrder();
- // Sort by z-order, bottom to top, so that the top-most task is reordered to the top last
- // in the WCT.
- taskInfos.sort(Comparator.comparingInt(task -> -allTasksInZOrder.indexOf(task.taskId)));
- for (RunningTaskInfo task : taskInfos) {
- wct.reorder(task.token, true);
- }
- }
-
- private void moveHomeTaskToFront(WindowContainerTransaction wct) {
- for (RunningTaskInfo task : mShellTaskOrganizer.getRunningTasks(mContext.getDisplayId())) {
- if (task.getActivityType() == ACTIVITY_TYPE_HOME) {
- wct.reorder(task.token, true /* onTop */);
- return;
- }
- }
- }
-
- /**
- * Update corner rects stored for a specific task
- * @param taskId task to update
- * @param taskCorners task's new corner handles
- */
- public void onTaskCornersChanged(int taskId, Region taskCorners) {
- mDesktopModeTaskRepository.updateTaskCorners(taskId, taskCorners);
- }
-
- /**
- * Remove corners saved for a task. Likely used due to task closure.
- * @param taskId task to remove
- */
- public void removeCornersForTask(int taskId) {
- mDesktopModeTaskRepository.removeTaskCorners(taskId);
- }
-
- /**
- * Moves a specifc task to the front.
- * @param taskInfo the task to show in front.
- */
- public void moveTaskToFront(RunningTaskInfo taskInfo) {
- WindowContainerTransaction wct = new WindowContainerTransaction();
- wct.reorder(taskInfo.token, true /* onTop */);
- if (Transitions.ENABLE_SHELL_TRANSITIONS) {
- mTransitions.startTransition(TRANSIT_TO_FRONT, wct, null);
- } else {
- mShellTaskOrganizer.applyTransaction(wct);
- }
- }
-
- /**
- * Turn desktop mode on or off
- * @param active the desired state for desktop mode setting
- */
- public void setDesktopModeActive(boolean active) {
- int value = active ? 1 : 0;
- Settings.System.putInt(mContext.getContentResolver(), Settings.System.DESKTOP_MODE, value);
- }
-
- /**
- * Returns the windowing mode of the display area with the specified displayId.
- * @param displayId
- * @return
- */
- public int getDisplayAreaWindowingMode(int displayId) {
- return mRootTaskDisplayAreaOrganizer.getDisplayAreaInfo(displayId)
- .configuration.windowConfiguration.getWindowingMode();
- }
-
- @Override
- public boolean startAnimation(@NonNull IBinder transition, @NonNull TransitionInfo info,
- @NonNull SurfaceControl.Transaction startTransaction,
- @NonNull SurfaceControl.Transaction finishTransaction,
- @NonNull Transitions.TransitionFinishCallback finishCallback) {
- // This handler should never be the sole handler, so should not animate anything.
- return false;
- }
-
- @Nullable
- @Override
- public WindowContainerTransaction handleRequest(@NonNull IBinder transition,
- @NonNull TransitionRequestInfo request) {
- RunningTaskInfo triggerTask = request.getTriggerTask();
- // Only do anything if we are in desktop mode and opening/moving-to-front a task/app in
- // freeform
- if (!DesktopModeStatus.isActive(mContext)) {
- ProtoLog.d(WM_SHELL_DESKTOP_MODE,
- "skip shell transition request: desktop mode not active");
- return null;
- }
- if (request.getType() != TRANSIT_OPEN && request.getType() != TRANSIT_TO_FRONT) {
- ProtoLog.d(WM_SHELL_DESKTOP_MODE,
- "skip shell transition request: unsupported type %s",
- WindowManager.transitTypeToString(request.getType()));
- return null;
- }
- if (triggerTask == null || triggerTask.getWindowingMode() != WINDOWING_MODE_FREEFORM) {
- ProtoLog.d(WM_SHELL_DESKTOP_MODE, "skip shell transition request: not freeform task");
- return null;
- }
- ProtoLog.d(WM_SHELL_DESKTOP_MODE, "handle shell transition request: %s", request);
-
- WindowContainerTransaction wct = new WindowContainerTransaction();
- bringDesktopAppsToFront(triggerTask.displayId, wct);
- wct.reorder(triggerTask.token, true /* onTop */);
-
- return wct;
- }
-
- /**
- * Applies the proper surface states (rounded corners) to tasks when desktop mode is active.
- * This is intended to be used when desktop mode is part of another animation but isn't, itself,
- * animating.
- */
- public void syncSurfaceState(@NonNull TransitionInfo info,
- SurfaceControl.Transaction finishTransaction) {
- // Add rounded corners to freeform windows
- final TypedArray ta = mContext.obtainStyledAttributes(
- new int[]{android.R.attr.dialogCornerRadius});
- final int cornerRadius = ta.getDimensionPixelSize(0, 0);
- ta.recycle();
- for (TransitionInfo.Change change: info.getChanges()) {
- if (change.getTaskInfo().getWindowingMode() == WINDOWING_MODE_FREEFORM) {
- finishTransaction.setCornerRadius(change.getLeash(), cornerRadius);
- }
- }
- }
-
- /**
- * A {@link ContentObserver} for listening to changes to {@link Settings.System#DESKTOP_MODE}
- */
- private final class SettingsObserver extends ContentObserver {
-
- private final Uri mDesktopModeSetting = Settings.System.getUriFor(
- Settings.System.DESKTOP_MODE);
-
- private final Context mContext;
-
- SettingsObserver(Context context, Handler handler) {
- super(handler);
- mContext = context;
- }
-
- public void observe() {
- // TODO(b/242867463): listen for setting change for all users
- mContext.getContentResolver().registerContentObserver(mDesktopModeSetting,
- false /* notifyForDescendants */, this /* observer */, UserHandle.USER_CURRENT);
- }
-
- @Override
- public void onChange(boolean selfChange, @Nullable Uri uri) {
- if (mDesktopModeSetting.equals(uri)) {
- ProtoLog.d(WM_SHELL_DESKTOP_MODE, "Received update for desktop mode setting");
- desktopModeSettingChanged();
- }
- }
-
- private void desktopModeSettingChanged() {
- boolean enabled = DesktopModeStatus.isActive(mContext);
- updateDesktopModeActive(enabled);
- }
- }
-
- /**
- * The interface for calls from outside the shell, within the host process.
- */
- @ExternalThread
- private final class DesktopModeImpl implements DesktopMode {
-
- @Override
- public void addVisibleTasksListener(
- DesktopModeTaskRepository.VisibleTasksListener listener,
- Executor callbackExecutor) {
- mMainExecutor.execute(() -> {
- DesktopModeController.this.addVisibleTasksListener(listener, callbackExecutor);
- });
- }
-
- @Override
- public void addDesktopGestureExclusionRegionListener(Consumer<Region> listener,
- Executor callbackExecutor) {
- mMainExecutor.execute(() -> {
- DesktopModeController.this.addTaskCornerListener(listener, callbackExecutor);
- });
- }
- }
-
- /**
- * The interface for calls from outside the host process.
- */
- @BinderThread
- private static class IDesktopModeImpl extends IDesktopMode.Stub
- implements ExternalInterfaceBinder {
-
- private DesktopModeController mController;
-
- IDesktopModeImpl(DesktopModeController controller) {
- mController = controller;
- }
-
- /**
- * Invalidates this instance, preventing future calls from updating the controller.
- */
- @Override
- public void invalidate() {
- mController = null;
- }
-
- @Override
- public void showDesktopApps(int displayId) {
- executeRemoteCallWithTaskPermission(mController, "showDesktopApps",
- controller -> controller.showDesktopApps(displayId));
- }
-
- @Override
- public void showDesktopApp(int taskId) throws RemoteException {
- // TODO
- }
-
- @Override
- public int getVisibleTaskCount(int displayId) throws RemoteException {
- int[] result = new int[1];
- executeRemoteCallWithTaskPermission(mController, "getVisibleTaskCount",
- controller -> result[0] = controller.getVisibleTaskCount(displayId),
- true /* blocking */
- );
- return result[0];
- }
-
- @Override
- public void onDesktopSplitSelectAnimComplete(RunningTaskInfo taskInfo) {
-
- }
-
- @Override
- public void stashDesktopApps(int displayId) throws RemoteException {
- // Stashing of desktop apps not needed. Apps always launch on desktop
- }
-
- @Override
- public void hideStashedDesktopApps(int displayId) throws RemoteException {
- // Stashing of desktop apps not needed. Apps always launch on desktop
- }
-
- @Override
- public void setTaskListener(IDesktopTaskListener listener) throws RemoteException {
- // TODO(b/261234402): move visibility from sysui state to listener
- }
- }
-}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeStatus.java b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeStatus.java
index 517f9f2..7783113 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeStatus.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeStatus.java
@@ -16,14 +16,7 @@
package com.android.wm.shell.desktopmode;
-import static com.android.wm.shell.protolog.ShellProtoLogGroup.WM_SHELL_DESKTOP_MODE;
-
-import android.content.Context;
import android.os.SystemProperties;
-import android.os.UserHandle;
-import android.provider.Settings;
-
-import com.android.internal.protolog.common.ProtoLog;
/**
* Constants for desktop mode feature
@@ -31,13 +24,7 @@
public class DesktopModeStatus {
/**
- * Flag to indicate whether desktop mode is available on the device
- */
- private static final boolean IS_SUPPORTED = SystemProperties.getBoolean(
- "persist.wm.debug.desktop_mode", false);
-
- /**
- * Flag to indicate whether desktop mode proto 2 is available on the device
+ * Flag to indicate whether desktop mode proto is available on the device
*/
private static final boolean IS_PROTO2_ENABLED = SystemProperties.getBoolean(
"persist.wm.debug.desktop_mode_2", false);
@@ -64,28 +51,13 @@
"persist.wm.debug.desktop_stashing", false);
/**
- * Return {@code true} if desktop mode support is enabled
- */
- public static boolean isProto1Enabled() {
- return IS_SUPPORTED;
- }
-
- /**
* Return {@code true} is desktop windowing proto 2 is enabled
*/
- public static boolean isProto2Enabled() {
+ public static boolean isEnabled() {
return IS_PROTO2_ENABLED;
}
/**
- * Return {@code true} if proto 1 or 2 is enabled.
- * Can be used to guard logic that is common for both prototypes.
- */
- public static boolean isAnyEnabled() {
- return isProto1Enabled() || isProto2Enabled();
- }
-
- /**
* Return {@code true} if veiled resizing is active. If false, fluid resizing is used.
*/
public static boolean isVeiledResizeEnabled() {
@@ -99,26 +71,4 @@
public static boolean isStashingEnabled() {
return IS_STASHING_ENABLED;
}
- /**
- * Check if desktop mode is active
- *
- * @return {@code true} if active
- */
- public static boolean isActive(Context context) {
- if (!isAnyEnabled()) {
- return false;
- }
- if (isProto2Enabled()) {
- // Desktop mode is always active in prototype 2
- return true;
- }
- try {
- int result = Settings.System.getIntForUser(context.getContentResolver(),
- Settings.System.DESKTOP_MODE, UserHandle.USER_CURRENT);
- return result != 0;
- } catch (Exception e) {
- ProtoLog.e(WM_SHELL_DESKTOP_MODE, "Failed to read DESKTOP_MODE setting %s", e);
- return false;
- }
- }
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeVisualIndicator.java b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeVisualIndicator.java
index 0f0d572..a587bed 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeVisualIndicator.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeVisualIndicator.java
@@ -27,6 +27,7 @@
import android.content.Context;
import android.content.res.Resources;
import android.graphics.PixelFormat;
+import android.graphics.PointF;
import android.graphics.Rect;
import android.util.DisplayMetrics;
import android.view.SurfaceControl;
@@ -47,6 +48,15 @@
* Animated visual indicator for Desktop Mode windowing transitions.
*/
public class DesktopModeVisualIndicator {
+ public static final int INVALID_INDICATOR = -1;
+ /** Indicates impending transition into desktop mode */
+ public static final int TO_DESKTOP_INDICATOR = 1;
+ /** Indicates impending transition into fullscreen */
+ public static final int TO_FULLSCREEN_INDICATOR = 2;
+ /** Indicates impending transition into split select on the left side */
+ public static final int TO_SPLIT_LEFT_INDICATOR = 3;
+ /** Indicates impending transition into split select on the right side */
+ public static final int TO_SPLIT_RIGHT_INDICATOR = 4;
private final Context mContext;
private final DisplayController mDisplayController;
@@ -54,6 +64,7 @@
private final RootTaskDisplayAreaOrganizer mRootTdaOrganizer;
private final ActivityManager.RunningTaskInfo mTaskInfo;
private final SurfaceControl mTaskSurface;
+ private final Rect mIndicatorRange = new Rect();
private SurfaceControl mLeash;
private final SyncTransactionQueue mSyncQueue;
@@ -61,11 +72,12 @@
private View mView;
private boolean mIsFullscreen;
+ private int mType;
public DesktopModeVisualIndicator(SyncTransactionQueue syncQueue,
ActivityManager.RunningTaskInfo taskInfo, DisplayController displayController,
Context context, SurfaceControl taskSurface, ShellTaskOrganizer taskOrganizer,
- RootTaskDisplayAreaOrganizer taskDisplayAreaOrganizer) {
+ RootTaskDisplayAreaOrganizer taskDisplayAreaOrganizer, int type) {
mSyncQueue = syncQueue;
mTaskInfo = taskInfo;
mDisplayController = displayController;
@@ -73,10 +85,64 @@
mTaskSurface = taskSurface;
mTaskOrganizer = taskOrganizer;
mRootTdaOrganizer = taskDisplayAreaOrganizer;
+ mType = type;
+ defineIndicatorRange();
createView();
}
/**
+ * If an indicator is warranted based on the input and task bounds, return the type of
+ * indicator that should be created.
+ */
+ public static int determineIndicatorType(PointF inputCoordinates, Rect taskBounds,
+ DisplayLayout layout, Context context) {
+ int transitionAreaHeight = context.getResources().getDimensionPixelSize(
+ com.android.wm.shell.R.dimen.desktop_mode_transition_area_height);
+ int transitionAreaWidth = context.getResources().getDimensionPixelSize(
+ com.android.wm.shell.R.dimen.desktop_mode_transition_area_width);
+ if (taskBounds.top <= transitionAreaHeight) return TO_FULLSCREEN_INDICATOR;
+ if (inputCoordinates.x <= transitionAreaWidth) return TO_SPLIT_LEFT_INDICATOR;
+ if (inputCoordinates.x >= layout.width() - transitionAreaWidth) {
+ return TO_SPLIT_RIGHT_INDICATOR;
+ }
+ return INVALID_INDICATOR;
+ }
+
+ /**
+ * Determine range of inputs that will keep this indicator displaying.
+ */
+ private void defineIndicatorRange() {
+ DisplayLayout layout = mDisplayController.getDisplayLayout(mTaskInfo.displayId);
+ int captionHeight = mContext.getResources().getDimensionPixelSize(
+ com.android.wm.shell.R.dimen.freeform_decor_caption_height);
+ int transitionAreaHeight = mContext.getResources().getDimensionPixelSize(
+ com.android.wm.shell.R.dimen.desktop_mode_transition_area_height);
+ int transitionAreaWidth = mContext.getResources().getDimensionPixelSize(
+ com.android.wm.shell.R.dimen.desktop_mode_transition_area_width);
+ switch (mType) {
+ case TO_DESKTOP_INDICATOR:
+ // TO_DESKTOP indicator is only dismissed on release; entire display is valid.
+ mIndicatorRange.set(0, 0, layout.width(), layout.height());
+ break;
+ case TO_FULLSCREEN_INDICATOR:
+ // If drag results in caption going above the top edge of the display, we still
+ // want to transition to fullscreen.
+ mIndicatorRange.set(0, -captionHeight, layout.width(), transitionAreaHeight);
+ break;
+ case TO_SPLIT_LEFT_INDICATOR:
+ mIndicatorRange.set(0, transitionAreaHeight, transitionAreaWidth, layout.height());
+ break;
+ case TO_SPLIT_RIGHT_INDICATOR:
+ mIndicatorRange.set(layout.width() - transitionAreaWidth, transitionAreaHeight,
+ layout.width(), layout.height());
+ break;
+ default:
+ break;
+ }
+ }
+
+
+ /**
* Create a fullscreen indicator with no animation
*/
private void createView() {
@@ -85,11 +151,30 @@
final DisplayMetrics metrics = resources.getDisplayMetrics();
final int screenWidth = metrics.widthPixels;
final int screenHeight = metrics.heightPixels;
+
mView = new View(mContext);
final SurfaceControl.Builder builder = new SurfaceControl.Builder();
mRootTdaOrganizer.attachToDisplayArea(mTaskInfo.displayId, builder);
+ String description;
+ switch (mType) {
+ case TO_DESKTOP_INDICATOR:
+ description = "Desktop indicator";
+ break;
+ case TO_FULLSCREEN_INDICATOR:
+ description = "Fullscreen indicator";
+ break;
+ case TO_SPLIT_LEFT_INDICATOR:
+ description = "Split Left indicator";
+ break;
+ case TO_SPLIT_RIGHT_INDICATOR:
+ description = "Split Right indicator";
+ break;
+ default:
+ description = "Invalid indicator";
+ break;
+ }
mLeash = builder
- .setName("Fullscreen Indicator")
+ .setName(description)
.setContainerLayer()
.build();
t.show(mLeash);
@@ -97,14 +182,14 @@
new WindowManager.LayoutParams(screenWidth, screenHeight,
WindowManager.LayoutParams.TYPE_APPLICATION,
WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE, PixelFormat.TRANSPARENT);
- lp.setTitle("Fullscreen indicator for Task=" + mTaskInfo.taskId);
+ lp.setTitle(description + " for Task=" + mTaskInfo.taskId);
lp.setTrustedOverlay();
final WindowlessWindowManager windowManager = new WindowlessWindowManager(
mTaskInfo.configuration, mLeash,
null /* hostInputToken */);
mViewHost = new SurfaceControlViewHost(mContext,
mDisplayController.getDisplay(mTaskInfo.displayId), windowManager,
- "FullscreenVisualIndicator");
+ "DesktopModeVisualIndicator");
mViewHost.setView(mView, lp);
// We want this indicator to be behind the dragged task, but in front of all others.
t.setRelativeLayer(mLeash, mTaskSurface, -1);
@@ -116,24 +201,13 @@
}
/**
- * Create fullscreen indicator and fades it in.
+ * Create an indicator. Animator fades it in while expanding the bounds outwards.
*/
- public void createFullscreenIndicator() {
- mIsFullscreen = true;
- mView.setBackgroundResource(R.drawable.desktop_windowing_transition_background);
- final VisualIndicatorAnimator animator = VisualIndicatorAnimator.toFullscreenAnimator(
- mView, mDisplayController.getDisplayLayout(mTaskInfo.displayId));
- animator.start();
- }
-
- /**
- * Create a fullscreen indicator. Animator fades it in while expanding the bounds outwards.
- */
- public void createFullscreenIndicatorWithAnimatedBounds() {
- mIsFullscreen = true;
+ public void createIndicatorWithAnimatedBounds() {
+ mIsFullscreen = mType == TO_FULLSCREEN_INDICATOR;
mView.setBackgroundResource(R.drawable.desktop_windowing_transition_background);
final VisualIndicatorAnimator animator = VisualIndicatorAnimator
- .toFullscreenAnimatorWithAnimatedBounds(mView,
+ .animateBounds(mView, mType,
mDisplayController.getDisplayLayout(mTaskInfo.displayId));
animator.start();
}
@@ -143,6 +217,7 @@
*/
public void transitionFullscreenIndicatorToFreeform() {
mIsFullscreen = false;
+ mType = TO_DESKTOP_INDICATOR;
final VisualIndicatorAnimator animator = VisualIndicatorAnimator.toFreeformAnimator(
mView, mDisplayController.getDisplayLayout(mTaskInfo.displayId));
animator.start();
@@ -153,6 +228,7 @@
*/
public void transitionFreeformIndicatorToFullscreen() {
mIsFullscreen = true;
+ mType = TO_FULLSCREEN_INDICATOR;
final VisualIndicatorAnimator animator =
VisualIndicatorAnimator.toFullscreenAnimatorWithAnimatedBounds(
mView, mDisplayController.getDisplayLayout(mTaskInfo.displayId));
@@ -160,6 +236,14 @@
}
/**
+ * Determine if a MotionEvent is in the same range that enabled the indicator.
+ * Used to dismiss the indicator when a transition will no longer result from releasing.
+ */
+ public boolean eventOutsideRange(float x, float y) {
+ return !mIndicatorRange.contains((int) x, (int) y);
+ }
+
+ /**
* Release the indicator and its components when it is no longer needed.
*/
public void releaseVisualIndicator(SurfaceControl.Transaction t) {
@@ -210,23 +294,6 @@
* @param view the view for this indicator
* @param displayLayout information about the display the transitioning task is currently on
*/
- public static VisualIndicatorAnimator toFullscreenAnimator(@NonNull View view,
- @NonNull DisplayLayout displayLayout) {
- final Rect bounds = getMaxBounds(displayLayout);
- final VisualIndicatorAnimator animator = new VisualIndicatorAnimator(
- view, bounds, bounds);
- animator.setInterpolator(new DecelerateInterpolator());
- setupIndicatorAnimation(animator);
- return animator;
- }
-
-
- /**
- * Create animator for visual indicator of fullscreen transition
- *
- * @param view the view for this indicator
- * @param displayLayout information about the display the transitioning task is currently on
- */
public static VisualIndicatorAnimator toFullscreenAnimatorWithAnimatedBounds(
@NonNull View view, @NonNull DisplayLayout displayLayout) {
final int padding = displayLayout.stableInsets().top;
@@ -235,7 +302,37 @@
view.getBackground().setBounds(startBounds);
final VisualIndicatorAnimator animator = new VisualIndicatorAnimator(
- view, startBounds, getMaxBounds(displayLayout));
+ view, startBounds, getMaxBounds(startBounds));
+ animator.setInterpolator(new DecelerateInterpolator());
+ setupIndicatorAnimation(animator);
+ return animator;
+ }
+
+ public static VisualIndicatorAnimator animateBounds(
+ @NonNull View view, int type, @NonNull DisplayLayout displayLayout) {
+ final int padding = displayLayout.stableInsets().top;
+ Rect startBounds = new Rect();
+ switch (type) {
+ case TO_FULLSCREEN_INDICATOR:
+ startBounds.set(padding, padding,
+ displayLayout.width() - padding,
+ displayLayout.height() - padding);
+ break;
+ case TO_SPLIT_LEFT_INDICATOR:
+ startBounds.set(padding, padding,
+ displayLayout.width() / 2 - padding,
+ displayLayout.height() - padding);
+ break;
+ case TO_SPLIT_RIGHT_INDICATOR:
+ startBounds.set(displayLayout.width() / 2 + padding, padding,
+ displayLayout.width() - padding,
+ displayLayout.height() - padding);
+ break;
+ }
+ view.getBackground().setBounds(startBounds);
+
+ final VisualIndicatorAnimator animator = new VisualIndicatorAnimator(
+ view, startBounds, getMaxBounds(startBounds));
animator.setInterpolator(new DecelerateInterpolator());
setupIndicatorAnimation(animator);
return animator;
@@ -252,12 +349,13 @@
final float adjustmentPercentage = 1f - FINAL_FREEFORM_SCALE;
final int width = displayLayout.width();
final int height = displayLayout.height();
+ Rect startBounds = new Rect(0, 0, width, height);
Rect endBounds = new Rect((int) (adjustmentPercentage * width / 2),
(int) (adjustmentPercentage * height / 2),
(int) (displayLayout.width() - (adjustmentPercentage * width / 2)),
(int) (displayLayout.height() - (adjustmentPercentage * height / 2)));
final VisualIndicatorAnimator animator = new VisualIndicatorAnimator(
- view, getMaxBounds(displayLayout), endBounds);
+ view, startBounds, endBounds);
animator.setInterpolator(new DecelerateInterpolator());
setupIndicatorAnimation(animator);
return animator;
@@ -310,21 +408,17 @@
}
/**
- * Return the max bounds of a fullscreen indicator
+ * Return the max bounds of a visual indicator
*/
- private static Rect getMaxBounds(@NonNull DisplayLayout displayLayout) {
- final int padding = displayLayout.stableInsets().top;
- final int width = displayLayout.width() - 2 * padding;
- final int height = displayLayout.height() - 2 * padding;
- Rect endBounds = new Rect((int) (padding
- - (FULLSCREEN_SCALE_ADJUSTMENT_PERCENT * width)),
- (int) (padding
- - (FULLSCREEN_SCALE_ADJUSTMENT_PERCENT * height)),
- (int) (displayLayout.width() - padding
- + (FULLSCREEN_SCALE_ADJUSTMENT_PERCENT * width)),
- (int) (displayLayout.height() - padding
- + (FULLSCREEN_SCALE_ADJUSTMENT_PERCENT * height)));
- return endBounds;
+ private static Rect getMaxBounds(Rect startBounds) {
+ return new Rect((int) (startBounds.left
+ - (FULLSCREEN_SCALE_ADJUSTMENT_PERCENT * startBounds.width())),
+ (int) (startBounds.top
+ - (FULLSCREEN_SCALE_ADJUSTMENT_PERCENT * startBounds.height())),
+ (int) (startBounds.right
+ + (FULLSCREEN_SCALE_ADJUSTMENT_PERCENT * startBounds.width())),
+ (int) (startBounds.bottom
+ + (FULLSCREEN_SCALE_ADJUSTMENT_PERCENT * startBounds.height())));
}
}
}
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 b0f75c6..b918c83 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
@@ -29,6 +29,7 @@
import android.content.Context
import android.content.res.TypedArray
import android.graphics.Point
+import android.graphics.PointF
import android.graphics.Rect
import android.graphics.Region
import android.os.IBinder
@@ -55,7 +56,10 @@
import com.android.wm.shell.common.SyncTransactionQueue
import com.android.wm.shell.common.annotations.ExternalThread
import com.android.wm.shell.common.annotations.ShellMainThread
+import com.android.wm.shell.common.split.SplitScreenConstants.SPLIT_POSITION_BOTTOM_OR_RIGHT
+import com.android.wm.shell.common.split.SplitScreenConstants.SPLIT_POSITION_TOP_OR_LEFT
import com.android.wm.shell.desktopmode.DesktopModeTaskRepository.VisibleTasksListener
+import com.android.wm.shell.desktopmode.DesktopModeVisualIndicator.TO_DESKTOP_INDICATOR
import com.android.wm.shell.protolog.ShellProtoLogGroup.WM_SHELL_DESKTOP_MODE
import com.android.wm.shell.splitscreen.SplitScreenController
import com.android.wm.shell.sysui.ShellCommandHandler
@@ -105,14 +109,20 @@
private val transitionAreaHeight
get() = context.resources.getDimensionPixelSize(
- com.android.wm.shell.R.dimen.desktop_mode_transition_area_height)
+ com.android.wm.shell.R.dimen.desktop_mode_transition_area_height
+ )
+
+ private val transitionAreaWidth
+ get() = context.resources.getDimensionPixelSize(
+ com.android.wm.shell.R.dimen.desktop_mode_transition_area_width
+ )
// This is public to avoid cyclic dependency; it is set by SplitScreenController
lateinit var splitScreenController: SplitScreenController
init {
desktopMode = DesktopModeImpl()
- if (DesktopModeStatus.isProto2Enabled()) {
+ if (DesktopModeStatus.isEnabled()) {
shellInit.addInitCallback({ onInit() }, this)
}
}
@@ -485,6 +495,55 @@
}
}
+ /**
+ * Quick-resize to the right or left half of the stable bounds.
+ *
+ * @param position the portion of the screen (RIGHT or LEFT) we want to snap the task to.
+ */
+ fun snapToHalfScreen(
+ taskInfo: RunningTaskInfo,
+ windowDecor: DesktopModeWindowDecoration,
+ position: SnapPosition
+ ) {
+ val displayLayout = displayController.getDisplayLayout(taskInfo.displayId) ?: return
+
+ val stableBounds = Rect()
+ displayLayout.getStableBounds(stableBounds)
+
+ val destinationWidth = stableBounds.width() / 2
+ val destinationBounds = when (position) {
+ SnapPosition.LEFT -> {
+ Rect(
+ stableBounds.left,
+ stableBounds.top,
+ stableBounds.left + destinationWidth,
+ stableBounds.bottom
+ )
+ }
+ SnapPosition.RIGHT -> {
+ Rect(
+ stableBounds.right - destinationWidth,
+ stableBounds.top,
+ stableBounds.right,
+ stableBounds.bottom
+ )
+ }
+ }
+
+ if (destinationBounds == taskInfo.configuration.windowConfiguration.bounds) return
+
+ val wct = WindowContainerTransaction().setBounds(taskInfo.token, destinationBounds)
+ if (Transitions.ENABLE_SHELL_TRANSITIONS) {
+ toggleResizeDesktopTaskTransitionHandler.startTransition(
+ wct,
+ taskInfo.taskId,
+ windowDecor
+ )
+ } else {
+ shellTaskOrganizer.applyTransaction(wct)
+ }
+ }
+
private fun getDefaultDesktopTaskBounds(density: Float, stableBounds: Rect, outBounds: Rect) {
val width = (DESKTOP_MODE_DEFAULT_WIDTH_DP * density + 0.5f).toInt()
val height = (DESKTOP_MODE_DEFAULT_HEIGHT_DP * density + 0.5f).toInt()
@@ -755,7 +814,8 @@
) {
val wct = WindowContainerTransaction()
addMoveToSplitChanges(wct, taskInfo)
- splitScreenController.requestEnterSplitSelect(taskInfo, wct)
+ splitScreenController.requestEnterSplitSelect(taskInfo, wct,
+ SPLIT_POSITION_BOTTOM_OR_RIGHT, taskInfo.configuration.windowConfiguration.bounds)
}
}
@@ -779,25 +839,36 @@
/**
* Perform checks required on drag move. Create/release fullscreen indicator as needed.
+ * Different sources for x and y coordinates are used due to different needs for each:
+ * We want split transitions to be based on input coordinates but fullscreen transition
+ * to be based on task edge coordinate.
*
* @param taskInfo the task being dragged.
* @param taskSurface SurfaceControl of dragged task.
- * @param y coordinate of dragged task. Used for checks against status bar height.
+ * @param inputCoordinate coordinates of input. Used for checks against left/right edge of screen.
+ * @param taskBounds bounds of dragged task. Used for checks against status bar height.
*/
fun onDragPositioningMove(
- taskInfo: RunningTaskInfo,
- taskSurface: SurfaceControl,
- y: Float
+ taskInfo: RunningTaskInfo,
+ taskSurface: SurfaceControl,
+ inputCoordinate: PointF,
+ taskBounds: Rect
) {
- if (taskInfo.windowingMode == WINDOWING_MODE_FREEFORM) {
- if (y <= transitionAreaHeight && visualIndicator == null) {
- visualIndicator = DesktopModeVisualIndicator(syncQueue, taskInfo,
- displayController, context, taskSurface, shellTaskOrganizer,
- rootTaskDisplayAreaOrganizer)
- visualIndicator?.createFullscreenIndicatorWithAnimatedBounds()
- } else if (y > transitionAreaHeight && visualIndicator != null) {
- releaseVisualIndicator()
- }
+ val displayLayout = displayController.getDisplayLayout(taskInfo.displayId) ?: return
+ if (taskInfo.windowingMode != WINDOWING_MODE_FREEFORM) return
+ var type = DesktopModeVisualIndicator.determineIndicatorType(inputCoordinate,
+ taskBounds, displayLayout, context)
+ if (type != DesktopModeVisualIndicator.INVALID_INDICATOR && visualIndicator == null) {
+ visualIndicator = DesktopModeVisualIndicator(
+ syncQueue, taskInfo,
+ displayController, context, taskSurface, shellTaskOrganizer,
+ rootTaskDisplayAreaOrganizer, type)
+ visualIndicator?.createIndicatorWithAnimatedBounds()
+ return
+ }
+ if (visualIndicator?.eventOutsideRange(inputCoordinate.x,
+ taskBounds.top.toFloat()) == true) {
+ releaseVisualIndicator()
}
}
@@ -806,19 +877,39 @@
*
* @param taskInfo the task being dragged.
* @param position position of surface when drag ends.
- * @param y the Y position of the top edge of the task
+ * @param inputCoordinate the coordinates of the motion event
+ * @param taskBounds the updated bounds of the task being dragged.
* @param windowDecor the window decoration for the task being dragged
*/
fun onDragPositioningEnd(
- taskInfo: RunningTaskInfo,
- position: Point,
- y: Float,
- windowDecor: DesktopModeWindowDecoration
+ taskInfo: RunningTaskInfo,
+ position: Point,
+ inputCoordinate: PointF,
+ taskBounds: Rect,
+ windowDecor: DesktopModeWindowDecoration
) {
- if (y <= transitionAreaHeight && taskInfo.windowingMode == WINDOWING_MODE_FREEFORM) {
+ if (taskInfo.configuration.windowConfiguration.windowingMode != WINDOWING_MODE_FREEFORM) {
+ return
+ }
+ if (taskBounds.top <= transitionAreaHeight) {
windowDecor.incrementRelayoutBlock()
moveToFullscreenWithAnimation(taskInfo, position)
}
+ if (inputCoordinate.x <= transitionAreaWidth) {
+ releaseVisualIndicator()
+ var wct = WindowContainerTransaction()
+ addMoveToSplitChanges(wct, taskInfo)
+ splitScreenController.requestEnterSplitSelect(taskInfo, wct,
+ SPLIT_POSITION_TOP_OR_LEFT, taskBounds)
+ }
+ if (inputCoordinate.x >= (displayController.getDisplayLayout(taskInfo.displayId)?.width()
+ ?.minus(transitionAreaWidth) ?: return)) {
+ releaseVisualIndicator()
+ var wct = WindowContainerTransaction()
+ addMoveToSplitChanges(wct, taskInfo)
+ splitScreenController.requestEnterSplitSelect(taskInfo, wct,
+ SPLIT_POSITION_BOTTOM_OR_RIGHT, taskBounds)
+ }
}
/**
@@ -842,8 +933,8 @@
if (visualIndicator == null) {
visualIndicator = DesktopModeVisualIndicator(syncQueue, taskInfo,
displayController, context, taskSurface, shellTaskOrganizer,
- rootTaskDisplayAreaOrganizer)
- visualIndicator?.createFullscreenIndicator()
+ rootTaskDisplayAreaOrganizer, TO_DESKTOP_INDICATOR)
+ visualIndicator?.createIndicatorWithAnimatedBounds()
}
val indicator = visualIndicator ?: return
if (y >= getFreeformTransitionStatusBarDragThreshold(taskInfo)) {
@@ -1077,4 +1168,7 @@
return DESKTOP_DENSITY_OVERRIDE in DESKTOP_DENSITY_ALLOWED_RANGE
}
}
+
+ /** The positions on a screen that a task can snap to. */
+ enum class SnapPosition { RIGHT, LEFT }
}
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 22541bbd..a80241e 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
@@ -68,7 +68,7 @@
private void onInit() {
mShellTaskOrganizer.addListenerForType(this, TASK_LISTENER_TYPE_FREEFORM);
- if (DesktopModeStatus.isAnyEnabled()) {
+ if (DesktopModeStatus.isEnabled()) {
mShellTaskOrganizer.addFocusListener(this);
}
}
@@ -90,7 +90,7 @@
t.apply();
}
- if (DesktopModeStatus.isAnyEnabled()) {
+ if (DesktopModeStatus.isEnabled()) {
mDesktopModeTaskRepository.ifPresent(repository -> {
repository.addOrMoveFreeformTaskToTop(taskInfo.taskId);
if (taskInfo.isVisible) {
@@ -111,7 +111,7 @@
taskInfo.taskId);
mTasks.remove(taskInfo.taskId);
- if (DesktopModeStatus.isAnyEnabled()) {
+ if (DesktopModeStatus.isEnabled()) {
mDesktopModeTaskRepository.ifPresent(repository -> {
repository.removeFreeformTask(taskInfo.taskId);
if (repository.removeActiveTask(taskInfo.taskId)) {
@@ -135,7 +135,7 @@
taskInfo.taskId);
mWindowDecorationViewModel.onTaskInfoChanged(taskInfo);
state.mTaskInfo = taskInfo;
- if (DesktopModeStatus.isAnyEnabled()) {
+ if (DesktopModeStatus.isEnabled()) {
mDesktopModeTaskRepository.ifPresent(repository -> {
if (taskInfo.isVisible) {
if (repository.addActiveTask(taskInfo.displayId, taskInfo.taskId)) {
@@ -154,7 +154,7 @@
ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TASK_ORG,
"Freeform Task Focus Changed: #%d focused=%b",
taskInfo.taskId, taskInfo.isFocused);
- if (DesktopModeStatus.isAnyEnabled() && taskInfo.isFocused) {
+ if (DesktopModeStatus.isEnabled() && taskInfo.isFocused) {
mDesktopModeTaskRepository.ifPresent(repository -> {
repository.addOrMoveFreeformTaskToTop(taskInfo.taskId);
});
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 2ef92ad..13c0ac4 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
@@ -16,7 +16,10 @@
package com.android.wm.shell.keyguard;
+import static android.app.ActivityTaskManager.INVALID_TASK_ID;
import static android.app.WindowConfiguration.ACTIVITY_TYPE_DREAM;
+import static android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM;
+import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
import static android.view.WindowManager.KEYGUARD_VISIBILITY_TRANSIT_FLAGS;
import static android.view.WindowManager.TRANSIT_FLAG_KEYGUARD_GOING_AWAY;
import static android.view.WindowManager.TRANSIT_FLAG_KEYGUARD_OCCLUDING;
@@ -27,6 +30,7 @@
import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.app.ActivityManager;
import android.os.Binder;
import android.os.Handler;
import android.os.IBinder;
@@ -165,10 +169,16 @@
if (sct != null) {
finishTransaction.merge(sct);
}
+ final WindowContainerTransaction mergedWct =
+ new WindowContainerTransaction();
+ if (wct != null) {
+ mergedWct.merge(wct, true);
+ }
+ maybeDismissFreeformOccludingKeyguard(mergedWct, info);
// Post our finish callback to let startAnimation finish first.
mMainExecutor.executeDelayed(() -> {
mStartedTransitions.remove(transition);
- finishCallback.onTransitionFinished(wct);
+ finishCallback.onTransitionFinished(mergedWct);
}, 0);
}
});
@@ -260,6 +270,26 @@
}
}
+ private void maybeDismissFreeformOccludingKeyguard(
+ WindowContainerTransaction wct, TransitionInfo info) {
+ if ((info.getFlags() & TRANSIT_FLAG_KEYGUARD_OCCLUDING) == 0) {
+ return;
+ }
+ // There's a window occluding the Keyguard, find it and if it's in freeform mode, change it
+ // to fullscreen.
+ for (int i = 0; i < info.getChanges().size(); i++) {
+ final TransitionInfo.Change change = info.getChanges().get(i);
+ final ActivityManager.RunningTaskInfo taskInfo = change.getTaskInfo();
+ if (taskInfo != null && taskInfo.taskId != INVALID_TASK_ID
+ && taskInfo.getWindowingMode() == WINDOWING_MODE_FREEFORM
+ && taskInfo.isFocused && change.getContainer() != null) {
+ wct.setWindowingMode(change.getContainer(), WINDOWING_MODE_FULLSCREEN);
+ wct.setBounds(change.getContainer(), null);
+ return;
+ }
+ }
+ }
+
private static class FakeFinishCallback extends IRemoteTransitionFinishedCallback.Stub {
@Override
public void onTransitionFinished(
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 ed9ff1c..9e8f9c6 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
@@ -82,6 +82,9 @@
import com.android.wm.shell.common.ShellExecutor;
import com.android.wm.shell.common.SyncTransactionQueue;
import com.android.wm.shell.common.annotations.ShellMainThread;
+import com.android.wm.shell.common.pip.PipBoundsAlgorithm;
+import com.android.wm.shell.common.pip.PipBoundsState;
+import com.android.wm.shell.common.pip.PipDisplayLayoutState;
import com.android.wm.shell.common.pip.PipUiEventLogger;
import com.android.wm.shell.common.pip.PipUtils;
import com.android.wm.shell.pip.phone.PipMotionHelper;
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 83e03dc..e3922d6 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
@@ -64,6 +64,9 @@
import com.android.internal.protolog.common.ProtoLog;
import com.android.wm.shell.R;
import com.android.wm.shell.ShellTaskOrganizer;
+import com.android.wm.shell.common.pip.PipBoundsAlgorithm;
+import com.android.wm.shell.common.pip.PipBoundsState;
+import com.android.wm.shell.common.pip.PipDisplayLayoutState;
import com.android.wm.shell.common.pip.PipUtils;
import com.android.wm.shell.protolog.ShellProtoLogGroup;
import com.android.wm.shell.splitscreen.SplitScreenController;
@@ -89,6 +92,7 @@
private final int mEnterExitAnimationDuration;
private final PipSurfaceTransactionHelper mSurfaceTransactionHelper;
private final Optional<SplitScreenController> mSplitScreenOptional;
+ private final PipAnimationController mPipAnimationController;
private @PipAnimationController.AnimationType int mEnterAnimationType = ANIM_TYPE_BOUNDS;
private Transitions.TransitionFinishCallback mFinishCallback;
private SurfaceControl.Transaction mFinishTransaction;
@@ -137,10 +141,11 @@
PipSurfaceTransactionHelper pipSurfaceTransactionHelper,
Optional<SplitScreenController> splitScreenOptional) {
super(shellInit, shellTaskOrganizer, transitions, pipBoundsState, pipMenuController,
- pipBoundsAlgorithm, pipAnimationController);
+ pipBoundsAlgorithm);
mContext = context;
mPipTransitionState = pipTransitionState;
mPipDisplayLayoutState = pipDisplayLayoutState;
+ mPipAnimationController = pipAnimationController;
mEnterExitAnimationDuration = context.getResources()
.getInteger(R.integer.config_pipResizeAnimationDuration);
mSurfaceTransactionHelper = pipSurfaceTransactionHelper;
@@ -148,6 +153,13 @@
}
@Override
+ protected void onInit() {
+ if (!PipUtils.isPip2ExperimentEnabled()) {
+ mTransitions.addHandler(this);
+ }
+ }
+
+ @Override
public void startExitTransition(int type, WindowContainerTransaction out,
@Nullable Rect destinationBounds) {
if (destinationBounds != null) {
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTransitionController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTransitionController.java
index 64bba67..20c57fa 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTransitionController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTransitionController.java
@@ -38,7 +38,8 @@
import androidx.annotation.NonNull;
import com.android.wm.shell.ShellTaskOrganizer;
-import com.android.wm.shell.common.pip.PipUtils;
+import com.android.wm.shell.common.pip.PipBoundsAlgorithm;
+import com.android.wm.shell.common.pip.PipBoundsState;
import com.android.wm.shell.common.split.SplitScreenUtils;
import com.android.wm.shell.sysui.ShellInit;
import com.android.wm.shell.transition.Transitions;
@@ -52,7 +53,6 @@
*/
public abstract class PipTransitionController implements Transitions.TransitionHandler {
- protected final PipAnimationController mPipAnimationController;
protected final PipBoundsAlgorithm mPipBoundsAlgorithm;
protected final PipBoundsState mPipBoundsState;
protected final ShellTaskOrganizer mShellTaskOrganizer;
@@ -135,22 +135,18 @@
@NonNull ShellTaskOrganizer shellTaskOrganizer,
@NonNull Transitions transitions,
PipBoundsState pipBoundsState,
- PipMenuController pipMenuController, PipBoundsAlgorithm pipBoundsAlgorithm,
- PipAnimationController pipAnimationController) {
+ PipMenuController pipMenuController, PipBoundsAlgorithm pipBoundsAlgorithm) {
mPipBoundsState = pipBoundsState;
mPipMenuController = pipMenuController;
mShellTaskOrganizer = shellTaskOrganizer;
mPipBoundsAlgorithm = pipBoundsAlgorithm;
- mPipAnimationController = pipAnimationController;
mTransitions = transitions;
- if (!PipUtils.isPip2ExperimentEnabled()) {
- if (Transitions.ENABLE_SHELL_TRANSITIONS) {
- shellInit.addInitCallback(this::onInit, this);
- }
+ if (Transitions.ENABLE_SHELL_TRANSITIONS) {
+ shellInit.addInitCallback(this::onInit, this);
}
}
- private void onInit() {
+ protected void onInit() {
mTransitions.addHandler(this);
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PhonePipMenuController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PhonePipMenuController.java
index cc182ba..7606526 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PhonePipMenuController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PhonePipMenuController.java
@@ -38,10 +38,10 @@
import com.android.internal.protolog.common.ProtoLog;
import com.android.wm.shell.common.ShellExecutor;
import com.android.wm.shell.common.SystemWindows;
+import com.android.wm.shell.common.pip.PipBoundsState;
import com.android.wm.shell.common.pip.PipMediaController;
import com.android.wm.shell.common.pip.PipMediaController.ActionListener;
import com.android.wm.shell.common.pip.PipUiEventLogger;
-import com.android.wm.shell.pip.PipBoundsState;
import com.android.wm.shell.pip.PipMenuController;
import com.android.wm.shell.pip.PipSurfaceTransactionHelper;
import com.android.wm.shell.protolog.ShellProtoLogGroup;
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipAccessibilityInteractionConnection.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipAccessibilityInteractionConnection.java
index 8c2879e..118ad9c 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipAccessibilityInteractionConnection.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipAccessibilityInteractionConnection.java
@@ -15,7 +15,7 @@
*/
package com.android.wm.shell.pip.phone;
-import static com.android.wm.shell.pip.PipBoundsState.STASH_TYPE_NONE;
+import static com.android.wm.shell.common.pip.PipBoundsState.STASH_TYPE_NONE;
import android.annotation.NonNull;
import android.content.Context;
@@ -36,8 +36,8 @@
import com.android.wm.shell.R;
import com.android.wm.shell.common.ShellExecutor;
-import com.android.wm.shell.pip.PipBoundsState;
-import com.android.wm.shell.pip.PipSnapAlgorithm;
+import com.android.wm.shell.common.pip.PipBoundsState;
+import com.android.wm.shell.common.pip.PipSnapAlgorithm;
import com.android.wm.shell.pip.PipTaskOrganizer;
import java.util.ArrayList;
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 ddea574..1064867 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
@@ -76,7 +76,12 @@
import com.android.wm.shell.common.TaskStackListenerCallback;
import com.android.wm.shell.common.TaskStackListenerImpl;
import com.android.wm.shell.common.pip.PipAppOpsListener;
+import com.android.wm.shell.common.pip.PipBoundsAlgorithm;
+import com.android.wm.shell.common.pip.PipBoundsState;
+import com.android.wm.shell.common.pip.PipDisplayLayoutState;
+import com.android.wm.shell.common.pip.PipKeepClearAlgorithmInterface;
import com.android.wm.shell.common.pip.PipMediaController;
+import com.android.wm.shell.common.pip.PipSnapAlgorithm;
import com.android.wm.shell.common.pip.PipUtils;
import com.android.wm.shell.onehanded.OneHandedController;
import com.android.wm.shell.onehanded.OneHandedTransitionCallback;
@@ -85,12 +90,7 @@
import com.android.wm.shell.pip.PinnedStackListenerForwarder;
import com.android.wm.shell.pip.Pip;
import com.android.wm.shell.pip.PipAnimationController;
-import com.android.wm.shell.pip.PipBoundsAlgorithm;
-import com.android.wm.shell.pip.PipBoundsState;
-import com.android.wm.shell.pip.PipDisplayLayoutState;
-import com.android.wm.shell.pip.PipKeepClearAlgorithmInterface;
import com.android.wm.shell.pip.PipParamsChangedForwarder;
-import com.android.wm.shell.pip.PipSnapAlgorithm;
import com.android.wm.shell.pip.PipTaskOrganizer;
import com.android.wm.shell.pip.PipTransitionController;
import com.android.wm.shell.pip.PipTransitionState;
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipDoubleTapHelper.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipDoubleTapHelper.java
index d7d335b..1b1ebc3 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipDoubleTapHelper.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipDoubleTapHelper.java
@@ -20,7 +20,7 @@
import android.annotation.NonNull;
import android.graphics.Rect;
-import com.android.wm.shell.pip.PipBoundsState;
+import com.android.wm.shell.common.pip.PipBoundsState;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipMotionHelper.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipMotionHelper.java
index 8f0a8e1..c708b86 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipMotionHelper.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipMotionHelper.java
@@ -20,10 +20,10 @@
import static androidx.dynamicanimation.animation.SpringForce.STIFFNESS_LOW;
import static androidx.dynamicanimation.animation.SpringForce.STIFFNESS_MEDIUM;
+import static com.android.wm.shell.common.pip.PipBoundsState.STASH_TYPE_LEFT;
+import static com.android.wm.shell.common.pip.PipBoundsState.STASH_TYPE_NONE;
+import static com.android.wm.shell.common.pip.PipBoundsState.STASH_TYPE_RIGHT;
import static com.android.wm.shell.pip.PipAnimationController.TRANSITION_DIRECTION_EXPAND_OR_UNEXPAND;
-import static com.android.wm.shell.pip.PipBoundsState.STASH_TYPE_LEFT;
-import static com.android.wm.shell.pip.PipBoundsState.STASH_TYPE_NONE;
-import static com.android.wm.shell.pip.PipBoundsState.STASH_TYPE_RIGHT;
import static com.android.wm.shell.pip.phone.PipMenuView.ANIM_TYPE_DISMISS;
import static com.android.wm.shell.pip.phone.PipMenuView.ANIM_TYPE_NONE;
@@ -41,8 +41,8 @@
import com.android.wm.shell.common.FloatingContentCoordinator;
import com.android.wm.shell.common.magnetictarget.MagnetizedObject;
import com.android.wm.shell.common.pip.PipAppOpsListener;
-import com.android.wm.shell.pip.PipBoundsState;
-import com.android.wm.shell.pip.PipSnapAlgorithm;
+import com.android.wm.shell.common.pip.PipBoundsState;
+import com.android.wm.shell.common.pip.PipSnapAlgorithm;
import com.android.wm.shell.pip.PipTaskOrganizer;
import com.android.wm.shell.pip.PipTransitionController;
import com.android.wm.shell.protolog.ShellProtoLogGroup;
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipResizeGestureHandler.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipResizeGestureHandler.java
index 4e687dd..e5f9fdc 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipResizeGestureHandler.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipResizeGestureHandler.java
@@ -46,10 +46,11 @@
import com.android.internal.policy.TaskResizingAlgorithm;
import com.android.wm.shell.R;
import com.android.wm.shell.common.ShellExecutor;
+import com.android.wm.shell.common.pip.PipBoundsAlgorithm;
+import com.android.wm.shell.common.pip.PipBoundsState;
+import com.android.wm.shell.common.pip.PipPinchResizingAlgorithm;
import com.android.wm.shell.common.pip.PipUiEventLogger;
import com.android.wm.shell.pip.PipAnimationController;
-import com.android.wm.shell.pip.PipBoundsAlgorithm;
-import com.android.wm.shell.pip.PipBoundsState;
import com.android.wm.shell.pip.PipTaskOrganizer;
import java.io.PrintWriter;
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipTouchHandler.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipTouchHandler.java
index cf54a71..2ce4fb9 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipTouchHandler.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipTouchHandler.java
@@ -18,10 +18,10 @@
import static com.android.internal.config.sysui.SystemUiDeviceConfigFlags.PIP_STASHING;
import static com.android.internal.config.sysui.SystemUiDeviceConfigFlags.PIP_STASH_MINIMUM_VELOCITY_THRESHOLD;
+import static com.android.wm.shell.common.pip.PipBoundsState.STASH_TYPE_LEFT;
+import static com.android.wm.shell.common.pip.PipBoundsState.STASH_TYPE_NONE;
+import static com.android.wm.shell.common.pip.PipBoundsState.STASH_TYPE_RIGHT;
import static com.android.wm.shell.pip.PipAnimationController.TRANSITION_DIRECTION_TO_PIP;
-import static com.android.wm.shell.pip.PipBoundsState.STASH_TYPE_LEFT;
-import static com.android.wm.shell.pip.PipBoundsState.STASH_TYPE_NONE;
-import static com.android.wm.shell.pip.PipBoundsState.STASH_TYPE_RIGHT;
import static com.android.wm.shell.pip.phone.PhonePipMenuController.MENU_STATE_FULL;
import static com.android.wm.shell.pip.phone.PhonePipMenuController.MENU_STATE_NONE;
import static com.android.wm.shell.pip.phone.PipMenuView.ANIM_TYPE_NONE;
@@ -50,12 +50,12 @@
import com.android.wm.shell.R;
import com.android.wm.shell.common.FloatingContentCoordinator;
import com.android.wm.shell.common.ShellExecutor;
+import com.android.wm.shell.common.pip.PipBoundsAlgorithm;
+import com.android.wm.shell.common.pip.PipBoundsState;
import com.android.wm.shell.common.pip.PipUiEventLogger;
import com.android.wm.shell.common.pip.PipUtils;
import com.android.wm.shell.common.pip.SizeSpecSource;
import com.android.wm.shell.pip.PipAnimationController;
-import com.android.wm.shell.pip.PipBoundsAlgorithm;
-import com.android.wm.shell.pip.PipBoundsState;
import com.android.wm.shell.pip.PipTaskOrganizer;
import com.android.wm.shell.pip.PipTransitionController;
import com.android.wm.shell.protolog.ShellProtoLogGroup;
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipBoundsAlgorithm.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipBoundsAlgorithm.java
index cd58ff4..a48e969f 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipBoundsAlgorithm.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipBoundsAlgorithm.java
@@ -36,11 +36,11 @@
import com.android.internal.protolog.common.ProtoLog;
import com.android.wm.shell.R;
import com.android.wm.shell.common.DisplayLayout;
+import com.android.wm.shell.common.pip.PipBoundsAlgorithm;
+import com.android.wm.shell.common.pip.PipDisplayLayoutState;
+import com.android.wm.shell.common.pip.PipKeepClearAlgorithmInterface;
+import com.android.wm.shell.common.pip.PipSnapAlgorithm;
import com.android.wm.shell.common.pip.SizeSpecSource;
-import com.android.wm.shell.pip.PipBoundsAlgorithm;
-import com.android.wm.shell.pip.PipDisplayLayoutState;
-import com.android.wm.shell.pip.PipKeepClearAlgorithmInterface;
-import com.android.wm.shell.pip.PipSnapAlgorithm;
import com.android.wm.shell.pip.tv.TvPipKeepClearAlgorithm.Placement;
import com.android.wm.shell.protolog.ShellProtoLogGroup;
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipBoundsController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipBoundsController.java
index 8d4a384..8a215b4 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipBoundsController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipBoundsController.java
@@ -16,7 +16,7 @@
package com.android.wm.shell.pip.tv;
-import static com.android.wm.shell.pip.PipBoundsState.STASH_TYPE_NONE;
+import static com.android.wm.shell.common.pip.PipBoundsState.STASH_TYPE_NONE;
import android.annotation.NonNull;
import android.annotation.Nullable;
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipBoundsState.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipBoundsState.java
index d11f4d5..2b3a93e 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipBoundsState.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipBoundsState.java
@@ -29,10 +29,10 @@
import android.view.Gravity;
import android.view.View;
+import com.android.wm.shell.common.pip.PipBoundsAlgorithm;
+import com.android.wm.shell.common.pip.PipBoundsState;
+import com.android.wm.shell.common.pip.PipDisplayLayoutState;
import com.android.wm.shell.common.pip.SizeSpecSource;
-import com.android.wm.shell.pip.PipBoundsAlgorithm;
-import com.android.wm.shell.pip.PipBoundsState;
-import com.android.wm.shell.pip.PipDisplayLayoutState;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipController.java
index 5f5d8ad..72115fd 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipController.java
@@ -48,11 +48,11 @@
import com.android.wm.shell.common.TaskStackListenerCallback;
import com.android.wm.shell.common.TaskStackListenerImpl;
import com.android.wm.shell.common.pip.PipAppOpsListener;
+import com.android.wm.shell.common.pip.PipDisplayLayoutState;
import com.android.wm.shell.common.pip.PipMediaController;
import com.android.wm.shell.pip.PinnedStackListenerForwarder;
import com.android.wm.shell.pip.Pip;
import com.android.wm.shell.pip.PipAnimationController;
-import com.android.wm.shell.pip.PipDisplayLayoutState;
import com.android.wm.shell.pip.PipParamsChangedForwarder;
import com.android.wm.shell.pip.PipTaskOrganizer;
import com.android.wm.shell.pip.PipTransitionController;
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipKeepClearAlgorithm.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipKeepClearAlgorithm.kt
index a94bd6e..93f6826 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipKeepClearAlgorithm.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipKeepClearAlgorithm.kt
@@ -21,12 +21,12 @@
import android.graphics.Rect
import android.util.Size
import android.view.Gravity
-import com.android.wm.shell.pip.PipBoundsState
-import com.android.wm.shell.pip.PipBoundsState.STASH_TYPE_BOTTOM
-import com.android.wm.shell.pip.PipBoundsState.STASH_TYPE_LEFT
-import com.android.wm.shell.pip.PipBoundsState.STASH_TYPE_NONE
-import com.android.wm.shell.pip.PipBoundsState.STASH_TYPE_RIGHT
-import com.android.wm.shell.pip.PipBoundsState.STASH_TYPE_TOP
+import com.android.wm.shell.common.pip.PipBoundsState
+import com.android.wm.shell.common.pip.PipBoundsState.STASH_TYPE_BOTTOM
+import com.android.wm.shell.common.pip.PipBoundsState.STASH_TYPE_LEFT
+import com.android.wm.shell.common.pip.PipBoundsState.STASH_TYPE_NONE
+import com.android.wm.shell.common.pip.PipBoundsState.STASH_TYPE_RIGHT
+import com.android.wm.shell.common.pip.PipBoundsState.STASH_TYPE_TOP
import kotlin.math.abs
import kotlin.math.max
import kotlin.math.min
@@ -54,11 +54,11 @@
* the unstash timeout if already stashed.
*/
data class Placement(
- val bounds: Rect,
- val anchorBounds: Rect,
- @PipBoundsState.StashType val stashType: Int = STASH_TYPE_NONE,
- val unstashDestinationBounds: Rect? = null,
- val triggerStash: Boolean = false
+ val bounds: Rect,
+ val anchorBounds: Rect,
+ @PipBoundsState.StashType val stashType: Int = STASH_TYPE_NONE,
+ val unstashDestinationBounds: Rect? = null,
+ val triggerStash: Boolean = false
) {
/** Bounds to use if the PiP should not be stashed. */
fun getUnstashedBounds() = unstashDestinationBounds ?: bounds
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipTaskOrganizer.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipTaskOrganizer.java
index 6720804..f315afb 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipTaskOrganizer.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipTaskOrganizer.java
@@ -25,12 +25,12 @@
import com.android.wm.shell.common.DisplayController;
import com.android.wm.shell.common.ShellExecutor;
import com.android.wm.shell.common.SyncTransactionQueue;
+import com.android.wm.shell.common.pip.PipBoundsAlgorithm;
+import com.android.wm.shell.common.pip.PipBoundsState;
+import com.android.wm.shell.common.pip.PipDisplayLayoutState;
import com.android.wm.shell.common.pip.PipUiEventLogger;
import com.android.wm.shell.common.pip.PipUtils;
import com.android.wm.shell.pip.PipAnimationController;
-import com.android.wm.shell.pip.PipBoundsAlgorithm;
-import com.android.wm.shell.pip.PipBoundsState;
-import com.android.wm.shell.pip.PipDisplayLayoutState;
import com.android.wm.shell.pip.PipMenuController;
import com.android.wm.shell.pip.PipParamsChangedForwarder;
import com.android.wm.shell.pip.PipSurfaceTransactionHelper;
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipTransition.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipTransition.java
index d3253a5..f24b2b3 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipTransition.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipTransition.java
@@ -21,8 +21,8 @@
import androidx.annotation.NonNull;
import com.android.wm.shell.ShellTaskOrganizer;
+import com.android.wm.shell.common.pip.PipDisplayLayoutState;
import com.android.wm.shell.pip.PipAnimationController;
-import com.android.wm.shell.pip.PipDisplayLayoutState;
import com.android.wm.shell.pip.PipSurfaceTransactionHelper;
import com.android.wm.shell.pip.PipTransition;
import com.android.wm.shell.pip.PipTransitionState;
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/OWNERS b/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/OWNERS
index ec09827..6dabb3b 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/OWNERS
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/OWNERS
@@ -1,3 +1,4 @@
# WM shell sub-module pip owner
hwwang@google.com
mateuszc@google.com
+gabiyev@google.com
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/PipTransition.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/PipTransition.java
index 8ab85d0..b8e4c04 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/PipTransition.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/PipTransition.java
@@ -17,27 +17,65 @@
package com.android.wm.shell.pip2;
import android.annotation.NonNull;
+import android.os.IBinder;
+import android.view.SurfaceControl;
+import android.window.TransitionInfo;
+import android.window.TransitionRequestInfo;
+import android.window.WindowContainerTransaction;
+
+import androidx.annotation.Nullable;
import com.android.wm.shell.ShellTaskOrganizer;
-import com.android.wm.shell.pip.PipAnimationController;
-import com.android.wm.shell.pip.PipBoundsAlgorithm;
-import com.android.wm.shell.pip.PipBoundsState;
+import com.android.wm.shell.common.pip.PipBoundsAlgorithm;
+import com.android.wm.shell.common.pip.PipBoundsState;
+import com.android.wm.shell.common.pip.PipUtils;
import com.android.wm.shell.pip.PipMenuController;
import com.android.wm.shell.pip.PipTransitionController;
import com.android.wm.shell.sysui.ShellInit;
import com.android.wm.shell.transition.Transitions;
/** Placeholder, for demonstrate purpose only. */
-public abstract class PipTransition extends PipTransitionController {
+public class PipTransition extends PipTransitionController {
public PipTransition(
@NonNull ShellInit shellInit,
@NonNull ShellTaskOrganizer shellTaskOrganizer,
@NonNull Transitions transitions,
PipBoundsState pipBoundsState,
PipMenuController pipMenuController,
- PipBoundsAlgorithm pipBoundsAlgorithm,
- PipAnimationController pipAnimationController) {
+ PipBoundsAlgorithm pipBoundsAlgorithm) {
super(shellInit, shellTaskOrganizer, transitions, pipBoundsState, pipMenuController,
- pipBoundsAlgorithm, pipAnimationController);
+ pipBoundsAlgorithm);
}
+
+ @Override
+ protected void onInit() {
+ if (PipUtils.isPip2ExperimentEnabled()) {
+ mTransitions.addHandler(this);
+ }
+ }
+
+ @Nullable
+ @Override
+ public WindowContainerTransaction handleRequest(@NonNull IBinder transition,
+ @NonNull TransitionRequestInfo request) {
+ return null;
+ }
+
+ @Override
+ public boolean startAnimation(@NonNull IBinder transition,
+ @NonNull TransitionInfo info,
+ @NonNull SurfaceControl.Transaction startTransaction,
+ @NonNull SurfaceControl.Transaction finishTransaction,
+ @NonNull Transitions.TransitionFinishCallback finishCallback) {
+ return false;
+ }
+
+ @Override
+ public void mergeAnimation(@NonNull IBinder transition, @NonNull TransitionInfo info,
+ @NonNull SurfaceControl.Transaction t, @NonNull IBinder mergeTarget,
+ @NonNull Transitions.TransitionFinishCallback finishCallback) {}
+
+ @Override
+ public void onTransitionConsumed(@NonNull IBinder transition, boolean aborted,
+ @Nullable SurfaceControl.Transaction finishT) {}
}
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 ac142e9..94e1b33 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
@@ -340,7 +340,7 @@
continue;
}
- if (DesktopModeStatus.isProto2Enabled() && mDesktopModeTaskRepository.isPresent()
+ if (DesktopModeStatus.isEnabled() && mDesktopModeTaskRepository.isPresent()
&& mDesktopModeTaskRepository.get().isActiveTask(taskInfo.taskId)) {
// Freeform tasks will be added as a separate entry
freeformTasks.add(taskInfo);
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/ISplitSelectListener.aidl b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/ISplitSelectListener.aidl
index 7171da5..a25f391 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/ISplitSelectListener.aidl
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/ISplitSelectListener.aidl
@@ -17,7 +17,7 @@
package com.android.wm.shell.splitscreen;
import android.app.ActivityManager.RunningTaskInfo;
-
+import android.graphics.Rect;
/**
* Listener interface that Launcher attaches to SystemUI to get split-select callbacks.
*/
@@ -25,5 +25,5 @@
/**
* Called when a task requests to enter split select
*/
- boolean onRequestSplitSelect(in RunningTaskInfo taskInfo);
+ boolean onRequestSplitSelect(in RunningTaskInfo taskInfo, int splitPosition, in Rect taskBounds);
}
\ No newline at end of file
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreen.java b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreen.java
index f20fe0b..ad40493 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreen.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreen.java
@@ -66,7 +66,8 @@
/** Callback interface for listening to requests to enter split select */
interface SplitSelectListener {
- default boolean onRequestEnterSplitSelect(ActivityManager.RunningTaskInfo taskInfo) {
+ default boolean onRequestEnterSplitSelect(ActivityManager.RunningTaskInfo taskInfo,
+ int splitPosition, Rect taskBounds) {
return false;
}
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenController.java
index 210bf68..f90ee58 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenController.java
@@ -507,10 +507,12 @@
* Move a task to split select
* @param taskInfo the task being moved to split select
* @param wct transaction to apply if this is a valid request
+ * @param splitPosition the split position this task should move to
+ * @param taskBounds current freeform bounds of the task entering split
*/
public void requestEnterSplitSelect(ActivityManager.RunningTaskInfo taskInfo,
- WindowContainerTransaction wct) {
- mStageCoordinator.requestEnterSplitSelect(taskInfo, wct);
+ WindowContainerTransaction wct, int splitPosition, Rect taskBounds) {
+ mStageCoordinator.requestEnterSplitSelect(taskInfo, wct, splitPosition, taskBounds);
}
public void startTask(int taskId, @SplitPosition int position, @Nullable Bundle options) {
@@ -1135,9 +1137,11 @@
new SplitScreen.SplitSelectListener() {
@Override
public boolean onRequestEnterSplitSelect(
- ActivityManager.RunningTaskInfo taskInfo) {
+ ActivityManager.RunningTaskInfo taskInfo, int splitPosition,
+ Rect taskBounds) {
AtomicBoolean result = new AtomicBoolean(false);
- mSelectListener.call(l -> result.set(l.onRequestSplitSelect(taskInfo)));
+ mSelectListener.call(l -> result.set(l.onRequestSplitSelect(taskInfo,
+ splitPosition, taskBounds)));
return result.get();
}
};
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 6970068..842b1bf 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
@@ -466,10 +466,11 @@
}
void requestEnterSplitSelect(ActivityManager.RunningTaskInfo taskInfo,
- WindowContainerTransaction wct) {
+ WindowContainerTransaction wct, int splitPosition, Rect taskBounds) {
boolean enteredSplitSelect = false;
for (SplitScreen.SplitSelectListener listener : mSelectListeners) {
- enteredSplitSelect |= listener.onRequestEnterSplitSelect(taskInfo);
+ enteredSplitSelect |= listener.onRequestEnterSplitSelect(taskInfo, splitPosition,
+ taskBounds);
}
if (enteredSplitSelect) mTaskOrganizer.applyTransaction(wct);
}
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 986560b..87ceaa4 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
@@ -43,7 +43,6 @@
import com.android.internal.protolog.common.ProtoLog;
import com.android.wm.shell.common.split.SplitScreenUtils;
-import com.android.wm.shell.desktopmode.DesktopModeController;
import com.android.wm.shell.desktopmode.DesktopModeStatus;
import com.android.wm.shell.desktopmode.DesktopTasksController;
import com.android.wm.shell.keyguard.KeyguardTransitionHandler;
@@ -71,7 +70,6 @@
private RecentsTransitionHandler mRecentsHandler;
private StageCoordinator mSplitHandler;
private final KeyguardTransitionHandler mKeyguardHandler;
- private DesktopModeController mDesktopModeController;
private DesktopTasksController mDesktopTasksController;
private UnfoldTransitionHandler mUnfoldHandler;
@@ -141,7 +139,6 @@
@Nullable PipTransitionController pipTransitionController,
Optional<RecentsTransitionHandler> recentsHandlerOptional,
KeyguardTransitionHandler keyguardHandler,
- Optional<DesktopModeController> desktopModeControllerOptional,
Optional<DesktopTasksController> desktopTasksControllerOptional,
Optional<UnfoldTransitionHandler> unfoldHandler) {
mPlayer = player;
@@ -161,7 +158,6 @@
if (mRecentsHandler != null) {
mRecentsHandler.addMixer(this);
}
- mDesktopModeController = desktopModeControllerOptional.orElse(null);
mDesktopTasksController = desktopTasksControllerOptional.orElse(null);
mUnfoldHandler = unfoldHandler.orElse(null);
}, this);
@@ -244,7 +240,7 @@
@Override
public Transitions.TransitionHandler handleRecentsRequest(WindowContainerTransaction outWCT) {
if (mRecentsHandler != null && (mSplitHandler.isSplitScreenVisible()
- || DesktopModeStatus.isActive(mPlayer.getContext()))) {
+ || DesktopModeStatus.isEnabled())) {
return this;
}
return null;
@@ -259,7 +255,7 @@
MixedTransition.TYPE_RECENTS_DURING_SPLIT, transition);
mixed.mLeftoversHandler = mRecentsHandler;
mActiveTransitions.add(mixed);
- } else if (DesktopModeStatus.isActive(mPlayer.getContext())) {
+ } else if (DesktopModeStatus.isEnabled()) {
ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TRANSITIONS, " Got a recents request while "
+ "desktop mode is active, so treat it as Mixed.");
final MixedTransition mixed = new MixedTransition(
@@ -666,11 +662,6 @@
if (!consumed) {
return false;
}
- //Sync desktop mode state (proto 1)
- if (mDesktopModeController != null) {
- mDesktopModeController.syncSurfaceState(info, finishTransaction);
- return true;
- }
//Sync desktop mode state (proto 2)
if (mDesktopTasksController != null) {
mDesktopTasksController.syncSurfaceState(info, finishTransaction);
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 b4d0a31..c74b3f3 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
@@ -21,6 +21,7 @@
import static android.view.WindowManager.TRANSIT_CHANGE;
import static android.view.WindowManager.TRANSIT_CLOSE;
import static android.view.WindowManager.TRANSIT_FIRST_CUSTOM;
+import static android.view.WindowManager.TRANSIT_FLAG_KEYGUARD_OCCLUDING;
import static android.view.WindowManager.TRANSIT_KEYGUARD_OCCLUDE;
import static android.view.WindowManager.TRANSIT_OPEN;
import static android.view.WindowManager.TRANSIT_SLEEP;
@@ -1104,7 +1105,9 @@
}
}
}
- if (request.getType() == TRANSIT_KEYGUARD_OCCLUDE && request.getTriggerTask() != null
+ final boolean isOccludingKeyguard = request.getType() == TRANSIT_KEYGUARD_OCCLUDE
+ || ((request.getFlags() & TRANSIT_FLAG_KEYGUARD_OCCLUDING) != 0);
+ if (isOccludingKeyguard && request.getTriggerTask() != null
&& request.getTriggerTask().getWindowingMode() == WINDOWING_MODE_FREEFORM) {
// This freeform task is on top of keyguard, so its windowing mode should be changed to
// fullscreen.
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 29fff03..abd2ad4 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
@@ -36,6 +36,7 @@
import android.app.ActivityTaskManager;
import android.content.Context;
import android.graphics.Point;
+import android.graphics.PointF;
import android.graphics.Rect;
import android.graphics.Region;
import android.hardware.input.InputManager;
@@ -63,13 +64,14 @@
import com.android.internal.annotations.VisibleForTesting;
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.DesktopModeController;
import com.android.wm.shell.desktopmode.DesktopModeStatus;
import com.android.wm.shell.desktopmode.DesktopTasksController;
+import com.android.wm.shell.desktopmode.DesktopTasksController.SnapPosition;
import com.android.wm.shell.freeform.FreeformTaskTransitionStarter;
import com.android.wm.shell.splitscreen.SplitScreen;
import com.android.wm.shell.splitscreen.SplitScreenController;
@@ -99,7 +101,6 @@
private final Choreographer mMainChoreographer;
private final DisplayController mDisplayController;
private final SyncTransactionQueue mSyncQueue;
- private final Optional<DesktopModeController> mDesktopModeController;
private final Optional<DesktopTasksController> mDesktopTasksController;
private boolean mTransitionDragActive;
@@ -120,6 +121,7 @@
private MoveToDesktopAnimator mMoveToDesktopAnimator;
private final Rect mDragToDesktopAnimationStartBounds = new Rect();
private final DesktopModeKeyguardChangeListener mDesktopModeKeyguardChangeListener;
+ private final RootTaskDisplayAreaOrganizer mRootTaskDisplayAreaOrganizer;
public DesktopModeWindowDecorViewModel(
Context context,
@@ -131,8 +133,8 @@
ShellController shellController,
SyncTransactionQueue syncQueue,
Transitions transitions,
- Optional<DesktopModeController> desktopModeController,
- Optional<DesktopTasksController> desktopTasksController
+ Optional<DesktopTasksController> desktopTasksController,
+ RootTaskDisplayAreaOrganizer rootTaskDisplayAreaOrganizer
) {
this(
context,
@@ -144,12 +146,12 @@
shellController,
syncQueue,
transitions,
- desktopModeController,
desktopTasksController,
new DesktopModeWindowDecoration.Factory(),
new InputMonitorFactory(),
SurfaceControl.Transaction::new,
- new DesktopModeKeyguardChangeListener());
+ new DesktopModeKeyguardChangeListener(),
+ rootTaskDisplayAreaOrganizer);
}
@VisibleForTesting
@@ -163,12 +165,12 @@
ShellController shellController,
SyncTransactionQueue syncQueue,
Transitions transitions,
- Optional<DesktopModeController> desktopModeController,
Optional<DesktopTasksController> desktopTasksController,
DesktopModeWindowDecoration.Factory desktopModeWindowDecorFactory,
InputMonitorFactory inputMonitorFactory,
Supplier<SurfaceControl.Transaction> transactionFactory,
- DesktopModeKeyguardChangeListener desktopModeKeyguardChangeListener) {
+ DesktopModeKeyguardChangeListener desktopModeKeyguardChangeListener,
+ RootTaskDisplayAreaOrganizer rootTaskDisplayAreaOrganizer) {
mContext = context;
mMainHandler = mainHandler;
mMainChoreographer = mainChoreographer;
@@ -178,13 +180,13 @@
mDisplayController = displayController;
mSyncQueue = syncQueue;
mTransitions = transitions;
- mDesktopModeController = desktopModeController;
mDesktopTasksController = desktopTasksController;
mDesktopModeWindowDecorFactory = desktopModeWindowDecorFactory;
mInputMonitorFactory = inputMonitorFactory;
mTransactionFactory = transactionFactory;
mDesktopModeKeyguardChangeListener = desktopModeKeyguardChangeListener;
+ mRootTaskDisplayAreaOrganizer = rootTaskDisplayAreaOrganizer;
shellInit.addInitCallback(this::onInit, this);
}
@@ -206,9 +208,8 @@
public void onTaskStageChanged(int taskId, int stage, boolean visible) {
if (visible) {
DesktopModeWindowDecoration decor = mWindowDecorByTaskId.get(taskId);
- if (decor != null && DesktopModeStatus.isActive(mContext)
+ if (decor != null && DesktopModeStatus.isEnabled()
&& decor.mTaskInfo.getWindowingMode() == WINDOWING_MODE_FREEFORM) {
- mDesktopModeController.ifPresent(c -> c.setDesktopModeActive(false));
mDesktopTasksController.ifPresent(c -> c.moveToSplit(decor.mTaskInfo));
}
}
@@ -318,7 +319,8 @@
}
private class DesktopModeTouchEventListener extends GestureDetector.SimpleOnGestureListener
- implements View.OnClickListener, View.OnTouchListener, DragDetector.MotionEventHandler {
+ implements View.OnClickListener, View.OnTouchListener, View.OnLongClickListener,
+ DragDetector.MotionEventHandler{
private final int mTaskId;
private final WindowContainerToken mTaskToken;
@@ -355,9 +357,11 @@
.getTaskInfo(remainingTaskPosition);
mSplitScreenController.moveTaskToFullscreen(remainingTask.taskId);
}
+ decoration.closeMaximizeMenu();
} else if (id == R.id.back_button) {
mTaskOperations.injectBackKey();
} else if (id == R.id.caption_handle || id == R.id.open_menu_button) {
+ decoration.closeMaximizeMenu();
if (!decoration.isHandleMenuActive()) {
moveTaskToFront(mTaskOrganizer.getRunningTaskInfo(mTaskId));
decoration.createHandleMenu();
@@ -365,7 +369,6 @@
decoration.closeHandleMenu();
}
} else if (id == R.id.desktop_button) {
- mDesktopModeController.ifPresent(c -> c.setDesktopModeActive(true));
if (mDesktopTasksController.isPresent()) {
final WindowContainerTransaction wct = new WindowContainerTransaction();
// App sometimes draws before the insets from WindowDecoration#relayout have
@@ -376,7 +379,6 @@
}
decoration.closeHandleMenu();
} else if (id == R.id.fullscreen_button) {
- mDesktopModeController.ifPresent(c -> c.setDesktopModeActive(false));
mDesktopTasksController.ifPresent(c -> c.moveToFullscreen(mTaskId));
decoration.closeHandleMenu();
} else if (id == R.id.split_screen_button) {
@@ -391,13 +393,35 @@
// TODO(b/278084491): dev option to enable display switching
// remove when select is implemented
mDesktopTasksController.ifPresent(c -> c.moveToNextDisplay(mTaskId));
- decoration.closeHandleMenu();
}
} else if (id == R.id.maximize_window) {
+ moveTaskToFront(decoration.mTaskInfo);
+ if (decoration.isMaximizeMenuActive()) {
+ decoration.closeMaximizeMenu();
+ return;
+ }
final RunningTaskInfo taskInfo = decoration.mTaskInfo;
mDesktopTasksController.ifPresent(c -> c.toggleDesktopTaskSize(
taskInfo, decoration));
decoration.closeHandleMenu();
+ } else if (id == R.id.maximize_menu_maximize_button) {
+ final RunningTaskInfo taskInfo = decoration.mTaskInfo;
+ mDesktopTasksController.ifPresent(c -> c.toggleDesktopTaskSize(
+ taskInfo, mWindowDecorByTaskId.get(taskInfo.taskId)));
+ decoration.closeHandleMenu();
+ decoration.closeMaximizeMenu();
+ } else if (id == R.id.maximize_menu_snap_left_button) {
+ final RunningTaskInfo taskInfo = decoration.mTaskInfo;
+ mDesktopTasksController.ifPresent(c -> c.snapToHalfScreen(
+ taskInfo, mWindowDecorByTaskId.get(taskInfo.taskId), SnapPosition.LEFT));
+ decoration.closeHandleMenu();
+ decoration.closeMaximizeMenu();
+ } else if (id == R.id.maximize_menu_snap_right_button) {
+ final RunningTaskInfo taskInfo = decoration.mTaskInfo;
+ mDesktopTasksController.ifPresent(c -> c.snapToHalfScreen(
+ taskInfo, mWindowDecorByTaskId.get(taskInfo.taskId), SnapPosition.RIGHT));
+ decoration.closeHandleMenu();
+ decoration.closeMaximizeMenu();
}
}
@@ -412,10 +436,26 @@
return mDragDetector.onMotionEvent(v, e);
}
+ @Override
+ public boolean onLongClick(View v) {
+ final int id = v.getId();
+ if (id == R.id.maximize_window) {
+ final DesktopModeWindowDecoration decoration = mWindowDecorByTaskId.get(mTaskId);
+ moveTaskToFront(decoration.mTaskInfo);
+ if (decoration.isMaximizeMenuActive()) {
+ decoration.closeMaximizeMenu();
+ } else {
+ decoration.closeHandleMenu();
+ decoration.createMaximizeMenu();
+ }
+ return true;
+ }
+ return false;
+ }
+
private void moveTaskToFront(RunningTaskInfo taskInfo) {
if (!taskInfo.isFocused) {
mDesktopTasksController.ifPresent(c -> c.moveTaskToFront(taskInfo));
- mDesktopModeController.ifPresent(c -> c.moveTaskToFront(taskInfo));
}
}
@@ -426,15 +466,10 @@
@Override
public boolean handleMotionEvent(@Nullable View v, MotionEvent e) {
final RunningTaskInfo taskInfo = mTaskOrganizer.getRunningTaskInfo(mTaskId);
- if (DesktopModeStatus.isProto2Enabled()
+ if (DesktopModeStatus.isEnabled()
&& taskInfo.getWindowingMode() == WINDOWING_MODE_FULLSCREEN) {
return false;
}
- if (DesktopModeStatus.isProto1Enabled() && mDesktopModeController.isPresent()
- && mDesktopModeController.get().getDisplayAreaWindowingMode(taskInfo.displayId)
- == WINDOWING_MODE_FULLSCREEN) {
- return false;
- }
if (mGestureDetector.onTouchEvent(e)) {
return true;
}
@@ -458,7 +493,9 @@
final Rect newTaskBounds = mDragPositioningCallback.onDragPositioningMove(
e.getRawX(dragPointerIdx), e.getRawY(dragPointerIdx));
mDesktopTasksController.ifPresent(c -> c.onDragPositioningMove(taskInfo,
- decoration.mTaskSurface, newTaskBounds.top));
+ decoration.mTaskSurface,
+ new PointF(e.getRawX(dragPointerIdx), e.getRawY(dragPointerIdx)),
+ newTaskBounds));
mIsDragging = true;
mShouldClick = false;
return true;
@@ -487,7 +524,9 @@
final Rect newTaskBounds = mDragPositioningCallback.onDragPositioningEnd(
e.getRawX(dragPointerIdx), e.getRawY(dragPointerIdx));
mDesktopTasksController.ifPresent(c -> c.onDragPositioningEnd(taskInfo,
- position, newTaskBounds.top, mWindowDecorByTaskId.get(mTaskId)));
+ position,
+ new PointF(e.getRawX(dragPointerIdx), e.getRawY(dragPointerIdx)),
+ newTaskBounds, mWindowDecorByTaskId.get(mTaskId)));
mIsDragging = false;
return true;
}
@@ -580,7 +619,7 @@
*/
private void handleReceivedMotionEvent(MotionEvent ev, InputMonitor inputMonitor) {
final DesktopModeWindowDecoration relevantDecor = getRelevantWindowDecor(ev);
- if (DesktopModeStatus.isProto2Enabled()) {
+ if (DesktopModeStatus.isEnabled()) {
if (relevantDecor == null
|| relevantDecor.mTaskInfo.getWindowingMode() != WINDOWING_MODE_FREEFORM
|| mTransitionDragActive) {
@@ -589,14 +628,10 @@
}
handleEventOutsideFocusedCaption(ev, relevantDecor);
// Prevent status bar from reacting to a caption drag.
- if (DesktopModeStatus.isProto2Enabled()) {
+ if (DesktopModeStatus.isEnabled()) {
if (mTransitionDragActive) {
inputMonitor.pilferPointers();
}
- } else if (DesktopModeStatus.isProto1Enabled()) {
- if (mTransitionDragActive && !DesktopModeStatus.isActive(mContext)) {
- inputMonitor.pilferPointers();
- }
}
}
@@ -629,7 +664,7 @@
mDragToDesktopAnimationStartBounds.set(
relevantDecor.mTaskInfo.configuration.windowConfiguration.getBounds());
boolean dragFromStatusBarAllowed = false;
- if (DesktopModeStatus.isProto2Enabled()) {
+ if (DesktopModeStatus.isEnabled()) {
// In proto2 any full screen or multi-window task can be dragged to
// freeform.
final int windowingMode = relevantDecor.mTaskInfo.getWindowingMode();
@@ -654,10 +689,8 @@
final int statusBarHeight = getStatusBarHeight(
relevantDecor.mTaskInfo.displayId);
if (ev.getY() > 2 * statusBarHeight) {
- if (DesktopModeStatus.isProto2Enabled()) {
+ if (DesktopModeStatus.isEnabled()) {
animateToDesktop(relevantDecor, ev);
- } else if (DesktopModeStatus.isProto1Enabled()) {
- mDesktopModeController.ifPresent(c -> c.setDesktopModeActive(true));
}
mMoveToDesktopAnimator = null;
return;
@@ -848,7 +881,7 @@
&& taskInfo.isFocused) {
return false;
}
- return DesktopModeStatus.isProto2Enabled()
+ return DesktopModeStatus.isEnabled()
&& taskInfo.getWindowingMode() != WINDOWING_MODE_PINNED
&& taskInfo.getActivityType() == ACTIVITY_TYPE_STANDARD
&& !taskInfo.configuration.windowConfiguration.isAlwaysOnTop()
@@ -875,7 +908,8 @@
taskSurface,
mMainHandler,
mMainChoreographer,
- mSyncQueue);
+ mSyncQueue,
+ mRootTaskDisplayAreaOrganizer);
mWindowDecorByTaskId.put(taskInfo.taskId, windowDecoration);
windowDecoration.createResizeVeil();
@@ -884,7 +918,8 @@
final DesktopModeTouchEventListener touchEventListener =
new DesktopModeTouchEventListener(taskInfo, dragPositioningCallback);
- windowDecoration.setCaptionListeners(touchEventListener, touchEventListener);
+ windowDecoration.setCaptionListeners(
+ touchEventListener, touchEventListener, touchEventListener);
windowDecoration.setCornersListener(mCornersListener);
windowDecoration.setDragPositioningCallback(dragPositioningCallback);
windowDecoration.setDragDetector(touchEventListener.mDragDetector);
@@ -911,7 +946,9 @@
implements DragPositioningCallbackUtility.DragStartListener {
@Override
public void onDragStart(int taskId) {
- mWindowDecorByTaskId.get(taskId).closeHandleMenu();
+ final DesktopModeWindowDecoration decoration = mWindowDecorByTaskId.get(taskId);
+ decoration.closeHandleMenu();
+ decoration.closeMaximizeMenu();
}
}
@@ -926,13 +963,11 @@
@Override
public void onTaskCornersChanged(int taskId, Region corner) {
- mDesktopModeController.ifPresent(d -> d.onTaskCornersChanged(taskId, corner));
mDesktopTasksController.ifPresent(d -> d.onTaskCornersChanged(taskId, corner));
}
@Override
public void onTaskCornersRemoved(int taskId) {
- mDesktopModeController.ifPresent(d -> d.removeCornersForTask(taskId));
mDesktopTasksController.ifPresent(d -> d.removeCornersForTask(taskId));
}
}
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 a359395..3e21c8c 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
@@ -23,6 +23,7 @@
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageManager;
import android.content.res.Configuration;
+import android.content.res.Resources;
import android.graphics.Point;
import android.graphics.PointF;
import android.graphics.Rect;
@@ -36,13 +37,16 @@
import android.view.SurfaceControl;
import android.view.View;
import android.view.ViewConfiguration;
+import android.widget.ImageButton;
import android.window.WindowContainerTransaction;
import com.android.internal.policy.ScreenDecorationsUtils;
import com.android.launcher3.icons.IconProvider;
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;
@@ -69,6 +73,7 @@
private DesktopModeWindowDecorationViewHolder mWindowDecorViewHolder;
private View.OnClickListener mOnCaptionButtonClickListener;
private View.OnTouchListener mOnCaptionTouchListener;
+ private View.OnLongClickListener mOnCaptionLongClickListener;
private DragPositioningCallback mDragPositioningCallback;
private DragResizeInputListener mDragResizeListener;
private DragDetector mDragDetector;
@@ -80,6 +85,8 @@
private final Point mPositionInParent = new Point();
private HandleMenu mHandleMenu;
+ private MaximizeMenu mMaximizeMenu;
+
private ResizeVeil mResizeVeil;
private Drawable mAppIcon;
@@ -89,6 +96,7 @@
private final Set<IBinder> mTransitionsPausingRelayout = new HashSet<>();
private int mRelayoutBlock;
+ private final RootTaskDisplayAreaOrganizer mRootTaskDisplayAreaOrganizer;
DesktopModeWindowDecoration(
Context context,
@@ -98,12 +106,14 @@
SurfaceControl taskSurface,
Handler handler,
Choreographer choreographer,
- SyncTransactionQueue syncQueue) {
+ SyncTransactionQueue syncQueue,
+ RootTaskDisplayAreaOrganizer rootTaskDisplayAreaOrganizer) {
super(context, displayController, taskOrganizer, taskInfo, taskSurface);
mHandler = handler;
mChoreographer = choreographer;
mSyncQueue = syncQueue;
+ mRootTaskDisplayAreaOrganizer = rootTaskDisplayAreaOrganizer;
loadAppInfo();
}
@@ -121,9 +131,11 @@
void setCaptionListeners(
View.OnClickListener onCaptionButtonClickListener,
- View.OnTouchListener onCaptionTouchListener) {
+ View.OnTouchListener onCaptionTouchListener,
+ View.OnLongClickListener onLongClickListener) {
mOnCaptionButtonClickListener = onCaptionButtonClickListener;
mOnCaptionTouchListener = onCaptionTouchListener;
+ mOnCaptionLongClickListener = onLongClickListener;
}
void setCornersListener(TaskCornersListener cornersListener) {
@@ -207,6 +219,7 @@
mResult.mRootView,
mOnCaptionTouchListener,
mOnCaptionButtonClickListener,
+ mOnCaptionLongClickListener,
mAppName,
mAppIcon
);
@@ -218,6 +231,7 @@
if (!mTaskInfo.isFocused) {
closeHandleMenu();
+ closeMaximizeMenu();
}
if (!isDragResizeable) {
@@ -255,6 +269,52 @@
mCornersListener.onTaskCornersChanged(mTaskInfo.taskId, getGlobalCornersRegion());
}
mPositionInParent.set(mTaskInfo.positionInParent);
+
+ if (isMaximizeMenuActive()) {
+ if (!mTaskInfo.isVisible()) {
+ closeMaximizeMenu();
+ } else {
+ mMaximizeMenu.positionMenu(calculateMaximizeMenuPosition(), startT);
+ }
+ }
+ }
+
+ private PointF calculateMaximizeMenuPosition() {
+ final PointF position = new PointF();
+ final Resources resources = mContext.getResources();
+ final DisplayLayout displayLayout =
+ mDisplayController.getDisplayLayout(mTaskInfo.displayId);
+ if (displayLayout == null) return position;
+
+ final int displayWidth = displayLayout.width();
+ final int displayHeight = displayLayout.height();
+ final int captionHeight = loadDimensionPixelSize(
+ resources, R.dimen.freeform_decor_caption_height);
+
+ final ImageButton maximizeWindowButton =
+ mResult.mRootView.findViewById(R.id.maximize_window);
+ final int[] maximizeButtonLocation = new int[2];
+ maximizeWindowButton.getLocationInWindow(maximizeButtonLocation);
+
+ final int menuWidth = loadDimensionPixelSize(
+ resources, R.dimen.desktop_mode_maximize_menu_width);
+ final int menuHeight = loadDimensionPixelSize(
+ resources, R.dimen.desktop_mode_maximize_menu_height);
+
+ float menuLeft = (mPositionInParent.x + maximizeButtonLocation[0]);
+ float menuTop = (mPositionInParent.y + captionHeight);
+ final float menuRight = menuLeft + menuWidth;
+ final float menuBottom = menuTop + menuHeight;
+
+ // If the menu is out of screen bounds, shift it up/left as needed
+ if (menuRight > displayWidth) {
+ menuLeft = (displayWidth - menuWidth);
+ }
+ if (menuBottom > displayHeight) {
+ menuTop = (displayHeight - menuHeight);
+ }
+
+ return new PointF(menuLeft, menuTop);
}
boolean isHandleMenuActive() {
@@ -335,6 +395,29 @@
}
/**
+ * Create and display maximize menu window
+ */
+ void createMaximizeMenu() {
+ mMaximizeMenu = new MaximizeMenu(mSyncQueue, mRootTaskDisplayAreaOrganizer,
+ mDisplayController, mTaskInfo, mOnCaptionButtonClickListener, mContext,
+ calculateMaximizeMenuPosition(), mSurfaceControlTransactionSupplier);
+ mMaximizeMenu.show();
+ }
+
+ /**
+ * Close the maximize menu window
+ */
+ void closeMaximizeMenu() {
+ if (!isMaximizeMenuActive()) return;
+ mMaximizeMenu.close();
+ mMaximizeMenu = null;
+ }
+
+ boolean isMaximizeMenuActive() {
+ return mMaximizeMenu != null;
+ }
+
+ /**
* Create and display handle menu window
*/
void createHandleMenu() {
@@ -345,7 +428,7 @@
.setOnTouchListener(mOnCaptionTouchListener)
.setLayoutId(mRelayoutParams.mLayoutResId)
.setCaptionPosition(mRelayoutParams.mCaptionX, mRelayoutParams.mCaptionY)
- .setWindowingButtonsVisible(DesktopModeStatus.isProto2Enabled())
+ .setWindowingButtonsVisible(DesktopModeStatus.isEnabled())
.build();
mHandleMenu.show();
}
@@ -466,9 +549,6 @@
}
private int getDesktopModeWindowDecorLayoutId(int windowingMode) {
- if (DesktopModeStatus.isProto1Enabled()) {
- return R.layout.desktop_mode_app_controls_window_decor;
- }
return windowingMode == WINDOWING_MODE_FREEFORM
? R.layout.desktop_mode_app_controls_window_decor
: R.layout.desktop_mode_focused_window_decor;
@@ -532,7 +612,8 @@
SurfaceControl taskSurface,
Handler handler,
Choreographer choreographer,
- SyncTransactionQueue syncQueue) {
+ SyncTransactionQueue syncQueue,
+ RootTaskDisplayAreaOrganizer rootTaskDisplayAreaOrganizer) {
return new DesktopModeWindowDecoration(
context,
displayController,
@@ -541,7 +622,8 @@
taskSurface,
handler,
choreographer,
- syncQueue);
+ syncQueue,
+ rootTaskDisplayAreaOrganizer);
}
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/HandleMenu.java b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/HandleMenu.java
index ac4a597..ca7cbfd 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/HandleMenu.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/HandleMenu.java
@@ -236,7 +236,7 @@
t.setPosition(mAppInfoPill.mWindowSurface,
mAppInfoPillPosition.x, mAppInfoPillPosition.y);
// Only show windowing buttons in proto2. Proto1 uses a system-level mode only.
- final boolean shouldShowWindowingPill = DesktopModeStatus.isProto2Enabled();
+ final boolean shouldShowWindowingPill = DesktopModeStatus.isEnabled();
if (shouldShowWindowingPill) {
t.setPosition(mWindowingPill.mWindowSurface,
mWindowingPillPosition.x, mWindowingPillPosition.y);
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/MaximizeMenu.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/MaximizeMenu.kt
new file mode 100644
index 0000000..050d1e9
--- /dev/null
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/MaximizeMenu.kt
@@ -0,0 +1,154 @@
+/*
+ * 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.windowdecor
+
+import android.app.ActivityManager.RunningTaskInfo
+import android.content.Context
+import android.content.res.Resources
+import android.graphics.PixelFormat
+import android.graphics.PointF
+import android.view.LayoutInflater
+import android.view.SurfaceControl
+import android.view.SurfaceControl.Transaction
+import android.view.SurfaceControlViewHost
+import android.view.View.OnClickListener
+import android.view.WindowManager
+import android.view.WindowlessWindowManager
+import android.widget.Button
+import android.window.TaskConstants
+import com.android.wm.shell.R
+import com.android.wm.shell.RootTaskDisplayAreaOrganizer
+import com.android.wm.shell.common.DisplayController
+import com.android.wm.shell.common.SyncTransactionQueue
+import com.android.wm.shell.windowdecor.WindowDecoration.AdditionalWindow
+import java.util.function.Supplier
+
+
+/**
+ * Menu that appears when user long clicks the maximize button. Gives the user the option to
+ * maximize the task or snap the task to the right or left half of the screen.
+ */
+class MaximizeMenu(
+ private val syncQueue: SyncTransactionQueue,
+ private val rootTdaOrganizer: RootTaskDisplayAreaOrganizer,
+ private val displayController: DisplayController,
+ private val taskInfo: RunningTaskInfo,
+ private val onClickListener: OnClickListener,
+ private val decorWindowContext: Context,
+ private val menuPosition: PointF,
+ private val transactionSupplier: Supplier<Transaction> = Supplier { Transaction() }
+) {
+ private var maximizeMenu: AdditionalWindow? = null
+ private lateinit var viewHost: SurfaceControlViewHost
+ private lateinit var leash: SurfaceControl
+ private val shadowRadius = loadDimensionPixelSize(
+ R.dimen.desktop_mode_maximize_menu_shadow_radius
+ ).toFloat()
+ private val cornerRadius = loadDimensionPixelSize(
+ R.dimen.desktop_mode_maximize_menu_corner_radius
+ ).toFloat()
+
+ /** Position the menu relative to the caption's position. */
+ fun positionMenu(position: PointF, t: Transaction) {
+ menuPosition.set(position)
+ t.setPosition(leash, menuPosition.x, menuPosition.y)
+ }
+
+ /** Creates and shows the maximize window. */
+ fun show() {
+ if (maximizeMenu != null) return
+ createMaximizeMenu()
+ setupMaximizeMenu()
+ }
+
+ /** Closes the maximize window and releases its view. */
+ fun close() {
+ maximizeMenu?.releaseView()
+ maximizeMenu = null
+ }
+
+ /** Create a maximize menu that is attached to the display area. */
+ private fun createMaximizeMenu() {
+ val t = transactionSupplier.get()
+ val v = LayoutInflater.from(decorWindowContext).inflate(
+ R.layout.desktop_mode_window_decor_maximize_menu,
+ null // Root
+ )
+ val builder = SurfaceControl.Builder()
+ rootTdaOrganizer.attachToDisplayArea(taskInfo.displayId, builder)
+ leash = builder
+ .setName("Maximize Menu")
+ .setContainerLayer()
+ .build()
+ val menuWidth = loadDimensionPixelSize(R.dimen.desktop_mode_maximize_menu_width)
+ val menuHeight = loadDimensionPixelSize(R.dimen.desktop_mode_maximize_menu_height)
+ val lp = WindowManager.LayoutParams(
+ menuWidth,
+ menuHeight,
+ WindowManager.LayoutParams.TYPE_APPLICATION,
+ WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE,
+ PixelFormat.TRANSPARENT
+ )
+ lp.title = "Maximize Menu for Task=" + taskInfo.taskId
+ lp.setTrustedOverlay()
+ val windowManager = WindowlessWindowManager(
+ taskInfo.configuration,
+ leash,
+ null // HostInputToken
+ )
+ viewHost = SurfaceControlViewHost(decorWindowContext,
+ displayController.getDisplay(taskInfo.displayId), windowManager,
+ "MaximizeMenu")
+ viewHost.setView(v, lp)
+
+ // Bring menu to front when open
+ t.setLayer(leash, TaskConstants.TASK_CHILD_LAYER_FLOATING_MENU)
+ .setPosition(leash, menuPosition.x, menuPosition.y)
+ .setWindowCrop(leash, menuWidth, menuHeight)
+ .setShadowRadius(leash, shadowRadius)
+ .setCornerRadius(leash, cornerRadius)
+ .show(leash)
+ maximizeMenu = AdditionalWindow(leash, viewHost, transactionSupplier)
+
+ syncQueue.runInSync { transaction ->
+ transaction.merge(t)
+ t.close()
+ }
+ }
+
+ private fun loadDimensionPixelSize(resourceId: Int): Int {
+ return if (resourceId == Resources.ID_NULL) {
+ 0
+ } else {
+ decorWindowContext.resources.getDimensionPixelSize(resourceId)
+ }
+ }
+
+ private fun setupMaximizeMenu() {
+ val maximizeMenuView = maximizeMenu?.mWindowViewHost?.view ?: return
+
+ maximizeMenuView.requireViewById<Button>(
+ R.id.maximize_menu_maximize_button
+ ).setOnClickListener(onClickListener)
+ maximizeMenuView.requireViewById<Button>(
+ R.id.maximize_menu_snap_right_button
+ ).setOnClickListener(onClickListener)
+ maximizeMenuView.requireViewById<Button>(
+ R.id.maximize_menu_snap_left_button
+ ).setOnClickListener(onClickListener)
+ }
+}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/viewholder/DesktopModeAppControlsWindowDecorationViewHolder.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/viewholder/DesktopModeAppControlsWindowDecorationViewHolder.kt
index a9eb882..6b59cce 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/viewholder/DesktopModeAppControlsWindowDecorationViewHolder.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/viewholder/DesktopModeAppControlsWindowDecorationViewHolder.kt
@@ -5,6 +5,7 @@
import android.graphics.drawable.Drawable
import android.graphics.drawable.GradientDrawable
import android.view.View
+import android.view.View.OnLongClickListener
import android.widget.ImageButton
import android.widget.ImageView
import android.widget.TextView
@@ -19,6 +20,7 @@
rootView: View,
onCaptionTouchListener: View.OnTouchListener,
onCaptionButtonClickListener: View.OnClickListener,
+ onLongClickListener: OnLongClickListener,
appName: CharSequence,
appIcon: Drawable
) : DesktopModeWindowDecorationViewHolder(rootView) {
@@ -39,6 +41,7 @@
openMenuButton.setOnTouchListener(onCaptionTouchListener)
closeWindowButton.setOnClickListener(onCaptionButtonClickListener)
maximizeWindowButton.setOnClickListener(onCaptionButtonClickListener)
+ maximizeWindowButton.onLongClickListener = onLongClickListener
closeWindowButton.setOnTouchListener(onCaptionTouchListener)
appNameTextView.text = appName
appIconImageView.setImageDrawable(appIcon)
diff --git a/libs/WindowManager/Shell/tests/flicker/Android.bp b/libs/WindowManager/Shell/tests/flicker/Android.bp
index dfbadae..434b008 100644
--- a/libs/WindowManager/Shell/tests/flicker/Android.bp
+++ b/libs/WindowManager/Shell/tests/flicker/Android.bp
@@ -69,10 +69,22 @@
}
filegroup {
+ name: "WMShellFlickerServicePlatinumTests-src",
+ srcs: [
+ "src/com/android/wm/shell/flicker/service/*/platinum/**/*.kt",
+ "src/com/android/wm/shell/flicker/service/*/scenarios/**/*.kt",
+ "src/com/android/wm/shell/flicker/service/common/**/*.kt",
+ ],
+}
+
+filegroup {
name: "WMShellFlickerServiceTests-src",
srcs: [
"src/com/android/wm/shell/flicker/service/**/*.kt",
],
+ exclude_srcs: [
+ "src/com/android/wm/shell/flicker/service/*/platinum/**/*.kt",
+ ],
}
java_library {
@@ -143,6 +155,7 @@
":WMShellFlickerTestsSplitScreenGroup2-src",
":WMShellFlickerTestsSplitScreenBase-src",
":WMShellFlickerServiceTests-src",
+ ":WMShellFlickerServicePlatinumTests-src",
],
}
@@ -210,3 +223,15 @@
":WMShellFlickerServiceTests-src",
],
}
+
+android_test {
+ name: "WMShellFlickerServicePlatinumTests",
+ defaults: ["WMShellFlickerTestsDefault"],
+ additional_manifests: ["manifests/AndroidManifestService.xml"],
+ package_name: "com.android.wm.shell.flicker.service",
+ instrumentation_target_package: "com.android.wm.shell.flicker.service",
+ srcs: [
+ ":WMShellFlickerTestsBase-src",
+ ":WMShellFlickerServicePlatinumTests-src",
+ ],
+}
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/Utils.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/common/Utils.kt
similarity index 97%
rename from libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/Utils.kt
rename to libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/common/Utils.kt
index fa723e3..5f15785 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/Utils.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/common/Utils.kt
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.wm.shell.flicker.service
+package com.android.wm.shell.flicker.service.common
import android.app.Instrumentation
import android.platform.test.rule.NavigationModeRule
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/flicker/CopyContentInSplitGesturalNavLandscape.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/flicker/CopyContentInSplitGesturalNavLandscape.kt
new file mode 100644
index 0000000..a5c5122
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/flicker/CopyContentInSplitGesturalNavLandscape.kt
@@ -0,0 +1,40 @@
+/*
+ * 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.flicker.service.splitscreen.flicker
+
+import android.tools.common.Rotation
+import android.tools.common.flicker.FlickerConfig
+import android.tools.common.flicker.annotation.ExpectedScenarios
+import android.tools.common.flicker.annotation.FlickerConfigProvider
+import android.tools.common.flicker.config.FlickerConfig
+import android.tools.common.flicker.config.FlickerServiceConfig
+import android.tools.device.flicker.junit.FlickerServiceJUnit4ClassRunner
+import com.android.wm.shell.flicker.service.splitscreen.scenarios.CopyContentInSplit
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@RunWith(FlickerServiceJUnit4ClassRunner::class)
+class CopyContentInSplitGesturalNavLandscape : CopyContentInSplit(Rotation.ROTATION_90) {
+ @ExpectedScenarios([]) @Test override fun copyContentInSplit() = super.copyContentInSplit()
+
+ companion object {
+ @JvmStatic
+ @FlickerConfigProvider
+ fun flickerConfigProvider(): FlickerConfig =
+ FlickerConfig().use(FlickerServiceConfig.DEFAULT)
+ }
+}
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/flicker/CopyContentInSplitGesturalNavPortrait.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/flicker/CopyContentInSplitGesturalNavPortrait.kt
new file mode 100644
index 0000000..092fb67
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/flicker/CopyContentInSplitGesturalNavPortrait.kt
@@ -0,0 +1,40 @@
+/*
+ * 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.flicker.service.splitscreen.flicker
+
+import android.tools.common.Rotation
+import android.tools.common.flicker.FlickerConfig
+import android.tools.common.flicker.annotation.ExpectedScenarios
+import android.tools.common.flicker.annotation.FlickerConfigProvider
+import android.tools.common.flicker.config.FlickerConfig
+import android.tools.common.flicker.config.FlickerServiceConfig
+import android.tools.device.flicker.junit.FlickerServiceJUnit4ClassRunner
+import com.android.wm.shell.flicker.service.splitscreen.scenarios.CopyContentInSplit
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@RunWith(FlickerServiceJUnit4ClassRunner::class)
+class CopyContentInSplitGesturalNavPortrait : CopyContentInSplit(Rotation.ROTATION_0) {
+ @ExpectedScenarios([]) @Test override fun copyContentInSplit() = super.copyContentInSplit()
+
+ companion object {
+ @JvmStatic
+ @FlickerConfigProvider
+ fun flickerConfigProvider(): FlickerConfig =
+ FlickerConfig().use(FlickerServiceConfig.DEFAULT)
+ }
+}
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/flicker/DismissSplitScreenByDividerGesturalNavLandscape.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/flicker/DismissSplitScreenByDividerGesturalNavLandscape.kt
new file mode 100644
index 0000000..8cb25fe
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/flicker/DismissSplitScreenByDividerGesturalNavLandscape.kt
@@ -0,0 +1,44 @@
+/*
+ * 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.flicker.service.splitscreen.flicker
+
+import android.tools.common.Rotation
+import android.tools.common.flicker.FlickerConfig
+import android.tools.common.flicker.annotation.ExpectedScenarios
+import android.tools.common.flicker.annotation.FlickerConfigProvider
+import android.tools.common.flicker.config.FlickerConfig
+import android.tools.common.flicker.config.FlickerServiceConfig
+import android.tools.device.flicker.junit.FlickerServiceJUnit4ClassRunner
+import com.android.wm.shell.flicker.service.splitscreen.scenarios.DismissSplitScreenByDivider
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@RunWith(FlickerServiceJUnit4ClassRunner::class)
+class DismissSplitScreenByDividerGesturalNavLandscape :
+ DismissSplitScreenByDivider(Rotation.ROTATION_90) {
+
+ @ExpectedScenarios(["SPLIT_SCREEN_EXIT"])
+ @Test
+ override fun dismissSplitScreenByDivider() = super.dismissSplitScreenByDivider()
+
+ companion object {
+ @JvmStatic
+ @FlickerConfigProvider
+ fun flickerConfigProvider(): FlickerConfig =
+ FlickerConfig().use(FlickerServiceConfig.DEFAULT)
+ }
+}
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/flicker/DismissSplitScreenByDividerGesturalNavPortrait.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/flicker/DismissSplitScreenByDividerGesturalNavPortrait.kt
new file mode 100644
index 0000000..fa1be63
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/flicker/DismissSplitScreenByDividerGesturalNavPortrait.kt
@@ -0,0 +1,44 @@
+/*
+ * 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.flicker.service.splitscreen.flicker
+
+import android.tools.common.Rotation
+import android.tools.common.flicker.FlickerConfig
+import android.tools.common.flicker.annotation.ExpectedScenarios
+import android.tools.common.flicker.annotation.FlickerConfigProvider
+import android.tools.common.flicker.config.FlickerConfig
+import android.tools.common.flicker.config.FlickerServiceConfig
+import android.tools.device.flicker.junit.FlickerServiceJUnit4ClassRunner
+import com.android.wm.shell.flicker.service.splitscreen.scenarios.DismissSplitScreenByDivider
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@RunWith(FlickerServiceJUnit4ClassRunner::class)
+class DismissSplitScreenByDividerGesturalNavPortrait :
+ DismissSplitScreenByDivider(Rotation.ROTATION_0) {
+
+ @ExpectedScenarios(["SPLIT_SCREEN_EXIT"])
+ @Test
+ override fun dismissSplitScreenByDivider() = super.dismissSplitScreenByDivider()
+
+ companion object {
+ @JvmStatic
+ @FlickerConfigProvider
+ fun flickerConfigProvider(): FlickerConfig =
+ FlickerConfig().use(FlickerServiceConfig.DEFAULT)
+ }
+}
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/flicker/DismissSplitScreenByGoHomeGesturalNavLandscape.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/flicker/DismissSplitScreenByGoHomeGesturalNavLandscape.kt
new file mode 100644
index 0000000..aa35237
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/flicker/DismissSplitScreenByGoHomeGesturalNavLandscape.kt
@@ -0,0 +1,44 @@
+/*
+ * 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.flicker.service.splitscreen.flicker
+
+import android.tools.common.Rotation
+import android.tools.common.flicker.FlickerConfig
+import android.tools.common.flicker.annotation.ExpectedScenarios
+import android.tools.common.flicker.annotation.FlickerConfigProvider
+import android.tools.common.flicker.config.FlickerConfig
+import android.tools.common.flicker.config.FlickerServiceConfig
+import android.tools.device.flicker.junit.FlickerServiceJUnit4ClassRunner
+import com.android.wm.shell.flicker.service.splitscreen.scenarios.DismissSplitScreenByGoHome
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@RunWith(FlickerServiceJUnit4ClassRunner::class)
+class DismissSplitScreenByGoHomeGesturalNavLandscape :
+ DismissSplitScreenByGoHome(Rotation.ROTATION_90) {
+
+ @ExpectedScenarios(["SPLIT_SCREEN_EXIT"])
+ @Test
+ override fun dismissSplitScreenByGoHome() = super.dismissSplitScreenByGoHome()
+
+ companion object {
+ @JvmStatic
+ @FlickerConfigProvider
+ fun flickerConfigProvider(): FlickerConfig =
+ FlickerConfig().use(FlickerServiceConfig.DEFAULT)
+ }
+}
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/flicker/DismissSplitScreenByGoHomeGesturalNavPortrait.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/flicker/DismissSplitScreenByGoHomeGesturalNavPortrait.kt
new file mode 100644
index 0000000..e195360
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/flicker/DismissSplitScreenByGoHomeGesturalNavPortrait.kt
@@ -0,0 +1,44 @@
+/*
+ * 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.flicker.service.splitscreen.flicker
+
+import android.tools.common.Rotation
+import android.tools.common.flicker.FlickerConfig
+import android.tools.common.flicker.annotation.ExpectedScenarios
+import android.tools.common.flicker.annotation.FlickerConfigProvider
+import android.tools.common.flicker.config.FlickerConfig
+import android.tools.common.flicker.config.FlickerServiceConfig
+import android.tools.device.flicker.junit.FlickerServiceJUnit4ClassRunner
+import com.android.wm.shell.flicker.service.splitscreen.scenarios.DismissSplitScreenByGoHome
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@RunWith(FlickerServiceJUnit4ClassRunner::class)
+class DismissSplitScreenByGoHomeGesturalNavPortrait :
+ DismissSplitScreenByGoHome(Rotation.ROTATION_0) {
+
+ @ExpectedScenarios(["SPLIT_SCREEN_EXIT"])
+ @Test
+ override fun dismissSplitScreenByGoHome() = super.dismissSplitScreenByGoHome()
+
+ companion object {
+ @JvmStatic
+ @FlickerConfigProvider
+ fun flickerConfigProvider(): FlickerConfig =
+ FlickerConfig().use(FlickerServiceConfig.DEFAULT)
+ }
+}
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/flicker/DragDividerToResizeGesturalNavLandscape.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/flicker/DragDividerToResizeGesturalNavLandscape.kt
new file mode 100644
index 0000000..c1b3aad
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/flicker/DragDividerToResizeGesturalNavLandscape.kt
@@ -0,0 +1,43 @@
+/*
+ * 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.flicker.service.splitscreen.flicker
+
+import android.tools.common.Rotation
+import android.tools.common.flicker.FlickerConfig
+import android.tools.common.flicker.annotation.ExpectedScenarios
+import android.tools.common.flicker.annotation.FlickerConfigProvider
+import android.tools.common.flicker.config.FlickerConfig
+import android.tools.common.flicker.config.FlickerServiceConfig
+import android.tools.device.flicker.junit.FlickerServiceJUnit4ClassRunner
+import com.android.wm.shell.flicker.service.splitscreen.scenarios.DragDividerToResize
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@RunWith(FlickerServiceJUnit4ClassRunner::class)
+class DragDividerToResizeGesturalNavLandscape : DragDividerToResize(Rotation.ROTATION_90) {
+
+ @ExpectedScenarios(["SPLIT_SCREEN_RESIZE"])
+ @Test
+ override fun dragDividerToResize() = super.dragDividerToResize()
+
+ companion object {
+ @JvmStatic
+ @FlickerConfigProvider
+ fun flickerConfigProvider(): FlickerConfig =
+ FlickerConfig().use(FlickerServiceConfig.DEFAULT)
+ }
+}
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/flicker/DragDividerToResizeGesturalNavPortrait.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/flicker/DragDividerToResizeGesturalNavPortrait.kt
new file mode 100644
index 0000000..c6e2e85
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/flicker/DragDividerToResizeGesturalNavPortrait.kt
@@ -0,0 +1,43 @@
+/*
+ * 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.flicker.service.splitscreen.flicker
+
+import android.tools.common.Rotation
+import android.tools.common.flicker.FlickerConfig
+import android.tools.common.flicker.annotation.ExpectedScenarios
+import android.tools.common.flicker.annotation.FlickerConfigProvider
+import android.tools.common.flicker.config.FlickerConfig
+import android.tools.common.flicker.config.FlickerServiceConfig
+import android.tools.device.flicker.junit.FlickerServiceJUnit4ClassRunner
+import com.android.wm.shell.flicker.service.splitscreen.scenarios.DragDividerToResize
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@RunWith(FlickerServiceJUnit4ClassRunner::class)
+class DragDividerToResizeGesturalNavPortrait : DragDividerToResize(Rotation.ROTATION_0) {
+
+ @ExpectedScenarios(["SPLIT_SCREEN_RESIZE"])
+ @Test
+ override fun dragDividerToResize() = super.dragDividerToResize()
+
+ companion object {
+ @JvmStatic
+ @FlickerConfigProvider
+ fun flickerConfigProvider(): FlickerConfig =
+ FlickerConfig().use(FlickerServiceConfig.DEFAULT)
+ }
+}
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/flicker/EnterSplitScreenByDragFromAllAppsGesturalNavLandscape.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/flicker/EnterSplitScreenByDragFromAllAppsGesturalNavLandscape.kt
new file mode 100644
index 0000000..5f771c7
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/flicker/EnterSplitScreenByDragFromAllAppsGesturalNavLandscape.kt
@@ -0,0 +1,44 @@
+/*
+ * 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.flicker.service.splitscreen.flicker
+
+import android.tools.common.Rotation
+import android.tools.common.flicker.FlickerConfig
+import android.tools.common.flicker.annotation.ExpectedScenarios
+import android.tools.common.flicker.annotation.FlickerConfigProvider
+import android.tools.common.flicker.config.FlickerConfig
+import android.tools.common.flicker.config.FlickerServiceConfig
+import android.tools.device.flicker.junit.FlickerServiceJUnit4ClassRunner
+import com.android.wm.shell.flicker.service.splitscreen.scenarios.EnterSplitScreenByDragFromAllApps
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@RunWith(FlickerServiceJUnit4ClassRunner::class)
+class EnterSplitScreenByDragFromAllAppsGesturalNavLandscape :
+ EnterSplitScreenByDragFromAllApps(Rotation.ROTATION_90) {
+
+ @ExpectedScenarios(["SPLIT_SCREEN_ENTER"])
+ @Test
+ override fun enterSplitScreenByDragFromAllApps() = super.enterSplitScreenByDragFromAllApps()
+
+ companion object {
+ @JvmStatic
+ @FlickerConfigProvider
+ fun flickerConfigProvider(): FlickerConfig =
+ FlickerConfig().use(FlickerServiceConfig.DEFAULT)
+ }
+}
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/flicker/EnterSplitScreenByDragFromAllAppsGesturalNavPortrait.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/flicker/EnterSplitScreenByDragFromAllAppsGesturalNavPortrait.kt
new file mode 100644
index 0000000..729a401
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/flicker/EnterSplitScreenByDragFromAllAppsGesturalNavPortrait.kt
@@ -0,0 +1,44 @@
+/*
+ * 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.flicker.service.splitscreen.flicker
+
+import android.tools.common.Rotation
+import android.tools.common.flicker.FlickerConfig
+import android.tools.common.flicker.annotation.ExpectedScenarios
+import android.tools.common.flicker.annotation.FlickerConfigProvider
+import android.tools.common.flicker.config.FlickerConfig
+import android.tools.common.flicker.config.FlickerServiceConfig
+import android.tools.device.flicker.junit.FlickerServiceJUnit4ClassRunner
+import com.android.wm.shell.flicker.service.splitscreen.scenarios.EnterSplitScreenByDragFromAllApps
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@RunWith(FlickerServiceJUnit4ClassRunner::class)
+class EnterSplitScreenByDragFromAllAppsGesturalNavPortrait :
+ EnterSplitScreenByDragFromAllApps(Rotation.ROTATION_0) {
+
+ @ExpectedScenarios(["SPLIT_SCREEN_ENTER"])
+ @Test
+ override fun enterSplitScreenByDragFromAllApps() = super.enterSplitScreenByDragFromAllApps()
+
+ companion object {
+ @JvmStatic
+ @FlickerConfigProvider
+ fun flickerConfigProvider(): FlickerConfig =
+ FlickerConfig().use(FlickerServiceConfig.DEFAULT)
+ }
+}
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/flicker/EnterSplitScreenByDragFromNotificationGesturalNavLandscape.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/flicker/EnterSplitScreenByDragFromNotificationGesturalNavLandscape.kt
new file mode 100644
index 0000000..6e4cf9f
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/flicker/EnterSplitScreenByDragFromNotificationGesturalNavLandscape.kt
@@ -0,0 +1,45 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.wm.shell.flicker.service.splitscreen.flicker
+
+import android.tools.common.Rotation
+import android.tools.common.flicker.FlickerConfig
+import android.tools.common.flicker.annotation.ExpectedScenarios
+import android.tools.common.flicker.annotation.FlickerConfigProvider
+import android.tools.common.flicker.config.FlickerConfig
+import android.tools.common.flicker.config.FlickerServiceConfig
+import android.tools.device.flicker.junit.FlickerServiceJUnit4ClassRunner
+import com.android.wm.shell.flicker.service.splitscreen.scenarios.EnterSplitScreenByDragFromNotification
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@RunWith(FlickerServiceJUnit4ClassRunner::class)
+class EnterSplitScreenByDragFromNotificationGesturalNavLandscape :
+ EnterSplitScreenByDragFromNotification(Rotation.ROTATION_90) {
+
+ @ExpectedScenarios(["SPLIT_SCREEN_ENTER"])
+ @Test
+ override fun enterSplitScreenByDragFromNotification() =
+ super.enterSplitScreenByDragFromNotification()
+
+ companion object {
+ @JvmStatic
+ @FlickerConfigProvider
+ fun flickerConfigProvider(): FlickerConfig =
+ FlickerConfig().use(FlickerServiceConfig.DEFAULT)
+ }
+}
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/flicker/EnterSplitScreenByDragFromNotificationGesturalNavPortrait.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/flicker/EnterSplitScreenByDragFromNotificationGesturalNavPortrait.kt
new file mode 100644
index 0000000..cc28702
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/flicker/EnterSplitScreenByDragFromNotificationGesturalNavPortrait.kt
@@ -0,0 +1,45 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.wm.shell.flicker.service.splitscreen.flicker
+
+import android.tools.common.Rotation
+import android.tools.common.flicker.FlickerConfig
+import android.tools.common.flicker.annotation.ExpectedScenarios
+import android.tools.common.flicker.annotation.FlickerConfigProvider
+import android.tools.common.flicker.config.FlickerConfig
+import android.tools.common.flicker.config.FlickerServiceConfig
+import android.tools.device.flicker.junit.FlickerServiceJUnit4ClassRunner
+import com.android.wm.shell.flicker.service.splitscreen.scenarios.EnterSplitScreenByDragFromNotification
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@RunWith(FlickerServiceJUnit4ClassRunner::class)
+class EnterSplitScreenByDragFromNotificationGesturalNavPortrait :
+ EnterSplitScreenByDragFromNotification(Rotation.ROTATION_0) {
+
+ @ExpectedScenarios(["SPLIT_SCREEN_ENTER"])
+ @Test
+ override fun enterSplitScreenByDragFromNotification() =
+ super.enterSplitScreenByDragFromNotification()
+
+ companion object {
+ @JvmStatic
+ @FlickerConfigProvider
+ fun flickerConfigProvider(): FlickerConfig =
+ FlickerConfig().use(FlickerServiceConfig.DEFAULT)
+ }
+}
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/flicker/EnterSplitScreenByDragFromShortcutGesturalNavLandscape.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/flicker/EnterSplitScreenByDragFromShortcutGesturalNavLandscape.kt
new file mode 100644
index 0000000..736604f
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/flicker/EnterSplitScreenByDragFromShortcutGesturalNavLandscape.kt
@@ -0,0 +1,44 @@
+/*
+ * 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.flicker.service.splitscreen.flicker
+
+import android.tools.common.Rotation
+import android.tools.common.flicker.FlickerConfig
+import android.tools.common.flicker.annotation.ExpectedScenarios
+import android.tools.common.flicker.annotation.FlickerConfigProvider
+import android.tools.common.flicker.config.FlickerConfig
+import android.tools.common.flicker.config.FlickerServiceConfig
+import android.tools.device.flicker.junit.FlickerServiceJUnit4ClassRunner
+import com.android.wm.shell.flicker.service.splitscreen.scenarios.EnterSplitScreenByDragFromShortcut
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@RunWith(FlickerServiceJUnit4ClassRunner::class)
+class EnterSplitScreenByDragFromShortcutGesturalNavLandscape :
+ EnterSplitScreenByDragFromShortcut(Rotation.ROTATION_90) {
+
+ @ExpectedScenarios(["SPLIT_SCREEN_ENTER"])
+ @Test
+ override fun enterSplitScreenByDragFromShortcut() = super.enterSplitScreenByDragFromShortcut()
+
+ companion object {
+ @JvmStatic
+ @FlickerConfigProvider
+ fun flickerConfigProvider(): FlickerConfig =
+ FlickerConfig().use(FlickerServiceConfig.DEFAULT)
+ }
+}
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/flicker/EnterSplitScreenByDragFromShortcutGesturalNavPortrait.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/flicker/EnterSplitScreenByDragFromShortcutGesturalNavPortrait.kt
new file mode 100644
index 0000000..8df8dfa
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/flicker/EnterSplitScreenByDragFromShortcutGesturalNavPortrait.kt
@@ -0,0 +1,44 @@
+/*
+ * 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.flicker.service.splitscreen.flicker
+
+import android.tools.common.Rotation
+import android.tools.common.flicker.FlickerConfig
+import android.tools.common.flicker.annotation.ExpectedScenarios
+import android.tools.common.flicker.annotation.FlickerConfigProvider
+import android.tools.common.flicker.config.FlickerConfig
+import android.tools.common.flicker.config.FlickerServiceConfig
+import android.tools.device.flicker.junit.FlickerServiceJUnit4ClassRunner
+import com.android.wm.shell.flicker.service.splitscreen.scenarios.EnterSplitScreenByDragFromShortcut
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@RunWith(FlickerServiceJUnit4ClassRunner::class)
+class EnterSplitScreenByDragFromShortcutGesturalNavPortrait :
+ EnterSplitScreenByDragFromShortcut(Rotation.ROTATION_0) {
+
+ @ExpectedScenarios(["SPLIT_SCREEN_ENTER"])
+ @Test
+ override fun enterSplitScreenByDragFromShortcut() = super.enterSplitScreenByDragFromShortcut()
+
+ companion object {
+ @JvmStatic
+ @FlickerConfigProvider
+ fun flickerConfigProvider(): FlickerConfig =
+ FlickerConfig().use(FlickerServiceConfig.DEFAULT)
+ }
+}
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/flicker/EnterSplitScreenByDragFromTaskbarGesturalNavLandscape.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/flicker/EnterSplitScreenByDragFromTaskbarGesturalNavLandscape.kt
new file mode 100644
index 0000000..378f055
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/flicker/EnterSplitScreenByDragFromTaskbarGesturalNavLandscape.kt
@@ -0,0 +1,44 @@
+/*
+ * 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.flicker.service.splitscreen.flicker
+
+import android.tools.common.Rotation
+import android.tools.common.flicker.FlickerConfig
+import android.tools.common.flicker.annotation.ExpectedScenarios
+import android.tools.common.flicker.annotation.FlickerConfigProvider
+import android.tools.common.flicker.config.FlickerConfig
+import android.tools.common.flicker.config.FlickerServiceConfig
+import android.tools.device.flicker.junit.FlickerServiceJUnit4ClassRunner
+import com.android.wm.shell.flicker.service.splitscreen.scenarios.EnterSplitScreenByDragFromTaskbar
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@RunWith(FlickerServiceJUnit4ClassRunner::class)
+class EnterSplitScreenByDragFromTaskbarGesturalNavLandscape :
+ EnterSplitScreenByDragFromTaskbar(Rotation.ROTATION_90) {
+
+ @ExpectedScenarios(["SPLIT_SCREEN_ENTER"])
+ @Test
+ override fun enterSplitScreenByDragFromTaskbar() = super.enterSplitScreenByDragFromTaskbar()
+
+ companion object {
+ @JvmStatic
+ @FlickerConfigProvider
+ fun flickerConfigProvider(): FlickerConfig =
+ FlickerConfig().use(FlickerServiceConfig.DEFAULT)
+ }
+}
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/flicker/EnterSplitScreenByDragFromTaskbarGesturalNavPortrait.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/flicker/EnterSplitScreenByDragFromTaskbarGesturalNavPortrait.kt
new file mode 100644
index 0000000..b33d262
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/flicker/EnterSplitScreenByDragFromTaskbarGesturalNavPortrait.kt
@@ -0,0 +1,44 @@
+/*
+ * 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.flicker.service.splitscreen.flicker
+
+import android.tools.common.Rotation
+import android.tools.common.flicker.FlickerConfig
+import android.tools.common.flicker.annotation.ExpectedScenarios
+import android.tools.common.flicker.annotation.FlickerConfigProvider
+import android.tools.common.flicker.config.FlickerConfig
+import android.tools.common.flicker.config.FlickerServiceConfig
+import android.tools.device.flicker.junit.FlickerServiceJUnit4ClassRunner
+import com.android.wm.shell.flicker.service.splitscreen.scenarios.EnterSplitScreenByDragFromTaskbar
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@RunWith(FlickerServiceJUnit4ClassRunner::class)
+class EnterSplitScreenByDragFromTaskbarGesturalNavPortrait :
+ EnterSplitScreenByDragFromTaskbar(Rotation.ROTATION_0) {
+
+ @ExpectedScenarios(["SPLIT_SCREEN_ENTER"])
+ @Test
+ override fun enterSplitScreenByDragFromTaskbar() = super.enterSplitScreenByDragFromTaskbar()
+
+ companion object {
+ @JvmStatic
+ @FlickerConfigProvider
+ fun flickerConfigProvider(): FlickerConfig =
+ FlickerConfig().use(FlickerServiceConfig.DEFAULT)
+ }
+}
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/flicker/EnterSplitScreenFromOverviewGesturalNavLandscape.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/flicker/EnterSplitScreenFromOverviewGesturalNavLandscape.kt
new file mode 100644
index 0000000..b1d3858
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/flicker/EnterSplitScreenFromOverviewGesturalNavLandscape.kt
@@ -0,0 +1,44 @@
+/*
+ * 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.flicker.service.splitscreen.flicker
+
+import android.tools.common.Rotation
+import android.tools.common.flicker.FlickerConfig
+import android.tools.common.flicker.annotation.ExpectedScenarios
+import android.tools.common.flicker.annotation.FlickerConfigProvider
+import android.tools.common.flicker.config.FlickerConfig
+import android.tools.common.flicker.config.FlickerServiceConfig
+import android.tools.device.flicker.junit.FlickerServiceJUnit4ClassRunner
+import com.android.wm.shell.flicker.service.splitscreen.scenarios.EnterSplitScreenFromOverview
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@RunWith(FlickerServiceJUnit4ClassRunner::class)
+class EnterSplitScreenFromOverviewGesturalNavLandscape :
+ EnterSplitScreenFromOverview(Rotation.ROTATION_90) {
+
+ @ExpectedScenarios(["SPLIT_SCREEN_ENTER"])
+ @Test
+ override fun enterSplitScreenFromOverview() = super.enterSplitScreenFromOverview()
+
+ companion object {
+ @JvmStatic
+ @FlickerConfigProvider
+ fun flickerConfigProvider(): FlickerConfig =
+ FlickerConfig().use(FlickerServiceConfig.DEFAULT)
+ }
+}
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/flicker/EnterSplitScreenFromOverviewGesturalNavPortrait.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/flicker/EnterSplitScreenFromOverviewGesturalNavPortrait.kt
new file mode 100644
index 0000000..6d824c7
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/flicker/EnterSplitScreenFromOverviewGesturalNavPortrait.kt
@@ -0,0 +1,44 @@
+/*
+ * 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.flicker.service.splitscreen.flicker
+
+import android.tools.common.Rotation
+import android.tools.common.flicker.FlickerConfig
+import android.tools.common.flicker.annotation.ExpectedScenarios
+import android.tools.common.flicker.annotation.FlickerConfigProvider
+import android.tools.common.flicker.config.FlickerConfig
+import android.tools.common.flicker.config.FlickerServiceConfig
+import android.tools.device.flicker.junit.FlickerServiceJUnit4ClassRunner
+import com.android.wm.shell.flicker.service.splitscreen.scenarios.EnterSplitScreenFromOverview
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@RunWith(FlickerServiceJUnit4ClassRunner::class)
+class EnterSplitScreenFromOverviewGesturalNavPortrait :
+ EnterSplitScreenFromOverview(Rotation.ROTATION_0) {
+
+ @ExpectedScenarios(["SPLIT_SCREEN_ENTER"])
+ @Test
+ override fun enterSplitScreenFromOverview() = super.enterSplitScreenFromOverview()
+
+ companion object {
+ @JvmStatic
+ @FlickerConfigProvider
+ fun flickerConfigProvider(): FlickerConfig =
+ FlickerConfig().use(FlickerServiceConfig.DEFAULT)
+ }
+}
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/flicker/SwitchAppByDoubleTapDividerGesturalNavLandscape.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/flicker/SwitchAppByDoubleTapDividerGesturalNavLandscape.kt
new file mode 100644
index 0000000..f1d3d0c
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/flicker/SwitchAppByDoubleTapDividerGesturalNavLandscape.kt
@@ -0,0 +1,44 @@
+/*
+ * 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.flicker.service.splitscreen.flicker
+
+import android.tools.common.Rotation
+import android.tools.common.flicker.FlickerConfig
+import android.tools.common.flicker.annotation.ExpectedScenarios
+import android.tools.common.flicker.annotation.FlickerConfigProvider
+import android.tools.common.flicker.config.FlickerConfig
+import android.tools.common.flicker.config.FlickerServiceConfig
+import android.tools.device.flicker.junit.FlickerServiceJUnit4ClassRunner
+import com.android.wm.shell.flicker.service.splitscreen.scenarios.SwitchAppByDoubleTapDivider
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@RunWith(FlickerServiceJUnit4ClassRunner::class)
+class SwitchAppByDoubleTapDividerGesturalNavLandscape :
+ SwitchAppByDoubleTapDivider(Rotation.ROTATION_90) {
+
+ @ExpectedScenarios([])
+ @Test
+ override fun switchAppByDoubleTapDivider() = super.switchAppByDoubleTapDivider()
+
+ companion object {
+ @JvmStatic
+ @FlickerConfigProvider
+ fun flickerConfigProvider(): FlickerConfig =
+ FlickerConfig().use(FlickerServiceConfig.DEFAULT)
+ }
+}
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/flicker/SwitchAppByDoubleTapDividerGesturalNavPortrait.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/flicker/SwitchAppByDoubleTapDividerGesturalNavPortrait.kt
new file mode 100644
index 0000000..a867bac
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/flicker/SwitchAppByDoubleTapDividerGesturalNavPortrait.kt
@@ -0,0 +1,44 @@
+/*
+ * 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.flicker.service.splitscreen.flicker
+
+import android.tools.common.Rotation
+import android.tools.common.flicker.FlickerConfig
+import android.tools.common.flicker.annotation.ExpectedScenarios
+import android.tools.common.flicker.annotation.FlickerConfigProvider
+import android.tools.common.flicker.config.FlickerConfig
+import android.tools.common.flicker.config.FlickerServiceConfig
+import android.tools.device.flicker.junit.FlickerServiceJUnit4ClassRunner
+import com.android.wm.shell.flicker.service.splitscreen.scenarios.SwitchAppByDoubleTapDivider
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@RunWith(FlickerServiceJUnit4ClassRunner::class)
+class SwitchAppByDoubleTapDividerGesturalNavPortrait :
+ SwitchAppByDoubleTapDivider(Rotation.ROTATION_0) {
+
+ @ExpectedScenarios([])
+ @Test
+ override fun switchAppByDoubleTapDivider() = super.switchAppByDoubleTapDivider()
+
+ companion object {
+ @JvmStatic
+ @FlickerConfigProvider
+ fun flickerConfigProvider(): FlickerConfig =
+ FlickerConfig().use(FlickerServiceConfig.DEFAULT)
+ }
+}
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/flicker/SwitchBackToSplitFromAnotherAppGesturalNavLandscape.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/flicker/SwitchBackToSplitFromAnotherAppGesturalNavLandscape.kt
new file mode 100644
index 0000000..76247ba
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/flicker/SwitchBackToSplitFromAnotherAppGesturalNavLandscape.kt
@@ -0,0 +1,44 @@
+/*
+ * 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.flicker.service.splitscreen.flicker
+
+import android.tools.common.Rotation
+import android.tools.common.flicker.FlickerConfig
+import android.tools.common.flicker.annotation.ExpectedScenarios
+import android.tools.common.flicker.annotation.FlickerConfigProvider
+import android.tools.common.flicker.config.FlickerConfig
+import android.tools.common.flicker.config.FlickerServiceConfig
+import android.tools.device.flicker.junit.FlickerServiceJUnit4ClassRunner
+import com.android.wm.shell.flicker.service.splitscreen.scenarios.SwitchBackToSplitFromAnotherApp
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@RunWith(FlickerServiceJUnit4ClassRunner::class)
+class SwitchBackToSplitFromAnotherAppGesturalNavLandscape :
+ SwitchBackToSplitFromAnotherApp(Rotation.ROTATION_90) {
+
+ @ExpectedScenarios(["QUICKSWITCH"])
+ @Test
+ override fun switchBackToSplitFromAnotherApp() = super.switchBackToSplitFromAnotherApp()
+
+ companion object {
+ @JvmStatic
+ @FlickerConfigProvider
+ fun flickerConfigProvider(): FlickerConfig =
+ FlickerConfig().use(FlickerServiceConfig.DEFAULT)
+ }
+}
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/flicker/SwitchBackToSplitFromAnotherAppGesturalNavPortrait.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/flicker/SwitchBackToSplitFromAnotherAppGesturalNavPortrait.kt
new file mode 100644
index 0000000..e179da8
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/flicker/SwitchBackToSplitFromAnotherAppGesturalNavPortrait.kt
@@ -0,0 +1,44 @@
+/*
+ * 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.flicker.service.splitscreen.flicker
+
+import android.tools.common.Rotation
+import android.tools.common.flicker.FlickerConfig
+import android.tools.common.flicker.annotation.ExpectedScenarios
+import android.tools.common.flicker.annotation.FlickerConfigProvider
+import android.tools.common.flicker.config.FlickerConfig
+import android.tools.common.flicker.config.FlickerServiceConfig
+import android.tools.device.flicker.junit.FlickerServiceJUnit4ClassRunner
+import com.android.wm.shell.flicker.service.splitscreen.scenarios.SwitchBackToSplitFromAnotherApp
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@RunWith(FlickerServiceJUnit4ClassRunner::class)
+class SwitchBackToSplitFromAnotherAppGesturalNavPortrait :
+ SwitchBackToSplitFromAnotherApp(Rotation.ROTATION_0) {
+
+ @ExpectedScenarios(["QUICKSWITCH"])
+ @Test
+ override fun switchBackToSplitFromAnotherApp() = super.switchBackToSplitFromAnotherApp()
+
+ companion object {
+ @JvmStatic
+ @FlickerConfigProvider
+ fun flickerConfigProvider(): FlickerConfig =
+ FlickerConfig().use(FlickerServiceConfig.DEFAULT)
+ }
+}
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/flicker/SwitchBackToSplitFromHomeGesturalNavLandscape.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/flicker/SwitchBackToSplitFromHomeGesturalNavLandscape.kt
new file mode 100644
index 0000000..20f554f
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/flicker/SwitchBackToSplitFromHomeGesturalNavLandscape.kt
@@ -0,0 +1,44 @@
+/*
+ * 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.flicker.service.splitscreen.flicker
+
+import android.tools.common.Rotation
+import android.tools.common.flicker.FlickerConfig
+import android.tools.common.flicker.annotation.ExpectedScenarios
+import android.tools.common.flicker.annotation.FlickerConfigProvider
+import android.tools.common.flicker.config.FlickerConfig
+import android.tools.common.flicker.config.FlickerServiceConfig
+import android.tools.device.flicker.junit.FlickerServiceJUnit4ClassRunner
+import com.android.wm.shell.flicker.service.splitscreen.scenarios.SwitchBackToSplitFromHome
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@RunWith(FlickerServiceJUnit4ClassRunner::class)
+class SwitchBackToSplitFromHomeGesturalNavLandscape :
+ SwitchBackToSplitFromHome(Rotation.ROTATION_90) {
+
+ @ExpectedScenarios(["QUICKSWITCH"])
+ @Test
+ override fun switchBackToSplitFromHome() = super.switchBackToSplitFromHome()
+
+ companion object {
+ @JvmStatic
+ @FlickerConfigProvider
+ fun flickerConfigProvider(): FlickerConfig =
+ FlickerConfig().use(FlickerServiceConfig.DEFAULT)
+ }
+}
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/flicker/SwitchBackToSplitFromHomeGesturalNavPortrait.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/flicker/SwitchBackToSplitFromHomeGesturalNavPortrait.kt
new file mode 100644
index 0000000..f7776ee
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/flicker/SwitchBackToSplitFromHomeGesturalNavPortrait.kt
@@ -0,0 +1,44 @@
+/*
+ * 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.flicker.service.splitscreen.flicker
+
+import android.tools.common.Rotation
+import android.tools.common.flicker.FlickerConfig
+import android.tools.common.flicker.annotation.ExpectedScenarios
+import android.tools.common.flicker.annotation.FlickerConfigProvider
+import android.tools.common.flicker.config.FlickerConfig
+import android.tools.common.flicker.config.FlickerServiceConfig
+import android.tools.device.flicker.junit.FlickerServiceJUnit4ClassRunner
+import com.android.wm.shell.flicker.service.splitscreen.scenarios.SwitchBackToSplitFromHome
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@RunWith(FlickerServiceJUnit4ClassRunner::class)
+class SwitchBackToSplitFromHomeGesturalNavPortrait :
+ SwitchBackToSplitFromHome(Rotation.ROTATION_0) {
+
+ @ExpectedScenarios(["QUICKSWITCH"])
+ @Test
+ override fun switchBackToSplitFromHome() = super.switchBackToSplitFromHome()
+
+ companion object {
+ @JvmStatic
+ @FlickerConfigProvider
+ fun flickerConfigProvider(): FlickerConfig =
+ FlickerConfig().use(FlickerServiceConfig.DEFAULT)
+ }
+}
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/flicker/SwitchBackToSplitFromRecentGesturalNavLandscape.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/flicker/SwitchBackToSplitFromRecentGesturalNavLandscape.kt
new file mode 100644
index 0000000..00f6073
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/flicker/SwitchBackToSplitFromRecentGesturalNavLandscape.kt
@@ -0,0 +1,44 @@
+/*
+ * 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.flicker.service.splitscreen.flicker
+
+import android.tools.common.Rotation
+import android.tools.common.flicker.FlickerConfig
+import android.tools.common.flicker.annotation.ExpectedScenarios
+import android.tools.common.flicker.annotation.FlickerConfigProvider
+import android.tools.common.flicker.config.FlickerConfig
+import android.tools.common.flicker.config.FlickerServiceConfig
+import android.tools.device.flicker.junit.FlickerServiceJUnit4ClassRunner
+import com.android.wm.shell.flicker.service.splitscreen.scenarios.SwitchBackToSplitFromRecent
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@RunWith(FlickerServiceJUnit4ClassRunner::class)
+class SwitchBackToSplitFromRecentGesturalNavLandscape :
+ SwitchBackToSplitFromRecent(Rotation.ROTATION_90) {
+
+ @ExpectedScenarios(["QUICKSWITCH"])
+ @Test
+ override fun switchBackToSplitFromRecent() = super.switchBackToSplitFromRecent()
+
+ companion object {
+ @JvmStatic
+ @FlickerConfigProvider
+ fun flickerConfigProvider(): FlickerConfig =
+ FlickerConfig().use(FlickerServiceConfig.DEFAULT)
+ }
+}
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/flicker/SwitchBackToSplitFromRecentGesturalNavPortrait.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/flicker/SwitchBackToSplitFromRecentGesturalNavPortrait.kt
new file mode 100644
index 0000000..b3340e7
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/flicker/SwitchBackToSplitFromRecentGesturalNavPortrait.kt
@@ -0,0 +1,44 @@
+/*
+ * 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.flicker.service.splitscreen.flicker
+
+import android.tools.common.Rotation
+import android.tools.common.flicker.FlickerConfig
+import android.tools.common.flicker.annotation.ExpectedScenarios
+import android.tools.common.flicker.annotation.FlickerConfigProvider
+import android.tools.common.flicker.config.FlickerConfig
+import android.tools.common.flicker.config.FlickerServiceConfig
+import android.tools.device.flicker.junit.FlickerServiceJUnit4ClassRunner
+import com.android.wm.shell.flicker.service.splitscreen.scenarios.SwitchBackToSplitFromRecent
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@RunWith(FlickerServiceJUnit4ClassRunner::class)
+class SwitchBackToSplitFromRecentGesturalNavPortrait :
+ SwitchBackToSplitFromRecent(Rotation.ROTATION_0) {
+
+ @ExpectedScenarios(["QUICKSWITCH"])
+ @Test
+ override fun switchBackToSplitFromRecent() = super.switchBackToSplitFromRecent()
+
+ companion object {
+ @JvmStatic
+ @FlickerConfigProvider
+ fun flickerConfigProvider(): FlickerConfig =
+ FlickerConfig().use(FlickerServiceConfig.DEFAULT)
+ }
+}
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/flicker/SwitchBetweenSplitPairsGesturalNavLandscape.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/flicker/SwitchBetweenSplitPairsGesturalNavLandscape.kt
new file mode 100644
index 0000000..3da61e5
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/flicker/SwitchBetweenSplitPairsGesturalNavLandscape.kt
@@ -0,0 +1,43 @@
+/*
+ * 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.flicker.service.splitscreen.flicker
+
+import android.tools.common.Rotation
+import android.tools.common.flicker.FlickerConfig
+import android.tools.common.flicker.annotation.ExpectedScenarios
+import android.tools.common.flicker.annotation.FlickerConfigProvider
+import android.tools.common.flicker.config.FlickerConfig
+import android.tools.common.flicker.config.FlickerServiceConfig
+import android.tools.device.flicker.junit.FlickerServiceJUnit4ClassRunner
+import com.android.wm.shell.flicker.service.splitscreen.scenarios.SwitchBetweenSplitPairs
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@RunWith(FlickerServiceJUnit4ClassRunner::class)
+class SwitchBetweenSplitPairsGesturalNavLandscape : SwitchBetweenSplitPairs(Rotation.ROTATION_90) {
+
+ @ExpectedScenarios(["QUICKSWITCH"])
+ @Test
+ override fun switchBetweenSplitPairs() = super.switchBetweenSplitPairs()
+
+ companion object {
+ @JvmStatic
+ @FlickerConfigProvider
+ fun flickerConfigProvider(): FlickerConfig =
+ FlickerConfig().use(FlickerServiceConfig.DEFAULT)
+ }
+}
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/flicker/SwitchBetweenSplitPairsGesturalNavPortrait.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/flicker/SwitchBetweenSplitPairsGesturalNavPortrait.kt
new file mode 100644
index 0000000..627ae18
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/flicker/SwitchBetweenSplitPairsGesturalNavPortrait.kt
@@ -0,0 +1,43 @@
+/*
+ * 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.flicker.service.splitscreen.flicker
+
+import android.tools.common.Rotation
+import android.tools.common.flicker.FlickerConfig
+import android.tools.common.flicker.annotation.ExpectedScenarios
+import android.tools.common.flicker.annotation.FlickerConfigProvider
+import android.tools.common.flicker.config.FlickerConfig
+import android.tools.common.flicker.config.FlickerServiceConfig
+import android.tools.device.flicker.junit.FlickerServiceJUnit4ClassRunner
+import com.android.wm.shell.flicker.service.splitscreen.scenarios.SwitchBetweenSplitPairs
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@RunWith(FlickerServiceJUnit4ClassRunner::class)
+class SwitchBetweenSplitPairsGesturalNavPortrait : SwitchBetweenSplitPairs(Rotation.ROTATION_0) {
+
+ @ExpectedScenarios(["QUICKSWITCH"])
+ @Test
+ override fun switchBetweenSplitPairs() = super.switchBetweenSplitPairs()
+
+ companion object {
+ @JvmStatic
+ @FlickerConfigProvider
+ fun flickerConfigProvider(): FlickerConfig =
+ FlickerConfig().use(FlickerServiceConfig.DEFAULT)
+ }
+}
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/flicker/UnlockKeyguardToSplitScreenGesturalNavLandscape.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/flicker/UnlockKeyguardToSplitScreenGesturalNavLandscape.kt
new file mode 100644
index 0000000..f4e7298
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/flicker/UnlockKeyguardToSplitScreenGesturalNavLandscape.kt
@@ -0,0 +1,42 @@
+/*
+ * 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.flicker.service.splitscreen.flicker
+
+import android.tools.common.flicker.FlickerConfig
+import android.tools.common.flicker.annotation.ExpectedScenarios
+import android.tools.common.flicker.annotation.FlickerConfigProvider
+import android.tools.common.flicker.config.FlickerConfig
+import android.tools.common.flicker.config.FlickerServiceConfig
+import android.tools.device.flicker.junit.FlickerServiceJUnit4ClassRunner
+import com.android.wm.shell.flicker.service.splitscreen.scenarios.UnlockKeyguardToSplitScreen
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@RunWith(FlickerServiceJUnit4ClassRunner::class)
+class UnlockKeyguardToSplitScreenGesturalNavLandscape : UnlockKeyguardToSplitScreen() {
+
+ @ExpectedScenarios(["QUICKSWITCH"])
+ @Test
+ override fun unlockKeyguardToSplitScreen() = super.unlockKeyguardToSplitScreen()
+
+ companion object {
+ @JvmStatic
+ @FlickerConfigProvider
+ fun flickerConfigProvider(): FlickerConfig =
+ FlickerConfig().use(FlickerServiceConfig.DEFAULT)
+ }
+}
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/flicker/UnlockKeyguardToSplitScreenGesturalNavPortrait.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/flicker/UnlockKeyguardToSplitScreenGesturalNavPortrait.kt
new file mode 100644
index 0000000..f38b2e8
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/flicker/UnlockKeyguardToSplitScreenGesturalNavPortrait.kt
@@ -0,0 +1,42 @@
+/*
+ * 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.flicker.service.splitscreen.flicker
+
+import android.tools.common.flicker.FlickerConfig
+import android.tools.common.flicker.annotation.ExpectedScenarios
+import android.tools.common.flicker.annotation.FlickerConfigProvider
+import android.tools.common.flicker.config.FlickerConfig
+import android.tools.common.flicker.config.FlickerServiceConfig
+import android.tools.device.flicker.junit.FlickerServiceJUnit4ClassRunner
+import com.android.wm.shell.flicker.service.splitscreen.scenarios.UnlockKeyguardToSplitScreen
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@RunWith(FlickerServiceJUnit4ClassRunner::class)
+class UnlockKeyguardToSplitScreenGesturalNavPortrait : UnlockKeyguardToSplitScreen() {
+
+ @ExpectedScenarios(["QUICKSWITCH"])
+ @Test
+ override fun unlockKeyguardToSplitScreen() = super.unlockKeyguardToSplitScreen()
+
+ companion object {
+ @JvmStatic
+ @FlickerConfigProvider
+ fun flickerConfigProvider(): FlickerConfig =
+ FlickerConfig().use(FlickerServiceConfig.DEFAULT)
+ }
+}
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/scenarios/CopyContentInSplit.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/scenarios/CopyContentInSplit.kt
index e530f63..245184c 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/scenarios/CopyContentInSplit.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/scenarios/CopyContentInSplit.kt
@@ -23,7 +23,7 @@
import androidx.test.platform.app.InstrumentationRegistry
import androidx.test.uiautomator.UiDevice
import com.android.launcher3.tapl.LauncherInstrumentation
-import com.android.wm.shell.flicker.service.Utils
+import com.android.wm.shell.flicker.service.common.Utils
import com.android.wm.shell.flicker.utils.SplitScreenUtils
import org.junit.After
import org.junit.Before
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/scenarios/DismissSplitScreenByDivider.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/scenarios/DismissSplitScreenByDivider.kt
index e9fc437..1f2f1ec 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/scenarios/DismissSplitScreenByDivider.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/scenarios/DismissSplitScreenByDivider.kt
@@ -23,7 +23,7 @@
import androidx.test.platform.app.InstrumentationRegistry
import androidx.test.uiautomator.UiDevice
import com.android.launcher3.tapl.LauncherInstrumentation
-import com.android.wm.shell.flicker.service.Utils
+import com.android.wm.shell.flicker.service.common.Utils
import com.android.wm.shell.flicker.utils.SplitScreenUtils
import org.junit.After
import org.junit.Before
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/scenarios/DismissSplitScreenByGoHome.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/scenarios/DismissSplitScreenByGoHome.kt
index 416692c..ebbf7c5 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/scenarios/DismissSplitScreenByGoHome.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/scenarios/DismissSplitScreenByGoHome.kt
@@ -23,7 +23,7 @@
import androidx.test.platform.app.InstrumentationRegistry
import androidx.test.uiautomator.UiDevice
import com.android.launcher3.tapl.LauncherInstrumentation
-import com.android.wm.shell.flicker.service.Utils
+import com.android.wm.shell.flicker.service.common.Utils
import com.android.wm.shell.flicker.utils.SplitScreenUtils
import org.junit.After
import org.junit.Before
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/scenarios/DragDividerToResize.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/scenarios/DragDividerToResize.kt
index 494a246..71e701c 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/scenarios/DragDividerToResize.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/scenarios/DragDividerToResize.kt
@@ -23,7 +23,7 @@
import androidx.test.platform.app.InstrumentationRegistry
import androidx.test.uiautomator.UiDevice
import com.android.launcher3.tapl.LauncherInstrumentation
-import com.android.wm.shell.flicker.service.Utils
+import com.android.wm.shell.flicker.service.common.Utils
import com.android.wm.shell.flicker.utils.SplitScreenUtils
import org.junit.After
import org.junit.Before
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/scenarios/EnterSplitScreenByDragFromAllApps.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/scenarios/EnterSplitScreenByDragFromAllApps.kt
index 9b43816..c433b21 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/scenarios/EnterSplitScreenByDragFromAllApps.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/scenarios/EnterSplitScreenByDragFromAllApps.kt
@@ -23,7 +23,7 @@
import androidx.test.platform.app.InstrumentationRegistry
import androidx.test.uiautomator.UiDevice
import com.android.launcher3.tapl.LauncherInstrumentation
-import com.android.wm.shell.flicker.service.Utils
+import com.android.wm.shell.flicker.service.common.Utils
import com.android.wm.shell.flicker.utils.SplitScreenUtils
import org.junit.After
import org.junit.Assume
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/scenarios/EnterSplitScreenByDragFromNotification.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/scenarios/EnterSplitScreenByDragFromNotification.kt
index 50151f1..3f087a5 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/scenarios/EnterSplitScreenByDragFromNotification.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/scenarios/EnterSplitScreenByDragFromNotification.kt
@@ -23,7 +23,7 @@
import androidx.test.platform.app.InstrumentationRegistry
import androidx.test.uiautomator.UiDevice
import com.android.launcher3.tapl.LauncherInstrumentation
-import com.android.wm.shell.flicker.service.Utils
+import com.android.wm.shell.flicker.service.common.Utils
import com.android.wm.shell.flicker.utils.SplitScreenUtils
import org.junit.After
import org.junit.Assume
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/scenarios/EnterSplitScreenByDragFromShortcut.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/scenarios/EnterSplitScreenByDragFromShortcut.kt
index 76fbf60..767e7b5 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/scenarios/EnterSplitScreenByDragFromShortcut.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/scenarios/EnterSplitScreenByDragFromShortcut.kt
@@ -23,7 +23,7 @@
import androidx.test.platform.app.InstrumentationRegistry
import androidx.test.uiautomator.UiDevice
import com.android.launcher3.tapl.LauncherInstrumentation
-import com.android.wm.shell.flicker.service.Utils
+import com.android.wm.shell.flicker.service.common.Utils
import com.android.wm.shell.flicker.utils.SplitScreenUtils
import org.junit.After
import org.junit.Assume
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/scenarios/EnterSplitScreenByDragFromTaskbar.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/scenarios/EnterSplitScreenByDragFromTaskbar.kt
index f8e43f1..2592fd4 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/scenarios/EnterSplitScreenByDragFromTaskbar.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/scenarios/EnterSplitScreenByDragFromTaskbar.kt
@@ -23,7 +23,7 @@
import androidx.test.platform.app.InstrumentationRegistry
import androidx.test.uiautomator.UiDevice
import com.android.launcher3.tapl.LauncherInstrumentation
-import com.android.wm.shell.flicker.service.Utils
+import com.android.wm.shell.flicker.service.common.Utils
import com.android.wm.shell.flicker.utils.SplitScreenUtils
import org.junit.After
import org.junit.Assume
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/scenarios/EnterSplitScreenFromOverview.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/scenarios/EnterSplitScreenFromOverview.kt
index c2100f6..f2cbf24 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/scenarios/EnterSplitScreenFromOverview.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/scenarios/EnterSplitScreenFromOverview.kt
@@ -23,7 +23,7 @@
import androidx.test.platform.app.InstrumentationRegistry
import androidx.test.uiautomator.UiDevice
import com.android.launcher3.tapl.LauncherInstrumentation
-import com.android.wm.shell.flicker.service.Utils
+import com.android.wm.shell.flicker.service.common.Utils
import com.android.wm.shell.flicker.utils.SplitScreenUtils
import org.junit.After
import org.junit.Before
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/scenarios/SwitchAppByDoubleTapDivider.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/scenarios/SwitchAppByDoubleTapDivider.kt
index 70f3bed..538ed96 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/scenarios/SwitchAppByDoubleTapDivider.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/scenarios/SwitchAppByDoubleTapDivider.kt
@@ -25,7 +25,7 @@
import androidx.test.platform.app.InstrumentationRegistry
import androidx.test.uiautomator.UiDevice
import com.android.launcher3.tapl.LauncherInstrumentation
-import com.android.wm.shell.flicker.service.Utils
+import com.android.wm.shell.flicker.service.common.Utils
import com.android.wm.shell.flicker.utils.SplitScreenUtils
import org.junit.After
import org.junit.Before
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/scenarios/SwitchBackToSplitFromAnotherApp.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/scenarios/SwitchBackToSplitFromAnotherApp.kt
index 86f394d..0dab5ad 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/scenarios/SwitchBackToSplitFromAnotherApp.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/scenarios/SwitchBackToSplitFromAnotherApp.kt
@@ -23,7 +23,7 @@
import androidx.test.platform.app.InstrumentationRegistry
import androidx.test.uiautomator.UiDevice
import com.android.launcher3.tapl.LauncherInstrumentation
-import com.android.wm.shell.flicker.service.Utils
+import com.android.wm.shell.flicker.service.common.Utils
import com.android.wm.shell.flicker.utils.SplitScreenUtils
import org.junit.After
import org.junit.Before
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/scenarios/SwitchBackToSplitFromHome.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/scenarios/SwitchBackToSplitFromHome.kt
index d7b611e..ad3a2d4 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/scenarios/SwitchBackToSplitFromHome.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/scenarios/SwitchBackToSplitFromHome.kt
@@ -23,7 +23,7 @@
import androidx.test.platform.app.InstrumentationRegistry
import androidx.test.uiautomator.UiDevice
import com.android.launcher3.tapl.LauncherInstrumentation
-import com.android.wm.shell.flicker.service.Utils
+import com.android.wm.shell.flicker.service.common.Utils
import com.android.wm.shell.flicker.utils.SplitScreenUtils
import org.junit.After
import org.junit.Before
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/scenarios/SwitchBackToSplitFromRecent.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/scenarios/SwitchBackToSplitFromRecent.kt
index 3cc5df0..b780a16 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/scenarios/SwitchBackToSplitFromRecent.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/scenarios/SwitchBackToSplitFromRecent.kt
@@ -23,7 +23,7 @@
import androidx.test.platform.app.InstrumentationRegistry
import androidx.test.uiautomator.UiDevice
import com.android.launcher3.tapl.LauncherInstrumentation
-import com.android.wm.shell.flicker.service.Utils
+import com.android.wm.shell.flicker.service.common.Utils
import com.android.wm.shell.flicker.utils.SplitScreenUtils
import org.junit.After
import org.junit.Before
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/scenarios/SwitchBetweenSplitPairs.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/scenarios/SwitchBetweenSplitPairs.kt
index 4a9c32f..329d61d 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/scenarios/SwitchBetweenSplitPairs.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/scenarios/SwitchBetweenSplitPairs.kt
@@ -23,7 +23,7 @@
import androidx.test.platform.app.InstrumentationRegistry
import androidx.test.uiautomator.UiDevice
import com.android.launcher3.tapl.LauncherInstrumentation
-import com.android.wm.shell.flicker.service.Utils
+import com.android.wm.shell.flicker.service.common.Utils
import com.android.wm.shell.flicker.utils.SplitScreenUtils
import org.junit.After
import org.junit.Before
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/scenarios/UnlockKeyguardToSplitScreen.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/scenarios/UnlockKeyguardToSplitScreen.kt
index 383a6b3..a9933bbe 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/scenarios/UnlockKeyguardToSplitScreen.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/scenarios/UnlockKeyguardToSplitScreen.kt
@@ -23,7 +23,7 @@
import androidx.test.platform.app.InstrumentationRegistry
import androidx.test.uiautomator.UiDevice
import com.android.launcher3.tapl.LauncherInstrumentation
-import com.android.wm.shell.flicker.service.Utils
+import com.android.wm.shell.flicker.service.common.Utils
import com.android.wm.shell.flicker.utils.SplitScreenUtils
import org.junit.After
import org.junit.Before
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/bubbles/BubblesTransitionObserverTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/bubbles/BubblesTransitionObserverTest.java
new file mode 100644
index 0000000..9655f97
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/bubbles/BubblesTransitionObserverTest.java
@@ -0,0 +1,234 @@
+/*
+ * 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.bubbles;
+
+import static android.app.ActivityTaskManager.INVALID_TASK_ID;
+import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
+import static android.view.WindowManager.TRANSIT_CHANGE;
+import static android.view.WindowManager.TRANSIT_CLOSE;
+import static android.view.WindowManager.TRANSIT_OPEN;
+import static android.view.WindowManager.TRANSIT_TO_BACK;
+import static android.view.WindowManager.TRANSIT_TO_FRONT;
+
+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 static org.mockito.Mockito.when;
+
+import android.app.ActivityManager;
+import android.os.IBinder;
+import android.view.SurfaceControl;
+import android.window.IWindowContainerToken;
+import android.window.TransitionInfo;
+import android.window.WindowContainerToken;
+
+import androidx.test.filters.SmallTest;
+
+import com.android.wm.shell.TransitionInfoBuilder;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+/**
+ * Tests of {@link BubblesTransitionObserver}.
+ */
+@SmallTest
+public class BubblesTransitionObserverTest {
+
+ @Mock
+ private BubbleController mBubbleController;
+ @Mock
+ private BubbleData mBubbleData;
+
+ @Mock
+ private IBinder mTransition;
+ @Mock
+ private SurfaceControl.Transaction mStartT;
+ @Mock
+ private SurfaceControl.Transaction mFinishT;
+
+ @Mock
+ private Bubble mBubble;
+
+ private BubblesTransitionObserver mTransitionObserver;
+
+
+ @Before
+ public void setUp() {
+ MockitoAnnotations.initMocks(this);
+
+ mTransitionObserver = new BubblesTransitionObserver(mBubbleController, mBubbleData);
+ }
+
+ @Test
+ public void testOnTransitionReady_open_collapsesStack() {
+ when(mBubbleData.isExpanded()).thenReturn(true);
+ when(mBubbleData.getSelectedBubble()).thenReturn(mBubble);
+ when(mBubble.getTaskId()).thenReturn(1);
+ when(mBubbleController.isStackAnimating()).thenReturn(false);
+
+ TransitionInfo info = createTransitionInfo(TRANSIT_OPEN, createTaskInfo(2));
+
+ mTransitionObserver.onTransitionReady(mTransition, info, mStartT, mFinishT);
+
+ verify(mBubbleData).setExpanded(eq(false));
+ }
+
+ @Test
+ public void testOnTransitionReady_toFront_collapsesStack() {
+ when(mBubbleData.isExpanded()).thenReturn(true);
+ when(mBubbleData.getSelectedBubble()).thenReturn(mBubble);
+ when(mBubble.getTaskId()).thenReturn(1);
+ when(mBubbleController.isStackAnimating()).thenReturn(false);
+
+ TransitionInfo info = createTransitionInfo(TRANSIT_TO_FRONT, createTaskInfo(2));
+
+ mTransitionObserver.onTransitionReady(mTransition, info, mStartT, mFinishT);
+
+ verify(mBubbleData).setExpanded(eq(false));
+ }
+
+ @Test
+ public void testOnTransitionReady_noTaskInfo_skip() {
+ when(mBubbleData.isExpanded()).thenReturn(true);
+ when(mBubbleData.getSelectedBubble()).thenReturn(mBubble);
+ when(mBubble.getTaskId()).thenReturn(1);
+ when(mBubbleController.isStackAnimating()).thenReturn(false);
+
+ // Null task info
+ TransitionInfo info = createTransitionInfo(TRANSIT_TO_FRONT, null /* taskInfo */);
+
+ mTransitionObserver.onTransitionReady(mTransition, info, mStartT, mFinishT);
+
+ verify(mBubbleData, never()).setExpanded(eq(false));
+ }
+
+ @Test
+ public void testOnTransitionReady_noTaskId_skip() {
+ when(mBubbleData.isExpanded()).thenReturn(true);
+ when(mBubbleData.getSelectedBubble()).thenReturn(mBubble);
+ when(mBubble.getTaskId()).thenReturn(1);
+ when(mBubbleController.isStackAnimating()).thenReturn(false);
+
+ // Invalid task id
+ TransitionInfo info = createTransitionInfo(TRANSIT_TO_FRONT,
+ createTaskInfo(INVALID_TASK_ID));
+
+ mTransitionObserver.onTransitionReady(mTransition, info, mStartT, mFinishT);
+
+ verify(mBubbleData, never()).setExpanded(eq(false));
+ }
+
+ @Test
+ public void testOnTransitionReady_notOpening_skip() {
+ when(mBubbleData.isExpanded()).thenReturn(true);
+ when(mBubbleData.getSelectedBubble()).thenReturn(mBubble);
+ when(mBubble.getTaskId()).thenReturn(1);
+ when(mBubbleController.isStackAnimating()).thenReturn(false);
+
+ // Transits that aren't opening
+ TransitionInfo info = createTransitionInfo(TRANSIT_CHANGE, createTaskInfo(2));
+ mTransitionObserver.onTransitionReady(mTransition, info, mStartT, mFinishT);
+
+ info = createTransitionInfo(TRANSIT_CLOSE, createTaskInfo(3));
+ mTransitionObserver.onTransitionReady(mTransition, info, mStartT, mFinishT);
+
+ info = createTransitionInfo(TRANSIT_TO_BACK, createTaskInfo(4));
+ mTransitionObserver.onTransitionReady(mTransition, info, mStartT, mFinishT);
+
+ verify(mBubbleData, never()).setExpanded(eq(false));
+ }
+
+ @Test
+ public void testOnTransitionReady_stackAnimating_skip() {
+ when(mBubbleData.isExpanded()).thenReturn(true);
+ when(mBubbleData.getSelectedBubble()).thenReturn(mBubble);
+ when(mBubble.getTaskId()).thenReturn(1);
+ when(mBubbleController.isStackAnimating()).thenReturn(true); // Stack is animating
+
+ TransitionInfo info = createTransitionInfo(TRANSIT_OPEN, createTaskInfo(2));
+
+ mTransitionObserver.onTransitionReady(mTransition, info, mStartT, mFinishT);
+
+ verify(mBubbleData, never()).setExpanded(eq(false));
+ }
+
+ @Test
+ public void testOnTransitionReady_stackNotExpanded_skip() {
+ when(mBubbleData.isExpanded()).thenReturn(false); // Stack is not expanded
+ when(mBubbleData.getSelectedBubble()).thenReturn(mBubble);
+ when(mBubble.getTaskId()).thenReturn(1);
+ when(mBubbleController.isStackAnimating()).thenReturn(false);
+
+ TransitionInfo info = createTransitionInfo(TRANSIT_TO_FRONT, createTaskInfo(2));
+
+ mTransitionObserver.onTransitionReady(mTransition, info, mStartT, mFinishT);
+
+ verify(mBubbleData, never()).setExpanded(eq(false));
+ }
+
+ @Test
+ public void testOnTransitionReady_noSelectedBubble_skip() {
+ when(mBubbleData.isExpanded()).thenReturn(true);
+ when(mBubbleData.getSelectedBubble()).thenReturn(null); // No selected bubble
+ when(mBubble.getTaskId()).thenReturn(1);
+ when(mBubbleController.isStackAnimating()).thenReturn(false);
+
+ TransitionInfo info = createTransitionInfo(TRANSIT_OPEN, createTaskInfo(2));
+
+ mTransitionObserver.onTransitionReady(mTransition, info, mStartT, mFinishT);
+
+ verify(mBubbleData, never()).setExpanded(eq(false));
+ }
+
+ @Test
+ public void testOnTransitionReady_openingMatchesExpanded_skip() {
+ when(mBubbleData.isExpanded()).thenReturn(true);
+ when(mBubbleData.getSelectedBubble()).thenReturn(mBubble);
+ when(mBubble.getTaskId()).thenReturn(1);
+ when(mBubbleController.isStackAnimating()).thenReturn(false);
+
+ // What's moving to front is same as the opened bubble
+ TransitionInfo info = createTransitionInfo(TRANSIT_TO_FRONT, createTaskInfo(1));
+
+ mTransitionObserver.onTransitionReady(mTransition, info, mStartT, mFinishT);
+
+ verify(mBubbleData, never()).setExpanded(eq(false));
+ }
+
+ private ActivityManager.RunningTaskInfo createTaskInfo(int taskId) {
+ final ActivityManager.RunningTaskInfo taskInfo = new ActivityManager.RunningTaskInfo();
+ taskInfo.taskId = taskId;
+ taskInfo.configuration.windowConfiguration.setWindowingMode(WINDOWING_MODE_FULLSCREEN);
+ return taskInfo;
+ }
+
+ private TransitionInfo createTransitionInfo(int changeType,
+ ActivityManager.RunningTaskInfo info) {
+ final TransitionInfo.Change change = new TransitionInfo.Change(
+ new WindowContainerToken(mock(IWindowContainerToken.class)),
+ mock(SurfaceControl.class));
+ change.setMode(changeType);
+ change.setTaskInfo(info);
+
+ return new TransitionInfoBuilder(TRANSIT_OPEN, 0)
+ .addChange(change).build();
+ }
+
+}
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopModeControllerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopModeControllerTest.java
deleted file mode 100644
index 605a762..0000000
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopModeControllerTest.java
+++ /dev/null
@@ -1,531 +0,0 @@
-/*
- * Copyright (C) 2022 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 static android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM;
-import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
-import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED;
-import static android.app.WindowConfiguration.WINDOW_CONFIG_BOUNDS;
-import static android.view.Display.DEFAULT_DISPLAY;
-import static android.view.WindowManager.TRANSIT_CHANGE;
-import static android.view.WindowManager.TRANSIT_CLOSE;
-import static android.view.WindowManager.TRANSIT_NONE;
-import static android.view.WindowManager.TRANSIT_OPEN;
-import static android.view.WindowManager.TRANSIT_TO_FRONT;
-import static android.window.WindowContainerTransaction.HierarchyOp.HIERARCHY_OP_TYPE_REORDER;
-
-import static com.android.dx.mockito.inline.extended.ExtendedMockito.mockitoSession;
-import static com.android.wm.shell.desktopmode.DesktopTestHelpers.createFreeformTask;
-import static com.android.wm.shell.desktopmode.DesktopTestHelpers.createFullscreenTask;
-import static com.android.wm.shell.desktopmode.DesktopTestHelpers.createHomeTask;
-
-import static com.google.common.truth.Truth.assertThat;
-
-import static org.mockito.ArgumentMatchers.any;
-import static org.mockito.ArgumentMatchers.anyInt;
-import static org.mockito.ArgumentMatchers.eq;
-import static org.mockito.Mockito.clearInvocations;
-import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.never;
-import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.verifyZeroInteractions;
-import static org.mockito.Mockito.when;
-
-import android.app.ActivityManager.RunningTaskInfo;
-import android.os.Binder;
-import android.os.Handler;
-import android.os.IBinder;
-import android.testing.AndroidTestingRunner;
-import android.window.DisplayAreaInfo;
-import android.window.TransitionRequestInfo;
-import android.window.WindowContainerTransaction;
-import android.window.WindowContainerTransaction.Change;
-import android.window.WindowContainerTransaction.HierarchyOp;
-
-import androidx.test.filters.SmallTest;
-
-import com.android.dx.mockito.inline.extended.StaticMockitoSession;
-import com.android.wm.shell.MockToken;
-import com.android.wm.shell.RootTaskDisplayAreaOrganizer;
-import com.android.wm.shell.ShellTaskOrganizer;
-import com.android.wm.shell.ShellTestCase;
-import com.android.wm.shell.TestShellExecutor;
-import com.android.wm.shell.common.ShellExecutor;
-import com.android.wm.shell.sysui.ShellController;
-import com.android.wm.shell.sysui.ShellInit;
-import com.android.wm.shell.transition.Transitions;
-
-import org.junit.After;
-import org.junit.Before;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.mockito.ArgumentCaptor;
-import org.mockito.Mock;
-import org.mockito.Mockito;
-
-import java.util.ArrayList;
-import java.util.Arrays;
-
-@SmallTest
-@RunWith(AndroidTestingRunner.class)
-public class DesktopModeControllerTest extends ShellTestCase {
-
- private static final int SECOND_DISPLAY = 2;
-
- @Mock
- private ShellController mShellController;
- @Mock
- private ShellTaskOrganizer mShellTaskOrganizer;
- @Mock
- private RootTaskDisplayAreaOrganizer mRootTaskDisplayAreaOrganizer;
- @Mock
- private ShellExecutor mTestExecutor;
- @Mock
- private Handler mMockHandler;
- @Mock
- private Transitions mTransitions;
- private DesktopModeController mController;
- private DesktopModeTaskRepository mDesktopModeTaskRepository;
- private ShellInit mShellInit;
- private StaticMockitoSession mMockitoSession;
-
- @Before
- public void setUp() {
- mMockitoSession = mockitoSession().mockStatic(DesktopModeStatus.class).startMocking();
- when(DesktopModeStatus.isProto1Enabled()).thenReturn(true);
- when(DesktopModeStatus.isActive(any())).thenReturn(true);
-
- mShellInit = Mockito.spy(new ShellInit(mTestExecutor));
-
- mDesktopModeTaskRepository = new DesktopModeTaskRepository();
-
- mController = createController();
-
- when(mShellTaskOrganizer.getRunningTasks(anyInt())).thenReturn(new ArrayList<>());
-
- mShellInit.init();
- clearInvocations(mShellTaskOrganizer);
- clearInvocations(mRootTaskDisplayAreaOrganizer);
- clearInvocations(mTransitions);
- }
-
- @After
- public void tearDown() {
- mMockitoSession.finishMocking();
- }
-
- @Test
- public void instantiate_addInitCallback() {
- verify(mShellInit).addInitCallback(any(), any());
- }
-
- @Test
- public void instantiate_flagOff_doNotAddInitCallback() {
- when(DesktopModeStatus.isProto1Enabled()).thenReturn(false);
- clearInvocations(mShellInit);
-
- createController();
-
- verify(mShellInit, never()).addInitCallback(any(), any());
- }
-
- @Test
- public void testDesktopModeEnabled_rootTdaSetToFreeform() {
- DisplayAreaInfo displayAreaInfo = createMockDisplayArea();
-
- mController.updateDesktopModeActive(true);
- WindowContainerTransaction wct = getDesktopModeSwitchTransaction();
-
- // 1 change: Root TDA windowing mode
- assertThat(wct.getChanges().size()).isEqualTo(1);
- // Verify WCT has a change for setting windowing mode to freeform
- Change change = wct.getChanges().get(displayAreaInfo.token.asBinder());
- assertThat(change).isNotNull();
- assertThat(change.getWindowingMode()).isEqualTo(WINDOWING_MODE_FREEFORM);
- }
-
- @Test
- public void testDesktopModeDisabled_rootTdaSetToFullscreen() {
- DisplayAreaInfo displayAreaInfo = createMockDisplayArea();
-
- mController.updateDesktopModeActive(false);
- WindowContainerTransaction wct = getDesktopModeSwitchTransaction();
-
- // 1 change: Root TDA windowing mode
- assertThat(wct.getChanges().size()).isEqualTo(1);
- // Verify WCT has a change for setting windowing mode to fullscreen
- Change change = wct.getChanges().get(displayAreaInfo.token.asBinder());
- assertThat(change).isNotNull();
- assertThat(change.getWindowingMode()).isEqualTo(WINDOWING_MODE_FULLSCREEN);
- }
-
- @Test
- public void testDesktopModeEnabled_windowingModeCleared() {
- createMockDisplayArea();
- RunningTaskInfo freeformTask = createFreeformTask();
- RunningTaskInfo fullscreenTask = createFullscreenTask();
- RunningTaskInfo homeTask = createHomeTask();
- when(mShellTaskOrganizer.getRunningTasks(anyInt())).thenReturn(new ArrayList<>(
- Arrays.asList(freeformTask, fullscreenTask, homeTask)));
-
- mController.updateDesktopModeActive(true);
- WindowContainerTransaction wct = getDesktopModeSwitchTransaction();
-
- // 2 changes: Root TDA windowing mode and 1 task
- assertThat(wct.getChanges().size()).isEqualTo(2);
- // No changes for tasks that are not standard or freeform
- assertThat(wct.getChanges().get(fullscreenTask.token.asBinder())).isNull();
- assertThat(wct.getChanges().get(homeTask.token.asBinder())).isNull();
- // Standard freeform task has windowing mode cleared
- Change change = wct.getChanges().get(freeformTask.token.asBinder());
- assertThat(change).isNotNull();
- assertThat(change.getWindowingMode()).isEqualTo(WINDOWING_MODE_UNDEFINED);
- }
-
- @Test
- public void testDesktopModeDisabled_windowingModeAndBoundsCleared() {
- createMockDisplayArea();
- RunningTaskInfo freeformTask = createFreeformTask();
- RunningTaskInfo fullscreenTask = createFullscreenTask();
- RunningTaskInfo homeTask = createHomeTask();
- when(mShellTaskOrganizer.getRunningTasks(anyInt())).thenReturn(new ArrayList<>(
- Arrays.asList(freeformTask, fullscreenTask, homeTask)));
-
- mController.updateDesktopModeActive(false);
- WindowContainerTransaction wct = getDesktopModeSwitchTransaction();
-
- // 3 changes: Root TDA windowing mode and 2 tasks
- assertThat(wct.getChanges().size()).isEqualTo(3);
- // No changes to home task
- assertThat(wct.getChanges().get(homeTask.token.asBinder())).isNull();
- // Standard tasks have bounds cleared
- assertThatBoundsCleared(wct.getChanges().get(freeformTask.token.asBinder()));
- assertThatBoundsCleared(wct.getChanges().get(fullscreenTask.token.asBinder()));
- // Freeform standard tasks have windowing mode cleared
- assertThat(wct.getChanges().get(
- freeformTask.token.asBinder()).getWindowingMode()).isEqualTo(
- WINDOWING_MODE_UNDEFINED);
- }
-
- @Test
- public void testDesktopModeEnabled_homeTaskBehindVisibleTask() {
- createMockDisplayArea();
- RunningTaskInfo fullscreenTask1 = createFullscreenTask();
- fullscreenTask1.isVisible = true;
- RunningTaskInfo fullscreenTask2 = createFullscreenTask();
- fullscreenTask2.isVisible = false;
- RunningTaskInfo homeTask = createHomeTask();
- when(mShellTaskOrganizer.getRunningTasks(anyInt())).thenReturn(new ArrayList<>(
- Arrays.asList(fullscreenTask1, fullscreenTask2, homeTask)));
-
- mController.updateDesktopModeActive(true);
- WindowContainerTransaction wct = getDesktopModeSwitchTransaction();
-
- // Check that there are hierarchy changes for home task and visible task
- assertThat(wct.getHierarchyOps()).hasSize(2);
- // First show home task
- HierarchyOp op1 = wct.getHierarchyOps().get(0);
- assertThat(op1.getType()).isEqualTo(HIERARCHY_OP_TYPE_REORDER);
- assertThat(op1.getContainer()).isEqualTo(homeTask.token.asBinder());
-
- // Then visible task on top of it
- HierarchyOp op2 = wct.getHierarchyOps().get(1);
- assertThat(op2.getType()).isEqualTo(HIERARCHY_OP_TYPE_REORDER);
- assertThat(op2.getContainer()).isEqualTo(fullscreenTask1.token.asBinder());
- }
-
- @Test
- public void testShowDesktopApps_allAppsInvisible_bringsToFront() {
- // Set up two active tasks on desktop, task2 is on top of task1.
- RunningTaskInfo freeformTask1 = createFreeformTask();
- mDesktopModeTaskRepository.addActiveTask(DEFAULT_DISPLAY, freeformTask1.taskId);
- mDesktopModeTaskRepository.addOrMoveFreeformTaskToTop(freeformTask1.taskId);
- mDesktopModeTaskRepository.updateVisibleFreeformTasks(
- DEFAULT_DISPLAY, freeformTask1.taskId, false /* visible */);
- RunningTaskInfo freeformTask2 = createFreeformTask();
- mDesktopModeTaskRepository.addActiveTask(DEFAULT_DISPLAY, freeformTask2.taskId);
- mDesktopModeTaskRepository.addOrMoveFreeformTaskToTop(freeformTask2.taskId);
- mDesktopModeTaskRepository.updateVisibleFreeformTasks(
- DEFAULT_DISPLAY, freeformTask2.taskId, false /* visible */);
- when(mShellTaskOrganizer.getRunningTaskInfo(freeformTask1.taskId)).thenReturn(
- freeformTask1);
- when(mShellTaskOrganizer.getRunningTaskInfo(freeformTask2.taskId)).thenReturn(
- freeformTask2);
-
- // Run show desktop apps logic
- mController.showDesktopApps(DEFAULT_DISPLAY);
-
- final WindowContainerTransaction wct = getBringAppsToFrontTransaction();
- // Check wct has reorder calls
- assertThat(wct.getHierarchyOps()).hasSize(2);
-
- // Task 1 appeared first, must be first reorder to top.
- HierarchyOp op1 = wct.getHierarchyOps().get(0);
- assertThat(op1.getType()).isEqualTo(HIERARCHY_OP_TYPE_REORDER);
- assertThat(op1.getContainer()).isEqualTo(freeformTask1.token.asBinder());
-
- // Task 2 appeared last, must be last reorder to top.
- HierarchyOp op2 = wct.getHierarchyOps().get(1);
- assertThat(op2.getType()).isEqualTo(HIERARCHY_OP_TYPE_REORDER);
- assertThat(op2.getContainer()).isEqualTo(freeformTask2.token.asBinder());
- }
-
- @Test
- public void testShowDesktopApps_appsAlreadyVisible_bringsToFront() {
- final RunningTaskInfo task1 = createFreeformTask();
- mDesktopModeTaskRepository.addActiveTask(DEFAULT_DISPLAY, task1.taskId);
- mDesktopModeTaskRepository.addOrMoveFreeformTaskToTop(task1.taskId);
- mDesktopModeTaskRepository.updateVisibleFreeformTasks(DEFAULT_DISPLAY, task1.taskId,
- true /* visible */);
- when(mShellTaskOrganizer.getRunningTaskInfo(task1.taskId)).thenReturn(task1);
- final RunningTaskInfo task2 = createFreeformTask();
- mDesktopModeTaskRepository.addActiveTask(DEFAULT_DISPLAY, task2.taskId);
- mDesktopModeTaskRepository.addOrMoveFreeformTaskToTop(task2.taskId);
- mDesktopModeTaskRepository.updateVisibleFreeformTasks(DEFAULT_DISPLAY, task2.taskId,
- true /* visible */);
- when(mShellTaskOrganizer.getRunningTaskInfo(task2.taskId)).thenReturn(task2);
-
- mController.showDesktopApps(DEFAULT_DISPLAY);
-
- final WindowContainerTransaction wct = getBringAppsToFrontTransaction();
- // Check wct has reorder calls
- assertThat(wct.getHierarchyOps()).hasSize(2);
- // Task 1 appeared first, must be first reorder to top.
- HierarchyOp op1 = wct.getHierarchyOps().get(0);
- assertThat(op1.getType()).isEqualTo(HIERARCHY_OP_TYPE_REORDER);
- assertThat(op1.getContainer()).isEqualTo(task1.token.asBinder());
-
- // Task 2 appeared last, must be last reorder to top.
- HierarchyOp op2 = wct.getHierarchyOps().get(1);
- assertThat(op2.getType()).isEqualTo(HIERARCHY_OP_TYPE_REORDER);
- assertThat(op2.getContainer()).isEqualTo(task2.token.asBinder());
- }
-
- @Test
- public void testShowDesktopApps_someAppsInvisible_reordersAll() {
- final RunningTaskInfo task1 = createFreeformTask();
- mDesktopModeTaskRepository.addActiveTask(DEFAULT_DISPLAY, task1.taskId);
- mDesktopModeTaskRepository.addOrMoveFreeformTaskToTop(task1.taskId);
- mDesktopModeTaskRepository.updateVisibleFreeformTasks(DEFAULT_DISPLAY, task1.taskId,
- false /* visible */);
- when(mShellTaskOrganizer.getRunningTaskInfo(task1.taskId)).thenReturn(task1);
- final RunningTaskInfo task2 = createFreeformTask();
- mDesktopModeTaskRepository.addActiveTask(DEFAULT_DISPLAY, task2.taskId);
- mDesktopModeTaskRepository.addOrMoveFreeformTaskToTop(task2.taskId);
- mDesktopModeTaskRepository.updateVisibleFreeformTasks(DEFAULT_DISPLAY, task2.taskId,
- true /* visible */);
- when(mShellTaskOrganizer.getRunningTaskInfo(task2.taskId)).thenReturn(task2);
-
- mController.showDesktopApps(DEFAULT_DISPLAY);
-
- final WindowContainerTransaction wct = getBringAppsToFrontTransaction();
- // Both tasks should be reordered to top, even if one was already visible.
- assertThat(wct.getHierarchyOps()).hasSize(2);
- final HierarchyOp op1 = wct.getHierarchyOps().get(0);
- assertThat(op1.getType()).isEqualTo(HIERARCHY_OP_TYPE_REORDER);
- assertThat(op1.getContainer()).isEqualTo(task1.token.asBinder());
- final HierarchyOp op2 = wct.getHierarchyOps().get(1);
- assertThat(op2.getType()).isEqualTo(HIERARCHY_OP_TYPE_REORDER);
- assertThat(op2.getContainer()).isEqualTo(task2.token.asBinder());
- }
-
- @Test
- public void testShowDesktopApps_twoDisplays_bringsToFrontOnlyOneDisplay() {
- RunningTaskInfo taskDefaultDisplay = createFreeformTask(DEFAULT_DISPLAY);
- mDesktopModeTaskRepository.addActiveTask(DEFAULT_DISPLAY, taskDefaultDisplay.taskId);
- mDesktopModeTaskRepository.addOrMoveFreeformTaskToTop(taskDefaultDisplay.taskId);
- mDesktopModeTaskRepository.updateVisibleFreeformTasks(
- DEFAULT_DISPLAY, taskDefaultDisplay.taskId, false /* visible */);
- when(mShellTaskOrganizer.getRunningTaskInfo(taskDefaultDisplay.taskId)).thenReturn(
- taskDefaultDisplay);
-
- RunningTaskInfo taskSecondDisplay = createFreeformTask(SECOND_DISPLAY);
- mDesktopModeTaskRepository.addActiveTask(SECOND_DISPLAY, taskSecondDisplay.taskId);
- mDesktopModeTaskRepository.addOrMoveFreeformTaskToTop(taskSecondDisplay.taskId);
- mDesktopModeTaskRepository.updateVisibleFreeformTasks(
- SECOND_DISPLAY, taskSecondDisplay.taskId, false /* visible */);
- when(mShellTaskOrganizer.getRunningTaskInfo(taskSecondDisplay.taskId)).thenReturn(
- taskSecondDisplay);
-
- mController.showDesktopApps(DEFAULT_DISPLAY);
-
- WindowContainerTransaction wct = getBringAppsToFrontTransaction();
- assertThat(wct.getHierarchyOps()).hasSize(1);
- HierarchyOp op = wct.getHierarchyOps().get(0);
- assertThat(op.getContainer()).isEqualTo(taskDefaultDisplay.token.asBinder());
- }
-
- @Test
- public void testGetVisibleTaskCount_noTasks_returnsZero() {
- assertThat(mController.getVisibleTaskCount(DEFAULT_DISPLAY)).isEqualTo(0);
- }
-
- @Test
- public void testGetVisibleTaskCount_twoTasks_bothVisible_returnsTwo() {
- RunningTaskInfo task1 = createFreeformTask();
- mDesktopModeTaskRepository.addActiveTask(DEFAULT_DISPLAY, task1.taskId);
- mDesktopModeTaskRepository.addOrMoveFreeformTaskToTop(task1.taskId);
- mDesktopModeTaskRepository.updateVisibleFreeformTasks(DEFAULT_DISPLAY, task1.taskId,
- true /* visible */);
-
- RunningTaskInfo task2 = createFreeformTask();
- mDesktopModeTaskRepository.addActiveTask(DEFAULT_DISPLAY, task2.taskId);
- mDesktopModeTaskRepository.addOrMoveFreeformTaskToTop(task2.taskId);
- mDesktopModeTaskRepository.updateVisibleFreeformTasks(DEFAULT_DISPLAY, task2.taskId,
- true /* visible */);
-
- assertThat(mController.getVisibleTaskCount(DEFAULT_DISPLAY)).isEqualTo(2);
- }
-
- @Test
- public void testGetVisibleTaskCount_twoTasks_oneVisible_returnsOne() {
- RunningTaskInfo task1 = createFreeformTask();
- mDesktopModeTaskRepository.addActiveTask(DEFAULT_DISPLAY, task1.taskId);
- mDesktopModeTaskRepository.addOrMoveFreeformTaskToTop(task1.taskId);
- mDesktopModeTaskRepository.updateVisibleFreeformTasks(DEFAULT_DISPLAY, task1.taskId,
- true /* visible */);
-
- RunningTaskInfo task2 = createFreeformTask();
- mDesktopModeTaskRepository.addActiveTask(DEFAULT_DISPLAY, task2.taskId);
- mDesktopModeTaskRepository.addOrMoveFreeformTaskToTop(task2.taskId);
- mDesktopModeTaskRepository.updateVisibleFreeformTasks(DEFAULT_DISPLAY, task2.taskId,
- false /* visible */);
-
- assertThat(mController.getVisibleTaskCount(DEFAULT_DISPLAY)).isEqualTo(1);
- }
-
- @Test
- public void testGetVisibleTaskCount_twoTasksVisibleOnDifferentDisplays_returnsOne() {
- RunningTaskInfo taskDefaultDisplay = createFreeformTask();
- mDesktopModeTaskRepository.addActiveTask(DEFAULT_DISPLAY, taskDefaultDisplay.taskId);
- mDesktopModeTaskRepository.addOrMoveFreeformTaskToTop(taskDefaultDisplay.taskId);
- mDesktopModeTaskRepository.updateVisibleFreeformTasks(DEFAULT_DISPLAY,
- taskDefaultDisplay.taskId,
- true /* visible */);
-
- RunningTaskInfo taskSecondDisplay = createFreeformTask();
- mDesktopModeTaskRepository.addActiveTask(SECOND_DISPLAY, taskSecondDisplay.taskId);
- mDesktopModeTaskRepository.addOrMoveFreeformTaskToTop(taskSecondDisplay.taskId);
- mDesktopModeTaskRepository.updateVisibleFreeformTasks(SECOND_DISPLAY,
- taskSecondDisplay.taskId,
- true /* visible */);
-
- assertThat(mController.getVisibleTaskCount(SECOND_DISPLAY)).isEqualTo(1);
- }
-
- @Test
- public void testHandleTransitionRequest_desktopModeNotActive_returnsNull() {
- when(DesktopModeStatus.isActive(any())).thenReturn(false);
- WindowContainerTransaction wct = mController.handleRequest(
- new Binder(),
- new TransitionRequestInfo(TRANSIT_OPEN, null /* trigger */, null /* remote */));
- assertThat(wct).isNull();
- }
-
- @Test
- public void testHandleTransitionRequest_unsupportedTransit_returnsNull() {
- WindowContainerTransaction wct = mController.handleRequest(
- new Binder(),
- new TransitionRequestInfo(TRANSIT_CLOSE, null /* trigger */, null /* remote */));
- assertThat(wct).isNull();
- }
-
- @Test
- public void testHandleTransitionRequest_notFreeform_returnsNull() {
- RunningTaskInfo trigger = new RunningTaskInfo();
- trigger.configuration.windowConfiguration.setWindowingMode(WINDOWING_MODE_FULLSCREEN);
- WindowContainerTransaction wct = mController.handleRequest(
- new Binder(),
- new TransitionRequestInfo(TRANSIT_TO_FRONT, trigger, null /* remote */));
- assertThat(wct).isNull();
- }
-
- @Test
- public void testHandleTransitionRequest_taskOpen_returnsWct() {
- RunningTaskInfo trigger = new RunningTaskInfo();
- trigger.token = new MockToken().token();
- trigger.configuration.windowConfiguration.setWindowingMode(WINDOWING_MODE_FREEFORM);
- WindowContainerTransaction wct = mController.handleRequest(
- mock(IBinder.class),
- new TransitionRequestInfo(TRANSIT_OPEN, trigger, null /* remote */));
- assertThat(wct).isNotNull();
- }
-
- @Test
- public void testHandleTransitionRequest_taskToFront_returnsWct() {
- RunningTaskInfo trigger = new RunningTaskInfo();
- trigger.token = new MockToken().token();
- trigger.configuration.windowConfiguration.setWindowingMode(WINDOWING_MODE_FREEFORM);
- WindowContainerTransaction wct = mController.handleRequest(
- mock(IBinder.class),
- new TransitionRequestInfo(TRANSIT_TO_FRONT, trigger, null /* remote */));
- assertThat(wct).isNotNull();
- }
-
- @Test
- public void testHandleTransitionRequest_taskOpen_doesNotStartAnotherTransition() {
- RunningTaskInfo trigger = new RunningTaskInfo();
- trigger.token = new MockToken().token();
- trigger.configuration.windowConfiguration.setWindowingMode(WINDOWING_MODE_FREEFORM);
- mController.handleRequest(
- mock(IBinder.class),
- new TransitionRequestInfo(TRANSIT_OPEN, trigger, null /* remote */));
- verifyZeroInteractions(mTransitions);
- }
-
- private DesktopModeController createController() {
- return new DesktopModeController(mContext, mShellInit, mShellController,
- mShellTaskOrganizer, mRootTaskDisplayAreaOrganizer, mTransitions,
- mDesktopModeTaskRepository, mMockHandler, new TestShellExecutor());
- }
-
- private DisplayAreaInfo createMockDisplayArea() {
- DisplayAreaInfo displayAreaInfo = new DisplayAreaInfo(new MockToken().token(),
- mContext.getDisplayId(), 0);
- when(mRootTaskDisplayAreaOrganizer.getDisplayAreaInfo(mContext.getDisplayId()))
- .thenReturn(displayAreaInfo);
- return displayAreaInfo;
- }
-
- private WindowContainerTransaction getDesktopModeSwitchTransaction() {
- ArgumentCaptor<WindowContainerTransaction> arg = ArgumentCaptor.forClass(
- WindowContainerTransaction.class);
- if (Transitions.ENABLE_SHELL_TRANSITIONS) {
- verify(mTransitions).startTransition(eq(TRANSIT_CHANGE), arg.capture(), any());
- } else {
- verify(mRootTaskDisplayAreaOrganizer).applyTransaction(arg.capture());
- }
- return arg.getValue();
- }
-
- private WindowContainerTransaction getBringAppsToFrontTransaction() {
- final ArgumentCaptor<WindowContainerTransaction> arg = ArgumentCaptor.forClass(
- WindowContainerTransaction.class);
- if (Transitions.ENABLE_SHELL_TRANSITIONS) {
- verify(mTransitions).startTransition(eq(TRANSIT_NONE), arg.capture(), any());
- } else {
- verify(mShellTaskOrganizer).applyTransaction(arg.capture());
- }
- return arg.getValue();
- }
-
- private void assertThatBoundsCleared(Change change) {
- assertThat((change.getWindowSetMask() & WINDOW_CONFIG_BOUNDS) != 0).isTrue();
- assertThat(change.getConfiguration().windowConfiguration.getBounds().isEmpty()).isTrue();
- }
-
-}
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 5d87cf8..be4a287 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
@@ -107,7 +107,7 @@
@Before
fun setUp() {
mockitoSession = mockitoSession().mockStatic(DesktopModeStatus::class.java).startMocking()
- whenever(DesktopModeStatus.isProto2Enabled()).thenReturn(true)
+ whenever(DesktopModeStatus.isEnabled()).thenReturn(true)
shellInit = Mockito.spy(ShellInit(testExecutor))
desktopModeTaskRepository = DesktopModeTaskRepository()
@@ -154,7 +154,7 @@
@Test
fun instantiate_flagOff_doNotAddInitCallback() {
- whenever(DesktopModeStatus.isProto2Enabled()).thenReturn(false)
+ whenever(DesktopModeStatus.isEnabled()).thenReturn(false)
clearInvocations(shellInit)
createController()
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/PipBoundsAlgorithmTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/PipBoundsAlgorithmTest.java
index bf1b7f9..46259a8 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/PipBoundsAlgorithmTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/PipBoundsAlgorithmTest.java
@@ -33,6 +33,11 @@
import com.android.wm.shell.ShellTestCase;
import com.android.wm.shell.common.DisplayLayout;
import com.android.wm.shell.common.pip.PhoneSizeSpecSource;
+import com.android.wm.shell.common.pip.PipBoundsAlgorithm;
+import com.android.wm.shell.common.pip.PipBoundsState;
+import com.android.wm.shell.common.pip.PipDisplayLayoutState;
+import com.android.wm.shell.common.pip.PipKeepClearAlgorithmInterface;
+import com.android.wm.shell.common.pip.PipSnapAlgorithm;
import com.android.wm.shell.common.pip.SizeSpecSource;
import org.junit.Before;
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/PipBoundsStateTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/PipBoundsStateTest.java
index 4341c4c..d34e27b 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/PipBoundsStateTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/PipBoundsStateTest.java
@@ -36,6 +36,8 @@
import com.android.wm.shell.R;
import com.android.wm.shell.ShellTestCase;
import com.android.wm.shell.common.pip.PhoneSizeSpecSource;
+import com.android.wm.shell.common.pip.PipBoundsState;
+import com.android.wm.shell.common.pip.PipDisplayLayoutState;
import com.android.wm.shell.common.pip.SizeSpecSource;
import org.junit.Before;
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/PipSnapAlgorithmTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/PipSnapAlgorithmTest.java
index b9226d2..ac13d7f 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/PipSnapAlgorithmTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/PipSnapAlgorithmTest.java
@@ -25,6 +25,8 @@
import androidx.test.filters.SmallTest;
import com.android.wm.shell.ShellTestCase;
+import com.android.wm.shell.common.pip.PipBoundsState;
+import com.android.wm.shell.common.pip.PipSnapAlgorithm;
import org.junit.Before;
import org.junit.Test;
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/PipTaskOrganizerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/PipTaskOrganizerTest.java
index 248d665..4e2b7f6 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/PipTaskOrganizerTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/PipTaskOrganizerTest.java
@@ -53,6 +53,11 @@
import com.android.wm.shell.common.DisplayLayout;
import com.android.wm.shell.common.SyncTransactionQueue;
import com.android.wm.shell.common.pip.PhoneSizeSpecSource;
+import com.android.wm.shell.common.pip.PipBoundsAlgorithm;
+import com.android.wm.shell.common.pip.PipBoundsState;
+import com.android.wm.shell.common.pip.PipDisplayLayoutState;
+import com.android.wm.shell.common.pip.PipKeepClearAlgorithmInterface;
+import com.android.wm.shell.common.pip.PipSnapAlgorithm;
import com.android.wm.shell.common.pip.PipUiEventLogger;
import com.android.wm.shell.common.pip.SizeSpecSource;
import com.android.wm.shell.pip.phone.PhonePipMenuController;
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/phone/PhonePipKeepClearAlgorithmTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/phone/PhonePipKeepClearAlgorithmTest.java
index cc9e26b..8c7b47e 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/phone/PhonePipKeepClearAlgorithmTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/phone/PhonePipKeepClearAlgorithmTest.java
@@ -29,8 +29,9 @@
import androidx.test.filters.SmallTest;
import com.android.wm.shell.ShellTestCase;
-import com.android.wm.shell.pip.PipBoundsAlgorithm;
-import com.android.wm.shell.pip.PipBoundsState;
+import com.android.wm.shell.common.pip.PhonePipKeepClearAlgorithm;
+import com.android.wm.shell.common.pip.PipBoundsAlgorithm;
+import com.android.wm.shell.common.pip.PipBoundsState;
import org.junit.Before;
import org.junit.Test;
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/phone/PhoneSizeSpecSourceTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/phone/PhoneSizeSpecSourceTest.java
index 024cba3..3d5cd69 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/phone/PhoneSizeSpecSourceTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/phone/PhoneSizeSpecSourceTest.java
@@ -33,8 +33,8 @@
import com.android.wm.shell.ShellTestCase;
import com.android.wm.shell.common.DisplayLayout;
import com.android.wm.shell.common.pip.PhoneSizeSpecSource;
+import com.android.wm.shell.common.pip.PipDisplayLayoutState;
import com.android.wm.shell.common.pip.SizeSpecSource;
-import com.android.wm.shell.pip.PipDisplayLayoutState;
import org.junit.After;
import org.junit.Assert;
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/phone/PipControllerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/phone/PipControllerTest.java
index 911f5e1..4eb5193 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/phone/PipControllerTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/phone/PipControllerTest.java
@@ -55,15 +55,16 @@
import com.android.wm.shell.common.ShellExecutor;
import com.android.wm.shell.common.TabletopModeController;
import com.android.wm.shell.common.TaskStackListenerImpl;
+import com.android.wm.shell.common.pip.PhonePipKeepClearAlgorithm;
import com.android.wm.shell.common.pip.PipAppOpsListener;
+import com.android.wm.shell.common.pip.PipBoundsAlgorithm;
+import com.android.wm.shell.common.pip.PipBoundsState;
+import com.android.wm.shell.common.pip.PipDisplayLayoutState;
import com.android.wm.shell.common.pip.PipMediaController;
+import com.android.wm.shell.common.pip.PipSnapAlgorithm;
import com.android.wm.shell.onehanded.OneHandedController;
import com.android.wm.shell.pip.PipAnimationController;
-import com.android.wm.shell.pip.PipBoundsAlgorithm;
-import com.android.wm.shell.pip.PipBoundsState;
-import com.android.wm.shell.pip.PipDisplayLayoutState;
import com.android.wm.shell.pip.PipParamsChangedForwarder;
-import com.android.wm.shell.pip.PipSnapAlgorithm;
import com.android.wm.shell.pip.PipTaskOrganizer;
import com.android.wm.shell.pip.PipTransitionController;
import com.android.wm.shell.pip.PipTransitionState;
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/phone/PipDoubleTapHelperTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/phone/PipDoubleTapHelperTest.java
index 8ce3ca4..0f8db85 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/phone/PipDoubleTapHelperTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/phone/PipDoubleTapHelperTest.java
@@ -29,7 +29,7 @@
import android.testing.AndroidTestingRunner;
import com.android.wm.shell.ShellTestCase;
-import com.android.wm.shell.pip.PipBoundsState;
+import com.android.wm.shell.common.pip.PipBoundsState;
import org.junit.Assert;
import org.junit.Before;
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/phone/PipResizeGestureHandlerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/phone/PipResizeGestureHandlerTest.java
index 12b4f3e..6777a5b 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/phone/PipResizeGestureHandlerTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/phone/PipResizeGestureHandlerTest.java
@@ -37,13 +37,13 @@
import com.android.wm.shell.common.FloatingContentCoordinator;
import com.android.wm.shell.common.ShellExecutor;
import com.android.wm.shell.common.pip.PhoneSizeSpecSource;
+import com.android.wm.shell.common.pip.PipBoundsAlgorithm;
+import com.android.wm.shell.common.pip.PipBoundsState;
+import com.android.wm.shell.common.pip.PipDisplayLayoutState;
+import com.android.wm.shell.common.pip.PipKeepClearAlgorithmInterface;
+import com.android.wm.shell.common.pip.PipSnapAlgorithm;
import com.android.wm.shell.common.pip.PipUiEventLogger;
import com.android.wm.shell.common.pip.SizeSpecSource;
-import com.android.wm.shell.pip.PipBoundsAlgorithm;
-import com.android.wm.shell.pip.PipBoundsState;
-import com.android.wm.shell.pip.PipDisplayLayoutState;
-import com.android.wm.shell.pip.PipKeepClearAlgorithmInterface;
-import com.android.wm.shell.pip.PipSnapAlgorithm;
import com.android.wm.shell.pip.PipTaskOrganizer;
import com.android.wm.shell.pip.PipTransitionController;
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/phone/PipTouchHandlerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/phone/PipTouchHandlerTest.java
index 314f195d..9aaabd1 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/phone/PipTouchHandlerTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/phone/PipTouchHandlerTest.java
@@ -33,13 +33,13 @@
import com.android.wm.shell.common.FloatingContentCoordinator;
import com.android.wm.shell.common.ShellExecutor;
import com.android.wm.shell.common.pip.PhoneSizeSpecSource;
+import com.android.wm.shell.common.pip.PipBoundsAlgorithm;
+import com.android.wm.shell.common.pip.PipBoundsState;
+import com.android.wm.shell.common.pip.PipDisplayLayoutState;
+import com.android.wm.shell.common.pip.PipKeepClearAlgorithmInterface;
+import com.android.wm.shell.common.pip.PipSnapAlgorithm;
import com.android.wm.shell.common.pip.PipUiEventLogger;
import com.android.wm.shell.common.pip.SizeSpecSource;
-import com.android.wm.shell.pip.PipBoundsAlgorithm;
-import com.android.wm.shell.pip.PipBoundsState;
-import com.android.wm.shell.pip.PipDisplayLayoutState;
-import com.android.wm.shell.pip.PipKeepClearAlgorithmInterface;
-import com.android.wm.shell.pip.PipSnapAlgorithm;
import com.android.wm.shell.pip.PipTaskOrganizer;
import com.android.wm.shell.pip.PipTransitionController;
import com.android.wm.shell.sysui.ShellInit;
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/tv/TvPipBoundsControllerTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/tv/TvPipBoundsControllerTest.kt
index 7370ed7..94f2b91 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/tv/TvPipBoundsControllerTest.kt
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/tv/TvPipBoundsControllerTest.kt
@@ -25,7 +25,7 @@
import com.android.wm.shell.R
import com.android.wm.shell.ShellTestCase
-import com.android.wm.shell.pip.PipBoundsState.STASH_TYPE_RIGHT
+import com.android.wm.shell.common.pip.PipBoundsState.STASH_TYPE_RIGHT
import com.android.wm.shell.pip.tv.TvPipBoundsController.POSITION_DEBOUNCE_TIMEOUT_MILLIS
import com.android.wm.shell.pip.tv.TvPipKeepClearAlgorithm.Placement
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/tv/TvPipGravityTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/tv/TvPipGravityTest.java
index 256610b..974539f 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/tv/TvPipGravityTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/tv/TvPipGravityTest.java
@@ -27,9 +27,9 @@
import com.android.wm.shell.ShellTestCase;
import com.android.wm.shell.common.pip.LegacySizeSpecSource;
+import com.android.wm.shell.common.pip.PipDisplayLayoutState;
+import com.android.wm.shell.common.pip.PipSnapAlgorithm;
import com.android.wm.shell.common.pip.SizeSpecSource;
-import com.android.wm.shell.pip.PipDisplayLayoutState;
-import com.android.wm.shell.pip.PipSnapAlgorithm;
import org.junit.Before;
import org.junit.Test;
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/tv/TvPipKeepClearAlgorithmTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/tv/TvPipKeepClearAlgorithmTest.kt
index aedf65d..998060f 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/tv/TvPipKeepClearAlgorithmTest.kt
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/tv/TvPipKeepClearAlgorithmTest.kt
@@ -22,10 +22,10 @@
import android.util.Size
import android.view.Gravity
import com.android.wm.shell.ShellTestCase
-import com.android.wm.shell.pip.PipBoundsState.STASH_TYPE_BOTTOM
-import com.android.wm.shell.pip.PipBoundsState.STASH_TYPE_NONE
-import com.android.wm.shell.pip.PipBoundsState.STASH_TYPE_RIGHT
-import com.android.wm.shell.pip.PipBoundsState.STASH_TYPE_TOP
+import com.android.wm.shell.common.pip.PipBoundsState.STASH_TYPE_BOTTOM
+import com.android.wm.shell.common.pip.PipBoundsState.STASH_TYPE_NONE
+import com.android.wm.shell.common.pip.PipBoundsState.STASH_TYPE_RIGHT
+import com.android.wm.shell.common.pip.PipBoundsState.STASH_TYPE_TOP
import com.android.wm.shell.pip.tv.TvPipKeepClearAlgorithm.Placement
import junit.framework.Assert.assertEquals
import junit.framework.Assert.assertFalse
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 9e9e1ca..40ce785 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
@@ -257,7 +257,7 @@
public void testGetRecentTasks_hasActiveDesktopTasks_proto2Enabled_groupFreeformTasks() {
StaticMockitoSession mockitoSession = mockitoSession().mockStatic(
DesktopModeStatus.class).startMocking();
- when(DesktopModeStatus.isProto2Enabled()).thenReturn(true);
+ when(DesktopModeStatus.isEnabled()).thenReturn(true);
ActivityManager.RecentTaskInfo t1 = makeTaskInfo(1);
ActivityManager.RecentTaskInfo t2 = makeTaskInfo(2);
@@ -297,7 +297,7 @@
public void testGetRecentTasks_hasActiveDesktopTasks_proto2Disabled_doNotGroupFreeformTasks() {
StaticMockitoSession mockitoSession = mockitoSession().mockStatic(
DesktopModeStatus.class).startMocking();
- when(DesktopModeStatus.isProto2Enabled()).thenReturn(false);
+ when(DesktopModeStatus.isEnabled()).thenReturn(false);
ActivityManager.RecentTaskInfo t1 = makeTaskInfo(1);
ActivityManager.RecentTaskInfo t2 = makeTaskInfo(2);
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/unfold/UnfoldTransitionHandlerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/unfold/UnfoldTransitionHandlerTest.java
index 63a685e..1d94c9c 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/unfold/UnfoldTransitionHandlerTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/unfold/UnfoldTransitionHandlerTest.java
@@ -92,7 +92,7 @@
TransitionRequestInfo.DisplayChange displayChange = new TransitionRequestInfo.DisplayChange(
Display.DEFAULT_DISPLAY).setPhysicalDisplayChanged(true);
TransitionRequestInfo requestInfo = new TransitionRequestInfo(TRANSIT_CHANGE,
- triggerTaskInfo, /* remoteTransition= */ null, displayChange);
+ triggerTaskInfo, /* remoteTransition= */ null, displayChange, 0 /* flags */);
WindowContainerTransaction result = mUnfoldTransitionHandler.handleRequest(mTransition,
requestInfo);
@@ -106,7 +106,7 @@
TransitionRequestInfo.DisplayChange displayChange = new TransitionRequestInfo.DisplayChange(
Display.DEFAULT_DISPLAY).setPhysicalDisplayChanged(false);
TransitionRequestInfo requestInfo = new TransitionRequestInfo(TRANSIT_CHANGE,
- triggerTaskInfo, /* remoteTransition= */ null, displayChange);
+ triggerTaskInfo, /* remoteTransition= */ null, displayChange, 0 /* flags */);
WindowContainerTransaction result = mUnfoldTransitionHandler.handleRequest(mTransition,
requestInfo);
@@ -212,7 +212,7 @@
TransitionRequestInfo.DisplayChange displayChange = new TransitionRequestInfo.DisplayChange(
Display.DEFAULT_DISPLAY).setPhysicalDisplayChanged(true);
return new TransitionRequestInfo(TRANSIT_CHANGE,
- triggerTaskInfo, /* remoteTransition= */ null, displayChange);
+ triggerTaskInfo, /* remoteTransition= */ null, displayChange, 0 /* flags */);
}
private static class TestShellUnfoldProgressProvider implements ShellUnfoldProgressProvider,
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModelTests.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModelTests.java
index 596d6dd..d8afe68 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModelTests.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModelTests.java
@@ -48,13 +48,13 @@
import androidx.test.filters.SmallTest;
+import com.android.wm.shell.RootTaskDisplayAreaOrganizer;
import com.android.wm.shell.ShellTaskOrganizer;
import com.android.wm.shell.ShellTestCase;
import com.android.wm.shell.TestRunningTaskInfoBuilder;
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.DesktopModeController;
import com.android.wm.shell.desktopmode.DesktopTasksController;
import com.android.wm.shell.sysui.ShellController;
import com.android.wm.shell.sysui.ShellInit;
@@ -87,7 +87,6 @@
@Mock private DisplayController mDisplayController;
@Mock private DisplayLayout mDisplayLayout;
@Mock private SyncTransactionQueue mSyncQueue;
- @Mock private DesktopModeController mDesktopModeController;
@Mock private DesktopTasksController mDesktopTasksController;
@Mock private InputMonitor mInputMonitor;
@Mock private InputManager mInputManager;
@@ -100,6 +99,7 @@
@Mock private ShellInit mShellInit;
@Mock private DesktopModeWindowDecorViewModel.DesktopModeKeyguardChangeListener
mDesktopModeKeyguardChangeListener;
+ @Mock private RootTaskDisplayAreaOrganizer mRootTaskDisplayAreaOrganizer;
private final List<InputManager> mMockInputManagers = new ArrayList<>();
private DesktopModeWindowDecorViewModel mDesktopModeWindowDecorViewModel;
@@ -109,27 +109,27 @@
mMockInputManagers.add(mInputManager);
mDesktopModeWindowDecorViewModel =
- new DesktopModeWindowDecorViewModel(
- mContext,
- mMainHandler,
- mMainChoreographer,
- mShellInit,
- mTaskOrganizer,
- mDisplayController,
- mShellController,
- mSyncQueue,
- mTransitions,
- Optional.of(mDesktopModeController),
- Optional.of(mDesktopTasksController),
- mDesktopModeWindowDecorFactory,
- mMockInputMonitorFactory,
- mTransactionFactory,
- mDesktopModeKeyguardChangeListener
- );
+ new DesktopModeWindowDecorViewModel(
+ mContext,
+ mMainHandler,
+ mMainChoreographer,
+ mShellInit,
+ mTaskOrganizer,
+ mDisplayController,
+ mShellController,
+ mSyncQueue,
+ mTransitions,
+ Optional.of(mDesktopTasksController),
+ mDesktopModeWindowDecorFactory,
+ mMockInputMonitorFactory,
+ mTransactionFactory,
+ mDesktopModeKeyguardChangeListener,
+ mRootTaskDisplayAreaOrganizer
+ );
doReturn(mDesktopModeWindowDecoration)
- .when(mDesktopModeWindowDecorFactory)
- .create(any(), any(), any(), any(), any(), any(), any(), any());
+ .when(mDesktopModeWindowDecorFactory)
+ .create(any(), any(), any(), any(), any(), any(), any(), any(), any());
doReturn(mTransaction).when(mTransactionFactory).get();
doReturn(mDisplayLayout).when(mDisplayController).getDisplayLayout(anyInt());
doReturn(STABLE_INSETS).when(mDisplayLayout).stableInsets();
@@ -172,7 +172,8 @@
surfaceControl,
mMainHandler,
mMainChoreographer,
- mSyncQueue);
+ mSyncQueue,
+ mRootTaskDisplayAreaOrganizer);
verify(mDesktopModeWindowDecoration).close();
}
@@ -205,7 +206,8 @@
surfaceControl,
mMainHandler,
mMainChoreographer,
- mSyncQueue);
+ mSyncQueue,
+ mRootTaskDisplayAreaOrganizer);
}
@Test
@@ -291,7 +293,7 @@
taskInfo, surfaceControl, startT, finishT);
});
verify(mDesktopModeWindowDecorFactory, never())
- .create(any(), any(), any(), any(), any(), any(), any(), any());
+ .create(any(), any(), any(), any(), any(), any(), any(), any(), any());
}
private void runOnMainThread(Runnable r) throws Exception {
@@ -307,10 +309,10 @@
private static ActivityManager.RunningTaskInfo createTaskInfo(int taskId,
int displayId, @WindowConfiguration.WindowingMode int windowingMode) {
ActivityManager.RunningTaskInfo taskInfo =
- new TestRunningTaskInfoBuilder()
- .setDisplayId(displayId)
- .setVisible(true)
- .build();
+ new TestRunningTaskInfoBuilder()
+ .setDisplayId(displayId)
+ .setVisible(true)
+ .build();
taskInfo.taskId = taskId;
taskInfo.configuration.windowConfiguration.setWindowingMode(windowingMode);
return taskInfo;
diff --git a/libs/androidfw/AssetManager2.cpp b/libs/androidfw/AssetManager2.cpp
index b1ef4e5..6523469 100644
--- a/libs/androidfw/AssetManager2.cpp
+++ b/libs/androidfw/AssetManager2.cpp
@@ -91,12 +91,17 @@
StringPoolRef entry_string_ref;
};
-AssetManager2::AssetManager2(ApkAssetsList apk_assets, const ResTable_config& configuration)
- : configuration_(configuration) {
+AssetManager2::AssetManager2(ApkAssetsList apk_assets, const ResTable_config& configuration) {
+ configurations_.push_back(configuration);
+
// Don't invalidate caches here as there's nothing cached yet.
SetApkAssets(apk_assets, false);
}
+AssetManager2::AssetManager2() {
+ configurations_.resize(1);
+}
+
bool AssetManager2::SetApkAssets(ApkAssetsList apk_assets, bool invalidate_caches) {
BuildDynamicRefTable(apk_assets);
RebuildFilterList();
@@ -421,9 +426,16 @@
return false;
}
-void AssetManager2::SetConfiguration(const ResTable_config& configuration) {
- const int diff = configuration_.diff(configuration);
- configuration_ = configuration;
+void AssetManager2::SetConfigurations(std::vector<ResTable_config> configurations) {
+ int diff = 0;
+ if (configurations_.size() != configurations.size()) {
+ diff = -1;
+ } else {
+ for (int i = 0; i < configurations_.size(); i++) {
+ diff |= configurations_[i].diff(configurations[i]);
+ }
+ }
+ configurations_ = std::move(configurations);
if (diff) {
RebuildFilterList();
@@ -620,16 +632,6 @@
auto op = StartOperation();
- // Might use this if density_override != 0.
- ResTable_config density_override_config;
-
- // Select our configuration or generate a density override configuration.
- const ResTable_config* desired_config = &configuration_;
- if (density_override != 0 && density_override != configuration_.density) {
- density_override_config = configuration_;
- density_override_config.density = density_override;
- desired_config = &density_override_config;
- }
// Retrieve the package group from the package id of the resource id.
if (UNLIKELY(!is_valid_resid(resid))) {
@@ -648,119 +650,160 @@
}
const PackageGroup& package_group = package_groups_[package_idx];
- auto result = FindEntryInternal(package_group, type_idx, entry_idx, *desired_config,
- stop_at_first_match, ignore_configuration);
- if (UNLIKELY(!result.has_value())) {
- return base::unexpected(result.error());
- }
+ std::optional<FindEntryResult> final_result;
+ bool final_has_locale = false;
+ bool final_overlaid = false;
+ for (auto & config : configurations_) {
+ // Might use this if density_override != 0.
+ ResTable_config density_override_config;
- bool overlaid = false;
- if (!stop_at_first_match && !ignore_configuration) {
- const auto& assets = GetApkAssets(result->cookie);
- if (!assets) {
- ALOGE("Found expired ApkAssets #%d for resource ID 0x%08x.", result->cookie, resid);
- return base::unexpected(std::nullopt);
+ // Select our configuration or generate a density override configuration.
+ const ResTable_config* desired_config = &config;
+ if (density_override != 0 && density_override != config.density) {
+ density_override_config = config;
+ density_override_config.density = density_override;
+ desired_config = &density_override_config;
}
- if (!assets->IsLoader()) {
- for (const auto& id_map : package_group.overlays_) {
- auto overlay_entry = id_map.overlay_res_maps_.Lookup(resid);
- if (!overlay_entry) {
- // No id map entry exists for this target resource.
- continue;
- }
- if (overlay_entry.IsInlineValue()) {
- // The target resource is overlaid by an inline value not represented by a resource.
- ConfigDescription best_frro_config;
- Res_value best_frro_value;
- bool frro_found = false;
- for( const auto& [config, value] : overlay_entry.GetInlineValue()) {
- if ((!frro_found || config.isBetterThan(best_frro_config, desired_config))
- && config.match(*desired_config)) {
- frro_found = true;
- best_frro_config = config;
- best_frro_value = value;
- }
- }
- if (!frro_found) {
+
+ auto result = FindEntryInternal(package_group, type_idx, entry_idx, *desired_config,
+ stop_at_first_match, ignore_configuration);
+ if (UNLIKELY(!result.has_value())) {
+ return base::unexpected(result.error());
+ }
+ bool overlaid = false;
+ if (!stop_at_first_match && !ignore_configuration) {
+ const auto& assets = GetApkAssets(result->cookie);
+ if (!assets) {
+ ALOGE("Found expired ApkAssets #%d for resource ID 0x%08x.", result->cookie, resid);
+ return base::unexpected(std::nullopt);
+ }
+ if (!assets->IsLoader()) {
+ for (const auto& id_map : package_group.overlays_) {
+ auto overlay_entry = id_map.overlay_res_maps_.Lookup(resid);
+ if (!overlay_entry) {
+ // No id map entry exists for this target resource.
continue;
}
- result->entry = best_frro_value;
+ if (overlay_entry.IsInlineValue()) {
+ // The target resource is overlaid by an inline value not represented by a resource.
+ ConfigDescription best_frro_config;
+ Res_value best_frro_value;
+ bool frro_found = false;
+ for( const auto& [config, value] : overlay_entry.GetInlineValue()) {
+ if ((!frro_found || config.isBetterThan(best_frro_config, desired_config))
+ && config.match(*desired_config)) {
+ frro_found = true;
+ best_frro_config = config;
+ best_frro_value = value;
+ }
+ }
+ if (!frro_found) {
+ continue;
+ }
+ result->entry = best_frro_value;
+ result->dynamic_ref_table = id_map.overlay_res_maps_.GetOverlayDynamicRefTable();
+ result->cookie = id_map.cookie;
+
+ if (UNLIKELY(logging_enabled)) {
+ last_resolution_.steps.push_back(Resolution::Step{
+ Resolution::Step::Type::OVERLAID_INLINE, result->cookie, String8()});
+ if (auto path = assets->GetPath()) {
+ const std::string overlay_path = path->data();
+ if (IsFabricatedOverlay(overlay_path)) {
+ // FRRO don't have package name so we use the creating package here.
+ String8 frro_name = String8("FRRO");
+ // Get the first part of it since the expected one should be like
+ // {overlayPackageName}-{overlayName}-{4 alphanumeric chars}.frro
+ // under /data/resource-cache/.
+ const std::string name = overlay_path.substr(overlay_path.rfind('/') + 1);
+ const size_t end = name.find('-');
+ if (frro_name.size() != overlay_path.size() && end != std::string::npos) {
+ frro_name.append(base::StringPrintf(" created by %s",
+ name.substr(0 /* pos */,
+ end).c_str()).c_str());
+ }
+ last_resolution_.best_package_name = frro_name;
+ } else {
+ last_resolution_.best_package_name = result->package_name->c_str();
+ }
+ }
+ overlaid = true;
+ }
+ continue;
+ }
+
+ auto overlay_result = FindEntry(overlay_entry.GetResourceId(), density_override,
+ false /* stop_at_first_match */,
+ false /* ignore_configuration */);
+ if (UNLIKELY(IsIOError(overlay_result))) {
+ return base::unexpected(overlay_result.error());
+ }
+ if (!overlay_result.has_value()) {
+ continue;
+ }
+
+ if (!overlay_result->config.isBetterThan(result->config, desired_config)
+ && overlay_result->config.compare(result->config) != 0) {
+ // The configuration of the entry for the overlay must be equal to or better than the
+ // target configuration to be chosen as the better value.
+ continue;
+ }
+
+ result->cookie = overlay_result->cookie;
+ result->entry = overlay_result->entry;
+ result->config = overlay_result->config;
result->dynamic_ref_table = id_map.overlay_res_maps_.GetOverlayDynamicRefTable();
- result->cookie = id_map.cookie;
if (UNLIKELY(logging_enabled)) {
last_resolution_.steps.push_back(
- Resolution::Step{Resolution::Step::Type::OVERLAID_INLINE, result->cookie, String8()});
- if (auto path = assets->GetPath()) {
- const std::string overlay_path = path->data();
- if (IsFabricatedOverlay(overlay_path)) {
- // FRRO don't have package name so we use the creating package here.
- String8 frro_name = String8("FRRO");
- // Get the first part of it since the expected one should be like
- // {overlayPackageName}-{overlayName}-{4 alphanumeric chars}.frro
- // under /data/resource-cache/.
- const std::string name = overlay_path.substr(overlay_path.rfind('/') + 1);
- const size_t end = name.find('-');
- if (frro_name.size() != overlay_path.size() && end != std::string::npos) {
- frro_name.append(base::StringPrintf(" created by %s",
- name.substr(0 /* pos */,
- end).c_str()).c_str());
- }
- last_resolution_.best_package_name = frro_name;
- } else {
- last_resolution_.best_package_name = result->package_name->c_str();
- }
- }
+ Resolution::Step{Resolution::Step::Type::OVERLAID, overlay_result->cookie,
+ overlay_result->config.toString()});
+ last_resolution_.best_package_name =
+ overlay_result->package_name->c_str();
overlaid = true;
}
- continue;
- }
-
- auto overlay_result = FindEntry(overlay_entry.GetResourceId(), density_override,
- false /* stop_at_first_match */,
- false /* ignore_configuration */);
- if (UNLIKELY(IsIOError(overlay_result))) {
- return base::unexpected(overlay_result.error());
- }
- if (!overlay_result.has_value()) {
- continue;
- }
-
- if (!overlay_result->config.isBetterThan(result->config, desired_config)
- && overlay_result->config.compare(result->config) != 0) {
- // The configuration of the entry for the overlay must be equal to or better than the target
- // configuration to be chosen as the better value.
- continue;
- }
-
- result->cookie = overlay_result->cookie;
- result->entry = overlay_result->entry;
- result->config = overlay_result->config;
- result->dynamic_ref_table = id_map.overlay_res_maps_.GetOverlayDynamicRefTable();
-
- if (UNLIKELY(logging_enabled)) {
- last_resolution_.steps.push_back(
- Resolution::Step{Resolution::Step::Type::OVERLAID, overlay_result->cookie,
- overlay_result->config.toString()});
- last_resolution_.best_package_name =
- overlay_result->package_name->c_str();
- overlaid = true;
}
}
}
+
+ bool has_locale = false;
+ if (result->config.locale == 0) {
+ if (default_locale_ != 0) {
+ ResTable_config conf;
+ conf.locale = default_locale_;
+ // Since we know conf has a locale and only a locale, match will tell us if that locale
+ // matches
+ has_locale = conf.match(config);
+ }
+ } else {
+ has_locale = true;
+ }
+
+ // if we don't have a result yet
+ if (!final_result ||
+ // or this config is better before the locale than the existing result
+ result->config.isBetterThanBeforeLocale(final_result->config, desired_config) ||
+ // or the existing config isn't better before locale and this one specifies a locale
+ // whereas the existing one doesn't
+ (!final_result->config.isBetterThanBeforeLocale(result->config, desired_config)
+ && has_locale && !final_has_locale)) {
+ final_result = result.value();
+ final_overlaid = overlaid;
+ final_has_locale = has_locale;
+ }
}
if (UNLIKELY(logging_enabled)) {
- last_resolution_.cookie = result->cookie;
- last_resolution_.type_string_ref = result->type_string_ref;
- last_resolution_.entry_string_ref = result->entry_string_ref;
- last_resolution_.best_config_name = result->config.toString();
- if (!overlaid) {
- last_resolution_.best_package_name = result->package_name->c_str();
+ last_resolution_.cookie = final_result->cookie;
+ last_resolution_.type_string_ref = final_result->type_string_ref;
+ last_resolution_.entry_string_ref = final_result->entry_string_ref;
+ last_resolution_.best_config_name = final_result->config.toString();
+ if (!final_overlaid) {
+ last_resolution_.best_package_name = final_result->package_name->c_str();
}
}
- return result;
+ return *final_result;
}
base::expected<FindEntryResult, NullOrIOError> AssetManager2::FindEntryInternal(
@@ -778,8 +821,10 @@
// If `desired_config` is not the same as the set configuration or the caller will accept a value
// from any configuration, then we cannot use our filtered list of types since it only it contains
// types matched to the set configuration.
- const bool use_filtered = !ignore_configuration && &desired_config == &configuration_;
-
+ const bool use_filtered = !ignore_configuration && std::find_if(
+ configurations_.begin(), configurations_.end(),
+ [&desired_config](auto& value) { return &desired_config == &value; })
+ != configurations_.end();
const size_t package_count = package_group.packages_.size();
for (size_t pi = 0; pi < package_count; pi++) {
const ConfiguredPackage& loaded_package_impl = package_group.packages_[pi];
@@ -934,10 +979,22 @@
}
std::stringstream log_stream;
- log_stream << base::StringPrintf("Resolution for 0x%08x %s\n"
- "\tFor config - %s", resid, resource_name_string.c_str(),
- configuration_.toString().c_str());
-
+ if (configurations_.size() == 1) {
+ log_stream << base::StringPrintf("Resolution for 0x%08x %s\n"
+ "\tFor config - %s", resid, resource_name_string.c_str(),
+ configurations_[0].toString().c_str());
+ } else {
+ ResTable_config conf = configurations_[0];
+ conf.clearLocale();
+ log_stream << base::StringPrintf("Resolution for 0x%08x %s\n\tFor config - %s and locales",
+ resid, resource_name_string.c_str(), conf.toString().c_str());
+ char str[40];
+ str[0] = '\0';
+ for(auto iter = configurations_.begin(); iter < configurations_.end(); iter++) {
+ iter->getBcp47Locale(str);
+ log_stream << base::StringPrintf(" %s%s", str, iter < configurations_.end() ? "," : "");
+ }
+ }
for (const Resolution::Step& step : last_resolution_.steps) {
constexpr static std::array kStepStrings = {
"Found initial",
@@ -956,13 +1013,13 @@
const auto& assets = GetApkAssets(step.cookie);
log_stream << "\n\t" << prefix << ": " << (assets ? assets->GetDebugName() : "<null>")
<< " #" << step.cookie;
- if (!step.config_name.isEmpty()) {
+ if (!step.config_name.empty()) {
log_stream << " - " << step.config_name;
}
}
log_stream << "\nBest matching is from "
- << (last_resolution_.best_config_name.isEmpty() ? "default"
+ << (last_resolution_.best_config_name.empty() ? "default"
: last_resolution_.best_config_name)
<< " configuration of " << last_resolution_.best_package_name;
return log_stream.str();
@@ -1427,11 +1484,14 @@
package.loaded_package_->ForEachTypeSpec([&](const TypeSpec& type_spec, uint8_t type_id) {
FilteredConfigGroup* group = nullptr;
for (const auto& type_entry : type_spec.type_entries) {
- if (type_entry.config.match(configuration_)) {
- if (!group) {
- group = &package.filtered_configs_.editItemAt(type_id - 1);
+ for (auto & config : configurations_) {
+ if (type_entry.config.match(config)) {
+ if (!group) {
+ group = &package.filtered_configs_.editItemAt(type_id - 1);
+ }
+ group->type_entries.push_back(&type_entry);
+ break;
}
- group->type_entries.push_back(&type_entry);
}
}
});
diff --git a/libs/androidfw/ResourceTypes.cpp b/libs/androidfw/ResourceTypes.cpp
index 112058f..ec14316 100644
--- a/libs/androidfw/ResourceTypes.cpp
+++ b/libs/androidfw/ResourceTypes.cpp
@@ -2568,6 +2568,22 @@
return false;
}
+bool ResTable_config::isBetterThanBeforeLocale(const ResTable_config& o,
+ const ResTable_config* requested) const {
+ if (requested) {
+ if (imsi || o.imsi) {
+ if ((mcc != o.mcc) && requested->mcc) {
+ return (mcc);
+ }
+
+ if ((mnc != o.mnc) && requested->mnc) {
+ return (mnc);
+ }
+ }
+ }
+ return false;
+}
+
bool ResTable_config::isBetterThan(const ResTable_config& o,
const ResTable_config* requested) const {
if (requested) {
diff --git a/libs/androidfw/include/androidfw/AssetManager2.h b/libs/androidfw/include/androidfw/AssetManager2.h
index f611d0d..d9ff35b 100644
--- a/libs/androidfw/include/androidfw/AssetManager2.h
+++ b/libs/androidfw/include/androidfw/AssetManager2.h
@@ -100,7 +100,7 @@
using ApkAssetsWPtr = wp<const ApkAssets>;
using ApkAssetsList = std::span<const ApkAssetsPtr>;
- AssetManager2() = default;
+ AssetManager2();
explicit AssetManager2(AssetManager2&& other) = default;
AssetManager2(ApkAssetsList apk_assets, const ResTable_config& configuration);
@@ -156,10 +156,14 @@
// Sets/resets the configuration for this AssetManager. This will cause all
// caches that are related to the configuration change to be invalidated.
- void SetConfiguration(const ResTable_config& configuration);
+ void SetConfigurations(std::vector<ResTable_config> configurations);
- inline const ResTable_config& GetConfiguration() const {
- return configuration_;
+ inline const std::vector<ResTable_config>& GetConfigurations() const {
+ return configurations_;
+ }
+
+ inline void SetDefaultLocale(uint32_t default_locale) {
+ default_locale_ = default_locale;
}
// Returns all configurations for which there are resources defined, or an I/O error if reading
@@ -465,9 +469,11 @@
// without taking too much memory.
std::array<uint8_t, std::numeric_limits<uint8_t>::max() + 1> package_ids_;
- // The current configuration set for this AssetManager. When this changes, cached resources
+ uint32_t default_locale_;
+
+ // The current configurations set for this AssetManager. When this changes, cached resources
// may need to be purged.
- ResTable_config configuration_ = {};
+ std::vector<ResTable_config> configurations_;
// Cached set of bags. These are cached because they can inherit keys from parent bags,
// which involves some calculation.
diff --git a/libs/androidfw/include/androidfw/ResourceTypes.h b/libs/androidfw/include/androidfw/ResourceTypes.h
index 6de1d1e..fdb3551 100644
--- a/libs/androidfw/include/androidfw/ResourceTypes.h
+++ b/libs/androidfw/include/androidfw/ResourceTypes.h
@@ -1375,6 +1375,8 @@
// match the requested configuration at all.
bool isLocaleBetterThan(const ResTable_config& o, const ResTable_config* requested) const;
+ bool isBetterThanBeforeLocale(const ResTable_config& o, const ResTable_config* requested) const;
+
String8 toString() const;
};
diff --git a/libs/androidfw/tests/AssetManager2_bench.cpp b/libs/androidfw/tests/AssetManager2_bench.cpp
index 6fae72a..2caa98c 100644
--- a/libs/androidfw/tests/AssetManager2_bench.cpp
+++ b/libs/androidfw/tests/AssetManager2_bench.cpp
@@ -228,10 +228,12 @@
ResTable_config config;
memset(&config, 0, sizeof(config));
+ std::vector<ResTable_config> configs;
+ configs.push_back(config);
while (state.KeepRunning()) {
- config.sdkVersion = ~config.sdkVersion;
- assets.SetConfiguration(config);
+ configs[0].sdkVersion = ~configs[0].sdkVersion;
+ assets.SetConfigurations(configs);
}
}
BENCHMARK(BM_AssetManagerSetConfigurationFramework);
diff --git a/libs/androidfw/tests/AssetManager2_test.cpp b/libs/androidfw/tests/AssetManager2_test.cpp
index df3fa02..c62f095 100644
--- a/libs/androidfw/tests/AssetManager2_test.cpp
+++ b/libs/androidfw/tests/AssetManager2_test.cpp
@@ -113,7 +113,7 @@
desired_config.language[1] = 'e';
AssetManager2 assetmanager;
- assetmanager.SetConfiguration(desired_config);
+ assetmanager.SetConfigurations({desired_config});
assetmanager.SetApkAssets({basic_assets_});
auto value = assetmanager.GetResource(basic::R::string::test1);
@@ -137,7 +137,7 @@
desired_config.language[1] = 'e';
AssetManager2 assetmanager;
- assetmanager.SetConfiguration(desired_config);
+ assetmanager.SetConfigurations({desired_config});
assetmanager.SetApkAssets({basic_assets_, basic_de_fr_assets_});
auto value = assetmanager.GetResource(basic::R::string::test1);
@@ -466,10 +466,10 @@
TEST_F(AssetManager2Test, DensityOverride) {
AssetManager2 assetmanager;
assetmanager.SetApkAssets({basic_assets_, basic_xhdpi_assets_, basic_xxhdpi_assets_});
- assetmanager.SetConfiguration({
+ assetmanager.SetConfigurations({{
.density = ResTable_config::DENSITY_XHIGH,
.sdkVersion = 21,
- });
+ }});
auto value = assetmanager.GetResource(basic::R::string::density, false /*may_be_bag*/);
ASSERT_TRUE(value.has_value());
@@ -721,7 +721,7 @@
ResTable_config desired_config;
AssetManager2 assetmanager;
- assetmanager.SetConfiguration(desired_config);
+ assetmanager.SetConfigurations({desired_config});
assetmanager.SetApkAssets({basic_assets_});
assetmanager.SetResourceResolutionLoggingEnabled(false);
@@ -736,7 +736,7 @@
ResTable_config desired_config;
AssetManager2 assetmanager;
- assetmanager.SetConfiguration(desired_config);
+ assetmanager.SetConfigurations({desired_config});
assetmanager.SetApkAssets({basic_assets_});
auto result = assetmanager.GetLastResourceResolution();
@@ -751,7 +751,7 @@
AssetManager2 assetmanager;
assetmanager.SetResourceResolutionLoggingEnabled(true);
- assetmanager.SetConfiguration(desired_config);
+ assetmanager.SetConfigurations({desired_config});
assetmanager.SetApkAssets({basic_assets_});
auto value = assetmanager.GetResource(basic::R::string::test1);
@@ -774,7 +774,7 @@
AssetManager2 assetmanager;
assetmanager.SetResourceResolutionLoggingEnabled(true);
- assetmanager.SetConfiguration(desired_config);
+ assetmanager.SetConfigurations({desired_config});
assetmanager.SetApkAssets({basic_assets_, basic_de_fr_assets_});
auto value = assetmanager.GetResource(basic::R::string::test1);
@@ -796,7 +796,7 @@
AssetManager2 assetmanager;
assetmanager.SetResourceResolutionLoggingEnabled(true);
- assetmanager.SetConfiguration(desired_config);
+ assetmanager.SetConfigurations({desired_config});
assetmanager.SetApkAssets({basic_assets_});
auto value = assetmanager.GetResource(basic::R::string::test1);
@@ -817,7 +817,7 @@
AssetManager2 assetmanager;
assetmanager.SetResourceResolutionLoggingEnabled(true);
- assetmanager.SetConfiguration(desired_config);
+ assetmanager.SetConfigurations({desired_config});
assetmanager.SetApkAssets({overlayable_assets_});
const auto map = assetmanager.GetOverlayableMapForPackage(0x7f);
diff --git a/libs/androidfw/tests/BenchmarkHelpers.cpp b/libs/androidfw/tests/BenchmarkHelpers.cpp
index b97dd96..8b883f4 100644
--- a/libs/androidfw/tests/BenchmarkHelpers.cpp
+++ b/libs/androidfw/tests/BenchmarkHelpers.cpp
@@ -66,7 +66,7 @@
AssetManager2 assetmanager;
assetmanager.SetApkAssets(apk_assets);
if (config != nullptr) {
- assetmanager.SetConfiguration(*config);
+ assetmanager.SetConfigurations({*config});
}
while (state.KeepRunning()) {
diff --git a/libs/androidfw/tests/Theme_test.cpp b/libs/androidfw/tests/Theme_test.cpp
index e08a6a7..181d141 100644
--- a/libs/androidfw/tests/Theme_test.cpp
+++ b/libs/androidfw/tests/Theme_test.cpp
@@ -260,7 +260,7 @@
ResTable_config night{};
night.uiMode = ResTable_config::UI_MODE_NIGHT_YES;
night.version = 8u;
- am_night.SetConfiguration(night);
+ am_night.SetConfigurations({night});
auto theme = am.NewTheme();
{
diff --git a/libs/hwui/jni/Paint.cpp b/libs/hwui/jni/Paint.cpp
index a17f2f7..7aef7a5 100644
--- a/libs/hwui/jni/Paint.cpp
+++ b/libs/hwui/jni/Paint.cpp
@@ -203,10 +203,9 @@
if (advances) {
advancesArray.reset(new jfloat[count]);
}
- minikin::MinikinRect bounds;
const float advance = MinikinUtils::measureText(
paint, static_cast<minikin::Bidi>(bidiFlags), typeface, text, start, count,
- contextCount, advancesArray.get(), &bounds);
+ contextCount, advancesArray.get(), nullptr);
if (advances) {
env->SetFloatArrayRegion(advances, advancesIndex, count, advancesArray.get());
}
diff --git a/libs/hwui/renderthread/CanvasContext.cpp b/libs/hwui/renderthread/CanvasContext.cpp
index 4064bb9..d54c5f5 100644
--- a/libs/hwui/renderthread/CanvasContext.cpp
+++ b/libs/hwui/renderthread/CanvasContext.cpp
@@ -195,6 +195,7 @@
void CanvasContext::setSurface(ANativeWindow* window, bool enableTimeout) {
ATRACE_CALL();
+ startHintSession();
if (window) {
mNativeSurface = std::make_unique<ReliableSurface>(window);
mNativeSurface->init();
diff --git a/libs/hwui/renderthread/HintSessionWrapper.cpp b/libs/hwui/renderthread/HintSessionWrapper.cpp
index 814ac4d..1f338ee 100644
--- a/libs/hwui/renderthread/HintSessionWrapper.cpp
+++ b/libs/hwui/renderthread/HintSessionWrapper.cpp
@@ -93,8 +93,17 @@
: mUiThreadId(uiThreadId), mRenderThreadId(renderThreadId) {}
HintSessionWrapper::~HintSessionWrapper() {
+ destroy();
+}
+
+void HintSessionWrapper::destroy() {
+ if (mHintSessionFuture.valid()) {
+ mHintSession = mHintSessionFuture.get();
+ }
if (mHintSession) {
gAPH_closeSessionFn(mHintSession);
+ mSessionValid = true;
+ mHintSession = nullptr;
}
}
diff --git a/libs/hwui/renderthread/HintSessionWrapper.h b/libs/hwui/renderthread/HintSessionWrapper.h
index 24b8150..bdb9959 100644
--- a/libs/hwui/renderthread/HintSessionWrapper.h
+++ b/libs/hwui/renderthread/HintSessionWrapper.h
@@ -37,6 +37,7 @@
void sendLoadResetHint();
void sendLoadIncreaseHint();
bool init();
+ void destroy();
private:
APerformanceHintSession* mHintSession = nullptr;
diff --git a/libs/hwui/renderthread/VulkanManager.cpp b/libs/hwui/renderthread/VulkanManager.cpp
index a6e8c08f..e2b541a 100644
--- a/libs/hwui/renderthread/VulkanManager.cpp
+++ b/libs/hwui/renderthread/VulkanManager.cpp
@@ -384,25 +384,23 @@
}
void VulkanManager::initialize() {
- std::lock_guard _lock{mInitializeLock};
+ std::call_once(mInitFlag, [&] {
+ GET_PROC(EnumerateInstanceVersion);
+ uint32_t instanceVersion;
+ LOG_ALWAYS_FATAL_IF(mEnumerateInstanceVersion(&instanceVersion));
+ LOG_ALWAYS_FATAL_IF(instanceVersion < VK_MAKE_VERSION(1, 1, 0));
- if (mDevice != VK_NULL_HANDLE) {
- return;
- }
+ this->setupDevice(mExtensions, mPhysicalDeviceFeatures2);
- GET_PROC(EnumerateInstanceVersion);
- uint32_t instanceVersion;
- LOG_ALWAYS_FATAL_IF(mEnumerateInstanceVersion(&instanceVersion));
- LOG_ALWAYS_FATAL_IF(instanceVersion < VK_MAKE_VERSION(1, 1, 0));
+ mGetDeviceQueue(mDevice, mGraphicsQueueIndex, 0, &mGraphicsQueue);
+ mGetDeviceQueue(mDevice, mGraphicsQueueIndex, 1, &mAHBUploadQueue);
- this->setupDevice(mExtensions, mPhysicalDeviceFeatures2);
+ if (Properties::enablePartialUpdates && Properties::useBufferAge) {
+ mSwapBehavior = SwapBehavior::BufferAge;
+ }
- mGetDeviceQueue(mDevice, mGraphicsQueueIndex, 0, &mGraphicsQueue);
- mGetDeviceQueue(mDevice, mGraphicsQueueIndex, 1, &mAHBUploadQueue);
-
- if (Properties::enablePartialUpdates && Properties::useBufferAge) {
- mSwapBehavior = SwapBehavior::BufferAge;
- }
+ mInitialized = true;
+ });
}
static void onGrContextReleased(void* context) {
diff --git a/libs/hwui/renderthread/VulkanManager.h b/libs/hwui/renderthread/VulkanManager.h
index 2be1ffd..dbef7fb 100644
--- a/libs/hwui/renderthread/VulkanManager.h
+++ b/libs/hwui/renderthread/VulkanManager.h
@@ -70,7 +70,7 @@
void initialize();
// Quick check to see if the VulkanManager has been initialized.
- bool hasVkContext() { return mDevice != VK_NULL_HANDLE; }
+ bool hasVkContext() { return mInitialized; }
// Create and destroy functions for wrapping an ANativeWindow in a VulkanSurface
VulkanSurface* createSurface(ANativeWindow* window,
@@ -204,7 +204,8 @@
VkSemaphore mSwapSemaphore = VK_NULL_HANDLE;
void* mDestroySemaphoreContext = nullptr;
- std::mutex mInitializeLock;
+ std::once_flag mInitFlag;
+ std::atomic_bool mInitialized = false;
};
} /* namespace renderthread */
diff --git a/libs/incident/src/IncidentReportArgs.cpp b/libs/incident/src/IncidentReportArgs.cpp
index d344a981..858813f 100644
--- a/libs/incident/src/IncidentReportArgs.cpp
+++ b/libs/incident/src/IncidentReportArgs.cpp
@@ -152,8 +152,8 @@
}
mPrivacyPolicy = privacyPolicy;
- mReceiverPkg = String8(in->readString16()).string();
- mReceiverCls = String8(in->readString16()).string();
+ mReceiverPkg = String8(in->readString16()).c_str();
+ mReceiverCls = String8(in->readString16()).c_str();
int32_t gzip;
err = in->readInt32(&gzip);
diff --git a/libs/input/PointerController.cpp b/libs/input/PointerController.cpp
index 435452c..c41cd04 100644
--- a/libs/input/PointerController.cpp
+++ b/libs/input/PointerController.cpp
@@ -63,10 +63,10 @@
std::shared_ptr<PointerController> PointerController::create(
const sp<PointerControllerPolicyInterface>& policy, const sp<Looper>& looper,
- SpriteController& spriteController) {
+ SpriteController& spriteController, bool enabled) {
// using 'new' to access non-public constructor
std::shared_ptr<PointerController> controller = std::shared_ptr<PointerController>(
- new PointerController(policy, looper, spriteController));
+ new PointerController(policy, looper, spriteController, enabled));
/*
* Now we need to hook up the constructed PointerController object to its callbacks.
@@ -85,9 +85,10 @@
}
PointerController::PointerController(const sp<PointerControllerPolicyInterface>& policy,
- const sp<Looper>& looper, SpriteController& spriteController)
+ const sp<Looper>& looper, SpriteController& spriteController,
+ bool enabled)
: PointerController(
- policy, looper, spriteController,
+ policy, looper, spriteController, enabled,
[](const sp<android::gui::WindowInfosListener>& listener) {
SurfaceComposerClient::getDefault()->addWindowInfosListener(listener);
},
@@ -97,9 +98,10 @@
PointerController::PointerController(const sp<PointerControllerPolicyInterface>& policy,
const sp<Looper>& looper, SpriteController& spriteController,
- WindowListenerConsumer registerListener,
+ bool enabled, WindowListenerConsumer registerListener,
WindowListenerConsumer unregisterListener)
- : mContext(policy, looper, spriteController, *this),
+ : mEnabled(enabled),
+ mContext(policy, looper, spriteController, *this),
mCursorController(mContext),
mDisplayInfoListener(sp<DisplayInfoListener>::make(this)),
mUnregisterWindowInfosListener(std::move(unregisterListener)) {
@@ -119,10 +121,14 @@
}
std::optional<FloatRect> PointerController::getBounds() const {
+ if (!mEnabled) return {};
+
return mCursorController.getBounds();
}
void PointerController::move(float deltaX, float deltaY) {
+ if (!mEnabled) return;
+
const int32_t displayId = mCursorController.getDisplayId();
vec2 transformed;
{
@@ -134,6 +140,8 @@
}
void PointerController::setPosition(float x, float y) {
+ if (!mEnabled) return;
+
const int32_t displayId = mCursorController.getDisplayId();
vec2 transformed;
{
@@ -145,6 +153,11 @@
}
FloatPoint PointerController::getPosition() const {
+ if (!mEnabled) {
+ return FloatPoint{AMOTION_EVENT_INVALID_CURSOR_POSITION,
+ AMOTION_EVENT_INVALID_CURSOR_POSITION};
+ }
+
const int32_t displayId = mCursorController.getDisplayId();
const auto p = mCursorController.getPosition();
{
@@ -155,20 +168,28 @@
}
int32_t PointerController::getDisplayId() const {
+ if (!mEnabled) return ADISPLAY_ID_NONE;
+
return mCursorController.getDisplayId();
}
void PointerController::fade(Transition transition) {
+ if (!mEnabled) return;
+
std::scoped_lock lock(getLock());
mCursorController.fade(transition);
}
void PointerController::unfade(Transition transition) {
+ if (!mEnabled) return;
+
std::scoped_lock lock(getLock());
mCursorController.unfade(transition);
}
void PointerController::setPresentation(Presentation presentation) {
+ if (!mEnabled) return;
+
std::scoped_lock lock(getLock());
if (mLocked.presentation == presentation) {
@@ -193,6 +214,8 @@
void PointerController::setSpots(const PointerCoords* spotCoords, const uint32_t* spotIdToIndex,
BitSet32 spotIdBits, int32_t displayId) {
+ if (!mEnabled) return;
+
std::scoped_lock lock(getLock());
std::array<PointerCoords, MAX_POINTERS> outSpotCoords{};
const ui::Transform& transform = getTransformForDisplayLocked(displayId);
@@ -216,6 +239,8 @@
}
void PointerController::clearSpots() {
+ if (!mEnabled) return;
+
std::scoped_lock lock(getLock());
clearSpotsLocked();
}
@@ -277,11 +302,15 @@
}
void PointerController::updatePointerIcon(PointerIconStyle iconId) {
+ if (!mEnabled) return;
+
std::scoped_lock lock(getLock());
mCursorController.updatePointerIcon(iconId);
}
void PointerController::setCustomPointerIcon(const SpriteIcon& icon) {
+ if (!mEnabled) return;
+
std::scoped_lock lock(getLock());
mCursorController.setCustomPointerIcon(icon);
}
@@ -290,7 +319,7 @@
fade(Transition::GRADUAL);
}
-void PointerController::onDisplayViewportsUpdated(std::vector<DisplayViewport>& viewports) {
+void PointerController::onDisplayViewportsUpdated(const std::vector<DisplayViewport>& viewports) {
std::unordered_set<int32_t> displayIdSet;
for (const DisplayViewport& viewport : viewports) {
displayIdSet.insert(viewport.displayId);
@@ -326,6 +355,11 @@
}
void PointerController::dump(std::string& dump) {
+ if (!mEnabled) {
+ dump += INDENT "PointerController: DISABLED due to ongoing PointerChoreographer refactor\n";
+ return;
+ }
+
dump += INDENT "PointerController:\n";
std::scoped_lock lock(getLock());
dump += StringPrintf(INDENT2 "Presentation: %s\n",
diff --git a/libs/input/PointerController.h b/libs/input/PointerController.h
index c7e772d..de39eda 100644
--- a/libs/input/PointerController.h
+++ b/libs/input/PointerController.h
@@ -47,7 +47,7 @@
public:
static std::shared_ptr<PointerController> create(
const sp<PointerControllerPolicyInterface>& policy, const sp<Looper>& looper,
- SpriteController& spriteController);
+ SpriteController& spriteController, bool enabled);
~PointerController() override;
@@ -70,7 +70,7 @@
void setInactivityTimeout(InactivityTimeout inactivityTimeout);
void doInactivityTimeout();
void reloadPointerResources();
- void onDisplayViewportsUpdated(std::vector<DisplayViewport>& viewports);
+ void onDisplayViewportsUpdated(const std::vector<DisplayViewport>& viewports);
void onDisplayInfosChangedLocked(const std::vector<gui::DisplayInfo>& displayInfos)
REQUIRES(getLock());
@@ -83,12 +83,13 @@
// Constructor used to test WindowInfosListener registration.
PointerController(const sp<PointerControllerPolicyInterface>& policy, const sp<Looper>& looper,
- SpriteController& spriteController, WindowListenerConsumer registerListener,
+ SpriteController& spriteController, bool enabled,
+ WindowListenerConsumer registerListener,
WindowListenerConsumer unregisterListener);
private:
PointerController(const sp<PointerControllerPolicyInterface>& policy, const sp<Looper>& looper,
- SpriteController& spriteController);
+ SpriteController& spriteController, bool enabled);
friend PointerControllerContext::LooperCallback;
friend PointerControllerContext::MessageHandler;
@@ -99,6 +100,8 @@
// we use the DisplayInfoListener's lock in PointerController.
std::mutex& getLock() const;
+ const bool mEnabled;
+
PointerControllerContext mContext;
MouseCursorController mCursorController;
diff --git a/libs/input/tests/PointerController_test.cpp b/libs/input/tests/PointerController_test.cpp
index 3e2e43f..94faf4a 100644
--- a/libs/input/tests/PointerController_test.cpp
+++ b/libs/input/tests/PointerController_test.cpp
@@ -181,7 +181,8 @@
EXPECT_CALL(*mSpriteController, createSprite())
.WillOnce(Return(mPointerSprite));
- mPointerController = PointerController::create(mPolicy, mLooper, *mSpriteController);
+ mPointerController =
+ PointerController::create(mPolicy, mLooper, *mSpriteController, /*enabled=*/true);
}
PointerControllerTest::~PointerControllerTest() {
@@ -321,6 +322,7 @@
const sp<Looper>& looper, SpriteController& spriteController)
: PointerController(
new MockPointerControllerPolicyInterface(), looper, spriteController,
+ /*enabled=*/true,
[®isteredListener](const sp<android::gui::WindowInfosListener>& listener) {
// Register listener
registeredListener = listener;
diff --git a/libs/services/src/os/DropBoxManager.cpp b/libs/services/src/os/DropBoxManager.cpp
index 3716e01..60bb00a 100644
--- a/libs/services/src/os/DropBoxManager.cpp
+++ b/libs/services/src/os/DropBoxManager.cpp
@@ -196,7 +196,7 @@
vector<uint8_t> dataArg;
dataArg.assign(data, data + size);
Status status = service->addData(tag, dataArg, flags);
- ALOGD("service->add returned %s", status.toString8().string());
+ ALOGD("service->add returned %s", status.toString8().c_str());
return status;
}
@@ -230,7 +230,7 @@
android::base::unique_fd uniqueFd(fd);
android::os::ParcelFileDescriptor parcelFd(std::move(uniqueFd));
Status status = service->addFile(tag, parcelFd, flags);
- ALOGD("service->add returned %s", status.toString8().string());
+ ALOGD("service->add returned %s", status.toString8().c_str());
return status;
}
diff --git a/location/java/android/location/GnssMeasurement.java b/location/java/android/location/GnssMeasurement.java
index b0769ab..e28ad67 100644
--- a/location/java/android/location/GnssMeasurement.java
+++ b/location/java/android/location/GnssMeasurement.java
@@ -1525,50 +1525,61 @@
/**
* Gets the GNSS measurement's code type.
*
- * <p>Similar to the Attribute field described in RINEX 3.03, e.g., in Tables 4-10, and Table
- * A2 at the RINEX 3.03 Update 1 Document.
+ * <p>Similar to the Attribute field described in RINEX 4.00, e.g., in Tables 9-16 (see
+ * https://igs.org/wg/rinex/#documents-formats).
*
- * <p>Returns "A" for GALILEO E1A, GALILEO E6A, IRNSS L5A, IRNSS SA.
+ * <p>Returns "A" for GALILEO E1A, GALILEO E6A, IRNSS L5A SPS, IRNSS SA SPS, GLONASS G1a L1OCd,
+ * GLONASS G2a L2CSI.
*
- * <p>Returns "B" for GALILEO E1B, GALILEO E6B, IRNSS L5B, IRNSS SB.
+ * <p>Returns "B" for GALILEO E1B, GALILEO E6B, IRNSS L5B RS (D), IRNSS SB RS (D), GLONASS G1a
+ * L1OCp, GLONASS G2a L2OCp, QZSS L1Sb.
*
- * <p>Returns "C" for GPS L1 C/A, GPS L2 C/A, GLONASS G1 C/A, GLONASS G2 C/A, GALILEO E1C,
- * GALILEO E6C, SBAS L1 C/A, QZSS L1 C/A, IRNSS L5C.
+ * <p>Returns "C" for GPS L1 C/A, GPS L2 C/A, GLONASS G1 C/A, GLONASS G2 C/A, GALILEO E1C,
+ * GALILEO E6C, SBAS L1 C/A, QZSS L1 C/A, IRNSS L5C RS (P), IRNSS SC RS (P).
*
- * <p>Returns "D" for BDS B1C D.
+ * <p>Returns "D" for GPS L2 (L1(C/A) + (P2-P1) (semi-codeless)), QZSS L5S(I), BDS B1C Data,
+ * BDS B2a Data, BDS B2b Data, BDS B2 (B2a+B2b) Data, BDS B3a Data.
+ *
+ * <p>Returns “E” for QZSS L1 C/B, QZSS L6E.
*
* <p>Returns "I" for GPS L5 I, GLONASS G3 I, GALILEO E5a I, GALILEO E5b I, GALILEO E5a+b I,
* SBAS L5 I, QZSS L5 I, BDS B1 I, BDS B2 I, BDS B3 I.
*
- * <p>Returns "L" for GPS L1C (P), GPS L2C (L), QZSS L1C (P), QZSS L2C (L), LEX(6) L.
+ * <p>Returns "L" for GPS L1C (P), GPS L2C (L), QZSS L1C (P), QZSS L2C (L), QZSS L6P, BDS
+ * B1a Pilot.
*
* <p>Returns "M" for GPS L1M, GPS L2M.
*
* <p>Returns "N" for GPS L1 codeless, GPS L2 codeless.
*
- * <p>Returns "P" for GPS L1P, GPS L2P, GLONASS G1P, GLONASS G2P, BDS B1C P.
+ * <p>Returns "P" for GPS L1P, GPS L2P, GLONASS G1P, GLONASS G2P, BDS B1C Pilot, BDS B2a Pilot,
+ * BDS B2b Pilot, BDS B2 (B2a+B2b) Pilot, BDS B3a Pilot, QZSS L5S(Q).
*
* <p>Returns "Q" for GPS L5 Q, GLONASS G3 Q, GALILEO E5a Q, GALILEO E5b Q, GALILEO E5a+b Q,
* SBAS L5 Q, QZSS L5 Q, BDS B1 Q, BDS B2 Q, BDS B3 Q.
*
- * <p>Returns "S" for GPS L1C (D), GPS L2C (M), QZSS L1C (D), QZSS L2C (M), LEX(6) S.
+ * <p>Returns "S" for GPS L1C (D), GPS L2C (M), QZSS L1C (D), QZSS L2C (M), QZSS L6D, BDS B1a
+ * Data.
*
* <p>Returns "W" for GPS L1 Z-tracking, GPS L2 Z-tracking.
*
- * <p>Returns "X" for GPS L1C (D+P), GPS L2C (M+L), GPS L5 (I+Q), GLONASS G3 (I+Q), GALILEO
- * E1 (B+C), GALILEO E5a (I+Q), GALILEO E5b (I+Q), GALILEO E5a+b(I+Q), GALILEO E6 (B+C), SBAS
- * L5 (I+Q), QZSS L1C (D+P), QZSS L2C (M+L), QZSS L5 (I+Q), LEX(6) (S+L), BDS B1 (I+Q), BDS
- * B1C (D+P), BDS B2 (I+Q), BDS B3 (I+Q), IRNSS L5 (B+C).
+ * <p>Returns "X" for GPS L1C (D+P), GPS L2C (M+L), GPS L5 (I+Q), GLONASS G1a L1OCd+L1OCp,
+ * GLONASS G2a L2CSI+L2OCp, GLONASS G3 (I+Q), GALILEO E1 (B+C), GALILEO E5a (I+Q), GALILEO
+ * E5b (I+Q), GALILEO E5a+b (I+Q), GALILEO E6 (B+C), SBAS L5 (I+Q), QZSS L1C (D+P), QZSS L2C
+ * (M+L), QZSS L5 (I+Q), QZSS L6 (D+P), BDS B1 (I+Q), BDS B1C Data+Pilot, BDS B2a Data+Pilot,
+ * BDS B2 (I+Q), BDS B2 (B2a+B2b) Data+Pilot, BDS B3 (I+Q), IRNSS L5 (B+C), IRNSS S (B+C).
*
* <p>Returns "Y" for GPS L1Y, GPS L2Y.
*
- * <p>Returns "Z" for GALILEO E1 (A+B+C), GALILEO E6 (A+B+C), QZSS L1-SAIF.
+ * <p>Returns "Z" for GALILEO E1 (A+B+C), GALILEO E6 (A+B+C), QZSS L1S/L1-SAIF, QZSS L5S (I+Q),
+ * QZSS L6(D+E), BDS B1A Data+Pilot, BDS B2b Data+Pilot, BDS B3a Data+Pilot.
*
* <p>Returns "UNKNOWN" if the GNSS Measurement's code type is unknown.
*
- * <p>This is used to specify the observation descriptor defined in GNSS Observation Data File
- * Header Section Description in the RINEX standard (Version 3.XX), in cases where the code type
- * does not align with the above listed values. For example, if a code type "G" is added, this
+ * <p>The code type is used to specify the observation descriptor defined in GNSS Observation
+ * Data File Header Section Description in the RINEX standard (Version 4.00). In cases where
+ * the code type does not align with the above listed values, the code type from the most
+ * recent version of RINEX should be used. For example, if a code type "G" is added, this
* string shall be set to "G".
*/
@NonNull
diff --git a/media/java/android/media/RingtoneManager.java b/media/java/android/media/RingtoneManager.java
index 9234479..c4f2159 100644
--- a/media/java/android/media/RingtoneManager.java
+++ b/media/java/android/media/RingtoneManager.java
@@ -38,7 +38,6 @@
import android.net.Uri;
import android.os.Environment;
import android.os.FileUtils;
-import android.os.ParcelFileDescriptor;
import android.os.SystemProperties;
import android.os.UserHandle;
import android.os.UserManager;
@@ -993,18 +992,6 @@
Settings.System.putStringForUser(resolver, setting,
ringtoneUri != null ? ringtoneUri.toString() : null, context.getUserId());
-
- // Stream selected ringtone into cache so it's available for playback
- // when CE storage is still locked
- if (ringtoneUri != null) {
- final Uri cacheUri = getCacheForType(type, context.getUserId());
- try (InputStream in = openRingtone(context, ringtoneUri);
- OutputStream out = resolver.openOutputStream(cacheUri, "wt")) {
- FileUtils.copy(in, out);
- } catch (IOException e) {
- Log.w(TAG, "Failed to cache ringtone: " + e);
- }
- }
}
private static boolean isInternalRingtoneUri(Uri uri) {
@@ -1100,28 +1087,6 @@
}
}
- /**
- * Try opening the given ringtone locally first, but failover to
- * {@link IRingtonePlayer} if we can't access it directly. Typically happens
- * when process doesn't hold
- * {@link android.Manifest.permission#READ_EXTERNAL_STORAGE}.
- */
- private static InputStream openRingtone(Context context, Uri uri) throws IOException {
- final ContentResolver resolver = context.getContentResolver();
- try {
- return resolver.openInputStream(uri);
- } catch (SecurityException | IOException e) {
- Log.w(TAG, "Failed to open directly; attempting failover: " + e);
- final IRingtonePlayer player = context.getSystemService(AudioManager.class)
- .getRingtonePlayer();
- try {
- return new ParcelFileDescriptor.AutoCloseInputStream(player.openRingtone(uri));
- } catch (Exception e2) {
- throw new IOException(e2);
- }
- }
- }
-
private static String getSettingForType(int type) {
if ((type & TYPE_RINGTONE) != 0) {
return Settings.System.RINGTONE;
diff --git a/media/jni/android_media_ImageReader.cpp b/media/jni/android_media_ImageReader.cpp
index da2e56f..382e65d 100644
--- a/media/jni/android_media_ImageReader.cpp
+++ b/media/jni/android_media_ImageReader.cpp
@@ -617,7 +617,7 @@
"match the ImageReader's configured buffer format 0x%x.",
bufferFormat, ctx->getBufferFormat());
jniThrowException(env, "java/lang/UnsupportedOperationException",
- msg.string());
+ msg.c_str());
return -1;
}
}
@@ -795,7 +795,7 @@
String8 msg;
msg.appendFormat("Format 0x%x is opaque, thus not writable, the number of planes (%d)"
" must be 0", halReaderFormat, numPlanes);
- jniThrowException(env, "java/lang/IllegalArgumentException", msg.string());
+ jniThrowException(env, "java/lang/IllegalArgumentException", msg.c_str());
return NULL;
}
@@ -860,7 +860,7 @@
String8 msg;
msg.appendFormat("Format 0x%x is opaque, thus not writable, the number of planes (%d)"
" must be 0", halReaderFormat, numPlanes);
- jniThrowException(env, "java/lang/IllegalArgumentException", msg.string());
+ jniThrowException(env, "java/lang/IllegalArgumentException", msg.c_str());
return NULL;
}
diff --git a/media/jni/android_media_ImageWriter.cpp b/media/jni/android_media_ImageWriter.cpp
index 1927b6c..f64233f 100644
--- a/media/jni/android_media_ImageWriter.cpp
+++ b/media/jni/android_media_ImageWriter.cpp
@@ -1068,7 +1068,7 @@
String8 msg;
msg.appendFormat("Format 0x%x is opaque, thus not writable, the number of planes (%d)"
" must be 0", writerFormat, numPlanes);
- jniThrowException(env, "java/lang/IllegalArgumentException", msg.string());
+ jniThrowException(env, "java/lang/IllegalArgumentException", msg.c_str());
return NULL;
}
diff --git a/media/jni/android_media_MediaCrypto.cpp b/media/jni/android_media_MediaCrypto.cpp
index f491be8..5506f61 100644
--- a/media/jni/android_media_MediaCrypto.cpp
+++ b/media/jni/android_media_MediaCrypto.cpp
@@ -299,7 +299,7 @@
std::string strerr(StrCryptoError(err));
msg.appendFormat(": general failure (%s)", strerr.c_str());
}
- jniThrowException(env, "android/media/MediaCryptoException", msg.string());
+ jniThrowException(env, "android/media/MediaCryptoException", msg.c_str());
}
}
diff --git a/media/jni/android_media_MediaDrm.cpp b/media/jni/android_media_MediaDrm.cpp
index b70818d..c616b84f 100644
--- a/media/jni/android_media_MediaDrm.cpp
+++ b/media/jni/android_media_MediaDrm.cpp
@@ -708,8 +708,8 @@
jclass clazz = gFields.hashmapClassId;
jobject hashMap = env->NewObject(clazz, gFields.hashmap.init);
for (size_t i = 0; i < map.size(); ++i) {
- jstring jkey = env->NewStringUTF(map.keyAt(i).string());
- jstring jvalue = env->NewStringUTF(map.valueAt(i).string());
+ jstring jkey = env->NewStringUTF(map.keyAt(i).c_str());
+ jstring jvalue = env->NewStringUTF(map.valueAt(i).c_str());
env->CallObjectMethod(hashMap, gFields.hashmap.put, jkey, jvalue);
env->DeleteLocalRef(jkey);
env->DeleteLocalRef(jvalue);
@@ -1169,7 +1169,7 @@
jbyteArray jrequest = VectorToJByteArray(env, request);
env->SetObjectField(keyObj, gFields.keyRequest.data, jrequest);
- jstring jdefaultUrl = env->NewStringUTF(defaultUrl.string());
+ jstring jdefaultUrl = env->NewStringUTF(defaultUrl.c_str());
env->SetObjectField(keyObj, gFields.keyRequest.defaultUrl, jdefaultUrl);
switch (keyRequestType) {
@@ -1332,7 +1332,7 @@
jbyteArray jrequest = VectorToJByteArray(env, request);
env->SetObjectField(provisionObj, gFields.provisionRequest.data, jrequest);
- jstring jdefaultUrl = env->NewStringUTF(defaultUrl.string());
+ jstring jdefaultUrl = env->NewStringUTF(defaultUrl.c_str());
env->SetObjectField(provisionObj, gFields.provisionRequest.defaultUrl, jdefaultUrl);
}
@@ -1686,7 +1686,7 @@
return NULL;
}
- return env->NewStringUTF(value.string());
+ return env->NewStringUTF(value.c_str());
}
static jbyteArray android_media_MediaDrm_getPropertyByteArray(
diff --git a/media/jni/android_media_MediaMetadataRetriever.cpp b/media/jni/android_media_MediaMetadataRetriever.cpp
index ddc51cd..1458758 100644
--- a/media/jni/android_media_MediaMetadataRetriever.cpp
+++ b/media/jni/android_media_MediaMetadataRetriever.cpp
@@ -126,7 +126,7 @@
tmp = NULL;
// Don't let somebody trick us in to reading some random block of memory
- if (strncmp("mem://", pathStr.string(), 6) == 0) {
+ if (strncmp("mem://", pathStr.c_str(), 6) == 0) {
jniThrowException(
env, "java/lang/IllegalArgumentException", "Invalid pathname");
return;
@@ -149,7 +149,7 @@
env,
retriever->setDataSource(
httpService,
- pathStr.string(),
+ pathStr.c_str(),
headersVector.size() > 0 ? &headersVector : NULL),
"java/lang/RuntimeException",
diff --git a/media/jni/android_media_MediaPlayer.cpp b/media/jni/android_media_MediaPlayer.cpp
index 44aff64..3551ea4 100644
--- a/media/jni/android_media_MediaPlayer.cpp
+++ b/media/jni/android_media_MediaPlayer.cpp
@@ -1234,7 +1234,7 @@
String8 vendorMessage;
if (err >= ERROR_DRM_VENDOR_MIN && err <= ERROR_DRM_VENDOR_MAX) {
vendorMessage = String8::format("DRM vendor-defined error: %d", err);
- drmMessage = vendorMessage.string();
+ drmMessage = vendorMessage.c_str();
}
if (err == BAD_VALUE) {
@@ -1260,7 +1260,7 @@
msg = drmMessage;
} else {
errbuf = String8::format("%s: %s", msg, drmMessage);
- msg = errbuf.string();
+ msg = errbuf.c_str();
}
}
throwDrmStateException(env, msg, err);
diff --git a/media/jni/android_media_Streams.cpp b/media/jni/android_media_Streams.cpp
index 4fd5153..dffeb89 100644
--- a/media/jni/android_media_Streams.cpp
+++ b/media/jni/android_media_Streams.cpp
@@ -38,7 +38,7 @@
FileStream::FileStream(const String8 filename)
: mPosition(0) {
- mFile = fopen(filename.string(), "r");
+ mFile = fopen(filename.c_str(), "r");
if (mFile == NULL) {
return;
}
@@ -86,7 +86,7 @@
if (!piex::IsRaw(stream)) {
// Format not supported.
- ALOGV("Format not supported: %s", filename.string());
+ ALOGV("Format not supported: %s", filename.c_str());
return false;
}
@@ -94,7 +94,7 @@
if (err != piex::Error::kOk) {
// The input data seems to be broken.
- ALOGV("Raw image not detected: %s (piex error code: %d)", filename.string(), (int32_t)err);
+ ALOGV("Raw image not detected: %s (piex error code: %d)", filename.c_str(), (int32_t)err);
return false;
}
diff --git a/native/android/asset_manager.cpp b/native/android/asset_manager.cpp
index 69cf804..1878716 100644
--- a/native/android/asset_manager.cpp
+++ b/native/android/asset_manager.cpp
@@ -118,7 +118,7 @@
// the string to return and advance the iterator for next time.
if (index < max) {
assetDir->mCachedFileName = assetDir->mAssetDir->getFileName(index);
- returnName = assetDir->mCachedFileName.string();
+ returnName = assetDir->mCachedFileName.c_str();
index++;
}
@@ -134,7 +134,7 @@
const char* AAssetDir_getFileName(AAssetDir* assetDir, int index)
{
assetDir->mCachedFileName = assetDir->mAssetDir->getFileName(index);
- return assetDir->mCachedFileName.string();
+ return assetDir->mCachedFileName.c_str();
}
void AAssetDir_close(AAssetDir* assetDir)
diff --git a/native/android/configuration.cpp b/native/android/configuration.cpp
index b50514d..283445f 100644
--- a/native/android/configuration.cpp
+++ b/native/android/configuration.cpp
@@ -36,7 +36,7 @@
void AConfiguration_fromAssetManager(AConfiguration* out, AAssetManager* am) {
ScopedLock<AssetManager2> locked_mgr(*AssetManagerForNdkAssetManager(am));
- ResTable_config config = locked_mgr->GetConfiguration();
+ ResTable_config config = locked_mgr->GetConfigurations()[0];
// AConfiguration is not a virtual subclass, so we can memcpy.
memcpy(out, &config, sizeof(config));
diff --git a/native/android/sensor.cpp b/native/android/sensor.cpp
index 968de34..bb8708b 100644
--- a/native/android/sensor.cpp
+++ b/native/android/sensor.cpp
@@ -304,12 +304,12 @@
const char* ASensor_getName(ASensor const* sensor) {
RETURN_IF_SENSOR_IS_NULL(nullptr);
- return static_cast<Sensor const*>(sensor)->getName().string();
+ return static_cast<Sensor const*>(sensor)->getName().c_str();
}
const char* ASensor_getVendor(ASensor const* sensor) {
RETURN_IF_SENSOR_IS_NULL(nullptr);
- return static_cast<Sensor const*>(sensor)->getVendor().string();
+ return static_cast<Sensor const*>(sensor)->getVendor().c_str();
}
int ASensor_getType(ASensor const* sensor) {
@@ -339,7 +339,7 @@
const char* ASensor_getStringType(ASensor const* sensor) {
RETURN_IF_SENSOR_IS_NULL(nullptr);
- return static_cast<Sensor const*>(sensor)->getStringType().string();
+ return static_cast<Sensor const*>(sensor)->getStringType().c_str();
}
int ASensor_getReportingMode(ASensor const* sensor) {
diff --git a/native/android/storage_manager.cpp b/native/android/storage_manager.cpp
index 294ca9c..6db87df 100644
--- a/native/android/storage_manager.cpp
+++ b/native/android/storage_manager.cpp
@@ -175,7 +175,7 @@
String16 filename16(filename);
String16 path16;
if (mMountService->getMountedObbPath(filename16, path16)) {
- return String8(path16).string();
+ return String8(path16).c_str();
} else {
return NULL;
}
@@ -183,7 +183,7 @@
};
void ObbActionListener::onObbResult(const android::String16& filename, const int32_t nonce, const int32_t state) {
- mStorageManager->fireCallback(String8(filename).string(), nonce, state);
+ mStorageManager->fireCallback(String8(filename).c_str(), nonce, state);
}
diff --git a/packages/CredentialManager/res/values-fa/strings.xml b/packages/CredentialManager/res/values-fa/strings.xml
index 8e4113d..88afea4 100644
--- a/packages/CredentialManager/res/values-fa/strings.xml
+++ b/packages/CredentialManager/res/values-fa/strings.xml
@@ -78,7 +78,7 @@
<string name="get_dialog_title_choose_sign_in_for" msgid="3048870756117876514">"انتخاب روش ورود به سیستم برای <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>
<string name="get_dialog_title_use_info_on" msgid="8863708099535435146">"از این اطلاعات در <xliff:g id="APP_NAME">%1$s</xliff:g> استفاده شود؟"</string>
- <string name="get_dialog_use_saved_passkey_for" msgid="4618100798664888512">"ورود به سیستم به روشی دیگر"</string>
+ <string name="get_dialog_use_saved_passkey_for" msgid="4618100798664888512">"ورود به سیستم به یک روش دیگر"</string>
<string name="snackbar_action" msgid="37373514216505085">"مشاهده گزینهها"</string>
<string name="get_dialog_button_label_continue" msgid="6446201694794283870">"ادامه"</string>
<string name="get_dialog_title_sign_in_options" msgid="2092876443114893618">"گزینههای ورود به سیستم"</string>
diff --git a/packages/PrintSpooler/res/values-am/strings.xml b/packages/PrintSpooler/res/values-am/strings.xml
index d5dc97a..a830653 100644
--- a/packages/PrintSpooler/res/values-am/strings.xml
+++ b/packages/PrintSpooler/res/values-am/strings.xml
@@ -31,8 +31,8 @@
<string name="template_all_pages" msgid="3322235982020148762">"ሁሉም <xliff:g id="PAGE_COUNT">%1$s</xliff:g>"</string>
<string name="template_page_range" msgid="428638530038286328">"የ<xliff:g id="PAGE_COUNT">%1$s</xliff:g> ክልል"</string>
<string name="pages_range_example" msgid="8558694453556945172">"ለምሳሌ፦ 1–5,8,11–13"</string>
- <string name="print_preview" msgid="8010217796057763343">"የህትመት ቅድመ እይታ"</string>
- <string name="install_for_print_preview" msgid="6366303997385509332">"ለቅድመ-እይታ የፒ ዲ ኤፍ መመልከቻ ይጫኑ"</string>
+ <string name="print_preview" msgid="8010217796057763343">"የህትመት ቅድመ ዕይታ"</string>
+ <string name="install_for_print_preview" msgid="6366303997385509332">"ለቅድመ-ዕይታ የፒ ዲ ኤፍ መመልከቻ ይጫኑ"</string>
<string name="printing_app_crashed" msgid="854477616686566398">"የአታሚ መተግበሪያ ተበላሽቷል"</string>
<string name="generating_print_job" msgid="3119608742651698916">"የህትመት ስራን በማመንጨት ላይ"</string>
<string name="save_as_pdf" msgid="5718454119847596853">"እንደ PDF አስቀምጥ"</string>
diff --git a/packages/SettingsLib/Spa/gradle/libs.versions.toml b/packages/SettingsLib/Spa/gradle/libs.versions.toml
index 0f467b9..8b56336 100644
--- a/packages/SettingsLib/Spa/gradle/libs.versions.toml
+++ b/packages/SettingsLib/Spa/gradle/libs.versions.toml
@@ -15,7 +15,7 @@
#
[versions]
-agp = "8.1.0"
+agp = "8.1.1"
compose-compiler = "1.5.1"
dexmaker-mockito = "2.28.3"
kotlin = "1.9.0"
diff --git a/packages/SettingsLib/Spa/gradle/wrapper/gradle-wrapper.jar b/packages/SettingsLib/Spa/gradle/wrapper/gradle-wrapper.jar
index c1962a7..033e24c 100644
--- a/packages/SettingsLib/Spa/gradle/wrapper/gradle-wrapper.jar
+++ b/packages/SettingsLib/Spa/gradle/wrapper/gradle-wrapper.jar
Binary files differ
diff --git a/packages/SettingsLib/Spa/gradle/wrapper/gradle-wrapper.properties b/packages/SettingsLib/Spa/gradle/wrapper/gradle-wrapper.properties
index 5b0ac44..da04f42 100644
--- a/packages/SettingsLib/Spa/gradle/wrapper/gradle-wrapper.properties
+++ b/packages/SettingsLib/Spa/gradle/wrapper/gradle-wrapper.properties
@@ -16,6 +16,8 @@
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
-distributionUrl=https\://services.gradle.org/distributions/gradle-8.2.1-bin.zip
+distributionUrl=https\://services.gradle.org/distributions/gradle-8.3-bin.zip
+networkTimeout=10000
+validateDistributionUrl=true
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
diff --git a/packages/SettingsLib/Spa/gradlew b/packages/SettingsLib/Spa/gradlew
index aeb74cb..fcb6fca 100755
--- a/packages/SettingsLib/Spa/gradlew
+++ b/packages/SettingsLib/Spa/gradlew
@@ -130,10 +130,13 @@
fi
else
JAVACMD=java
- which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
+ if ! command -v java >/dev/null 2>&1
+ then
+ die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
Please set the JAVA_HOME variable in your environment to match the
location of your Java installation."
+ fi
fi
# Increase the maximum file descriptors if we can.
diff --git a/packages/SettingsLib/Spa/spa/build.gradle.kts b/packages/SettingsLib/Spa/spa/build.gradle.kts
index 84198de..b810511 100644
--- a/packages/SettingsLib/Spa/spa/build.gradle.kts
+++ b/packages/SettingsLib/Spa/spa/build.gradle.kts
@@ -63,7 +63,7 @@
api("androidx.compose.ui:ui-tooling-preview:$jetpackComposeVersion")
api("androidx.lifecycle:lifecycle-livedata-ktx")
api("androidx.lifecycle:lifecycle-runtime-compose")
- api("androidx.navigation:navigation-compose:2.7.0-rc01")
+ api("androidx.navigation:navigation-compose:2.7.1")
api("com.github.PhilJay:MPAndroidChart:v3.1.0-alpha")
api("com.google.android.material:material:1.7.0-alpha03")
debugApi("androidx.compose.ui:ui-tooling:$jetpackComposeVersion")
diff --git a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/framework/common/SettingsEntryBuilder.kt b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/framework/common/SettingsEntryBuilder.kt
index 97d8de3..0d489e8 100644
--- a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/framework/common/SettingsEntryBuilder.kt
+++ b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/framework/common/SettingsEntryBuilder.kt
@@ -147,19 +147,20 @@
return create(entryName, owner).setLink(toPage = owner)
}
- fun create(owner: SettingsPage, entryName: String, label: String? = null):
- SettingsEntryBuilder {
- return SettingsEntryBuilder(entryName, owner).setLabel(label ?: entryName)
- }
+ fun create(
+ owner: SettingsPage,
+ entryName: String,
+ label: String = entryName,
+ ): SettingsEntryBuilder = SettingsEntryBuilder(entryName, owner).setLabel(label)
- fun createInject(owner: SettingsPage, label: String? = null): SettingsEntryBuilder {
- val label = label ?: "${INJECT_ENTRY_LABEL}_${owner.displayName}"
- return createLinkTo(INJECT_ENTRY_LABEL, owner).setLabel(label)
- }
+ fun createInject(
+ owner: SettingsPage,
+ label: String = "${INJECT_ENTRY_LABEL}_${owner.displayName}",
+ ): SettingsEntryBuilder = createLinkTo(INJECT_ENTRY_LABEL, owner).setLabel(label)
- fun createRoot(owner: SettingsPage, label: String? = null): SettingsEntryBuilder {
- val label = label ?: "${ROOT_ENTRY_LABEL}_${owner.displayName}"
- return createLinkTo(ROOT_ENTRY_LABEL, owner).setLabel(label)
- }
+ fun createRoot(
+ owner: SettingsPage,
+ label: String = "${ROOT_ENTRY_LABEL}_${owner.displayName}",
+ ): SettingsEntryBuilder = createLinkTo(ROOT_ENTRY_LABEL, owner).setLabel(label)
}
}
diff --git a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/slice/SpaSliceProvider.kt b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/slice/SpaSliceProvider.kt
index b809c0f..3496f02 100644
--- a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/slice/SpaSliceProvider.kt
+++ b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/slice/SpaSliceProvider.kt
@@ -64,8 +64,8 @@
}
}
- override fun onChanged(slice: Slice?) {
- val uri = slice?.uri ?: return
+ override fun onChanged(value: Slice?) {
+ val uri = value?.uri ?: return
Log.d(TAG, "onChanged: $uri")
context?.contentResolver?.notifyChange(uri, null)
}
@@ -74,4 +74,4 @@
Log.d(TAG, "onCreateSliceProvider")
return true
}
-}
\ No newline at end of file
+}
diff --git a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/slice/presenter/Demo.kt b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/slice/presenter/Demo.kt
index cff1c0c..ee24a09 100644
--- a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/slice/presenter/Demo.kt
+++ b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/slice/presenter/Demo.kt
@@ -17,7 +17,7 @@
package com.android.settingslib.spa.slice.presenter
import android.net.Uri
-import androidx.compose.material3.Divider
+import androidx.compose.material3.HorizontalDivider
import androidx.compose.runtime.Composable
import androidx.compose.runtime.remember
import androidx.compose.ui.platform.LocalContext
@@ -34,7 +34,7 @@
SliceLiveData.fromUri(context, sliceUri)
}
- Divider()
+ HorizontalDivider()
AndroidView(
factory = { localContext ->
val view = SliceView(localContext)
diff --git a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/preference/BaseLayout.kt b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/preference/BaseLayout.kt
index 6135203..6330ddf 100644
--- a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/preference/BaseLayout.kt
+++ b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/preference/BaseLayout.kt
@@ -24,7 +24,7 @@
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size
import androidx.compose.foundation.layout.width
-import androidx.compose.material3.Divider
+import androidx.compose.material3.HorizontalDivider
import androidx.compose.runtime.Composable
import androidx.compose.runtime.State
import androidx.compose.ui.Alignment
@@ -105,7 +105,7 @@
BaseLayout(
title = "Title",
subTitle = {
- Divider(thickness = 10.dp)
+ HorizontalDivider(thickness = 10.dp)
}
)
}
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 67f4418..d437e35 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
@@ -99,8 +99,8 @@
.fillMaxSize(),
) {
content(
- bottomPadding = paddingValues.calculateBottomPadding(),
- searchQuery = remember {
+ paddingValues.calculateBottomPadding(),
+ remember {
derivedStateOf { if (isSearchMode) viewModel.searchQuery.text else "" }
},
)
diff --git a/packages/SettingsLib/res/values-af/strings.xml b/packages/SettingsLib/res/values-af/strings.xml
index df32ef2..8e4c6a4 100644
--- a/packages/SettingsLib/res/values-af/strings.xml
+++ b/packages/SettingsLib/res/values-af/strings.xml
@@ -107,10 +107,8 @@
<string name="bluetooth_profile_opp" msgid="6692618568149493430">"Lêeroordrag"</string>
<string name="bluetooth_profile_hid" msgid="2969922922664315866">"Invoertoestel"</string>
<string name="bluetooth_profile_pan" msgid="1006235139308318188">"Internettoegang"</string>
- <!-- no translation found for bluetooth_profile_pbap (2103406516858653017) -->
- <skip />
- <!-- no translation found for bluetooth_profile_pbap_summary (402819589201138227) -->
- <skip />
+ <string name="bluetooth_profile_pbap" msgid="2103406516858653017">"Gee toegang tot kontakte en oproepgeskiedenis"</string>
+ <string name="bluetooth_profile_pbap_summary" msgid="402819589201138227">"Inligting sal vir oproepaankondigings en meer gebruik word"</string>
<string name="bluetooth_profile_pan_nap" msgid="7871974753822470050">"Deling van internetverbinding"</string>
<string name="bluetooth_profile_map" msgid="8907204701162107271">"Teksboodskappe"</string>
<string name="bluetooth_profile_sap" msgid="8304170950447934386">"SIM-toegang"</string>
diff --git a/packages/SettingsLib/res/values-am/strings.xml b/packages/SettingsLib/res/values-am/strings.xml
index c20a9606..6011eb16 100644
--- a/packages/SettingsLib/res/values-am/strings.xml
+++ b/packages/SettingsLib/res/values-am/strings.xml
@@ -107,10 +107,8 @@
<string name="bluetooth_profile_opp" msgid="6692618568149493430">"ፋይል ማስተላለፍ"</string>
<string name="bluetooth_profile_hid" msgid="2969922922664315866">"ግቤት መሣሪያ"</string>
<string name="bluetooth_profile_pan" msgid="1006235139308318188">"የበይነመረብ ድረስ"</string>
- <!-- no translation found for bluetooth_profile_pbap (2103406516858653017) -->
- <skip />
- <!-- no translation found for bluetooth_profile_pbap_summary (402819589201138227) -->
- <skip />
+ <string name="bluetooth_profile_pbap" msgid="2103406516858653017">"ለዕውቂያዎች እና ለጥሪ ታሪክ መዳረሻ ይፍቀዱ"</string>
+ <string name="bluetooth_profile_pbap_summary" msgid="402819589201138227">"መረጃ ለጥሪ ማስታወቂያዎች እና ሌሎችም ጥቅም ላይ ይውላል"</string>
<string name="bluetooth_profile_pan_nap" msgid="7871974753822470050">"የበይነ መረብ ተያያዥ ማጋሪያ"</string>
<string name="bluetooth_profile_map" msgid="8907204701162107271">"የጽሑፍ መልዕክቶች"</string>
<string name="bluetooth_profile_sap" msgid="8304170950447934386">"የሲም መዳረሻ"</string>
diff --git a/packages/SettingsLib/res/values-ar/strings.xml b/packages/SettingsLib/res/values-ar/strings.xml
index 7337d73..40c92e5 100644
--- a/packages/SettingsLib/res/values-ar/strings.xml
+++ b/packages/SettingsLib/res/values-ar/strings.xml
@@ -107,10 +107,8 @@
<string name="bluetooth_profile_opp" msgid="6692618568149493430">"نقل الملف"</string>
<string name="bluetooth_profile_hid" msgid="2969922922664315866">"جهاز الإرسال"</string>
<string name="bluetooth_profile_pan" msgid="1006235139308318188">"الوصول إلى الإنترنت"</string>
- <!-- no translation found for bluetooth_profile_pbap (2103406516858653017) -->
- <skip />
- <!-- no translation found for bluetooth_profile_pbap_summary (402819589201138227) -->
- <skip />
+ <string name="bluetooth_profile_pbap" msgid="2103406516858653017">"السماح بالوصول إلى جهات الاتصال وسجلّ المكالمات"</string>
+ <string name="bluetooth_profile_pbap_summary" msgid="402819589201138227">"سيتم استخدام المعلومات لإرسال إشعارات المكالمات وغيرها"</string>
<string name="bluetooth_profile_pan_nap" msgid="7871974753822470050">"مشاركة اتصال الإنترنت"</string>
<string name="bluetooth_profile_map" msgid="8907204701162107271">"الرسائل النصية"</string>
<string name="bluetooth_profile_sap" msgid="8304170950447934386">"الوصول إلى شريحة SIM"</string>
diff --git a/packages/SettingsLib/res/values-as/strings.xml b/packages/SettingsLib/res/values-as/strings.xml
index dd3e5f4..ef72afc 100644
--- a/packages/SettingsLib/res/values-as/strings.xml
+++ b/packages/SettingsLib/res/values-as/strings.xml
@@ -107,10 +107,8 @@
<string name="bluetooth_profile_opp" msgid="6692618568149493430">"ফাইল স্থানান্তৰণ"</string>
<string name="bluetooth_profile_hid" msgid="2969922922664315866">"ইনপুট ডিভাইচ"</string>
<string name="bluetooth_profile_pan" msgid="1006235139308318188">"ইণ্টাৰনেট সংযোগ"</string>
- <!-- no translation found for bluetooth_profile_pbap (2103406516858653017) -->
- <skip />
- <!-- no translation found for bluetooth_profile_pbap_summary (402819589201138227) -->
- <skip />
+ <string name="bluetooth_profile_pbap" msgid="2103406516858653017">"আপোনাৰ সম্পৰ্কসূচী আৰু কলৰ ইতিহাস এক্সেছ কৰিবলৈ অনুমতি দিয়ক"</string>
+ <string name="bluetooth_profile_pbap_summary" msgid="402819589201138227">"এই তথ্য কলৰ ঘোষণা আৰু অধিক কাৰ্যৰ বাবে ব্যৱহাৰ কৰা হ’ব"</string>
<string name="bluetooth_profile_pan_nap" msgid="7871974753822470050">"ইণ্টাৰনেট সংযোগ শ্বেয়াৰ"</string>
<string name="bluetooth_profile_map" msgid="8907204701162107271">"পাঠ বাৰ্তা"</string>
<string name="bluetooth_profile_sap" msgid="8304170950447934386">"ছিমৰ এক্সেছ"</string>
diff --git a/packages/SettingsLib/res/values-az/strings.xml b/packages/SettingsLib/res/values-az/strings.xml
index 05231ac..a76f8ba 100644
--- a/packages/SettingsLib/res/values-az/strings.xml
+++ b/packages/SettingsLib/res/values-az/strings.xml
@@ -107,10 +107,8 @@
<string name="bluetooth_profile_opp" msgid="6692618568149493430">"Fayl transferi"</string>
<string name="bluetooth_profile_hid" msgid="2969922922664315866">"Daxiletmə cihazı"</string>
<string name="bluetooth_profile_pan" msgid="1006235139308318188">"İnternetə giriş"</string>
- <!-- no translation found for bluetooth_profile_pbap (2103406516858653017) -->
- <skip />
- <!-- no translation found for bluetooth_profile_pbap_summary (402819589201138227) -->
- <skip />
+ <string name="bluetooth_profile_pbap" msgid="2103406516858653017">"Kontakt və zəng tarixçəsinə giriş verin"</string>
+ <string name="bluetooth_profile_pbap_summary" msgid="402819589201138227">"Məlumat zəng elanı və s. üçün istifadə ediləcək"</string>
<string name="bluetooth_profile_pan_nap" msgid="7871974753822470050">"internet bağlantı paylaşımı"</string>
<string name="bluetooth_profile_map" msgid="8907204701162107271">"Mətn Mesajları"</string>
<string name="bluetooth_profile_sap" msgid="8304170950447934386">"SIM-karta giriş"</string>
diff --git a/packages/SettingsLib/res/values-b+sr+Latn/strings.xml b/packages/SettingsLib/res/values-b+sr+Latn/strings.xml
index 3cefd59..4d7a5fe 100644
--- a/packages/SettingsLib/res/values-b+sr+Latn/strings.xml
+++ b/packages/SettingsLib/res/values-b+sr+Latn/strings.xml
@@ -107,10 +107,8 @@
<string name="bluetooth_profile_opp" msgid="6692618568149493430">"Prenos datoteke"</string>
<string name="bluetooth_profile_hid" msgid="2969922922664315866">"Ulazni uređaj"</string>
<string name="bluetooth_profile_pan" msgid="1006235139308318188">"Pristup Internetu"</string>
- <!-- no translation found for bluetooth_profile_pbap (2103406516858653017) -->
- <skip />
- <!-- no translation found for bluetooth_profile_pbap_summary (402819589201138227) -->
- <skip />
+ <string name="bluetooth_profile_pbap" msgid="2103406516858653017">"Dozvoli kontakte i istoriju poziva"</string>
+ <string name="bluetooth_profile_pbap_summary" msgid="402819589201138227">"Informacije će se koristiti za obaveštenja o pozivima i drugo"</string>
<string name="bluetooth_profile_pan_nap" msgid="7871974753822470050">"Deljenje internet veze"</string>
<string name="bluetooth_profile_map" msgid="8907204701162107271">"SMS-ovi"</string>
<string name="bluetooth_profile_sap" msgid="8304170950447934386">"Pristup SIM kartici"</string>
diff --git a/packages/SettingsLib/res/values-be/strings.xml b/packages/SettingsLib/res/values-be/strings.xml
index 47fa1db..f46adf8 100644
--- a/packages/SettingsLib/res/values-be/strings.xml
+++ b/packages/SettingsLib/res/values-be/strings.xml
@@ -107,10 +107,8 @@
<string name="bluetooth_profile_opp" msgid="6692618568149493430">"Перадача файлаў"</string>
<string name="bluetooth_profile_hid" msgid="2969922922664315866">"Прылада ўводу"</string>
<string name="bluetooth_profile_pan" msgid="1006235139308318188">"Доступ у інтэрнэт"</string>
- <!-- no translation found for bluetooth_profile_pbap (2103406516858653017) -->
- <skip />
- <!-- no translation found for bluetooth_profile_pbap_summary (402819589201138227) -->
- <skip />
+ <string name="bluetooth_profile_pbap" msgid="2103406516858653017">"Дазволіць доступ да кантактаў і гісторыі выклікаў"</string>
+ <string name="bluetooth_profile_pbap_summary" msgid="402819589201138227">"Гэтыя звесткі патрэбныя для апавяшчэнняў аб выкліках і інш."</string>
<string name="bluetooth_profile_pan_nap" msgid="7871974753822470050">"Прадастаўленне доступу да Інтэрнэту"</string>
<string name="bluetooth_profile_map" msgid="8907204701162107271">"Тэкставыя паведамленні"</string>
<string name="bluetooth_profile_sap" msgid="8304170950447934386">"Доступ да SIM-карты"</string>
diff --git a/packages/SettingsLib/res/values-bg/strings.xml b/packages/SettingsLib/res/values-bg/strings.xml
index bd117b0..fa3cba2 100644
--- a/packages/SettingsLib/res/values-bg/strings.xml
+++ b/packages/SettingsLib/res/values-bg/strings.xml
@@ -107,10 +107,8 @@
<string name="bluetooth_profile_opp" msgid="6692618568149493430">"Прехвърляне на файл"</string>
<string name="bluetooth_profile_hid" msgid="2969922922664315866">"Входно устройство"</string>
<string name="bluetooth_profile_pan" msgid="1006235139308318188">"Достъп до интернет"</string>
- <!-- no translation found for bluetooth_profile_pbap (2103406516858653017) -->
- <skip />
- <!-- no translation found for bluetooth_profile_pbap_summary (402819589201138227) -->
- <skip />
+ <string name="bluetooth_profile_pbap" msgid="2103406516858653017">"Разреш. на достъпа до контактите и историята на обажд."</string>
+ <string name="bluetooth_profile_pbap_summary" msgid="402819589201138227">"Информацията ще се ползва за съобщения чрез обаждания и др."</string>
<string name="bluetooth_profile_pan_nap" msgid="7871974753822470050">"Споделяне на връзката с интернет"</string>
<string name="bluetooth_profile_map" msgid="8907204701162107271">"Текстови съобщения"</string>
<string name="bluetooth_profile_sap" msgid="8304170950447934386">"Достъп до SIM картата"</string>
diff --git a/packages/SettingsLib/res/values-bn/strings.xml b/packages/SettingsLib/res/values-bn/strings.xml
index d701894..d3dc359 100644
--- a/packages/SettingsLib/res/values-bn/strings.xml
+++ b/packages/SettingsLib/res/values-bn/strings.xml
@@ -107,10 +107,8 @@
<string name="bluetooth_profile_opp" msgid="6692618568149493430">"ফাইল স্থানান্তর"</string>
<string name="bluetooth_profile_hid" msgid="2969922922664315866">"ইনপুট ডিভাইস"</string>
<string name="bluetooth_profile_pan" msgid="1006235139308318188">"ইন্টারনেট অ্যাক্সেস"</string>
- <!-- no translation found for bluetooth_profile_pbap (2103406516858653017) -->
- <skip />
- <!-- no translation found for bluetooth_profile_pbap_summary (402819589201138227) -->
- <skip />
+ <string name="bluetooth_profile_pbap" msgid="2103406516858653017">"পরিচিতি ও কলের ইতিহাস অ্যাক্সেস করার অনুমতি দিন"</string>
+ <string name="bluetooth_profile_pbap_summary" msgid="402819589201138227">"কল সম্পর্কিত ঘোষণা ও আরও অনেক কিছুর জন্য তথ্য ব্যবহার করা হবে"</string>
<string name="bluetooth_profile_pan_nap" msgid="7871974753822470050">"ইন্টারনেট কানেকশন শেয়ার করা হচ্ছে"</string>
<string name="bluetooth_profile_map" msgid="8907204701162107271">"এসএমএস"</string>
<string name="bluetooth_profile_sap" msgid="8304170950447934386">"সিম অ্যাক্সেস"</string>
diff --git a/packages/SettingsLib/res/values-bs/strings.xml b/packages/SettingsLib/res/values-bs/strings.xml
index 99e160d..0487e29 100644
--- a/packages/SettingsLib/res/values-bs/strings.xml
+++ b/packages/SettingsLib/res/values-bs/strings.xml
@@ -107,10 +107,8 @@
<string name="bluetooth_profile_opp" msgid="6692618568149493430">"Prenošenje fajla"</string>
<string name="bluetooth_profile_hid" msgid="2969922922664315866">"Ulazni uređaj"</string>
<string name="bluetooth_profile_pan" msgid="1006235139308318188">"Pristup internetu"</string>
- <!-- no translation found for bluetooth_profile_pbap (2103406516858653017) -->
- <skip />
- <!-- no translation found for bluetooth_profile_pbap_summary (402819589201138227) -->
- <skip />
+ <string name="bluetooth_profile_pbap" msgid="2103406516858653017">"Dozvoli pristup kontaktima i historiji poziva"</string>
+ <string name="bluetooth_profile_pbap_summary" msgid="402819589201138227">"Podaci će se koristiti za obavještenja o pozivu i drugo"</string>
<string name="bluetooth_profile_pan_nap" msgid="7871974753822470050">"Dijeljenje internet veze"</string>
<string name="bluetooth_profile_map" msgid="8907204701162107271">"SMS-ovi"</string>
<string name="bluetooth_profile_sap" msgid="8304170950447934386">"Pristup SIM-u"</string>
diff --git a/packages/SettingsLib/res/values-ca/strings.xml b/packages/SettingsLib/res/values-ca/strings.xml
index 49358bd..62938eb 100644
--- a/packages/SettingsLib/res/values-ca/strings.xml
+++ b/packages/SettingsLib/res/values-ca/strings.xml
@@ -107,10 +107,8 @@
<string name="bluetooth_profile_opp" msgid="6692618568149493430">"Transferència de fitxers"</string>
<string name="bluetooth_profile_hid" msgid="2969922922664315866">"Dispositiu d\'entrada"</string>
<string name="bluetooth_profile_pan" msgid="1006235139308318188">"Accés a Internet"</string>
- <!-- no translation found for bluetooth_profile_pbap (2103406516858653017) -->
- <skip />
- <!-- no translation found for bluetooth_profile_pbap_summary (402819589201138227) -->
- <skip />
+ <string name="bluetooth_profile_pbap" msgid="2103406516858653017">"Permet l\'accés als contactes i a l\'historial de trucades"</string>
+ <string name="bluetooth_profile_pbap_summary" msgid="402819589201138227">"La informació s\'utilitzarà per als avisos de trucades i més"</string>
<string name="bluetooth_profile_pan_nap" msgid="7871974753822470050">"Compartició de connexió d\'Internet"</string>
<string name="bluetooth_profile_map" msgid="8907204701162107271">"Missatges de text"</string>
<string name="bluetooth_profile_sap" msgid="8304170950447934386">"Accés a la SIM"</string>
diff --git a/packages/SettingsLib/res/values-cs/strings.xml b/packages/SettingsLib/res/values-cs/strings.xml
index dc2d456..d6aa84e 100644
--- a/packages/SettingsLib/res/values-cs/strings.xml
+++ b/packages/SettingsLib/res/values-cs/strings.xml
@@ -107,10 +107,8 @@
<string name="bluetooth_profile_opp" msgid="6692618568149493430">"Přenos souborů"</string>
<string name="bluetooth_profile_hid" msgid="2969922922664315866">"Vstupní zařízení"</string>
<string name="bluetooth_profile_pan" msgid="1006235139308318188">"Přístup k internetu"</string>
- <!-- no translation found for bluetooth_profile_pbap (2103406516858653017) -->
- <skip />
- <!-- no translation found for bluetooth_profile_pbap_summary (402819589201138227) -->
- <skip />
+ <string name="bluetooth_profile_pbap" msgid="2103406516858653017">"Povolit přístup ke kontaktům a historii"</string>
+ <string name="bluetooth_profile_pbap_summary" msgid="402819589201138227">"Informace poslouží k oznamování hovorů a k dalším účelům"</string>
<string name="bluetooth_profile_pan_nap" msgid="7871974753822470050">"Sdílení internetového připojení"</string>
<string name="bluetooth_profile_map" msgid="8907204701162107271">"Textové zprávy"</string>
<string name="bluetooth_profile_sap" msgid="8304170950447934386">"Přístup k SIM kartě"</string>
diff --git a/packages/SettingsLib/res/values-da/strings.xml b/packages/SettingsLib/res/values-da/strings.xml
index 83a67c1..d1b0222 100644
--- a/packages/SettingsLib/res/values-da/strings.xml
+++ b/packages/SettingsLib/res/values-da/strings.xml
@@ -107,10 +107,8 @@
<string name="bluetooth_profile_opp" msgid="6692618568149493430">"Filoverførsel"</string>
<string name="bluetooth_profile_hid" msgid="2969922922664315866">"Inputenhed"</string>
<string name="bluetooth_profile_pan" msgid="1006235139308318188">"Internetadgang"</string>
- <!-- no translation found for bluetooth_profile_pbap (2103406516858653017) -->
- <skip />
- <!-- no translation found for bluetooth_profile_pbap_summary (402819589201138227) -->
- <skip />
+ <string name="bluetooth_profile_pbap" msgid="2103406516858653017">"Tillad adgang til dine kontakter og din opkaldshistorik"</string>
+ <string name="bluetooth_profile_pbap_summary" msgid="402819589201138227">"Oplysningerne bruges til opkaldsmeddelelser m.m."</string>
<string name="bluetooth_profile_pan_nap" msgid="7871974753822470050">"Deling af internetforbindelse"</string>
<string name="bluetooth_profile_map" msgid="8907204701162107271">"Sms-beskeder"</string>
<string name="bluetooth_profile_sap" msgid="8304170950447934386">"Adgang til SIM-kort"</string>
diff --git a/packages/SettingsLib/res/values-de/strings.xml b/packages/SettingsLib/res/values-de/strings.xml
index 6575f20..a74374a 100644
--- a/packages/SettingsLib/res/values-de/strings.xml
+++ b/packages/SettingsLib/res/values-de/strings.xml
@@ -107,10 +107,8 @@
<string name="bluetooth_profile_opp" msgid="6692618568149493430">"Dateiübertragung"</string>
<string name="bluetooth_profile_hid" msgid="2969922922664315866">"Eingabegerät"</string>
<string name="bluetooth_profile_pan" msgid="1006235139308318188">"Internetzugriff"</string>
- <!-- no translation found for bluetooth_profile_pbap (2103406516858653017) -->
- <skip />
- <!-- no translation found for bluetooth_profile_pbap_summary (402819589201138227) -->
- <skip />
+ <string name="bluetooth_profile_pbap" msgid="2103406516858653017">"Zugriff auf Kontakte und Anrufliste geben"</string>
+ <string name="bluetooth_profile_pbap_summary" msgid="402819589201138227">"Die Daten werden z. B. für Anrufbenachrichtigungen verwendet"</string>
<string name="bluetooth_profile_pan_nap" msgid="7871974753822470050">"Freigabe der Internetverbindung"</string>
<string name="bluetooth_profile_map" msgid="8907204701162107271">"SMS"</string>
<string name="bluetooth_profile_sap" msgid="8304170950447934386">"Zugriff auf SIM"</string>
diff --git a/packages/SettingsLib/res/values-el/strings.xml b/packages/SettingsLib/res/values-el/strings.xml
index 1a36ef8..9f000bb 100644
--- a/packages/SettingsLib/res/values-el/strings.xml
+++ b/packages/SettingsLib/res/values-el/strings.xml
@@ -107,10 +107,8 @@
<string name="bluetooth_profile_opp" msgid="6692618568149493430">"Μεταφορά αρχείου"</string>
<string name="bluetooth_profile_hid" msgid="2969922922664315866">"Συσκευή εισόδου"</string>
<string name="bluetooth_profile_pan" msgid="1006235139308318188">"Πρόσβαση στο Διαδίκτυο"</string>
- <!-- no translation found for bluetooth_profile_pbap (2103406516858653017) -->
- <skip />
- <!-- no translation found for bluetooth_profile_pbap_summary (402819589201138227) -->
- <skip />
+ <string name="bluetooth_profile_pbap" msgid="2103406516858653017">"Επιτρ. πρόσβ. σε επαφές και ιστορ. κλήσ."</string>
+ <string name="bluetooth_profile_pbap_summary" msgid="402819589201138227">"Οι πληροφ. θα χρησιμοποιούνται για ανακοινώσεις κλήσεων κ.ά."</string>
<string name="bluetooth_profile_pan_nap" msgid="7871974753822470050">"Κοινή χρήση σύνδεσης στο Διαδίκτυο"</string>
<string name="bluetooth_profile_map" msgid="8907204701162107271">"Μηνύματα κειμένου"</string>
<string name="bluetooth_profile_sap" msgid="8304170950447934386">"Πρόσβαση SIM"</string>
diff --git a/packages/SettingsLib/res/values-en-rAU/strings.xml b/packages/SettingsLib/res/values-en-rAU/strings.xml
index fa00776..5bb58d1 100644
--- a/packages/SettingsLib/res/values-en-rAU/strings.xml
+++ b/packages/SettingsLib/res/values-en-rAU/strings.xml
@@ -107,10 +107,8 @@
<string name="bluetooth_profile_opp" msgid="6692618568149493430">"File transfer"</string>
<string name="bluetooth_profile_hid" msgid="2969922922664315866">"Input device"</string>
<string name="bluetooth_profile_pan" msgid="1006235139308318188">"Internet access"</string>
- <!-- no translation found for bluetooth_profile_pbap (2103406516858653017) -->
- <skip />
- <!-- no translation found for bluetooth_profile_pbap_summary (402819589201138227) -->
- <skip />
+ <string name="bluetooth_profile_pbap" msgid="2103406516858653017">"Allow access to contacts and call history"</string>
+ <string name="bluetooth_profile_pbap_summary" msgid="402819589201138227">"Info will be used for call announcements and more"</string>
<string name="bluetooth_profile_pan_nap" msgid="7871974753822470050">"Internet connection sharing"</string>
<string name="bluetooth_profile_map" msgid="8907204701162107271">"Text messages"</string>
<string name="bluetooth_profile_sap" msgid="8304170950447934386">"SIM access"</string>
diff --git a/packages/SettingsLib/res/values-en-rGB/strings.xml b/packages/SettingsLib/res/values-en-rGB/strings.xml
index fa00776..5bb58d1 100644
--- a/packages/SettingsLib/res/values-en-rGB/strings.xml
+++ b/packages/SettingsLib/res/values-en-rGB/strings.xml
@@ -107,10 +107,8 @@
<string name="bluetooth_profile_opp" msgid="6692618568149493430">"File transfer"</string>
<string name="bluetooth_profile_hid" msgid="2969922922664315866">"Input device"</string>
<string name="bluetooth_profile_pan" msgid="1006235139308318188">"Internet access"</string>
- <!-- no translation found for bluetooth_profile_pbap (2103406516858653017) -->
- <skip />
- <!-- no translation found for bluetooth_profile_pbap_summary (402819589201138227) -->
- <skip />
+ <string name="bluetooth_profile_pbap" msgid="2103406516858653017">"Allow access to contacts and call history"</string>
+ <string name="bluetooth_profile_pbap_summary" msgid="402819589201138227">"Info will be used for call announcements and more"</string>
<string name="bluetooth_profile_pan_nap" msgid="7871974753822470050">"Internet connection sharing"</string>
<string name="bluetooth_profile_map" msgid="8907204701162107271">"Text messages"</string>
<string name="bluetooth_profile_sap" msgid="8304170950447934386">"SIM access"</string>
diff --git a/packages/SettingsLib/res/values-en-rIN/strings.xml b/packages/SettingsLib/res/values-en-rIN/strings.xml
index fa00776..5bb58d1 100644
--- a/packages/SettingsLib/res/values-en-rIN/strings.xml
+++ b/packages/SettingsLib/res/values-en-rIN/strings.xml
@@ -107,10 +107,8 @@
<string name="bluetooth_profile_opp" msgid="6692618568149493430">"File transfer"</string>
<string name="bluetooth_profile_hid" msgid="2969922922664315866">"Input device"</string>
<string name="bluetooth_profile_pan" msgid="1006235139308318188">"Internet access"</string>
- <!-- no translation found for bluetooth_profile_pbap (2103406516858653017) -->
- <skip />
- <!-- no translation found for bluetooth_profile_pbap_summary (402819589201138227) -->
- <skip />
+ <string name="bluetooth_profile_pbap" msgid="2103406516858653017">"Allow access to contacts and call history"</string>
+ <string name="bluetooth_profile_pbap_summary" msgid="402819589201138227">"Info will be used for call announcements and more"</string>
<string name="bluetooth_profile_pan_nap" msgid="7871974753822470050">"Internet connection sharing"</string>
<string name="bluetooth_profile_map" msgid="8907204701162107271">"Text messages"</string>
<string name="bluetooth_profile_sap" msgid="8304170950447934386">"SIM access"</string>
diff --git a/packages/SettingsLib/res/values-es-rUS/strings.xml b/packages/SettingsLib/res/values-es-rUS/strings.xml
index ec6e152..9015ed5 100644
--- a/packages/SettingsLib/res/values-es-rUS/strings.xml
+++ b/packages/SettingsLib/res/values-es-rUS/strings.xml
@@ -107,10 +107,8 @@
<string name="bluetooth_profile_opp" msgid="6692618568149493430">"Transferencia de archivos"</string>
<string name="bluetooth_profile_hid" msgid="2969922922664315866">"Dispositivo de entrada"</string>
<string name="bluetooth_profile_pan" msgid="1006235139308318188">"Acceso a Internet"</string>
- <!-- no translation found for bluetooth_profile_pbap (2103406516858653017) -->
- <skip />
- <!-- no translation found for bluetooth_profile_pbap_summary (402819589201138227) -->
- <skip />
+ <string name="bluetooth_profile_pbap" msgid="2103406516858653017">"Permitir acc. a hist. de llam. y cont."</string>
+ <string name="bluetooth_profile_pbap_summary" msgid="402819589201138227">"Se usará la inf. para anuncios de llamadas y otras funciones"</string>
<string name="bluetooth_profile_pan_nap" msgid="7871974753822470050">"Compartir conexión a Internet"</string>
<string name="bluetooth_profile_map" msgid="8907204701162107271">"Mensajes de texto"</string>
<string name="bluetooth_profile_sap" msgid="8304170950447934386">"Acceso a SIM"</string>
diff --git a/packages/SettingsLib/res/values-es/strings.xml b/packages/SettingsLib/res/values-es/strings.xml
index 6934f8a..4ee2753 100644
--- a/packages/SettingsLib/res/values-es/strings.xml
+++ b/packages/SettingsLib/res/values-es/strings.xml
@@ -107,10 +107,8 @@
<string name="bluetooth_profile_opp" msgid="6692618568149493430">"Transferencia de archivos"</string>
<string name="bluetooth_profile_hid" msgid="2969922922664315866">"Dispositivo de entrada"</string>
<string name="bluetooth_profile_pan" msgid="1006235139308318188">"Acceso a Internet"</string>
- <!-- no translation found for bluetooth_profile_pbap (2103406516858653017) -->
- <skip />
- <!-- no translation found for bluetooth_profile_pbap_summary (402819589201138227) -->
- <skip />
+ <string name="bluetooth_profile_pbap" msgid="2103406516858653017">"Acceso a contactos e historial de llamadas"</string>
+ <string name="bluetooth_profile_pbap_summary" msgid="402819589201138227">"La información se utilizará para avisos de llamada y más"</string>
<string name="bluetooth_profile_pan_nap" msgid="7871974753822470050">"Compartir conexión a Internet"</string>
<string name="bluetooth_profile_map" msgid="8907204701162107271">"Mensajes de texto"</string>
<string name="bluetooth_profile_sap" msgid="8304170950447934386">"Acceso a tarjeta SIM"</string>
diff --git a/packages/SettingsLib/res/values-et/strings.xml b/packages/SettingsLib/res/values-et/strings.xml
index 9979eda..4667466 100644
--- a/packages/SettingsLib/res/values-et/strings.xml
+++ b/packages/SettingsLib/res/values-et/strings.xml
@@ -107,10 +107,8 @@
<string name="bluetooth_profile_opp" msgid="6692618568149493430">"Failiedastus"</string>
<string name="bluetooth_profile_hid" msgid="2969922922664315866">"Sisendseade"</string>
<string name="bluetooth_profile_pan" msgid="1006235139308318188">"Juurdepääs internetile"</string>
- <!-- no translation found for bluetooth_profile_pbap (2103406516858653017) -->
- <skip />
- <!-- no translation found for bluetooth_profile_pbap_summary (402819589201138227) -->
- <skip />
+ <string name="bluetooth_profile_pbap" msgid="2103406516858653017">"Luba juurdepääs kontakt-le ja kõneaj-le"</string>
+ <string name="bluetooth_profile_pbap_summary" msgid="402819589201138227">"Teavet kasutatakse kõne teadaannete ja muu jaoks"</string>
<string name="bluetooth_profile_pan_nap" msgid="7871974753822470050">"Interneti-ühenduse jagamine"</string>
<string name="bluetooth_profile_map" msgid="8907204701162107271">"Tekstsõnumid"</string>
<string name="bluetooth_profile_sap" msgid="8304170950447934386">"Juurdepääs SIM-ile"</string>
diff --git a/packages/SettingsLib/res/values-eu/strings.xml b/packages/SettingsLib/res/values-eu/strings.xml
index dacd8c6..d9c4ee7 100644
--- a/packages/SettingsLib/res/values-eu/strings.xml
+++ b/packages/SettingsLib/res/values-eu/strings.xml
@@ -107,10 +107,8 @@
<string name="bluetooth_profile_opp" msgid="6692618568149493430">"Fitxategi-transferentzia"</string>
<string name="bluetooth_profile_hid" msgid="2969922922664315866">"Sarrerako gailua"</string>
<string name="bluetooth_profile_pan" msgid="1006235139308318188">"Interneteko konexioa"</string>
- <!-- no translation found for bluetooth_profile_pbap (2103406516858653017) -->
- <skip />
- <!-- no translation found for bluetooth_profile_pbap_summary (402819589201138227) -->
- <skip />
+ <string name="bluetooth_profile_pbap" msgid="2103406516858653017">"Eman kontaktuak eta deien historia erabiltzeko baimena"</string>
+ <string name="bluetooth_profile_pbap_summary" msgid="402819589201138227">"Deiak iragartzeko eta abarrerako erabiliko da informazioa"</string>
<string name="bluetooth_profile_pan_nap" msgid="7871974753822470050">"Interneteko konexioa partekatzea"</string>
<string name="bluetooth_profile_map" msgid="8907204701162107271">"Testu-mezuak"</string>
<string name="bluetooth_profile_sap" msgid="8304170950447934386">"SIMerako sarbidea"</string>
diff --git a/packages/SettingsLib/res/values-fa/strings.xml b/packages/SettingsLib/res/values-fa/strings.xml
index b67b2d7..1d53864 100644
--- a/packages/SettingsLib/res/values-fa/strings.xml
+++ b/packages/SettingsLib/res/values-fa/strings.xml
@@ -107,10 +107,8 @@
<string name="bluetooth_profile_opp" msgid="6692618568149493430">"انتقال فایل"</string>
<string name="bluetooth_profile_hid" msgid="2969922922664315866">"دستگاه ورودی"</string>
<string name="bluetooth_profile_pan" msgid="1006235139308318188">"دسترسی به اینترنت"</string>
- <!-- no translation found for bluetooth_profile_pbap (2103406516858653017) -->
- <skip />
- <!-- no translation found for bluetooth_profile_pbap_summary (402819589201138227) -->
- <skip />
+ <string name="bluetooth_profile_pbap" msgid="2103406516858653017">"مجاز کردن دسترسی به مخاطبین و سابقه تماس"</string>
+ <string name="bluetooth_profile_pbap_summary" msgid="402819589201138227">"از اطلاعات برای اعلام تماسها و موارد دیگر استفاده خواهد شد"</string>
<string name="bluetooth_profile_pan_nap" msgid="7871974753822470050">"اشتراکگذاری اتصال اینترنت"</string>
<string name="bluetooth_profile_map" msgid="8907204701162107271">"پیامهای نوشتاری"</string>
<string name="bluetooth_profile_sap" msgid="8304170950447934386">"دسترسی سیمکارت"</string>
diff --git a/packages/SettingsLib/res/values-fi/strings.xml b/packages/SettingsLib/res/values-fi/strings.xml
index 45d9784..579771b 100644
--- a/packages/SettingsLib/res/values-fi/strings.xml
+++ b/packages/SettingsLib/res/values-fi/strings.xml
@@ -107,10 +107,8 @@
<string name="bluetooth_profile_opp" msgid="6692618568149493430">"Tiedostonsiirto"</string>
<string name="bluetooth_profile_hid" msgid="2969922922664315866">"Syöttölaite"</string>
<string name="bluetooth_profile_pan" msgid="1006235139308318188">"Internetyhteys"</string>
- <!-- no translation found for bluetooth_profile_pbap (2103406516858653017) -->
- <skip />
- <!-- no translation found for bluetooth_profile_pbap_summary (402819589201138227) -->
- <skip />
+ <string name="bluetooth_profile_pbap" msgid="2103406516858653017">"Myönnä pääsy kontakteihin ja soittohistoriaan"</string>
+ <string name="bluetooth_profile_pbap_summary" msgid="402819589201138227">"Tietoja käytetään esimerkiksi puheluilmoituksiin"</string>
<string name="bluetooth_profile_pan_nap" msgid="7871974753822470050">"Internetyhteyden jakaminen"</string>
<string name="bluetooth_profile_map" msgid="8907204701162107271">"Tekstiviestit"</string>
<string name="bluetooth_profile_sap" msgid="8304170950447934386">"SIM-kortin käyttö"</string>
diff --git a/packages/SettingsLib/res/values-fr-rCA/strings.xml b/packages/SettingsLib/res/values-fr-rCA/strings.xml
index c165acc5..f3c92e1 100644
--- a/packages/SettingsLib/res/values-fr-rCA/strings.xml
+++ b/packages/SettingsLib/res/values-fr-rCA/strings.xml
@@ -107,10 +107,8 @@
<string name="bluetooth_profile_opp" msgid="6692618568149493430">"Transfert de fichier"</string>
<string name="bluetooth_profile_hid" msgid="2969922922664315866">"Périphérique d\'entrée"</string>
<string name="bluetooth_profile_pan" msgid="1006235139308318188">"Accès Internet"</string>
- <!-- no translation found for bluetooth_profile_pbap (2103406516858653017) -->
- <skip />
- <!-- no translation found for bluetooth_profile_pbap_summary (402819589201138227) -->
- <skip />
+ <string name="bluetooth_profile_pbap" msgid="2103406516858653017">"Autoriser accès : contacts et hist. d\'app."</string>
+ <string name="bluetooth_profile_pbap_summary" msgid="402819589201138227">"Ces infos seront utilisées pour les annonces d\'appels et plus"</string>
<string name="bluetooth_profile_pan_nap" msgid="7871974753822470050">"Partage de connexion Internet"</string>
<string name="bluetooth_profile_map" msgid="8907204701162107271">"Messages texte"</string>
<string name="bluetooth_profile_sap" msgid="8304170950447934386">"Accès à la carte SIM"</string>
diff --git a/packages/SettingsLib/res/values-fr/strings.xml b/packages/SettingsLib/res/values-fr/strings.xml
index 55658e7..8e84b0b 100644
--- a/packages/SettingsLib/res/values-fr/strings.xml
+++ b/packages/SettingsLib/res/values-fr/strings.xml
@@ -107,10 +107,8 @@
<string name="bluetooth_profile_opp" msgid="6692618568149493430">"Transfert de fichiers"</string>
<string name="bluetooth_profile_hid" msgid="2969922922664315866">"Périphérique d\'entrée"</string>
<string name="bluetooth_profile_pan" msgid="1006235139308318188">"Accès Internet"</string>
- <!-- no translation found for bluetooth_profile_pbap (2103406516858653017) -->
- <skip />
- <!-- no translation found for bluetooth_profile_pbap_summary (402819589201138227) -->
- <skip />
+ <string name="bluetooth_profile_pbap" msgid="2103406516858653017">"Autoriser l\'accès aux contacts et à l\'historique des appels"</string>
+ <string name="bluetooth_profile_pbap_summary" msgid="402819589201138227">"Les infos seront utilisées pour les notifications d\'appels, entre autres"</string>
<string name="bluetooth_profile_pan_nap" msgid="7871974753822470050">"Partage de connexion Internet"</string>
<string name="bluetooth_profile_map" msgid="8907204701162107271">"SMS"</string>
<string name="bluetooth_profile_sap" msgid="8304170950447934386">"Accès à la SIM"</string>
diff --git a/packages/SettingsLib/res/values-gl/strings.xml b/packages/SettingsLib/res/values-gl/strings.xml
index f1d0ed1..d78ab17 100644
--- a/packages/SettingsLib/res/values-gl/strings.xml
+++ b/packages/SettingsLib/res/values-gl/strings.xml
@@ -107,10 +107,8 @@
<string name="bluetooth_profile_opp" msgid="6692618568149493430">"Transferencia de ficheiros"</string>
<string name="bluetooth_profile_hid" msgid="2969922922664315866">"Dispositivo de entrada"</string>
<string name="bluetooth_profile_pan" msgid="1006235139308318188">"Acceso a Internet"</string>
- <!-- no translation found for bluetooth_profile_pbap (2103406516858653017) -->
- <skip />
- <!-- no translation found for bluetooth_profile_pbap_summary (402819589201138227) -->
- <skip />
+ <string name="bluetooth_profile_pbap" msgid="2103406516858653017">"Permitir acceso a contactos e historial de chamadas"</string>
+ <string name="bluetooth_profile_pbap_summary" msgid="402819589201138227">"A información usarase para avisos de chamadas e moito máis"</string>
<string name="bluetooth_profile_pan_nap" msgid="7871974753822470050">"Uso compartido da conexión a Internet"</string>
<string name="bluetooth_profile_map" msgid="8907204701162107271">"Mensaxes de texto"</string>
<string name="bluetooth_profile_sap" msgid="8304170950447934386">"Acceso á SIM"</string>
diff --git a/packages/SettingsLib/res/values-gu/strings.xml b/packages/SettingsLib/res/values-gu/strings.xml
index 9f84dd4..2301bfd 100644
--- a/packages/SettingsLib/res/values-gu/strings.xml
+++ b/packages/SettingsLib/res/values-gu/strings.xml
@@ -107,10 +107,8 @@
<string name="bluetooth_profile_opp" msgid="6692618568149493430">"ફાઇલ સ્થાનાંતરણ"</string>
<string name="bluetooth_profile_hid" msgid="2969922922664315866">"ઇનપુટ ડિવાઇસ"</string>
<string name="bluetooth_profile_pan" msgid="1006235139308318188">"ઇન્ટરનેટ ઍક્સેસ"</string>
- <!-- no translation found for bluetooth_profile_pbap (2103406516858653017) -->
- <skip />
- <!-- no translation found for bluetooth_profile_pbap_summary (402819589201138227) -->
- <skip />
+ <string name="bluetooth_profile_pbap" msgid="2103406516858653017">"સંપર્કો અને કૉલ ઇતિહાસ ઍક્સેસ કરવા દો"</string>
+ <string name="bluetooth_profile_pbap_summary" msgid="402819589201138227">"માહિતીનો ઉપયોગ કૉલની ઘોષણાઓ અને વધુ બાબતો માટે કરવામાં આવશે"</string>
<string name="bluetooth_profile_pan_nap" msgid="7871974753822470050">"ઇન્ટરનેટ કનેક્શન શેરિંગ"</string>
<string name="bluetooth_profile_map" msgid="8907204701162107271">"ટેક્સ્ટ સંદેશા"</string>
<string name="bluetooth_profile_sap" msgid="8304170950447934386">"સિમ ઍક્સેસ"</string>
diff --git a/packages/SettingsLib/res/values-hi/strings.xml b/packages/SettingsLib/res/values-hi/strings.xml
index f417f8e..363793c 100644
--- a/packages/SettingsLib/res/values-hi/strings.xml
+++ b/packages/SettingsLib/res/values-hi/strings.xml
@@ -107,10 +107,8 @@
<string name="bluetooth_profile_opp" msgid="6692618568149493430">"फ़ाइल स्थानांतरण"</string>
<string name="bluetooth_profile_hid" msgid="2969922922664315866">"इनपुट डिवाइस"</string>
<string name="bluetooth_profile_pan" msgid="1006235139308318188">"इंटरनेट ऐक्सेस"</string>
- <!-- no translation found for bluetooth_profile_pbap (2103406516858653017) -->
- <skip />
- <!-- no translation found for bluetooth_profile_pbap_summary (402819589201138227) -->
- <skip />
+ <string name="bluetooth_profile_pbap" msgid="2103406516858653017">"संपर्क और कॉल इतिहास का ऐक्सेस दें"</string>
+ <string name="bluetooth_profile_pbap_summary" msgid="402819589201138227">"जानकारी का इस्तेमाल कॉल की सूचना देने वगैरह के लिए होगा"</string>
<string name="bluetooth_profile_pan_nap" msgid="7871974753822470050">"इंटरनेट कनेक्शन साझाकरण"</string>
<string name="bluetooth_profile_map" msgid="8907204701162107271">"लेख संदेश"</string>
<string name="bluetooth_profile_sap" msgid="8304170950447934386">"सिम ऐक्सेस"</string>
diff --git a/packages/SettingsLib/res/values-hr/strings.xml b/packages/SettingsLib/res/values-hr/strings.xml
index 0da7d1d..8be7676 100644
--- a/packages/SettingsLib/res/values-hr/strings.xml
+++ b/packages/SettingsLib/res/values-hr/strings.xml
@@ -107,10 +107,8 @@
<string name="bluetooth_profile_opp" msgid="6692618568149493430">"Prijenos datoteke"</string>
<string name="bluetooth_profile_hid" msgid="2969922922664315866">"Ulazni uređaj"</string>
<string name="bluetooth_profile_pan" msgid="1006235139308318188">"Pristup internetu"</string>
- <!-- no translation found for bluetooth_profile_pbap (2103406516858653017) -->
- <skip />
- <!-- no translation found for bluetooth_profile_pbap_summary (402819589201138227) -->
- <skip />
+ <string name="bluetooth_profile_pbap" msgid="2103406516858653017">"Dopusti pristup kontaktima i povijesti poziva"</string>
+ <string name="bluetooth_profile_pbap_summary" msgid="402819589201138227">"Podaci će se koristiti za najave poziva i drugo"</string>
<string name="bluetooth_profile_pan_nap" msgid="7871974753822470050">"Dijeljenje internetske veze"</string>
<string name="bluetooth_profile_map" msgid="8907204701162107271">"SMS-ovi"</string>
<string name="bluetooth_profile_sap" msgid="8304170950447934386">"Pristup SIM-u"</string>
diff --git a/packages/SettingsLib/res/values-hu/strings.xml b/packages/SettingsLib/res/values-hu/strings.xml
index 1bca6a0..6885a07 100644
--- a/packages/SettingsLib/res/values-hu/strings.xml
+++ b/packages/SettingsLib/res/values-hu/strings.xml
@@ -107,10 +107,8 @@
<string name="bluetooth_profile_opp" msgid="6692618568149493430">"Fájlátvitel"</string>
<string name="bluetooth_profile_hid" msgid="2969922922664315866">"Beviteli eszköz"</string>
<string name="bluetooth_profile_pan" msgid="1006235139308318188">"Internetelérés"</string>
- <!-- no translation found for bluetooth_profile_pbap (2103406516858653017) -->
- <skip />
- <!-- no translation found for bluetooth_profile_pbap_summary (402819589201138227) -->
- <skip />
+ <string name="bluetooth_profile_pbap" msgid="2103406516858653017">"Hozzáférés a címtárhoz és híváslistához"</string>
+ <string name="bluetooth_profile_pbap_summary" msgid="402819589201138227">"Az információk pl. a hívásértesítéshez lesznek felhasználva"</string>
<string name="bluetooth_profile_pan_nap" msgid="7871974753822470050">"Internetkapcsolat megosztása"</string>
<string name="bluetooth_profile_map" msgid="8907204701162107271">"Szöveges üzenetek"</string>
<string name="bluetooth_profile_sap" msgid="8304170950447934386">"SIM-elérés"</string>
diff --git a/packages/SettingsLib/res/values-hy/strings.xml b/packages/SettingsLib/res/values-hy/strings.xml
index a5bccea..c77769e 100644
--- a/packages/SettingsLib/res/values-hy/strings.xml
+++ b/packages/SettingsLib/res/values-hy/strings.xml
@@ -107,10 +107,8 @@
<string name="bluetooth_profile_opp" msgid="6692618568149493430">"Ֆայլերի փոխանցում"</string>
<string name="bluetooth_profile_hid" msgid="2969922922664315866">"Ներմուծման սարք"</string>
<string name="bluetooth_profile_pan" msgid="1006235139308318188">"Ինտերնետի հասանելիություն"</string>
- <!-- no translation found for bluetooth_profile_pbap (2103406516858653017) -->
- <skip />
- <!-- no translation found for bluetooth_profile_pbap_summary (402819589201138227) -->
- <skip />
+ <string name="bluetooth_profile_pbap" msgid="2103406516858653017">"Տրամադրել կոնտակտները և զանգերի պատմութ․"</string>
+ <string name="bluetooth_profile_pbap_summary" msgid="402819589201138227">"Տվյալները կօգտագործվեն զանգերի ծանուցումների համար և այլն"</string>
<string name="bluetooth_profile_pan_nap" msgid="7871974753822470050">"Ինտերնետ կապի տարածում"</string>
<string name="bluetooth_profile_map" msgid="8907204701162107271">"SMS հաղորդագրություններ"</string>
<string name="bluetooth_profile_sap" msgid="8304170950447934386">"SIM քարտի հասանելիություն"</string>
diff --git a/packages/SettingsLib/res/values-in/strings.xml b/packages/SettingsLib/res/values-in/strings.xml
index d9c0a06..2168e8f 100644
--- a/packages/SettingsLib/res/values-in/strings.xml
+++ b/packages/SettingsLib/res/values-in/strings.xml
@@ -107,10 +107,8 @@
<string name="bluetooth_profile_opp" msgid="6692618568149493430">"Transfer file"</string>
<string name="bluetooth_profile_hid" msgid="2969922922664315866">"Perangkat masukan"</string>
<string name="bluetooth_profile_pan" msgid="1006235139308318188">"Akses Internet"</string>
- <!-- no translation found for bluetooth_profile_pbap (2103406516858653017) -->
- <skip />
- <!-- no translation found for bluetooth_profile_pbap_summary (402819589201138227) -->
- <skip />
+ <string name="bluetooth_profile_pbap" msgid="2103406516858653017">"Izinkan akses ke kontak dan histori panggilan"</string>
+ <string name="bluetooth_profile_pbap_summary" msgid="402819589201138227">"Info akan digunakan untuk pengumuman panggilan dan lainnya"</string>
<string name="bluetooth_profile_pan_nap" msgid="7871974753822470050">"Berbagi koneksi internet"</string>
<string name="bluetooth_profile_map" msgid="8907204701162107271">"SMS"</string>
<string name="bluetooth_profile_sap" msgid="8304170950447934386">"Akses SIM"</string>
diff --git a/packages/SettingsLib/res/values-is/strings.xml b/packages/SettingsLib/res/values-is/strings.xml
index 7e746ff..46eb90c 100644
--- a/packages/SettingsLib/res/values-is/strings.xml
+++ b/packages/SettingsLib/res/values-is/strings.xml
@@ -107,10 +107,8 @@
<string name="bluetooth_profile_opp" msgid="6692618568149493430">"Skráaflutningur"</string>
<string name="bluetooth_profile_hid" msgid="2969922922664315866">"Inntakstæki"</string>
<string name="bluetooth_profile_pan" msgid="1006235139308318188">"Internetaðgangur"</string>
- <!-- no translation found for bluetooth_profile_pbap (2103406516858653017) -->
- <skip />
- <!-- no translation found for bluetooth_profile_pbap_summary (402819589201138227) -->
- <skip />
+ <string name="bluetooth_profile_pbap" msgid="2103406516858653017">"Veita aðgang að tengiliðum og símtalaferli"</string>
+ <string name="bluetooth_profile_pbap_summary" msgid="402819589201138227">"Upplýsingar verða notaðar fyrir símtalatilkynningar og fleira"</string>
<string name="bluetooth_profile_pan_nap" msgid="7871974753822470050">"Deiling nettengingar"</string>
<string name="bluetooth_profile_map" msgid="8907204701162107271">"Textaskilaboð"</string>
<string name="bluetooth_profile_sap" msgid="8304170950447934386">"Aðgangur að SIM-korti"</string>
diff --git a/packages/SettingsLib/res/values-it/strings.xml b/packages/SettingsLib/res/values-it/strings.xml
index 1e4b981..bbe0412 100644
--- a/packages/SettingsLib/res/values-it/strings.xml
+++ b/packages/SettingsLib/res/values-it/strings.xml
@@ -107,10 +107,8 @@
<string name="bluetooth_profile_opp" msgid="6692618568149493430">"Trasferimento file"</string>
<string name="bluetooth_profile_hid" msgid="2969922922664315866">"Dispositivo di input"</string>
<string name="bluetooth_profile_pan" msgid="1006235139308318188">"Accesso a Internet"</string>
- <!-- no translation found for bluetooth_profile_pbap (2103406516858653017) -->
- <skip />
- <!-- no translation found for bluetooth_profile_pbap_summary (402819589201138227) -->
- <skip />
+ <string name="bluetooth_profile_pbap" msgid="2103406516858653017">"Consenti l\'accesso ai contatti e alla cronologia delle chiamate"</string>
+ <string name="bluetooth_profile_pbap_summary" msgid="402819589201138227">"Le informazioni saranno utilizzate per gli annunci delle chiamate e altro ancora"</string>
<string name="bluetooth_profile_pan_nap" msgid="7871974753822470050">"Condivisione connessione Internet"</string>
<string name="bluetooth_profile_map" msgid="8907204701162107271">"SMS"</string>
<string name="bluetooth_profile_sap" msgid="8304170950447934386">"Accesso alla SIM"</string>
diff --git a/packages/SettingsLib/res/values-iw/strings.xml b/packages/SettingsLib/res/values-iw/strings.xml
index 328cc82..85e3a53 100644
--- a/packages/SettingsLib/res/values-iw/strings.xml
+++ b/packages/SettingsLib/res/values-iw/strings.xml
@@ -107,10 +107,8 @@
<string name="bluetooth_profile_opp" msgid="6692618568149493430">"העברת קבצים"</string>
<string name="bluetooth_profile_hid" msgid="2969922922664315866">"מכשיר קלט"</string>
<string name="bluetooth_profile_pan" msgid="1006235139308318188">"גישה לאינטרנט"</string>
- <!-- no translation found for bluetooth_profile_pbap (2103406516858653017) -->
- <skip />
- <!-- no translation found for bluetooth_profile_pbap_summary (402819589201138227) -->
- <skip />
+ <string name="bluetooth_profile_pbap" msgid="2103406516858653017">"אישור גישה אל אנשי קשר והיסטוריית שיחות"</string>
+ <string name="bluetooth_profile_pbap_summary" msgid="402819589201138227">"המידע ישמש להודעות על שיחות ועוד"</string>
<string name="bluetooth_profile_pan_nap" msgid="7871974753822470050">"שיתוף חיבור לאינטרנט"</string>
<string name="bluetooth_profile_map" msgid="8907204701162107271">"הודעות טקסט"</string>
<string name="bluetooth_profile_sap" msgid="8304170950447934386">"גישה ל-SIM"</string>
diff --git a/packages/SettingsLib/res/values-ja/strings.xml b/packages/SettingsLib/res/values-ja/strings.xml
index 46bb17e..71e70ba 100644
--- a/packages/SettingsLib/res/values-ja/strings.xml
+++ b/packages/SettingsLib/res/values-ja/strings.xml
@@ -107,10 +107,8 @@
<string name="bluetooth_profile_opp" msgid="6692618568149493430">"ファイル転送"</string>
<string name="bluetooth_profile_hid" msgid="2969922922664315866">"入力デバイス"</string>
<string name="bluetooth_profile_pan" msgid="1006235139308318188">"インターネットアクセス"</string>
- <!-- no translation found for bluetooth_profile_pbap (2103406516858653017) -->
- <skip />
- <!-- no translation found for bluetooth_profile_pbap_summary (402819589201138227) -->
- <skip />
+ <string name="bluetooth_profile_pbap" msgid="2103406516858653017">"連絡先と通話履歴へのアクセスを許可する"</string>
+ <string name="bluetooth_profile_pbap_summary" msgid="402819589201138227">"情報は着信の通知などに使用されます"</string>
<string name="bluetooth_profile_pan_nap" msgid="7871974753822470050">"インターネット接続の共有"</string>
<string name="bluetooth_profile_map" msgid="8907204701162107271">"テキスト メッセージ"</string>
<string name="bluetooth_profile_sap" msgid="8304170950447934386">"SIMアクセス"</string>
diff --git a/packages/SettingsLib/res/values-ka/strings.xml b/packages/SettingsLib/res/values-ka/strings.xml
index a0205bb..fa7db4a 100644
--- a/packages/SettingsLib/res/values-ka/strings.xml
+++ b/packages/SettingsLib/res/values-ka/strings.xml
@@ -107,10 +107,8 @@
<string name="bluetooth_profile_opp" msgid="6692618568149493430">"ფაილების გადაცემა"</string>
<string name="bluetooth_profile_hid" msgid="2969922922664315866">"შეყვანის მოწყობილობა"</string>
<string name="bluetooth_profile_pan" msgid="1006235139308318188">"ინტერნეტზე წვდომა"</string>
- <!-- no translation found for bluetooth_profile_pbap (2103406516858653017) -->
- <skip />
- <!-- no translation found for bluetooth_profile_pbap_summary (402819589201138227) -->
- <skip />
+ <string name="bluetooth_profile_pbap" msgid="2103406516858653017">"კონტაქტებსა და საუბრის ისტორიაზე წვდომის დაშვება"</string>
+ <string name="bluetooth_profile_pbap_summary" msgid="402819589201138227">"ინფორმაცია გამოყენებული იქნება ზარის გახმოვანებისა და სხვა მიზნებისთვის"</string>
<string name="bluetooth_profile_pan_nap" msgid="7871974753822470050">"ინტერნეტ კავშირის გაზიარება"</string>
<string name="bluetooth_profile_map" msgid="8907204701162107271">"ტექსტური შეტყობინებები"</string>
<string name="bluetooth_profile_sap" msgid="8304170950447934386">"SIM წვდომა"</string>
diff --git a/packages/SettingsLib/res/values-kk/strings.xml b/packages/SettingsLib/res/values-kk/strings.xml
index 3b240d4..efac4e8 100644
--- a/packages/SettingsLib/res/values-kk/strings.xml
+++ b/packages/SettingsLib/res/values-kk/strings.xml
@@ -107,10 +107,8 @@
<string name="bluetooth_profile_opp" msgid="6692618568149493430">"Файл жіберу"</string>
<string name="bluetooth_profile_hid" msgid="2969922922664315866">"Кіріс құрылғысы"</string>
<string name="bluetooth_profile_pan" msgid="1006235139308318188">"Интернетке қосылу"</string>
- <!-- no translation found for bluetooth_profile_pbap (2103406516858653017) -->
- <skip />
- <!-- no translation found for bluetooth_profile_pbap_summary (402819589201138227) -->
- <skip />
+ <string name="bluetooth_profile_pbap" msgid="2103406516858653017">"Контакт пен қоңырау тарихына рұқсат беру"</string>
+ <string name="bluetooth_profile_pbap_summary" msgid="402819589201138227">"Ақпарат қоңырау туралы хабарландыру, т.б. үшін қолданылады."</string>
<string name="bluetooth_profile_pan_nap" msgid="7871974753822470050">"Интернет байланысын ортақ қолдану"</string>
<string name="bluetooth_profile_map" msgid="8907204701162107271">"Мәтіндік хабарлар"</string>
<string name="bluetooth_profile_sap" msgid="8304170950447934386">"SIM картасына кіру"</string>
diff --git a/packages/SettingsLib/res/values-km/strings.xml b/packages/SettingsLib/res/values-km/strings.xml
index 36c25a9..11c71ca 100644
--- a/packages/SettingsLib/res/values-km/strings.xml
+++ b/packages/SettingsLib/res/values-km/strings.xml
@@ -107,10 +107,8 @@
<string name="bluetooth_profile_opp" msgid="6692618568149493430">"ផ្ទេរឯកសារ"</string>
<string name="bluetooth_profile_hid" msgid="2969922922664315866">"ឧបករណ៍បញ្ចូល"</string>
<string name="bluetooth_profile_pan" msgid="1006235139308318188">"ការចូលប្រើអ៊ីនធឺណិត"</string>
- <!-- no translation found for bluetooth_profile_pbap (2103406516858653017) -->
- <skip />
- <!-- no translation found for bluetooth_profile_pbap_summary (402819589201138227) -->
- <skip />
+ <string name="bluetooth_profile_pbap" msgid="2103406516858653017">"អនុញ្ញាតឱ្យចូលប្រើទំនាក់ទំនង និងប្រវត្តិហៅទូរសព្ទ"</string>
+ <string name="bluetooth_profile_pbap_summary" msgid="402819589201138227">"ព័ត៌មាននឹងត្រូវបានប្រើសម្រាប់ការប្រកាសអំពីការហៅទូរសព្ទ និងច្រើនទៀត"</string>
<string name="bluetooth_profile_pan_nap" msgid="7871974753822470050">"ចែករំលែកការតភ្ជាប់អ៊ីនធឺណិត"</string>
<string name="bluetooth_profile_map" msgid="8907204701162107271">"សារជាអក្សរ"</string>
<string name="bluetooth_profile_sap" msgid="8304170950447934386">"ការចូលដំណើរការស៊ីម"</string>
diff --git a/packages/SettingsLib/res/values-kn/strings.xml b/packages/SettingsLib/res/values-kn/strings.xml
index aa93942..fa3dbaf 100644
--- a/packages/SettingsLib/res/values-kn/strings.xml
+++ b/packages/SettingsLib/res/values-kn/strings.xml
@@ -107,10 +107,8 @@
<string name="bluetooth_profile_opp" msgid="6692618568149493430">"ಫೈಲ್ ವರ್ಗಾವಣೆ"</string>
<string name="bluetooth_profile_hid" msgid="2969922922664315866">"ಇನ್ಪುಟ್ ಸಾಧನ"</string>
<string name="bluetooth_profile_pan" msgid="1006235139308318188">"ಇಂಟರ್ನೆಟ್ ಆ್ಯಕ್ಸೆಸ್"</string>
- <!-- no translation found for bluetooth_profile_pbap (2103406516858653017) -->
- <skip />
- <!-- no translation found for bluetooth_profile_pbap_summary (402819589201138227) -->
- <skip />
+ <string name="bluetooth_profile_pbap" msgid="2103406516858653017">"ಸಂಪರ್ಕಗಳು, ಕರೆ ಇತಿಹಾಸಕ್ಕೆ ಆ್ಯಕ್ಸೆಸ್ ನೀಡಿ"</string>
+ <string name="bluetooth_profile_pbap_summary" msgid="402819589201138227">"ಮಾಹಿತಿಯನ್ನು ಕರೆ ಪ್ರಕಟಣೆಗಳು ಹಾಗೂ ಇತ್ಯಾದಿಗಳಿಗಾಗಿ ಬಳಸಲಾಗುತ್ತದೆ"</string>
<string name="bluetooth_profile_pan_nap" msgid="7871974753822470050">"ಇಂಟರ್ನೆಟ್ ಸಂಪರ್ಕ ಹಂಚಿಕೊಳ್ಳುವಿಕೆ"</string>
<string name="bluetooth_profile_map" msgid="8907204701162107271">"ಪಠ್ಯ ಸಂದೇಶಗಳು"</string>
<string name="bluetooth_profile_sap" msgid="8304170950447934386">"ಸಿಮ್ ಆ್ಯಕ್ಸೆಸ್"</string>
diff --git a/packages/SettingsLib/res/values-ko/strings.xml b/packages/SettingsLib/res/values-ko/strings.xml
index 2b2a09f..f484133 100644
--- a/packages/SettingsLib/res/values-ko/strings.xml
+++ b/packages/SettingsLib/res/values-ko/strings.xml
@@ -107,10 +107,8 @@
<string name="bluetooth_profile_opp" msgid="6692618568149493430">"파일 전송"</string>
<string name="bluetooth_profile_hid" msgid="2969922922664315866">"입력 장치"</string>
<string name="bluetooth_profile_pan" msgid="1006235139308318188">"인터넷 액세스"</string>
- <!-- no translation found for bluetooth_profile_pbap (2103406516858653017) -->
- <skip />
- <!-- no translation found for bluetooth_profile_pbap_summary (402819589201138227) -->
- <skip />
+ <string name="bluetooth_profile_pbap" msgid="2103406516858653017">"연락처 및 통화 기록에 대한 액세스를 허용합니다."</string>
+ <string name="bluetooth_profile_pbap_summary" msgid="402819589201138227">"정보는 전화 알림 등에 사용됩니다."</string>
<string name="bluetooth_profile_pan_nap" msgid="7871974753822470050">"인터넷 연결 공유"</string>
<string name="bluetooth_profile_map" msgid="8907204701162107271">"문자 메시지"</string>
<string name="bluetooth_profile_sap" msgid="8304170950447934386">"SIM 액세스"</string>
diff --git a/packages/SettingsLib/res/values-ky/strings.xml b/packages/SettingsLib/res/values-ky/strings.xml
index 92df614..bde9b08 100644
--- a/packages/SettingsLib/res/values-ky/strings.xml
+++ b/packages/SettingsLib/res/values-ky/strings.xml
@@ -107,10 +107,8 @@
<string name="bluetooth_profile_opp" msgid="6692618568149493430">"Файл алмашуу"</string>
<string name="bluetooth_profile_hid" msgid="2969922922664315866">"Киргизүү түзмөгү"</string>
<string name="bluetooth_profile_pan" msgid="1006235139308318188">"Интернетке мүмкүнчүлүк алуу"</string>
- <!-- no translation found for bluetooth_profile_pbap (2103406516858653017) -->
- <skip />
- <!-- no translation found for bluetooth_profile_pbap_summary (402819589201138227) -->
- <skip />
+ <string name="bluetooth_profile_pbap" msgid="2103406516858653017">"Байланыштар менен чалуулар таржымалына мүмкүнчүлүк берүү"</string>
+ <string name="bluetooth_profile_pbap_summary" msgid="402819589201138227">"Маалымат чалуу билдирмелери жана башкалар үчүн колдонулат"</string>
<string name="bluetooth_profile_pan_nap" msgid="7871974753822470050">"Интернет байланышын бөлүшүү"</string>
<string name="bluetooth_profile_map" msgid="8907204701162107271">"SMS билдирүүлөрү"</string>
<string name="bluetooth_profile_sap" msgid="8304170950447934386">"SIM картаны пайдалануу мүмкүнчүлүгү"</string>
diff --git a/packages/SettingsLib/res/values-lo/strings.xml b/packages/SettingsLib/res/values-lo/strings.xml
index fdd393d..2f4e7ff 100644
--- a/packages/SettingsLib/res/values-lo/strings.xml
+++ b/packages/SettingsLib/res/values-lo/strings.xml
@@ -107,10 +107,8 @@
<string name="bluetooth_profile_opp" msgid="6692618568149493430">"ການໂອນຍ້າຍໄຟລ໌"</string>
<string name="bluetooth_profile_hid" msgid="2969922922664315866">"ອຸປະກອນປ້ອນຂໍ້ມູນ"</string>
<string name="bluetooth_profile_pan" msgid="1006235139308318188">"ການເຂົ້າເຖິງອິນເຕີເນັດ"</string>
- <!-- no translation found for bluetooth_profile_pbap (2103406516858653017) -->
- <skip />
- <!-- no translation found for bluetooth_profile_pbap_summary (402819589201138227) -->
- <skip />
+ <string name="bluetooth_profile_pbap" msgid="2103406516858653017">"ອະນຸຍາດໃຫ້ເຂົ້າເຖິງລາຍຊື່ຜູ້ຕິດຕໍ່ ແລະ ປະຫວັດການໂທ"</string>
+ <string name="bluetooth_profile_pbap_summary" msgid="402819589201138227">"ຂໍ້ມູນຈະຖືກໃຊ້ສຳລັບປະກາດການໂທ ແລະ ອື່ນໆ"</string>
<string name="bluetooth_profile_pan_nap" msgid="7871974753822470050">"ການແບ່ງປັນການເຊື່ອມຕໍ່ອິນເຕີເນັດ"</string>
<string name="bluetooth_profile_map" msgid="8907204701162107271">"ຂໍ້ຄວາມ"</string>
<string name="bluetooth_profile_sap" msgid="8304170950447934386">"ການເຂົ້າເຖິງ SIM"</string>
diff --git a/packages/SettingsLib/res/values-lt/strings.xml b/packages/SettingsLib/res/values-lt/strings.xml
index 7376c11..59ab47b 100644
--- a/packages/SettingsLib/res/values-lt/strings.xml
+++ b/packages/SettingsLib/res/values-lt/strings.xml
@@ -107,10 +107,8 @@
<string name="bluetooth_profile_opp" msgid="6692618568149493430">"Failo perkėlimas"</string>
<string name="bluetooth_profile_hid" msgid="2969922922664315866">"Įvesties įrenginys"</string>
<string name="bluetooth_profile_pan" msgid="1006235139308318188">"Prieiga prie interneto"</string>
- <!-- no translation found for bluetooth_profile_pbap (2103406516858653017) -->
- <skip />
- <!-- no translation found for bluetooth_profile_pbap_summary (402819589201138227) -->
- <skip />
+ <string name="bluetooth_profile_pbap" msgid="2103406516858653017">"Leisti pasiekti kont. ir skamb. istoriją"</string>
+ <string name="bluetooth_profile_pbap_summary" msgid="402819589201138227">"Informacija bus naudojama skambučių pranešimams ir kt."</string>
<string name="bluetooth_profile_pan_nap" msgid="7871974753822470050">"Interneto ryšio bendrinimas"</string>
<string name="bluetooth_profile_map" msgid="8907204701162107271">"Teksto pranešimai"</string>
<string name="bluetooth_profile_sap" msgid="8304170950447934386">"SIM prieiga"</string>
diff --git a/packages/SettingsLib/res/values-lv/strings.xml b/packages/SettingsLib/res/values-lv/strings.xml
index 85ca364..a04bd54 100644
--- a/packages/SettingsLib/res/values-lv/strings.xml
+++ b/packages/SettingsLib/res/values-lv/strings.xml
@@ -107,10 +107,8 @@
<string name="bluetooth_profile_opp" msgid="6692618568149493430">"Failu pārsūtīšana"</string>
<string name="bluetooth_profile_hid" msgid="2969922922664315866">"Ievades ierīce"</string>
<string name="bluetooth_profile_pan" msgid="1006235139308318188">"Piekļuve internetam"</string>
- <!-- no translation found for bluetooth_profile_pbap (2103406516858653017) -->
- <skip />
- <!-- no translation found for bluetooth_profile_pbap_summary (402819589201138227) -->
- <skip />
+ <string name="bluetooth_profile_pbap" msgid="2103406516858653017">"Atļaujiet piekļuvi kontaktpersonām un vēsturei."</string>
+ <string name="bluetooth_profile_pbap_summary" msgid="402819589201138227">"Informācija tiks izmantota paziņojumiem par zvaniem u.c."</string>
<string name="bluetooth_profile_pan_nap" msgid="7871974753822470050">"Interneta savienojuma koplietošana"</string>
<string name="bluetooth_profile_map" msgid="8907204701162107271">"Īsziņas"</string>
<string name="bluetooth_profile_sap" msgid="8304170950447934386">"Piekļuve SIM kartei"</string>
diff --git a/packages/SettingsLib/res/values-mk/strings.xml b/packages/SettingsLib/res/values-mk/strings.xml
index cdc0831..37a448f 100644
--- a/packages/SettingsLib/res/values-mk/strings.xml
+++ b/packages/SettingsLib/res/values-mk/strings.xml
@@ -107,10 +107,8 @@
<string name="bluetooth_profile_opp" msgid="6692618568149493430">"Пренос на датотека"</string>
<string name="bluetooth_profile_hid" msgid="2969922922664315866">"Влезен уред"</string>
<string name="bluetooth_profile_pan" msgid="1006235139308318188">"Пристап до интернет"</string>
- <!-- no translation found for bluetooth_profile_pbap (2103406516858653017) -->
- <skip />
- <!-- no translation found for bluetooth_profile_pbap_summary (402819589201138227) -->
- <skip />
+ <string name="bluetooth_profile_pbap" msgid="2103406516858653017">"Дозволете пристап до контактите и историјата на повици"</string>
+ <string name="bluetooth_profile_pbap_summary" msgid="402819589201138227">"Податоците ќе се користат за известувања за повици и друго"</string>
<string name="bluetooth_profile_pan_nap" msgid="7871974753822470050">"Споделување конекција на интернет"</string>
<string name="bluetooth_profile_map" msgid="8907204701162107271">"Текстуални пораки"</string>
<string name="bluetooth_profile_sap" msgid="8304170950447934386">"Пристап до SIM"</string>
diff --git a/packages/SettingsLib/res/values-ml/strings.xml b/packages/SettingsLib/res/values-ml/strings.xml
index fe7a375..452a995 100644
--- a/packages/SettingsLib/res/values-ml/strings.xml
+++ b/packages/SettingsLib/res/values-ml/strings.xml
@@ -107,10 +107,8 @@
<string name="bluetooth_profile_opp" msgid="6692618568149493430">"ഫയൽ കൈമാറൽ"</string>
<string name="bluetooth_profile_hid" msgid="2969922922664315866">"ഇൻപുട്ട് ഉപകരണം"</string>
<string name="bluetooth_profile_pan" msgid="1006235139308318188">"ഇന്റർനെറ്റ് ആക്സസ്"</string>
- <!-- no translation found for bluetooth_profile_pbap (2103406516858653017) -->
- <skip />
- <!-- no translation found for bluetooth_profile_pbap_summary (402819589201138227) -->
- <skip />
+ <string name="bluetooth_profile_pbap" msgid="2103406516858653017">"കോൺടാക്റ്റുകളിലേക്കും കോൾ ചരിത്രത്തിലേക്കും ആക്സസ് അനുവദിക്കൂ"</string>
+ <string name="bluetooth_profile_pbap_summary" msgid="402819589201138227">"വിവരങ്ങൾ, കോൾ അറിയിപ്പുകൾക്കും മറ്റും ഉപയോഗിക്കും"</string>
<string name="bluetooth_profile_pan_nap" msgid="7871974753822470050">"ഇന്റർനെറ്റ് കണക്ഷൻ പങ്കിടൽ"</string>
<string name="bluetooth_profile_map" msgid="8907204701162107271">"അക്ഷര സന്ദേശങ്ങൾ"</string>
<string name="bluetooth_profile_sap" msgid="8304170950447934386">"സിം ആക്സസ്"</string>
diff --git a/packages/SettingsLib/res/values-mn/strings.xml b/packages/SettingsLib/res/values-mn/strings.xml
index 23b3c2e..6dfce5a 100644
--- a/packages/SettingsLib/res/values-mn/strings.xml
+++ b/packages/SettingsLib/res/values-mn/strings.xml
@@ -107,10 +107,8 @@
<string name="bluetooth_profile_opp" msgid="6692618568149493430">"Файл дамжуулалт"</string>
<string name="bluetooth_profile_hid" msgid="2969922922664315866">"Оруулах төхөөрөмж"</string>
<string name="bluetooth_profile_pan" msgid="1006235139308318188">"Интернэт хандалт"</string>
- <!-- no translation found for bluetooth_profile_pbap (2103406516858653017) -->
- <skip />
- <!-- no translation found for bluetooth_profile_pbap_summary (402819589201138227) -->
- <skip />
+ <string name="bluetooth_profile_pbap" msgid="2103406516858653017">"Харилцагч, дуудлагын түүхэд хандахыг зөвшөөр"</string>
+ <string name="bluetooth_profile_pbap_summary" msgid="402819589201138227">"Мэдээллийг дуудлагын мэдэгдэл болон бусад зүйлд ашиглана"</string>
<string name="bluetooth_profile_pan_nap" msgid="7871974753822470050">"Интернэт холболтыг хуваалцах"</string>
<string name="bluetooth_profile_map" msgid="8907204701162107271">"Мессеж"</string>
<string name="bluetooth_profile_sap" msgid="8304170950447934386">"SIM Хандалт"</string>
diff --git a/packages/SettingsLib/res/values-mr/strings.xml b/packages/SettingsLib/res/values-mr/strings.xml
index 8e10da7..4db523a 100644
--- a/packages/SettingsLib/res/values-mr/strings.xml
+++ b/packages/SettingsLib/res/values-mr/strings.xml
@@ -107,10 +107,8 @@
<string name="bluetooth_profile_opp" msgid="6692618568149493430">"फाइल स्थानांतरण"</string>
<string name="bluetooth_profile_hid" msgid="2969922922664315866">"इनपुट डिव्हाइस"</string>
<string name="bluetooth_profile_pan" msgid="1006235139308318188">"इंटरनेट अॅक्सेस"</string>
- <!-- no translation found for bluetooth_profile_pbap (2103406516858653017) -->
- <skip />
- <!-- no translation found for bluetooth_profile_pbap_summary (402819589201138227) -->
- <skip />
+ <string name="bluetooth_profile_pbap" msgid="2103406516858653017">"संपर्क आणि कॉल इतिहास अॅक्सेस करण्याची अनुमती द्या"</string>
+ <string name="bluetooth_profile_pbap_summary" msgid="402819589201138227">"कॉलसंबंधित घोषणा आणि आणखी बऱ्याच गोष्टींसाठी माहिती वापरली जाईल"</string>
<string name="bluetooth_profile_pan_nap" msgid="7871974753822470050">"इंटरनेट कनेक्शन शेअररण"</string>
<string name="bluetooth_profile_map" msgid="8907204701162107271">"मजकूर मेसेज"</string>
<string name="bluetooth_profile_sap" msgid="8304170950447934386">"सिम अॅक्सेस"</string>
diff --git a/packages/SettingsLib/res/values-ms/strings.xml b/packages/SettingsLib/res/values-ms/strings.xml
index 70d99a1..ef21812 100644
--- a/packages/SettingsLib/res/values-ms/strings.xml
+++ b/packages/SettingsLib/res/values-ms/strings.xml
@@ -107,10 +107,8 @@
<string name="bluetooth_profile_opp" msgid="6692618568149493430">"Pemindahan fail"</string>
<string name="bluetooth_profile_hid" msgid="2969922922664315866">"Peranti input"</string>
<string name="bluetooth_profile_pan" msgid="1006235139308318188">"Akses Internet"</string>
- <!-- no translation found for bluetooth_profile_pbap (2103406516858653017) -->
- <skip />
- <!-- no translation found for bluetooth_profile_pbap_summary (402819589201138227) -->
- <skip />
+ <string name="bluetooth_profile_pbap" msgid="2103406516858653017">"Benarkan akses kepada kenalan dan sejarah panggilan"</string>
+ <string name="bluetooth_profile_pbap_summary" msgid="402819589201138227">"Maklumat digunakan untuk makluman panggilan dan banyak lagi"</string>
<string name="bluetooth_profile_pan_nap" msgid="7871974753822470050">"Perkongsian sambungan Internet"</string>
<string name="bluetooth_profile_map" msgid="8907204701162107271">"Mesej Teks"</string>
<string name="bluetooth_profile_sap" msgid="8304170950447934386">"Akses SIM"</string>
diff --git a/packages/SettingsLib/res/values-my/strings.xml b/packages/SettingsLib/res/values-my/strings.xml
index 8c596ad..2e4a6c4 100644
--- a/packages/SettingsLib/res/values-my/strings.xml
+++ b/packages/SettingsLib/res/values-my/strings.xml
@@ -107,10 +107,8 @@
<string name="bluetooth_profile_opp" msgid="6692618568149493430">"ဖိုင်လွဲပြောင်းခြင်း"</string>
<string name="bluetooth_profile_hid" msgid="2969922922664315866">"ထည့်သွင်းသော စက်"</string>
<string name="bluetooth_profile_pan" msgid="1006235139308318188">"အင်တာနက်ချိတ်ဆက်ခြင်း"</string>
- <!-- no translation found for bluetooth_profile_pbap (2103406516858653017) -->
- <skip />
- <!-- no translation found for bluetooth_profile_pbap_summary (402819589201138227) -->
- <skip />
+ <string name="bluetooth_profile_pbap" msgid="2103406516858653017">"အဆက်အသွယ်၊ ခေါ်ဆိုမှုမှတ်တမ်း သုံးခွင့်ပေးရန်"</string>
+ <string name="bluetooth_profile_pbap_summary" msgid="402819589201138227">"အချက်အလက်ကို ဖုန်းခေါ်ဆိုမှု ကြေညာချက်စသည်တို့တွင် သုံးမည်"</string>
<string name="bluetooth_profile_pan_nap" msgid="7871974753822470050">"အင်တာနက်ဆက်သွယ်မှု မျှဝေခြင်း"</string>
<string name="bluetooth_profile_map" msgid="8907204701162107271">"မိုဘိုင်းမက်ဆေ့ဂျ်များ"</string>
<string name="bluetooth_profile_sap" msgid="8304170950447934386">"SIM အသုံးပြုခြင်း"</string>
diff --git a/packages/SettingsLib/res/values-nb/strings.xml b/packages/SettingsLib/res/values-nb/strings.xml
index f5b607d..e871ab9 100644
--- a/packages/SettingsLib/res/values-nb/strings.xml
+++ b/packages/SettingsLib/res/values-nb/strings.xml
@@ -107,10 +107,8 @@
<string name="bluetooth_profile_opp" msgid="6692618568149493430">"Filoverføring"</string>
<string name="bluetooth_profile_hid" msgid="2969922922664315866">"Inndataenhet"</string>
<string name="bluetooth_profile_pan" msgid="1006235139308318188">"Internett-tilgang"</string>
- <!-- no translation found for bluetooth_profile_pbap (2103406516858653017) -->
- <skip />
- <!-- no translation found for bluetooth_profile_pbap_summary (402819589201138227) -->
- <skip />
+ <string name="bluetooth_profile_pbap" msgid="2103406516858653017">"Gi tilgang til kontakter og anropsloggen"</string>
+ <string name="bluetooth_profile_pbap_summary" msgid="402819589201138227">"Informasjonen brukes til anropsvarsler med mer"</string>
<string name="bluetooth_profile_pan_nap" msgid="7871974753822470050">"Deling av internettilkobling"</string>
<string name="bluetooth_profile_map" msgid="8907204701162107271">"Tekstmeldinger"</string>
<string name="bluetooth_profile_sap" msgid="8304170950447934386">"Tilgang til SIM-kortet"</string>
diff --git a/packages/SettingsLib/res/values-ne/strings.xml b/packages/SettingsLib/res/values-ne/strings.xml
index a3c121a..a48dd1e 100644
--- a/packages/SettingsLib/res/values-ne/strings.xml
+++ b/packages/SettingsLib/res/values-ne/strings.xml
@@ -107,10 +107,8 @@
<string name="bluetooth_profile_opp" msgid="6692618568149493430">"फाइल स्थानान्तरण"</string>
<string name="bluetooth_profile_hid" msgid="2969922922664315866">"इनपुट उपकरण"</string>
<string name="bluetooth_profile_pan" msgid="1006235139308318188">"इन्टरनेट एक्सेस"</string>
- <!-- no translation found for bluetooth_profile_pbap (2103406516858653017) -->
- <skip />
- <!-- no translation found for bluetooth_profile_pbap_summary (402819589201138227) -->
- <skip />
+ <string name="bluetooth_profile_pbap" msgid="2103406516858653017">"कन्ट्याक्ट र कल हिस्ट्री एक्सेस गर्ने अनुमति दिइयोस्"</string>
+ <string name="bluetooth_profile_pbap_summary" msgid="402819589201138227">"यो जानकारीको प्रयोग कल आएको जानकारी दिने लगायतका कुराका लागि प्रयोग गरिने छ"</string>
<string name="bluetooth_profile_pan_nap" msgid="7871974753822470050">"इन्टरनेट जडान साझेदारी गर्दै"</string>
<string name="bluetooth_profile_map" msgid="8907204701162107271">"टेक्स्ट म्यासेजहरू"</string>
<string name="bluetooth_profile_sap" msgid="8304170950447934386">"SIM एक्सेस"</string>
diff --git a/packages/SettingsLib/res/values-nl/strings.xml b/packages/SettingsLib/res/values-nl/strings.xml
index aa7e7a0..dfc3389 100644
--- a/packages/SettingsLib/res/values-nl/strings.xml
+++ b/packages/SettingsLib/res/values-nl/strings.xml
@@ -107,10 +107,8 @@
<string name="bluetooth_profile_opp" msgid="6692618568149493430">"Bestandsoverdracht"</string>
<string name="bluetooth_profile_hid" msgid="2969922922664315866">"Invoerapparaat"</string>
<string name="bluetooth_profile_pan" msgid="1006235139308318188">"Internettoegang"</string>
- <!-- no translation found for bluetooth_profile_pbap (2103406516858653017) -->
- <skip />
- <!-- no translation found for bluetooth_profile_pbap_summary (402819589201138227) -->
- <skip />
+ <string name="bluetooth_profile_pbap" msgid="2103406516858653017">"Toegang geven tot contacten en gespreksgeschiedenis"</string>
+ <string name="bluetooth_profile_pbap_summary" msgid="402819589201138227">"De informatie wordt onder andere gebruikt voor gespreksaankondigingen"</string>
<string name="bluetooth_profile_pan_nap" msgid="7871974753822470050">"Internetverbinding delen"</string>
<string name="bluetooth_profile_map" msgid="8907204701162107271">"Sms-berichten"</string>
<string name="bluetooth_profile_sap" msgid="8304170950447934386">"Sim-toegang"</string>
diff --git a/packages/SettingsLib/res/values-or/strings.xml b/packages/SettingsLib/res/values-or/strings.xml
index 6154db1..a29fb0f 100644
--- a/packages/SettingsLib/res/values-or/strings.xml
+++ b/packages/SettingsLib/res/values-or/strings.xml
@@ -107,10 +107,8 @@
<string name="bluetooth_profile_opp" msgid="6692618568149493430">"ଫାଇଲ୍ ଟ୍ରାନ୍ସଫର୍"</string>
<string name="bluetooth_profile_hid" msgid="2969922922664315866">"ଇନ୍ପୁଟ୍ ଡିଭାଇସ୍"</string>
<string name="bluetooth_profile_pan" msgid="1006235139308318188">"ଇଣ୍ଟରନେଟ ଆକ୍ସେସ"</string>
- <!-- no translation found for bluetooth_profile_pbap (2103406516858653017) -->
- <skip />
- <!-- no translation found for bluetooth_profile_pbap_summary (402819589201138227) -->
- <skip />
+ <string name="bluetooth_profile_pbap" msgid="2103406516858653017">"କଣ୍ଟାକ୍ଟ ଓ କଲ ଇତିହାସକୁ ଆକ୍ସେସ କରିବା ପାଇଁ ଅନୁମତି ଦିଅନ୍ତୁ"</string>
+ <string name="bluetooth_profile_pbap_summary" msgid="402819589201138227">"ସୂଚନାକୁ କଲ ଘୋଷଣା ଏବଂ ଆହୁରି ଅଧିକ ପାଇଁ ବ୍ୟବହାର କରାଯିବ"</string>
<string name="bluetooth_profile_pan_nap" msgid="7871974753822470050">"ଇଣ୍ଟର୍ନେଟ୍ ସଂଯୋଗ ଶେୟାରିଙ୍ଗ"</string>
<string name="bluetooth_profile_map" msgid="8907204701162107271">"ଟେକ୍ସଟ୍ ମେସେଜ୍"</string>
<string name="bluetooth_profile_sap" msgid="8304170950447934386">"SIM ଆକ୍ସେସ"</string>
diff --git a/packages/SettingsLib/res/values-pa/strings.xml b/packages/SettingsLib/res/values-pa/strings.xml
index 1ab9dda..917d5f8 100644
--- a/packages/SettingsLib/res/values-pa/strings.xml
+++ b/packages/SettingsLib/res/values-pa/strings.xml
@@ -107,10 +107,8 @@
<string name="bluetooth_profile_opp" msgid="6692618568149493430">"ਫਾਈਲ ਟ੍ਰਾਂਸਫਰ"</string>
<string name="bluetooth_profile_hid" msgid="2969922922664315866">"ਇਨਪੁੱਟ ਡੀਵਾਈਸ"</string>
<string name="bluetooth_profile_pan" msgid="1006235139308318188">"ਇੰਟਰਨੈੱਟ ਪਹੁੰਚ"</string>
- <!-- no translation found for bluetooth_profile_pbap (2103406516858653017) -->
- <skip />
- <!-- no translation found for bluetooth_profile_pbap_summary (402819589201138227) -->
- <skip />
+ <string name="bluetooth_profile_pbap" msgid="2103406516858653017">"ਸੰਪਰਕਾਂ ਅਤੇ ਕਾਲ ਇਤਿਹਾਸ \'ਤੇ ਪਹੁੰਚ ਕਰਨ ਦਿਓ"</string>
+ <string name="bluetooth_profile_pbap_summary" msgid="402819589201138227">"ਜਾਣਕਾਰੀ ਦੀ ਵਰਤੋਂ ਕਾਲ ਘੋਸ਼ਣਾਵਾਂ ਅਤੇ ਹੋਰ ਚੀਜ਼ਾਂ ਲਈ ਕੀਤੀ ਜਾਵੇਗੀ"</string>
<string name="bluetooth_profile_pan_nap" msgid="7871974753822470050">"ਇੰਟਰਨੈੱਟ ਕਨੈਕਸ਼ਨ ਸਾਂਝਾਕਰਨ"</string>
<string name="bluetooth_profile_map" msgid="8907204701162107271">"ਲਿਖਤ ਸੁਨੇਹੇ"</string>
<string name="bluetooth_profile_sap" msgid="8304170950447934386">"ਸਿਮ ਪਹੁੰਚ"</string>
diff --git a/packages/SettingsLib/res/values-pl/strings.xml b/packages/SettingsLib/res/values-pl/strings.xml
index c0dce8d..0bf5b70 100644
--- a/packages/SettingsLib/res/values-pl/strings.xml
+++ b/packages/SettingsLib/res/values-pl/strings.xml
@@ -107,10 +107,8 @@
<string name="bluetooth_profile_opp" msgid="6692618568149493430">"Przesyłanie pliku"</string>
<string name="bluetooth_profile_hid" msgid="2969922922664315866">"Urządzenie wejściowe"</string>
<string name="bluetooth_profile_pan" msgid="1006235139308318188">"Dostęp do internetu"</string>
- <!-- no translation found for bluetooth_profile_pbap (2103406516858653017) -->
- <skip />
- <!-- no translation found for bluetooth_profile_pbap_summary (402819589201138227) -->
- <skip />
+ <string name="bluetooth_profile_pbap" msgid="2103406516858653017">"Zezwól na dostęp do kontaktów i historii połączeń"</string>
+ <string name="bluetooth_profile_pbap_summary" msgid="402819589201138227">"Informacje zostaną wykorzystane do powiadomień i nie tylko"</string>
<string name="bluetooth_profile_pan_nap" msgid="7871974753822470050">"Udostępnianie połączenia internetowego"</string>
<string name="bluetooth_profile_map" msgid="8907204701162107271">"SMS-y"</string>
<string name="bluetooth_profile_sap" msgid="8304170950447934386">"Dostęp do karty SIM"</string>
diff --git a/packages/SettingsLib/res/values-pt-rBR/strings.xml b/packages/SettingsLib/res/values-pt-rBR/strings.xml
index 9493e20..9af28a6 100644
--- a/packages/SettingsLib/res/values-pt-rBR/strings.xml
+++ b/packages/SettingsLib/res/values-pt-rBR/strings.xml
@@ -107,10 +107,8 @@
<string name="bluetooth_profile_opp" msgid="6692618568149493430">"Transferência de arquivo"</string>
<string name="bluetooth_profile_hid" msgid="2969922922664315866">"Dispositivo de entrada"</string>
<string name="bluetooth_profile_pan" msgid="1006235139308318188">"Acesso à Internet"</string>
- <!-- no translation found for bluetooth_profile_pbap (2103406516858653017) -->
- <skip />
- <!-- no translation found for bluetooth_profile_pbap_summary (402819589201138227) -->
- <skip />
+ <string name="bluetooth_profile_pbap" msgid="2103406516858653017">"Permitir acesso a contatos e ao histórico de ligações"</string>
+ <string name="bluetooth_profile_pbap_summary" msgid="402819589201138227">"As informações serão usadas para fazer o anúncio de ligações e mais"</string>
<string name="bluetooth_profile_pan_nap" msgid="7871974753822470050">"Compartilhamento de conexão à Internet"</string>
<string name="bluetooth_profile_map" msgid="8907204701162107271">"Mensagens de texto"</string>
<string name="bluetooth_profile_sap" msgid="8304170950447934386">"Acesso ao chip"</string>
diff --git a/packages/SettingsLib/res/values-pt-rPT/strings.xml b/packages/SettingsLib/res/values-pt-rPT/strings.xml
index 12d37d2..53221ed 100644
--- a/packages/SettingsLib/res/values-pt-rPT/strings.xml
+++ b/packages/SettingsLib/res/values-pt-rPT/strings.xml
@@ -107,10 +107,8 @@
<string name="bluetooth_profile_opp" msgid="6692618568149493430">"Transferência do ficheiro"</string>
<string name="bluetooth_profile_hid" msgid="2969922922664315866">"Dispositivo de entrada"</string>
<string name="bluetooth_profile_pan" msgid="1006235139308318188">"Acesso à internet"</string>
- <!-- no translation found for bluetooth_profile_pbap (2103406516858653017) -->
- <skip />
- <!-- no translation found for bluetooth_profile_pbap_summary (402819589201138227) -->
- <skip />
+ <string name="bluetooth_profile_pbap" msgid="2103406516858653017">"Perm. acesso a contactos e histór. cham."</string>
+ <string name="bluetooth_profile_pbap_summary" msgid="402819589201138227">"As informações são usadas para anúncios de chamadas e outros"</string>
<string name="bluetooth_profile_pan_nap" msgid="7871974753822470050">"Partilha da ligação à internet"</string>
<string name="bluetooth_profile_map" msgid="8907204701162107271">"Mensagens de texto"</string>
<string name="bluetooth_profile_sap" msgid="8304170950447934386">"Acesso ao SIM"</string>
diff --git a/packages/SettingsLib/res/values-pt/strings.xml b/packages/SettingsLib/res/values-pt/strings.xml
index 9493e20..9af28a6 100644
--- a/packages/SettingsLib/res/values-pt/strings.xml
+++ b/packages/SettingsLib/res/values-pt/strings.xml
@@ -107,10 +107,8 @@
<string name="bluetooth_profile_opp" msgid="6692618568149493430">"Transferência de arquivo"</string>
<string name="bluetooth_profile_hid" msgid="2969922922664315866">"Dispositivo de entrada"</string>
<string name="bluetooth_profile_pan" msgid="1006235139308318188">"Acesso à Internet"</string>
- <!-- no translation found for bluetooth_profile_pbap (2103406516858653017) -->
- <skip />
- <!-- no translation found for bluetooth_profile_pbap_summary (402819589201138227) -->
- <skip />
+ <string name="bluetooth_profile_pbap" msgid="2103406516858653017">"Permitir acesso a contatos e ao histórico de ligações"</string>
+ <string name="bluetooth_profile_pbap_summary" msgid="402819589201138227">"As informações serão usadas para fazer o anúncio de ligações e mais"</string>
<string name="bluetooth_profile_pan_nap" msgid="7871974753822470050">"Compartilhamento de conexão à Internet"</string>
<string name="bluetooth_profile_map" msgid="8907204701162107271">"Mensagens de texto"</string>
<string name="bluetooth_profile_sap" msgid="8304170950447934386">"Acesso ao chip"</string>
diff --git a/packages/SettingsLib/res/values-ro/strings.xml b/packages/SettingsLib/res/values-ro/strings.xml
index 0f68d27..db0993e 100644
--- a/packages/SettingsLib/res/values-ro/strings.xml
+++ b/packages/SettingsLib/res/values-ro/strings.xml
@@ -107,10 +107,8 @@
<string name="bluetooth_profile_opp" msgid="6692618568149493430">"Transfer de fișiere"</string>
<string name="bluetooth_profile_hid" msgid="2969922922664315866">"Dispozitiv de intrare"</string>
<string name="bluetooth_profile_pan" msgid="1006235139308318188">"Acces la internet"</string>
- <!-- no translation found for bluetooth_profile_pbap (2103406516858653017) -->
- <skip />
- <!-- no translation found for bluetooth_profile_pbap_summary (402819589201138227) -->
- <skip />
+ <string name="bluetooth_profile_pbap" msgid="2103406516858653017">"Permite accesul la agendă și la istoric"</string>
+ <string name="bluetooth_profile_pbap_summary" msgid="402819589201138227">"Informațiile se vor folosi pentru notificări de apeluri etc."</string>
<string name="bluetooth_profile_pan_nap" msgid="7871974753822470050">"Distribuirea conexiunii la internet"</string>
<string name="bluetooth_profile_map" msgid="8907204701162107271">"Mesaje text"</string>
<string name="bluetooth_profile_sap" msgid="8304170950447934386">"Acces la SIM"</string>
diff --git a/packages/SettingsLib/res/values-ru/strings.xml b/packages/SettingsLib/res/values-ru/strings.xml
index 2a390aa..e9f7b0a 100644
--- a/packages/SettingsLib/res/values-ru/strings.xml
+++ b/packages/SettingsLib/res/values-ru/strings.xml
@@ -107,10 +107,8 @@
<string name="bluetooth_profile_opp" msgid="6692618568149493430">"Профиль OPP"</string>
<string name="bluetooth_profile_hid" msgid="2969922922664315866">"Профиль HID"</string>
<string name="bluetooth_profile_pan" msgid="1006235139308318188">"Доступ к интернету"</string>
- <!-- no translation found for bluetooth_profile_pbap (2103406516858653017) -->
- <skip />
- <!-- no translation found for bluetooth_profile_pbap_summary (402819589201138227) -->
- <skip />
+ <string name="bluetooth_profile_pbap" msgid="2103406516858653017">"Разрешить доступ к контактам и журналу звонков"</string>
+ <string name="bluetooth_profile_pbap_summary" msgid="402819589201138227">"Эти сведения нужны для оповещений о звонках и других функций"</string>
<string name="bluetooth_profile_pan_nap" msgid="7871974753822470050">"Профиль PAN"</string>
<string name="bluetooth_profile_map" msgid="8907204701162107271">"Текстовые сообщения"</string>
<string name="bluetooth_profile_sap" msgid="8304170950447934386">"Доступ к SIM-карте"</string>
diff --git a/packages/SettingsLib/res/values-si/strings.xml b/packages/SettingsLib/res/values-si/strings.xml
index 15e00c9..d3bdd56 100644
--- a/packages/SettingsLib/res/values-si/strings.xml
+++ b/packages/SettingsLib/res/values-si/strings.xml
@@ -107,10 +107,8 @@
<string name="bluetooth_profile_opp" msgid="6692618568149493430">"ගොනු හුවමාරුව"</string>
<string name="bluetooth_profile_hid" msgid="2969922922664315866">"ආදාන උපාංගය"</string>
<string name="bluetooth_profile_pan" msgid="1006235139308318188">"අන්තර්ජාල ප්රවේශය"</string>
- <!-- no translation found for bluetooth_profile_pbap (2103406516858653017) -->
- <skip />
- <!-- no translation found for bluetooth_profile_pbap_summary (402819589201138227) -->
- <skip />
+ <string name="bluetooth_profile_pbap" msgid="2103406516858653017">"සම්බන්ධතා සහ ඇමතුම් ඉතිහාසය වෙත ප්රවේශ වීමට ඉඩ දෙන්න"</string>
+ <string name="bluetooth_profile_pbap_summary" msgid="402819589201138227">"ඇමතුම් නිවේදන සහ තවත් දේ සඳහා තතු භාවිත කෙරේ"</string>
<string name="bluetooth_profile_pan_nap" msgid="7871974753822470050">"අන්තර්ජාල සම්බන්ධතා බෙදාගැනීම"</string>
<string name="bluetooth_profile_map" msgid="8907204701162107271">"පෙළ පණිවිඩ"</string>
<string name="bluetooth_profile_sap" msgid="8304170950447934386">"SIM ප්රවේශය"</string>
diff --git a/packages/SettingsLib/res/values-sk/strings.xml b/packages/SettingsLib/res/values-sk/strings.xml
index 5be4d1b..b34fe9d 100644
--- a/packages/SettingsLib/res/values-sk/strings.xml
+++ b/packages/SettingsLib/res/values-sk/strings.xml
@@ -107,10 +107,8 @@
<string name="bluetooth_profile_opp" msgid="6692618568149493430">"Prenos súborov"</string>
<string name="bluetooth_profile_hid" msgid="2969922922664315866">"Vstupné zariadenie"</string>
<string name="bluetooth_profile_pan" msgid="1006235139308318188">"Prístup na internet"</string>
- <!-- no translation found for bluetooth_profile_pbap (2103406516858653017) -->
- <skip />
- <!-- no translation found for bluetooth_profile_pbap_summary (402819589201138227) -->
- <skip />
+ <string name="bluetooth_profile_pbap" msgid="2103406516858653017">"Povoľte aj prístup ku kontaktom a histórii hovorov"</string>
+ <string name="bluetooth_profile_pbap_summary" msgid="402819589201138227">"Pomocou informácií sa budú oznamovať hovory a viac"</string>
<string name="bluetooth_profile_pan_nap" msgid="7871974753822470050">"Zdieľanie pripojenia na Internet"</string>
<string name="bluetooth_profile_map" msgid="8907204701162107271">"Textové správy"</string>
<string name="bluetooth_profile_sap" msgid="8304170950447934386">"Prístup k SIM karte"</string>
diff --git a/packages/SettingsLib/res/values-sl/strings.xml b/packages/SettingsLib/res/values-sl/strings.xml
index ecef697..b142a6c 100644
--- a/packages/SettingsLib/res/values-sl/strings.xml
+++ b/packages/SettingsLib/res/values-sl/strings.xml
@@ -107,10 +107,8 @@
<string name="bluetooth_profile_opp" msgid="6692618568149493430">"Prenos datoteke"</string>
<string name="bluetooth_profile_hid" msgid="2969922922664315866">"Vnosna naprava"</string>
<string name="bluetooth_profile_pan" msgid="1006235139308318188">"Internetni dostop"</string>
- <!-- no translation found for bluetooth_profile_pbap (2103406516858653017) -->
- <skip />
- <!-- no translation found for bluetooth_profile_pbap_summary (402819589201138227) -->
- <skip />
+ <string name="bluetooth_profile_pbap" msgid="2103406516858653017">"Dovoli dostop do stikov in zgodovine klicev"</string>
+ <string name="bluetooth_profile_pbap_summary" msgid="402819589201138227">"Podatki bodo uporabljeni za najave klicev in drugo."</string>
<string name="bluetooth_profile_pan_nap" msgid="7871974753822470050">"Deljenje internetne povezave"</string>
<string name="bluetooth_profile_map" msgid="8907204701162107271">"Sporočila SMS"</string>
<string name="bluetooth_profile_sap" msgid="8304170950447934386">"Dostop do kartice SIM"</string>
diff --git a/packages/SettingsLib/res/values-sq/strings.xml b/packages/SettingsLib/res/values-sq/strings.xml
index e4d402d6..b27b6bd 100644
--- a/packages/SettingsLib/res/values-sq/strings.xml
+++ b/packages/SettingsLib/res/values-sq/strings.xml
@@ -107,10 +107,8 @@
<string name="bluetooth_profile_opp" msgid="6692618568149493430">"Transferimi i skedarëve"</string>
<string name="bluetooth_profile_hid" msgid="2969922922664315866">"Pajisja e hyrjes"</string>
<string name="bluetooth_profile_pan" msgid="1006235139308318188">"Qasje në internet"</string>
- <!-- no translation found for bluetooth_profile_pbap (2103406516858653017) -->
- <skip />
- <!-- no translation found for bluetooth_profile_pbap_summary (402819589201138227) -->
- <skip />
+ <string name="bluetooth_profile_pbap" msgid="2103406516858653017">"Lejo qasje te kontaktet dhe historiku i telefonatave"</string>
+ <string name="bluetooth_profile_pbap_summary" msgid="402819589201138227">"Informacioni do të përdoret për njoftime për telefonata etj."</string>
<string name="bluetooth_profile_pan_nap" msgid="7871974753822470050">"Ndarja e lidhjes së internetit"</string>
<string name="bluetooth_profile_map" msgid="8907204701162107271">"Mesazhet me tekst"</string>
<string name="bluetooth_profile_sap" msgid="8304170950447934386">"Qasje në kartën SIM"</string>
diff --git a/packages/SettingsLib/res/values-sr/strings.xml b/packages/SettingsLib/res/values-sr/strings.xml
index 2f5ebdd..286e908 100644
--- a/packages/SettingsLib/res/values-sr/strings.xml
+++ b/packages/SettingsLib/res/values-sr/strings.xml
@@ -107,10 +107,8 @@
<string name="bluetooth_profile_opp" msgid="6692618568149493430">"Пренос датотеке"</string>
<string name="bluetooth_profile_hid" msgid="2969922922664315866">"Улазни уређај"</string>
<string name="bluetooth_profile_pan" msgid="1006235139308318188">"Приступ Интернету"</string>
- <!-- no translation found for bluetooth_profile_pbap (2103406516858653017) -->
- <skip />
- <!-- no translation found for bluetooth_profile_pbap_summary (402819589201138227) -->
- <skip />
+ <string name="bluetooth_profile_pbap" msgid="2103406516858653017">"Дозволи контакте и историју позива"</string>
+ <string name="bluetooth_profile_pbap_summary" msgid="402819589201138227">"Информације ће се користити за обавештења о позивима и друго"</string>
<string name="bluetooth_profile_pan_nap" msgid="7871974753822470050">"Дељење интернет везе"</string>
<string name="bluetooth_profile_map" msgid="8907204701162107271">"SMS-ови"</string>
<string name="bluetooth_profile_sap" msgid="8304170950447934386">"Приступ SIM картици"</string>
diff --git a/packages/SettingsLib/res/values-sv/strings.xml b/packages/SettingsLib/res/values-sv/strings.xml
index 3cc2dc4..e2f7017 100644
--- a/packages/SettingsLib/res/values-sv/strings.xml
+++ b/packages/SettingsLib/res/values-sv/strings.xml
@@ -107,10 +107,8 @@
<string name="bluetooth_profile_opp" msgid="6692618568149493430">"Filöverföring"</string>
<string name="bluetooth_profile_hid" msgid="2969922922664315866">"Indataenhet"</string>
<string name="bluetooth_profile_pan" msgid="1006235139308318188">"Internetåtkomst"</string>
- <!-- no translation found for bluetooth_profile_pbap (2103406516858653017) -->
- <skip />
- <!-- no translation found for bluetooth_profile_pbap_summary (402819589201138227) -->
- <skip />
+ <string name="bluetooth_profile_pbap" msgid="2103406516858653017">"Tillåt åtkomst till kontakter och samtalshistorik"</string>
+ <string name="bluetooth_profile_pbap_summary" msgid="402819589201138227">"Informationen används för samtalsmeddelanden med mera"</string>
<string name="bluetooth_profile_pan_nap" msgid="7871974753822470050">"Delning av Internetanslutning"</string>
<string name="bluetooth_profile_map" msgid="8907204701162107271">"Sms"</string>
<string name="bluetooth_profile_sap" msgid="8304170950447934386">"SIM-åtkomst"</string>
diff --git a/packages/SettingsLib/res/values-sw/strings.xml b/packages/SettingsLib/res/values-sw/strings.xml
index 67ab4d3..f66684a 100644
--- a/packages/SettingsLib/res/values-sw/strings.xml
+++ b/packages/SettingsLib/res/values-sw/strings.xml
@@ -107,10 +107,8 @@
<string name="bluetooth_profile_opp" msgid="6692618568149493430">"Uhamishaji wa faili"</string>
<string name="bluetooth_profile_hid" msgid="2969922922664315866">"Kifaa cha kuingiza"</string>
<string name="bluetooth_profile_pan" msgid="1006235139308318188">"Ufikiaji wa intaneti"</string>
- <!-- no translation found for bluetooth_profile_pbap (2103406516858653017) -->
- <skip />
- <!-- no translation found for bluetooth_profile_pbap_summary (402819589201138227) -->
- <skip />
+ <string name="bluetooth_profile_pbap" msgid="2103406516858653017">"Ruhusu ufikiaji wa anwani na rekodi ya simu zilizopigwa"</string>
+ <string name="bluetooth_profile_pbap_summary" msgid="402819589201138227">"Maelezo yatatumiwa kwa matangazo ya simu na mengine"</string>
<string name="bluetooth_profile_pan_nap" msgid="7871974753822470050">"Kushiriki muunganisho wa tovuti"</string>
<string name="bluetooth_profile_map" msgid="8907204701162107271">"SMS"</string>
<string name="bluetooth_profile_sap" msgid="8304170950447934386">"Ufikiaji wa SIM"</string>
diff --git a/packages/SettingsLib/res/values-ta/strings.xml b/packages/SettingsLib/res/values-ta/strings.xml
index b99a0c5..22d4feb 100644
--- a/packages/SettingsLib/res/values-ta/strings.xml
+++ b/packages/SettingsLib/res/values-ta/strings.xml
@@ -107,10 +107,8 @@
<string name="bluetooth_profile_opp" msgid="6692618568149493430">"ஃபைல் இடமாற்றம்"</string>
<string name="bluetooth_profile_hid" msgid="2969922922664315866">"உள்ளீட்டுச் சாதனம்"</string>
<string name="bluetooth_profile_pan" msgid="1006235139308318188">"இணைய அணுகல்"</string>
- <!-- no translation found for bluetooth_profile_pbap (2103406516858653017) -->
- <skip />
- <!-- no translation found for bluetooth_profile_pbap_summary (402819589201138227) -->
- <skip />
+ <string name="bluetooth_profile_pbap" msgid="2103406516858653017">"தொடர்புகள் & அழைப்புப் பதிவை அணுக அனுமதி"</string>
+ <string name="bluetooth_profile_pbap_summary" msgid="402819589201138227">"அழைப்பு அறிவிப்புகள் & பலவற்றுக்குத் தகவல் பயன்படுத்தப்படும்"</string>
<string name="bluetooth_profile_pan_nap" msgid="7871974753822470050">"இணைய இணைப்பு பகிர்தல்"</string>
<string name="bluetooth_profile_map" msgid="8907204701162107271">"உரைச் செய்திகள்"</string>
<string name="bluetooth_profile_sap" msgid="8304170950447934386">"சிம் அணுகல்"</string>
diff --git a/packages/SettingsLib/res/values-te/strings.xml b/packages/SettingsLib/res/values-te/strings.xml
index 3c12cdc..dc5bacd 100644
--- a/packages/SettingsLib/res/values-te/strings.xml
+++ b/packages/SettingsLib/res/values-te/strings.xml
@@ -107,10 +107,8 @@
<string name="bluetooth_profile_opp" msgid="6692618568149493430">"ఫైల్ బదిలీ"</string>
<string name="bluetooth_profile_hid" msgid="2969922922664315866">"ఇన్పుట్ పరికరం"</string>
<string name="bluetooth_profile_pan" msgid="1006235139308318188">"ఇంటర్నెట్ యాక్సెస్"</string>
- <!-- no translation found for bluetooth_profile_pbap (2103406516858653017) -->
- <skip />
- <!-- no translation found for bluetooth_profile_pbap_summary (402819589201138227) -->
- <skip />
+ <string name="bluetooth_profile_pbap" msgid="2103406516858653017">"కాంటాక్ట్లు, కాల్ హిస్టరీకి యాక్సెస్ను అనుమతించండి"</string>
+ <string name="bluetooth_profile_pbap_summary" msgid="402819589201138227">"కాల్ అనౌన్స్మెంట్లు, ఇంకా మరిన్నింటి కోసం సమాచారం ఉపయోగించబడుతుంది"</string>
<string name="bluetooth_profile_pan_nap" msgid="7871974753822470050">"ఇంటర్నెట్ కనెక్షన్ షేరింగ్"</string>
<string name="bluetooth_profile_map" msgid="8907204701162107271">"టెక్స్ట్ మెసేజ్లు"</string>
<string name="bluetooth_profile_sap" msgid="8304170950447934386">"SIM యాక్సెస్"</string>
diff --git a/packages/SettingsLib/res/values-th/strings.xml b/packages/SettingsLib/res/values-th/strings.xml
index f3d08ba..7dd346a 100644
--- a/packages/SettingsLib/res/values-th/strings.xml
+++ b/packages/SettingsLib/res/values-th/strings.xml
@@ -107,10 +107,8 @@
<string name="bluetooth_profile_opp" msgid="6692618568149493430">"การถ่ายโอนไฟล์"</string>
<string name="bluetooth_profile_hid" msgid="2969922922664315866">"อุปกรณ์อินพุต"</string>
<string name="bluetooth_profile_pan" msgid="1006235139308318188">"การเข้าถึงอินเทอร์เน็ต"</string>
- <!-- no translation found for bluetooth_profile_pbap (2103406516858653017) -->
- <skip />
- <!-- no translation found for bluetooth_profile_pbap_summary (402819589201138227) -->
- <skip />
+ <string name="bluetooth_profile_pbap" msgid="2103406516858653017">"ให้เข้าถึงรายชื่อติดต่อและประวัติการโทร"</string>
+ <string name="bluetooth_profile_pbap_summary" msgid="402819589201138227">"จะมีการใช้ข้อมูลเพื่อประกาศการติดต่อและอื่นๆ"</string>
<string name="bluetooth_profile_pan_nap" msgid="7871974753822470050">"การแชร์การเชื่อมต่ออินเทอร์เน็ต"</string>
<string name="bluetooth_profile_map" msgid="8907204701162107271">"ข้อความ"</string>
<string name="bluetooth_profile_sap" msgid="8304170950447934386">"การเข้าถึงซิม"</string>
diff --git a/packages/SettingsLib/res/values-tl/strings.xml b/packages/SettingsLib/res/values-tl/strings.xml
index 9159ca4..8ad8fd7 100644
--- a/packages/SettingsLib/res/values-tl/strings.xml
+++ b/packages/SettingsLib/res/values-tl/strings.xml
@@ -107,10 +107,8 @@
<string name="bluetooth_profile_opp" msgid="6692618568149493430">"Paglilipat ng file"</string>
<string name="bluetooth_profile_hid" msgid="2969922922664315866">"Device sa pag-input"</string>
<string name="bluetooth_profile_pan" msgid="1006235139308318188">"Access sa internet"</string>
- <!-- no translation found for bluetooth_profile_pbap (2103406516858653017) -->
- <skip />
- <!-- no translation found for bluetooth_profile_pbap_summary (402819589201138227) -->
- <skip />
+ <string name="bluetooth_profile_pbap" msgid="2103406516858653017">"Ipa-access ang contacts at call history"</string>
+ <string name="bluetooth_profile_pbap_summary" msgid="402819589201138227">"Gagamitin ang impormasyon para sa anunsyo sa tawag atbp."</string>
<string name="bluetooth_profile_pan_nap" msgid="7871974753822470050">"Pagbabahagi ng koneksyon sa internet"</string>
<string name="bluetooth_profile_map" msgid="8907204701162107271">"Mga Text Message"</string>
<string name="bluetooth_profile_sap" msgid="8304170950447934386">"Access sa SIM"</string>
diff --git a/packages/SettingsLib/res/values-tr/strings.xml b/packages/SettingsLib/res/values-tr/strings.xml
index 0f8df02..0af470d 100644
--- a/packages/SettingsLib/res/values-tr/strings.xml
+++ b/packages/SettingsLib/res/values-tr/strings.xml
@@ -107,10 +107,8 @@
<string name="bluetooth_profile_opp" msgid="6692618568149493430">"Dosya aktarımı"</string>
<string name="bluetooth_profile_hid" msgid="2969922922664315866">"Giriş cihazı"</string>
<string name="bluetooth_profile_pan" msgid="1006235139308318188">"İnternet erişimi"</string>
- <!-- no translation found for bluetooth_profile_pbap (2103406516858653017) -->
- <skip />
- <!-- no translation found for bluetooth_profile_pbap_summary (402819589201138227) -->
- <skip />
+ <string name="bluetooth_profile_pbap" msgid="2103406516858653017">"Kişilere ve çağrı geçmişine erişime izin ver"</string>
+ <string name="bluetooth_profile_pbap_summary" msgid="402819589201138227">"Bilgi, arama bildirimleri ve daha fazlası için kullanılır"</string>
<string name="bluetooth_profile_pan_nap" msgid="7871974753822470050">"İnternet bağlantısı paylaşımı"</string>
<string name="bluetooth_profile_map" msgid="8907204701162107271">"Kısa Mesajlar"</string>
<string name="bluetooth_profile_sap" msgid="8304170950447934386">"SIM Erişimi"</string>
diff --git a/packages/SettingsLib/res/values-uk/strings.xml b/packages/SettingsLib/res/values-uk/strings.xml
index 8c24312..3b84923 100644
--- a/packages/SettingsLib/res/values-uk/strings.xml
+++ b/packages/SettingsLib/res/values-uk/strings.xml
@@ -107,10 +107,8 @@
<string name="bluetooth_profile_opp" msgid="6692618568149493430">"Передавання файлів"</string>
<string name="bluetooth_profile_hid" msgid="2969922922664315866">"Пристрій введення"</string>
<string name="bluetooth_profile_pan" msgid="1006235139308318188">"Доступ до Інтернету"</string>
- <!-- no translation found for bluetooth_profile_pbap (2103406516858653017) -->
- <skip />
- <!-- no translation found for bluetooth_profile_pbap_summary (402819589201138227) -->
- <skip />
+ <string name="bluetooth_profile_pbap" msgid="2103406516858653017">"Надати доступ до контактів і історії викликів"</string>
+ <string name="bluetooth_profile_pbap_summary" msgid="402819589201138227">"Дані використовуватимуться для сповіщень про виклики тощо"</string>
<string name="bluetooth_profile_pan_nap" msgid="7871974753822470050">"Надання доступу до Інтернету"</string>
<string name="bluetooth_profile_map" msgid="8907204701162107271">"Текстові повідомлення"</string>
<string name="bluetooth_profile_sap" msgid="8304170950447934386">"Доступ до SIM-карти"</string>
diff --git a/packages/SettingsLib/res/values-ur/strings.xml b/packages/SettingsLib/res/values-ur/strings.xml
index 5967add..9f7d18e 100644
--- a/packages/SettingsLib/res/values-ur/strings.xml
+++ b/packages/SettingsLib/res/values-ur/strings.xml
@@ -107,10 +107,8 @@
<string name="bluetooth_profile_opp" msgid="6692618568149493430">"فائل کی منتقلی"</string>
<string name="bluetooth_profile_hid" msgid="2969922922664315866">"ان پٹ آلہ"</string>
<string name="bluetooth_profile_pan" msgid="1006235139308318188">"انٹرنیٹ تک رسائی"</string>
- <!-- no translation found for bluetooth_profile_pbap (2103406516858653017) -->
- <skip />
- <!-- no translation found for bluetooth_profile_pbap_summary (402819589201138227) -->
- <skip />
+ <string name="bluetooth_profile_pbap" msgid="2103406516858653017">"رابطوں اور کال کی سرگزشت تک رسائی کی اجازت دیں"</string>
+ <string name="bluetooth_profile_pbap_summary" msgid="402819589201138227">"معلومات کو کال کے اعلانات اور بہت کچھ کے لیے استعمال کیا جائے گا"</string>
<string name="bluetooth_profile_pan_nap" msgid="7871974753822470050">"انٹرنیٹ کنکشن کا اشتراک کرنا"</string>
<string name="bluetooth_profile_map" msgid="8907204701162107271">"ٹیکسٹ پیغامات"</string>
<string name="bluetooth_profile_sap" msgid="8304170950447934386">"SIM رسائی"</string>
diff --git a/packages/SettingsLib/res/values-vi/strings.xml b/packages/SettingsLib/res/values-vi/strings.xml
index cc8dacd..0eaaf21 100644
--- a/packages/SettingsLib/res/values-vi/strings.xml
+++ b/packages/SettingsLib/res/values-vi/strings.xml
@@ -107,10 +107,8 @@
<string name="bluetooth_profile_opp" msgid="6692618568149493430">"Chuyển tệp"</string>
<string name="bluetooth_profile_hid" msgid="2969922922664315866">"Thiết bị đầu vào"</string>
<string name="bluetooth_profile_pan" msgid="1006235139308318188">"Truy cập Internet"</string>
- <!-- no translation found for bluetooth_profile_pbap (2103406516858653017) -->
- <skip />
- <!-- no translation found for bluetooth_profile_pbap_summary (402819589201138227) -->
- <skip />
+ <string name="bluetooth_profile_pbap" msgid="2103406516858653017">"Cho truy cập danh bạ và nhật ký cuộc gọi"</string>
+ <string name="bluetooth_profile_pbap_summary" msgid="402819589201138227">"Thông tin được dùng cho tính năng thông báo cuộc gọi, v.v."</string>
<string name="bluetooth_profile_pan_nap" msgid="7871974753822470050">"Chia sẻ kết nối internet"</string>
<string name="bluetooth_profile_map" msgid="8907204701162107271">"Tin nhắn văn bản"</string>
<string name="bluetooth_profile_sap" msgid="8304170950447934386">"Truy cập SIM"</string>
diff --git a/packages/SettingsLib/res/values-zh-rCN/strings.xml b/packages/SettingsLib/res/values-zh-rCN/strings.xml
index 84af4b5..74ab016e 100644
--- a/packages/SettingsLib/res/values-zh-rCN/strings.xml
+++ b/packages/SettingsLib/res/values-zh-rCN/strings.xml
@@ -107,10 +107,8 @@
<string name="bluetooth_profile_opp" msgid="6692618568149493430">"文件传输"</string>
<string name="bluetooth_profile_hid" msgid="2969922922664315866">"输入设备"</string>
<string name="bluetooth_profile_pan" msgid="1006235139308318188">"互联网连接"</string>
- <!-- no translation found for bluetooth_profile_pbap (2103406516858653017) -->
- <skip />
- <!-- no translation found for bluetooth_profile_pbap_summary (402819589201138227) -->
- <skip />
+ <string name="bluetooth_profile_pbap" msgid="2103406516858653017">"允许访问通讯录和通话记录"</string>
+ <string name="bluetooth_profile_pbap_summary" msgid="402819589201138227">"信息将用于来电通知等用途"</string>
<string name="bluetooth_profile_pan_nap" msgid="7871974753822470050">"共享互联网连接"</string>
<string name="bluetooth_profile_map" msgid="8907204701162107271">"短信"</string>
<string name="bluetooth_profile_sap" msgid="8304170950447934386">"SIM 卡访问权限"</string>
diff --git a/packages/SettingsLib/res/values-zh-rHK/strings.xml b/packages/SettingsLib/res/values-zh-rHK/strings.xml
index dfde69f..21f6d51 100644
--- a/packages/SettingsLib/res/values-zh-rHK/strings.xml
+++ b/packages/SettingsLib/res/values-zh-rHK/strings.xml
@@ -107,10 +107,8 @@
<string name="bluetooth_profile_opp" msgid="6692618568149493430">"檔案傳輸"</string>
<string name="bluetooth_profile_hid" msgid="2969922922664315866">"輸入裝置"</string>
<string name="bluetooth_profile_pan" msgid="1006235139308318188">"互聯網連線"</string>
- <!-- no translation found for bluetooth_profile_pbap (2103406516858653017) -->
- <skip />
- <!-- no translation found for bluetooth_profile_pbap_summary (402819589201138227) -->
- <skip />
+ <string name="bluetooth_profile_pbap" msgid="2103406516858653017">"允許存取通訊錄和通話記錄"</string>
+ <string name="bluetooth_profile_pbap_summary" msgid="402819589201138227">"資訊將用於來電通知等用途"</string>
<string name="bluetooth_profile_pan_nap" msgid="7871974753822470050">"互聯網連線分享"</string>
<string name="bluetooth_profile_map" msgid="8907204701162107271">"短訊"</string>
<string name="bluetooth_profile_sap" msgid="8304170950447934386">"SIM 卡存取"</string>
diff --git a/packages/SettingsLib/res/values-zh-rTW/strings.xml b/packages/SettingsLib/res/values-zh-rTW/strings.xml
index 0257527..09f8d13 100644
--- a/packages/SettingsLib/res/values-zh-rTW/strings.xml
+++ b/packages/SettingsLib/res/values-zh-rTW/strings.xml
@@ -107,10 +107,8 @@
<string name="bluetooth_profile_opp" msgid="6692618568149493430">"檔案傳輸"</string>
<string name="bluetooth_profile_hid" msgid="2969922922664315866">"輸入裝置"</string>
<string name="bluetooth_profile_pan" msgid="1006235139308318188">"網際網路連線"</string>
- <!-- no translation found for bluetooth_profile_pbap (2103406516858653017) -->
- <skip />
- <!-- no translation found for bluetooth_profile_pbap_summary (402819589201138227) -->
- <skip />
+ <string name="bluetooth_profile_pbap" msgid="2103406516858653017">"允許存取聯絡人和通話記錄"</string>
+ <string name="bluetooth_profile_pbap_summary" msgid="402819589201138227">"資訊將用於來電通知等用途"</string>
<string name="bluetooth_profile_pan_nap" msgid="7871974753822470050">"網際網路連線分享"</string>
<string name="bluetooth_profile_map" msgid="8907204701162107271">"簡訊"</string>
<string name="bluetooth_profile_sap" msgid="8304170950447934386">"SIM 卡存取權"</string>
diff --git a/packages/SettingsLib/res/values-zu/strings.xml b/packages/SettingsLib/res/values-zu/strings.xml
index 27bad75..5de118c 100644
--- a/packages/SettingsLib/res/values-zu/strings.xml
+++ b/packages/SettingsLib/res/values-zu/strings.xml
@@ -107,10 +107,8 @@
<string name="bluetooth_profile_opp" msgid="6692618568149493430">"Dlulisa ifayela"</string>
<string name="bluetooth_profile_hid" msgid="2969922922664315866">"Idivaysi yokufakwayo"</string>
<string name="bluetooth_profile_pan" msgid="1006235139308318188">"Ukufinyelela i-Inthanethi"</string>
- <!-- no translation found for bluetooth_profile_pbap (2103406516858653017) -->
- <skip />
- <!-- no translation found for bluetooth_profile_pbap_summary (402819589201138227) -->
- <skip />
+ <string name="bluetooth_profile_pbap" msgid="2103406516858653017">"Vumela ukufinyelela koxhumana nabo nomlando wekholi"</string>
+ <string name="bluetooth_profile_pbap_summary" msgid="402819589201138227">"Imininingwane izosetshenziselwa izaziso zocingo nokuningi"</string>
<string name="bluetooth_profile_pan_nap" msgid="7871974753822470050">"Ukwabelana ngoxhumano lwe-Inthanethi"</string>
<string name="bluetooth_profile_map" msgid="8907204701162107271">"Imilayezo yombhalo"</string>
<string name="bluetooth_profile_sap" msgid="8304170950447934386">"Ukufinyelela kwe-SIM"</string>
diff --git a/packages/SettingsLib/res/values/strings.xml b/packages/SettingsLib/res/values/strings.xml
index 0acce03..ec24ab7 100644
--- a/packages/SettingsLib/res/values/strings.xml
+++ b/packages/SettingsLib/res/values/strings.xml
@@ -954,9 +954,6 @@
<!-- UI debug setting: enable freeform window support summary [CHAR LIMIT=150] -->
<string name="enable_freeform_support_summary">Enable support for experimental freeform windows.</string>
- <!-- UI debug setting: enable desktop mode [CHAR LIMIT=25] -->
- <string name="desktop_mode">Desktop mode</string>
-
<!-- Local (desktop) backup password menu title [CHAR LIMIT=25] -->
<string name="local_backup_password_title">Desktop backup password</string>
<!-- Summary text of the "local backup password" setting when the user has not supplied a password -->
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/OWNERS b/packages/SettingsLib/src/com/android/settingslib/bluetooth/OWNERS
index 5e66972..7669e79b 100644
--- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/OWNERS
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/OWNERS
@@ -3,7 +3,10 @@
hughchen@google.com
timhypeng@google.com
robertluo@google.com
-changbetty@google.com
songferngwang@google.com
+yqian@google.com
+chelseahao@google.com
+yiyishen@google.com
+hahong@google.com
# Emergency approvers in case the above are not available
diff --git a/packages/SettingsLib/src/com/android/settingslib/qrcode/QrCodeGenerator.java b/packages/SettingsLib/src/com/android/settingslib/qrcode/QrCodeGenerator.java
index 5c48c54..6b855c0 100644
--- a/packages/SettingsLib/src/com/android/settingslib/qrcode/QrCodeGenerator.java
+++ b/packages/SettingsLib/src/com/android/settingslib/qrcode/QrCodeGenerator.java
@@ -31,6 +31,7 @@
import java.util.Map;
public final class QrCodeGenerator {
+ private static final int DEFAULT_MARGIN = -1;
/**
* Generates a barcode image with {@code contents}.
*
@@ -40,7 +41,20 @@
*/
public static Bitmap encodeQrCode(String contents, int size)
throws WriterException, IllegalArgumentException {
- return encodeQrCode(contents, size, /*invert=*/false);
+ return encodeQrCode(contents, size, DEFAULT_MARGIN, /*invert=*/false);
+ }
+
+ /**
+ * Generates a barcode image with {@code contents}.
+ *
+ * @param contents The contents to encode in the barcode
+ * @param size The preferred image size in pixels
+ * @param margin The margin around the actual barcode
+ * @return Barcode bitmap
+ */
+ public static Bitmap encodeQrCode(String contents, int size, int margin)
+ throws WriterException, IllegalArgumentException {
+ return encodeQrCode(contents, size, margin, /*invert=*/false);
}
/**
@@ -53,10 +67,27 @@
*/
public static Bitmap encodeQrCode(String contents, int size, boolean invert)
throws WriterException, IllegalArgumentException {
+ return encodeQrCode(contents, size, DEFAULT_MARGIN, /*invert=*/invert);
+ }
+
+ /**
+ * Generates a barcode image with {@code contents}.
+ *
+ * @param contents The contents to encode in the barcode
+ * @param size The preferred image size in pixels
+ * @param margin The margin around the actual barcode
+ * @param invert Whether to invert the black/white pixels (e.g. for dark mode)
+ * @return Barcode bitmap
+ */
+ public static Bitmap encodeQrCode(String contents, int size, int margin, boolean invert)
+ throws WriterException, IllegalArgumentException {
final Map<EncodeHintType, Object> hints = new HashMap<>();
if (!isIso88591(contents)) {
hints.put(EncodeHintType.CHARACTER_SET, StandardCharsets.UTF_8.name());
}
+ if (margin != DEFAULT_MARGIN) {
+ hints.put(EncodeHintType.MARGIN, margin);
+ }
final BitMatrix qrBits = new MultiFormatWriter().encode(contents, BarcodeFormat.QR_CODE,
size, size, hints);
diff --git a/packages/SettingsLib/tests/robotests/testutils/com/android/settingslib/testutils/shadow/ShadowUserManager.java b/packages/SettingsLib/tests/robotests/testutils/com/android/settingslib/testutils/shadow/ShadowUserManager.java
index 60d7721..78fb38f 100644
--- a/packages/SettingsLib/tests/robotests/testutils/com/android/settingslib/testutils/shadow/ShadowUserManager.java
+++ b/packages/SettingsLib/tests/robotests/testutils/com/android/settingslib/testutils/shadow/ShadowUserManager.java
@@ -16,8 +16,6 @@
package com.android.settingslib.testutils.shadow;
-import static android.os.Build.VERSION_CODES.N_MR1;
-
import android.annotation.UserIdInt;
import android.content.Context;
import android.content.pm.UserInfo;
@@ -27,7 +25,8 @@
import org.robolectric.annotation.Implementation;
import org.robolectric.annotation.Implements;
-import org.robolectric.shadows.ShadowBuild;
+import org.robolectric.versioning.AndroidVersions.NMR1;
+import org.robolectric.versioning.AndroidVersions.U;
import java.util.ArrayList;
import java.util.HashMap;
@@ -74,7 +73,7 @@
/**
* @return {@code false} by default, or the value specified via {@link #setIsAdminUser(boolean)}
*/
- @Implementation(minSdk = N_MR1)
+ @Implementation(minSdk = NMR1.SDK_INT)
public boolean isAdminUser() {
return getUserInfo(UserHandle.myUserId()).isAdmin();
}
@@ -98,7 +97,7 @@
mUserPropertiesMap.putIfAbsent(userId, userProperties);
}
- @Implementation(minSdk = ShadowBuild.UPSIDE_DOWN_CAKE)
+ @Implementation(minSdk = U.SDK_INT)
protected UserProperties getUserProperties(UserHandle user) {
return mUserPropertiesMap.getOrDefault(user.getIdentifier(),
new UserProperties(new UserProperties.Builder().build()));
diff --git a/packages/SettingsProvider/src/android/provider/settings/backup/GlobalSettings.java b/packages/SettingsProvider/src/android/provider/settings/backup/GlobalSettings.java
index fa2d677..1d25ac7 100644
--- a/packages/SettingsProvider/src/android/provider/settings/backup/GlobalSettings.java
+++ b/packages/SettingsProvider/src/android/provider/settings/backup/GlobalSettings.java
@@ -106,5 +106,10 @@
Settings.Global.Wearable.RTL_SWIPE_TO_DISMISS_ENABLED_DEV,
Settings.Global.Wearable.REDUCE_MOTION,
Settings.Global.Wearable.WEAR_LAUNCHER_UI_MODE,
+ Settings.Global.Wearable.USER_HFP_CLIENT_SETTING,
+ Settings.Global.Wearable.RSB_WAKE_ENABLED,
+ Settings.Global.Wearable.SCREENSHOT_ENABLED,
+ Settings.Global.Wearable.SCREEN_UNLOCK_SOUND_ENABLED,
+ Settings.Global.Wearable.CHARGING_SOUNDS_ENABLED,
};
}
diff --git a/packages/SettingsProvider/src/android/provider/settings/backup/SecureSettings.java b/packages/SettingsProvider/src/android/provider/settings/backup/SecureSettings.java
index 2d62e2a..8787c25 100644
--- a/packages/SettingsProvider/src/android/provider/settings/backup/SecureSettings.java
+++ b/packages/SettingsProvider/src/android/provider/settings/backup/SecureSettings.java
@@ -217,6 +217,7 @@
Settings.Secure.ACCESSIBILITY_FLOATING_MENU_ICON_TYPE,
Settings.Secure.ACCESSIBILITY_FLOATING_MENU_OPACITY,
Settings.Secure.ACCESSIBILITY_FLOATING_MENU_FADE_ENABLED,
+ Settings.Secure.ACCESSIBILITY_FORCE_INVERT_COLOR_ENABLED,
Settings.Secure.ACCESSIBILITY_MAGNIFICATION_ALWAYS_ON_ENABLED,
Settings.Secure.ACCESSIBILITY_MAGNIFICATION_JOYSTICK_ENABLED,
Settings.Secure.ODI_CAPTIONS_VOLUME_UI_ENABLED,
diff --git a/packages/SettingsProvider/src/android/provider/settings/validators/SecureSettingsValidators.java b/packages/SettingsProvider/src/android/provider/settings/validators/SecureSettingsValidators.java
index 4494765..dfc3cef 100644
--- a/packages/SettingsProvider/src/android/provider/settings/validators/SecureSettingsValidators.java
+++ b/packages/SettingsProvider/src/android/provider/settings/validators/SecureSettingsValidators.java
@@ -312,6 +312,7 @@
VALIDATORS.put(
Secure.ACCESSIBILITY_BUTTON_TARGETS,
ACCESSIBILITY_SHORTCUT_TARGET_LIST_VALIDATOR);
+ VALIDATORS.put(Secure.ACCESSIBILITY_FORCE_INVERT_COLOR_ENABLED, BOOLEAN_VALIDATOR);
VALIDATORS.put(Secure.ONE_HANDED_MODE_ACTIVATED, BOOLEAN_VALIDATOR);
VALIDATORS.put(Secure.ONE_HANDED_MODE_ENABLED, BOOLEAN_VALIDATOR);
VALIDATORS.put(Secure.ONE_HANDED_MODE_TIMEOUT, ANY_INTEGER_VALIDATOR);
diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java
index d2b444b..7186aba 100644
--- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java
+++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java
@@ -1842,6 +1842,10 @@
SecureSettingsProto.Accessibility
.ACCESSIBILITY_MAGNIFICATION_ALWAYS_ON_ENABLED);
dumpSetting(s, p,
+ Settings.Secure.ACCESSIBILITY_FORCE_INVERT_COLOR_ENABLED,
+ SecureSettingsProto.Accessibility
+ .ACCESSIBILITY_FORCE_INVERT_COLOR_ENABLED);
+ dumpSetting(s, p,
Settings.Secure.ACCESSIBILITY_MAGNIFICATION_JOYSTICK_ENABLED,
SecureSettingsProto.Accessibility
.ACCESSIBILITY_MAGNIFICATION_JOYSTICK_ENABLED);
diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java
index 1cde26b..c0d1671 100644
--- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java
+++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java
@@ -58,6 +58,7 @@
import android.content.BroadcastReceiver;
import android.content.ComponentName;
import android.content.ContentProvider;
+import android.content.ContentResolver;
import android.content.ContentValues;
import android.content.Context;
import android.content.Intent;
@@ -76,6 +77,7 @@
import android.database.sqlite.SQLiteQueryBuilder;
import android.hardware.camera2.utils.ArrayUtils;
import android.media.AudioManager;
+import android.media.IRingtonePlayer;
import android.net.Uri;
import android.os.Binder;
import android.os.Build;
@@ -110,6 +112,7 @@
import android.text.TextUtils;
import android.util.ArrayMap;
import android.util.ArraySet;
+import android.util.Log;
import android.util.Slog;
import android.util.SparseArray;
import android.util.SparseBooleanArray;
@@ -129,7 +132,10 @@
import java.io.File;
import java.io.FileDescriptor;
import java.io.FileNotFoundException;
+import java.io.FileOutputStream;
import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
import java.io.PrintWriter;
import java.nio.ByteBuffer;
import java.security.InvalidKeyException;
@@ -849,29 +855,68 @@
uri = ContentProvider.getUriWithoutUserId(uri);
final String cacheRingtoneSetting;
- final String cacheName;
if (Settings.System.RINGTONE_CACHE_URI.equals(uri)) {
cacheRingtoneSetting = Settings.System.RINGTONE;
- cacheName = Settings.System.RINGTONE_CACHE;
} else if (Settings.System.NOTIFICATION_SOUND_CACHE_URI.equals(uri)) {
cacheRingtoneSetting = Settings.System.NOTIFICATION_SOUND;
- cacheName = Settings.System.NOTIFICATION_SOUND_CACHE;
} else if (Settings.System.ALARM_ALERT_CACHE_URI.equals(uri)) {
cacheRingtoneSetting = Settings.System.ALARM_ALERT;
- cacheName = Settings.System.ALARM_ALERT_CACHE;
} else {
throw new FileNotFoundException("Direct file access no longer supported; "
+ "ringtone playback is available through android.media.Ringtone");
}
+ final File cacheFile = getCacheFile(cacheRingtoneSetting, userId);
+ return ParcelFileDescriptor.open(cacheFile, ParcelFileDescriptor.parseMode(mode));
+ }
+
+ @Nullable
+ private String getCacheName(String setting) {
+ if (Settings.System.RINGTONE.equals(setting)) {
+ return Settings.System.RINGTONE_CACHE;
+ } else if (Settings.System.NOTIFICATION_SOUND.equals(setting)) {
+ return Settings.System.NOTIFICATION_SOUND_CACHE;
+ } else if (Settings.System.ALARM_ALERT.equals(setting)) {
+ return Settings.System.ALARM_ALERT_CACHE;
+ }
+ return null;
+ }
+
+ @Nullable
+ private File getCacheFile(String setting, int userId) {
int actualCacheOwner;
// Redirect cache to parent if ringtone setting is owned by profile parent
synchronized (mLock) {
- actualCacheOwner = resolveOwningUserIdForSystemSettingLocked(userId,
- cacheRingtoneSetting);
+ actualCacheOwner = resolveOwningUserIdForSystemSettingLocked(userId, setting);
+ }
+ final String cacheName = getCacheName(setting);
+ if (cacheName == null) {
+ return null;
}
final File cacheFile = new File(getRingtoneCacheDir(actualCacheOwner), cacheName);
- return ParcelFileDescriptor.open(cacheFile, ParcelFileDescriptor.parseMode(mode));
+ return cacheFile;
+ }
+
+
+ /**
+ * Try opening the given ringtone locally first, but failover to
+ * {@link IRingtonePlayer} if we can't access it directly. Typically, happens
+ * when process doesn't hold {@link android.Manifest.permission#READ_EXTERNAL_STORAGE}.
+ */
+ private static InputStream openRingtone(Context context, Uri uri) throws IOException {
+ final ContentResolver resolver = context.getContentResolver();
+ try {
+ return resolver.openInputStream(uri);
+ } catch (SecurityException | IOException e) {
+ Log.w(LOG_TAG, "Failed to open directly; attempting failover: " + e);
+ final IRingtonePlayer player = context.getSystemService(AudioManager.class)
+ .getRingtonePlayer();
+ try {
+ return new ParcelFileDescriptor.AutoCloseInputStream(player.openRingtone(uri));
+ } catch (Exception e2) {
+ throw new IOException(e2);
+ }
+ }
}
private File getRingtoneCacheDir(int userId) {
@@ -1953,55 +1998,70 @@
return false;
}
- // Invalidate any relevant cache files
- String cacheName = null;
- if (Settings.System.RINGTONE.equals(name)) {
- cacheName = Settings.System.RINGTONE_CACHE;
- } else if (Settings.System.NOTIFICATION_SOUND.equals(name)) {
- cacheName = Settings.System.NOTIFICATION_SOUND_CACHE;
- } else if (Settings.System.ALARM_ALERT.equals(name)) {
- cacheName = Settings.System.ALARM_ALERT_CACHE;
- }
- if (cacheName != null) {
+ File cacheFile = getCacheFile(name, callingUserId);
+ if (cacheFile != null) {
if (!isValidAudioUri(name, value)) {
return false;
}
- final File cacheFile = new File(
- getRingtoneCacheDir(owningUserId), cacheName);
+ // Invalidate any relevant cache files
cacheFile.delete();
}
+ final boolean success;
// Mutate the value.
synchronized (mLock) {
switch (operation) {
case MUTATION_OPERATION_INSERT: {
validateSystemSettingValue(name, value);
- return mSettingsRegistry.insertSettingLocked(SETTINGS_TYPE_SYSTEM,
+ success = mSettingsRegistry.insertSettingLocked(SETTINGS_TYPE_SYSTEM,
owningUserId, name, value, null, false, callingPackage,
false, null, overrideableByRestore);
+ break;
}
case MUTATION_OPERATION_DELETE: {
- return mSettingsRegistry.deleteSettingLocked(SETTINGS_TYPE_SYSTEM,
+ success = mSettingsRegistry.deleteSettingLocked(SETTINGS_TYPE_SYSTEM,
owningUserId, name, false, null);
+ break;
}
case MUTATION_OPERATION_UPDATE: {
validateSystemSettingValue(name, value);
- return mSettingsRegistry.updateSettingLocked(SETTINGS_TYPE_SYSTEM,
+ success = mSettingsRegistry.updateSettingLocked(SETTINGS_TYPE_SYSTEM,
owningUserId, name, value, null, false, callingPackage,
false, null);
+ break;
}
case MUTATION_OPERATION_RESET: {
- mSettingsRegistry.resetSettingsLocked(SETTINGS_TYPE_SYSTEM,
+ success = mSettingsRegistry.resetSettingsLocked(SETTINGS_TYPE_SYSTEM,
runAsUserId, callingPackage, mode, tag);
- return true;
+ break;
}
+
+ default:
+ success = false;
+ Slog.e(LOG_TAG, "Unknown operation code: " + operation);
}
- Slog.e(LOG_TAG, "Unknown operation code: " + operation);
+ }
+
+ if (!success) {
return false;
}
+
+ if ((operation == MUTATION_OPERATION_INSERT || operation == MUTATION_OPERATION_UPDATE)
+ && cacheFile != null && value != null) {
+ final Uri ringtoneUri = Uri.parse(value);
+ // Stream selected ringtone into cache, so it's available for playback
+ // when CE storage is still locked
+ try (InputStream in = openRingtone(getContext(), ringtoneUri);
+ OutputStream out = new FileOutputStream(cacheFile)) {
+ FileUtils.copy(in, out);
+ } catch (IOException e) {
+ Slog.w(LOG_TAG, "Failed to cache ringtone: " + e);
+ }
+ }
+ return true;
}
private boolean isValidAudioUri(String name, String uri) {
@@ -3289,20 +3349,21 @@
return Global.SECURE_FRP_MODE.equals(setting.getName());
}
- public void resetSettingsLocked(int type, int userId, String packageName, int mode,
+ public boolean resetSettingsLocked(int type, int userId, String packageName, int mode,
String tag) {
- resetSettingsLocked(type, userId, packageName, mode, tag, /*prefix=*/
+ return resetSettingsLocked(type, userId, packageName, mode, tag, /*prefix=*/
null);
}
- public void resetSettingsLocked(int type, int userId, String packageName, int mode,
+ public boolean resetSettingsLocked(int type, int userId, String packageName, int mode,
String tag, @Nullable String prefix) {
final int key = makeKey(type, userId);
SettingsState settingsState = peekSettingsStateLocked(key);
if (settingsState == null) {
- return;
+ return false;
}
+ boolean success = false;
banConfigurationIfNecessary(type, prefix, settingsState);
switch (mode) {
case Settings.RESET_MODE_PACKAGE_DEFAULTS: {
@@ -3322,6 +3383,7 @@
}
if (someSettingChanged) {
settingsState.persistSyncLocked();
+ success = true;
}
}
} break;
@@ -3343,6 +3405,7 @@
}
if (someSettingChanged) {
settingsState.persistSyncLocked();
+ success = true;
}
}
} break;
@@ -3370,6 +3433,7 @@
}
if (someSettingChanged) {
settingsState.persistSyncLocked();
+ success = true;
}
}
} break;
@@ -3394,10 +3458,12 @@
}
if (someSettingChanged) {
settingsState.persistSyncLocked();
+ success = true;
}
}
} break;
}
+ return success;
}
public void removeSettingsForPackageLocked(String packageName, int userId) {
diff --git a/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java b/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java
index 203efbf..c0f6231 100644
--- a/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java
+++ b/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java
@@ -98,7 +98,6 @@
Settings.System.VOLUME_VOICE, // deprecated since API 2?
Settings.System.WHEN_TO_MAKE_WIFI_CALLS, // bug?
Settings.System.WINDOW_ORIENTATION_LISTENER_LOG, // used for debugging only
- Settings.System.DESKTOP_MODE, // developer setting for internal prototyping
Settings.System.MIN_REFRESH_RATE, // depends on hardware capabilities
Settings.System.PEAK_REFRESH_RATE, // depends on hardware capabilities
Settings.System.SCREEN_BRIGHTNESS_FLOAT,
@@ -658,7 +657,6 @@
Settings.Global.Wearable.COMPANION_BLE_ROLE,
Settings.Global.Wearable.COMPANION_NAME,
Settings.Global.Wearable.COMPANION_APP_NAME,
- Settings.Global.Wearable.USER_HFP_CLIENT_SETTING,
Settings.Global.Wearable.COMPANION_OS_VERSION,
Settings.Global.Wearable.ENABLE_ALL_LANGUAGES,
Settings.Global.Wearable.SETUP_LOCALE,
@@ -673,16 +671,12 @@
Settings.Global.Wearable.CLOCKWORK_LONG_PRESS_TO_ASSISTANT_ENABLED,
Settings.Global.Wearable.WET_MODE_ON,
Settings.Global.Wearable.COOLDOWN_MODE_ON,
- Settings.Global.Wearable.CHARGING_SOUNDS_ENABLED,
- Settings.Global.Wearable.SCREEN_UNLOCK_SOUND_ENABLED,
Settings.Global.Wearable.BEDTIME_MODE,
Settings.Global.Wearable.BEDTIME_HARD_MODE,
- Settings.Global.Wearable.RSB_WAKE_ENABLED,
Settings.Global.Wearable.LOCK_SCREEN_STATE,
Settings.Global.Wearable.ACCESSIBILITY_VIBRATION_WATCH_ENABLED,
Settings.Global.Wearable.ACCESSIBILITY_VIBRATION_WATCH_TYPE,
Settings.Global.Wearable.ACCESSIBILITY_VIBRATION_WATCH_SPEED,
- Settings.Global.Wearable.SCREENSHOT_ENABLED,
Settings.Global.Wearable.DISABLE_AOD_WHILE_PLUGGED,
Settings.Global.Wearable.NETWORK_LOCATION_OPT_IN,
Settings.Global.Wearable.CUSTOM_COLOR_FOREGROUND,
diff --git a/packages/Shell/AndroidManifest.xml b/packages/Shell/AndroidManifest.xml
index 323f65f..ffe28a6 100644
--- a/packages/Shell/AndroidManifest.xml
+++ b/packages/Shell/AndroidManifest.xml
@@ -167,6 +167,7 @@
<uses-permission android:name="android.permission.FORCE_BACK" />
<uses-permission android:name="android.permission.BATTERY_STATS" />
<uses-permission android:name="android.permission.PACKAGE_USAGE_STATS" />
+ <uses-permission android:name="android.permission.REPORT_USAGE_STATS" />
<uses-permission android:name="android.permission.INTERNAL_SYSTEM_WINDOW" />
<uses-permission android:name="android.permission.INJECT_EVENTS" />
<uses-permission android:name="android.permission.RETRIEVE_WINDOW_CONTENT" />
@@ -846,6 +847,8 @@
<uses-permission android:name="android.permission.LAUNCH_CREDENTIAL_SELECTOR" />
<!-- Permission required for CTS test IntentRedirectionTest -->
<uses-permission android:name="android.permission.QUERY_CLONED_APPS" />
+ <!-- Permission required for adb display commands `enable-display` and `disable-display`. -->
+ <uses-permission android:name="android.permission.MANAGE_DISPLAYS" />
<!-- Permission required for accessing all content provider mime types -->
<uses-permission android:name="android.permission.GET_ANY_PROVIDER_TYPE" />
diff --git a/packages/Shell/res/values-am/strings.xml b/packages/Shell/res/values-am/strings.xml
index 6cc13ec..99fb798 100644
--- a/packages/Shell/res/values-am/strings.xml
+++ b/packages/Shell/res/values-am/strings.xml
@@ -35,9 +35,9 @@
<string name="bugreport_add_details_to_zip_failed" msgid="1302931926486712371">"የሳንካ ሪፖርት ዝርዝሮችን ወደ ዚፕ ፋይል ማከል አልተቻለም"</string>
<string name="bugreport_unnamed" msgid="2800582406842092709">"ያልተሰየመ"</string>
<string name="bugreport_info_action" msgid="2158204228510576227">"ዝርዝሮች"</string>
- <string name="bugreport_screenshot_action" msgid="8677781721940614995">"ቅጽበታዊ ገፅ እይታ"</string>
- <string name="bugreport_screenshot_taken" msgid="5684211273096253120">"ቅጽበታዊ ገፅ እይታ በተሳካ ሁኔታ ተነስቷል"</string>
- <string name="bugreport_screenshot_failed" msgid="5853049140806834601">"ቅጽበታዊ ገፅ እይታ ሊነሳ አይችልም"</string>
+ <string name="bugreport_screenshot_action" msgid="8677781721940614995">"ቅጽበታዊ ገፅ ዕይታ"</string>
+ <string name="bugreport_screenshot_taken" msgid="5684211273096253120">"ቅጽበታዊ ገፅ ዕይታ በተሳካ ሁኔታ ተነስቷል"</string>
+ <string name="bugreport_screenshot_failed" msgid="5853049140806834601">"ቅጽበታዊ ገፅ ዕይታ ሊነሳ አይችልም"</string>
<string name="bugreport_info_dialog_title" msgid="1355948594292983332">"የሳንካ ሪፖርት <xliff:g id="ID">#%d</xliff:g> ዝርዝሮች"</string>
<string name="bugreport_info_name" msgid="4414036021935139527">"የፋይል ስም"</string>
<string name="bugreport_info_title" msgid="2306030793918239804">"የሳንካ ርዕስ"</string>
diff --git a/packages/SystemUI/Android.bp b/packages/SystemUI/Android.bp
index b472982..c92fe22 100644
--- a/packages/SystemUI/Android.bp
+++ b/packages/SystemUI/Android.bp
@@ -135,7 +135,10 @@
aconfig_declarations {
name: "systemui_aconfig_flags",
package: "com.android.systemui.aconfig",
- srcs: ["src/com/android/systemui/aconfig/systemui.aconfig"],
+ srcs: [
+ "src/com/android/systemui/aconfig/systemui.aconfig",
+ "src/com/android/systemui/accessibility/aconfig/accessibility.aconfig",
+ ],
}
java_aconfig_library {
@@ -323,6 +326,17 @@
"tests/src/com/android/systemui/statusbar/pipeline/wifi/data/repository/prod/WifiRepositoryImplTest.kt",
"tests/src/com/android/systemui/statusbar/pipeline/wifi/domain/interactor/WifiInteractorImplTest.kt",
"tests/src/com/android/systemui/statusbar/pipeline/wifi/ui/viewmodel/WifiViewModelTest.kt",
+
+ /* Bouncer UI tests */
+ "tests/src/com/android/keyguard/KeyguardPasswordViewControllerTest.kt",
+ "tests/src/com/android/keyguard/KeyguardPatternViewControllerTest.kt",
+ "tests/src/com/android/keyguard/KeyguardPinBasedInputViewControllerTest.java",
+ "tests/src/com/android/keyguard/KeyguardPinViewControllerTest.kt",
+ "tests/src/com/android/keyguard/KeyguardSecurityContainerTest.java",
+ "tests/src/com/android/keyguard/KeyguardSecurityContainerControllerTest.kt",
+ "tests/src/com/android/keyguard/KeyguardSecurityViewFlipperControllerTest.java",
+ "tests/src/com/android/keyguard/KeyguardSimPinViewControllerTest.kt",
+ "tests/src/com/android/keyguard/KeyguardSimPukViewControllerTest.kt",
],
path: "tests/src",
}
diff --git a/packages/SystemUI/AndroidManifest.xml b/packages/SystemUI/AndroidManifest.xml
index 6778d5a..b5b873c 100644
--- a/packages/SystemUI/AndroidManifest.xml
+++ b/packages/SystemUI/AndroidManifest.xml
@@ -349,6 +349,9 @@
<uses-permission android:name="android.permission.MONITOR_KEYBOARD_BACKLIGHT" />
+ <!-- Listen to (dis-)connection of external displays and enable / disable them. -->
+ <uses-permission android:name="android.permission.MANAGE_DISPLAYS" />
+
<protected-broadcast android:name="com.android.settingslib.action.REGISTER_SLICE_RECEIVER" />
<protected-broadcast android:name="com.android.settingslib.action.UNREGISTER_SLICE_RECEIVER" />
<protected-broadcast android:name="com.android.settings.flashlight.action.FLASHLIGHT_CHANGED" />
diff --git a/packages/SystemUI/accessibility/accessibilitymenu/res/values-am/strings.xml b/packages/SystemUI/accessibility/accessibilitymenu/res/values-am/strings.xml
index 0aeb410..7def587 100644
--- a/packages/SystemUI/accessibility/accessibilitymenu/res/values-am/strings.xml
+++ b/packages/SystemUI/accessibility/accessibilitymenu/res/values-am/strings.xml
@@ -12,7 +12,7 @@
<string name="lockscreen_label" msgid="648347953557887087">"ማያ ገፅ ቁልፍ"</string>
<string name="quick_settings_label" msgid="2999117381487601865">"ፈጣን ቅንብሮች"</string>
<string name="notifications_label" msgid="6829741046963013567">"ማሳወቂያዎች"</string>
- <string name="screenshot_label" msgid="863978141223970162">"ቅጽበታዊ ገፅ እይታ"</string>
+ <string name="screenshot_label" msgid="863978141223970162">"ቅጽበታዊ ገፅ ዕይታ"</string>
<string name="screenshot_utterance" msgid="1430760563401895074">"ቅጽበታዊ ገፅ እይታን ያነሳል"</string>
<string name="volume_up_label" msgid="8592766918780362870">"ድምፅ ጨምር"</string>
<string name="volume_down_label" msgid="8574981863656447346">"ድምፅ ቀንስ"</string>
diff --git a/packages/SystemUI/animation/src/com/android/systemui/animation/ActivityLaunchAnimator.kt b/packages/SystemUI/animation/src/com/android/systemui/animation/ActivityLaunchAnimator.kt
index 8306620..a3a1fa5 100644
--- a/packages/SystemUI/animation/src/com/android/systemui/animation/ActivityLaunchAnimator.kt
+++ b/packages/SystemUI/animation/src/com/android/systemui/animation/ActivityLaunchAnimator.kt
@@ -240,8 +240,15 @@
private fun Controller.callOnIntentStartedOnMainThread(willAnimate: Boolean) {
if (Looper.myLooper() != Looper.getMainLooper()) {
- this.launchContainer.context.mainExecutor.execute { this.onIntentStarted(willAnimate) }
+ this.launchContainer.context.mainExecutor.execute {
+ callOnIntentStartedOnMainThread(willAnimate)
+ }
} else {
+ // TODO(b/288507023): Remove this log.
+ Log.d(
+ TAG,
+ "Calling controller.onIntentStarted(willAnimate=$willAnimate) [controller=$this]"
+ )
this.onIntentStarted(willAnimate)
}
}
@@ -541,6 +548,9 @@
Log.i(TAG, "Aborting the animation as no window is opening")
removeTimeout()
iCallback?.invoke()
+
+ // TODO(b/288507023): Remove this log.
+ Log.d(TAG, "Calling controller.onLaunchAnimationCancelled() [no window opening]")
controller.onLaunchAnimationCancelled()
return
}
@@ -758,6 +768,9 @@
Log.i(TAG, "Remote animation timed out")
timedOut = true
+
+ // TODO(b/288507023): Remove this log.
+ Log.d(TAG, "Calling controller.onLaunchAnimationCancelled() [animation timed out]")
controller.onLaunchAnimationCancelled()
}
@@ -772,6 +785,12 @@
removeTimeout()
animation?.cancel()
+
+ // TODO(b/288507023): Remove this log.
+ Log.d(
+ TAG,
+ "Calling controller.onLaunchAnimationCancelled() [remote animation cancelled]",
+ )
controller.onLaunchAnimationCancelled()
}
diff --git a/packages/SystemUI/compose/core/src/com/android/compose/PlatformButtons.kt b/packages/SystemUI/compose/core/src/com/android/compose/PlatformButtons.kt
index 25269dc..44c4105 100644
--- a/packages/SystemUI/compose/core/src/com/android/compose/PlatformButtons.kt
+++ b/packages/SystemUI/compose/core/src/com/android/compose/PlatformButtons.kt
@@ -78,6 +78,7 @@
modifier = modifier,
enabled = enabled,
content = content,
+ colors = textButtonColors(),
)
}
@@ -85,26 +86,29 @@
@Composable
private fun filledButtonColors(): ButtonColors {
- val colors = LocalAndroidColorScheme.current.deprecated
+ val colors = LocalAndroidColorScheme.current
return ButtonDefaults.buttonColors(
- containerColor = colors.colorAccentPrimary,
- contentColor = colors.textColorOnAccent,
+ containerColor = colors.primary,
+ contentColor = colors.onPrimary,
)
}
@Composable
private fun outlineButtonColors(): ButtonColors {
- val colors = LocalAndroidColorScheme.current.deprecated
return ButtonDefaults.outlinedButtonColors(
- contentColor = colors.textColorPrimary,
+ contentColor = LocalAndroidColorScheme.current.onSurface,
)
}
@Composable
private fun outlineButtonBorder(): BorderStroke {
- val colors = LocalAndroidColorScheme.current.deprecated
return BorderStroke(
width = 1.dp,
- color = colors.colorAccentPrimaryVariant,
+ color = LocalAndroidColorScheme.current.primary,
)
}
+
+@Composable
+private fun textButtonColors(): ButtonColors {
+ return ButtonDefaults.textButtonColors(contentColor = LocalAndroidColorScheme.current.primary)
+}
diff --git a/packages/SystemUI/compose/core/src/com/android/compose/animation/scene/SwipeToScene.kt b/packages/SystemUI/compose/core/src/com/android/compose/animation/scene/SwipeToScene.kt
index d9a45cd..2069ebd 100644
--- a/packages/SystemUI/compose/core/src/com/android/compose/animation/scene/SwipeToScene.kt
+++ b/packages/SystemUI/compose/core/src/com/android/compose/animation/scene/SwipeToScene.kt
@@ -19,18 +19,25 @@
import androidx.compose.animation.core.Animatable
import androidx.compose.animation.core.Spring
import androidx.compose.animation.core.spring
+import androidx.compose.foundation.gestures.DraggableState
import androidx.compose.foundation.gestures.Orientation
import androidx.compose.foundation.gestures.draggable
import androidx.compose.foundation.gestures.rememberDraggableState
import androidx.compose.runtime.Composable
+import androidx.compose.runtime.DisposableEffect
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableFloatStateOf
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
+import androidx.compose.runtime.rememberCoroutineScope
import androidx.compose.runtime.setValue
import androidx.compose.ui.Modifier
+import androidx.compose.ui.geometry.Offset
+import androidx.compose.ui.input.nestedscroll.nestedScroll
import androidx.compose.ui.platform.LocalDensity
+import androidx.compose.ui.unit.Velocity
import androidx.compose.ui.unit.dp
+import com.android.compose.nestedscroll.PriorityPostNestedScrollConnection
import kotlin.math.absoluteValue
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Job
@@ -70,23 +77,38 @@
// the same as SwipeableV2Defaults.PositionalThreshold.
val positionalThreshold = with(LocalDensity.current) { 56.dp.toPx() }
- return draggable(
- orientation = orientation,
- enabled = enabled,
- startDragImmediately = startDragImmediately,
- onDragStarted = { onDragStarted(layoutImpl, transition, orientation) },
- state =
- rememberDraggableState { delta -> onDrag(layoutImpl, transition, orientation, delta) },
- onDragStopped = { velocity ->
- onDragStopped(
- layoutImpl,
- transition,
- velocity,
- velocityThreshold,
- positionalThreshold,
- )
- },
- )
+ val draggableState = rememberDraggableState { delta ->
+ onDrag(layoutImpl, transition, orientation, delta)
+ }
+
+ return nestedScroll(
+ connection =
+ rememberSwipeToSceneNestedScrollConnection(
+ orientation = orientation,
+ coroutineScope = rememberCoroutineScope(),
+ draggableState = draggableState,
+ transition = transition,
+ layoutImpl = layoutImpl,
+ velocityThreshold = velocityThreshold,
+ positionalThreshold = positionalThreshold
+ ),
+ )
+ .draggable(
+ state = draggableState,
+ orientation = orientation,
+ enabled = enabled,
+ startDragImmediately = startDragImmediately,
+ onDragStarted = { onDragStarted(layoutImpl, transition, orientation) },
+ onDragStopped = { velocity ->
+ onDragStopped(
+ layoutImpl = layoutImpl,
+ transition = transition,
+ velocity = velocity,
+ velocityThreshold = velocityThreshold,
+ positionalThreshold = positionalThreshold,
+ )
+ },
+ )
}
private class SwipeTransition(initialScene: Scene) : TransitionState.Transition {
@@ -235,35 +257,18 @@
// twice in a row to accelerate the transition and go from A => B then B => C really fast.
maybeHandleAcceleratedSwipe(transition, orientation)
- val fromScene = transition._fromScene
- val upOrLeft = fromScene.upOrLeft(orientation)
- val downOrRight = fromScene.downOrRight(orientation)
val offset = transition.dragOffset
+ val fromScene = transition._fromScene
// Compute the target scene depending on the current offset.
- val targetSceneKey: SceneKey
- val signedDistance: Float
- when {
- offset < 0f && upOrLeft != null -> {
- targetSceneKey = upOrLeft
- signedDistance = -transition.absoluteDistance
- }
- offset > 0f && downOrRight != null -> {
- targetSceneKey = downOrRight
- signedDistance = transition.absoluteDistance
- }
- else -> {
- targetSceneKey = fromScene.key
- signedDistance = 0f
- }
+ val target = fromScene.findTargetSceneAndDistance(orientation, offset, layoutImpl)
+
+ if (transition._toScene.key != target.sceneKey) {
+ transition._toScene = layoutImpl.scenes.getValue(target.sceneKey)
}
- if (transition._toScene.key != targetSceneKey) {
- transition._toScene = layoutImpl.scenes.getValue(targetSceneKey)
- }
-
- if (transition._distance != signedDistance) {
- transition._distance = signedDistance
+ if (transition._distance != target.distance) {
+ transition._distance = target.distance
}
}
@@ -299,12 +304,55 @@
// using fromScene and dragOffset.
}
+private data class TargetScene(
+ val sceneKey: SceneKey,
+ val distance: Float,
+)
+
+private fun Scene.findTargetSceneAndDistance(
+ orientation: Orientation,
+ directionOffset: Float,
+ layoutImpl: SceneTransitionLayoutImpl,
+): TargetScene {
+ val maxDistance =
+ when (orientation) {
+ Orientation.Horizontal -> layoutImpl.size.width
+ Orientation.Vertical -> layoutImpl.size.height
+ }.toFloat()
+
+ val upOrLeft = upOrLeft(orientation)
+ val downOrRight = downOrRight(orientation)
+
+ // Compute the target scene depending on the current offset.
+ return when {
+ directionOffset < 0f && upOrLeft != null -> {
+ TargetScene(
+ sceneKey = upOrLeft,
+ distance = -maxDistance,
+ )
+ }
+ directionOffset > 0f && downOrRight != null -> {
+ TargetScene(
+ sceneKey = downOrRight,
+ distance = maxDistance,
+ )
+ }
+ else -> {
+ TargetScene(
+ sceneKey = key,
+ distance = 0f,
+ )
+ }
+ }
+}
+
private fun CoroutineScope.onDragStopped(
layoutImpl: SceneTransitionLayoutImpl,
transition: SwipeTransition,
velocity: Float,
velocityThreshold: Float,
positionalThreshold: Float,
+ canChangeScene: Boolean = true,
) {
// The state was changed since the drag started; don't do anything.
if (layoutImpl.state.transitionState != transition) {
@@ -323,14 +371,15 @@
val offset = transition.dragOffset
val distance = transition.distance
if (
- shouldCommitSwipe(
- offset,
- distance,
- velocity,
- velocityThreshold,
- positionalThreshold,
- wasCommitted = transition._currentScene == transition._toScene,
- )
+ canChangeScene &&
+ shouldCommitSwipe(
+ offset,
+ distance,
+ velocity,
+ velocityThreshold,
+ positionalThreshold,
+ wasCommitted = transition._currentScene == transition._toScene,
+ )
) {
targetOffset = distance
targetScene = transition._toScene
@@ -348,31 +397,13 @@
layoutImpl.onChangeScene(targetScene.key)
}
- // Animate the offset.
- transition.offsetAnimationJob = launch {
- transition.offsetAnimatable.snapTo(offset)
- transition.isAnimatingOffset = true
-
- transition.offsetAnimatable.animateTo(
- targetOffset,
- // TODO(b/290184746): Make this spring spec configurable.
- spring(
- stiffness = Spring.StiffnessMediumLow,
- visibilityThreshold = OffsetVisibilityThreshold
- ),
- initialVelocity = velocity,
- )
-
- // Now that the animation is done, the state should be idle. Note that if the state was
- // changed since this animation started, some external code changed it and we shouldn't do
- // anything here. Note also that this job will be cancelled in the case where the user
- // intercepts this swipe.
- if (layoutImpl.state.transitionState == transition) {
- layoutImpl.state.transitionState = TransitionState.Idle(targetScene.key)
- }
-
- transition.offsetAnimationJob = null
- }
+ animateOffset(
+ transition = transition,
+ layoutImpl = layoutImpl,
+ initialVelocity = velocity,
+ targetOffset = targetOffset,
+ targetScene = targetScene.key
+ )
}
/**
@@ -412,8 +443,216 @@
}
}
+private fun CoroutineScope.animateOffset(
+ transition: SwipeTransition,
+ layoutImpl: SceneTransitionLayoutImpl,
+ initialVelocity: Float,
+ targetOffset: Float,
+ targetScene: SceneKey,
+) {
+ transition.offsetAnimationJob = launch {
+ if (!transition.isAnimatingOffset) {
+ transition.offsetAnimatable.snapTo(transition.dragOffset)
+ }
+ transition.isAnimatingOffset = true
+
+ transition.offsetAnimatable.animateTo(
+ targetOffset,
+ // TODO(b/290184746): Make this spring spec configurable.
+ spring(
+ stiffness = Spring.StiffnessMediumLow,
+ visibilityThreshold = OffsetVisibilityThreshold
+ ),
+ initialVelocity = initialVelocity,
+ )
+
+ // Now that the animation is done, the state should be idle. Note that if the state was
+ // changed since this animation started, some external code changed it and we shouldn't do
+ // anything here. Note also that this job will be cancelled in the case where the user
+ // intercepts this swipe.
+ if (layoutImpl.state.transitionState == transition) {
+ layoutImpl.state.transitionState = TransitionState.Idle(targetScene)
+ }
+
+ transition.offsetAnimationJob = null
+ }
+}
+
+private fun CoroutineScope.animateOverscroll(
+ layoutImpl: SceneTransitionLayoutImpl,
+ transition: SwipeTransition,
+ velocity: Velocity,
+ orientation: Orientation,
+): Velocity {
+ val velocityAmount =
+ when (orientation) {
+ Orientation.Vertical -> velocity.y
+ Orientation.Horizontal -> velocity.x
+ }
+
+ if (velocityAmount == 0f) {
+ // There is no remaining velocity
+ return Velocity.Zero
+ }
+
+ val fromScene = layoutImpl.scene(layoutImpl.state.transitionState.currentScene)
+ val target = fromScene.findTargetSceneAndDistance(orientation, velocityAmount, layoutImpl)
+ val isValidTarget = target.distance != 0f && target.sceneKey != fromScene.key
+
+ if (!isValidTarget || layoutImpl.state.transitionState == transition) {
+ // We have not found a valid target or we are already in a transition
+ return Velocity.Zero
+ }
+
+ transition._currentScene = fromScene
+ transition._fromScene = fromScene
+ transition._toScene = layoutImpl.scene(target.sceneKey)
+ transition._distance = target.distance
+ transition.absoluteDistance = target.distance.absoluteValue
+ transition.dragOffset = 0f
+ transition.isAnimatingOffset = false
+ transition.offsetAnimationJob = null
+
+ layoutImpl.state.transitionState = transition
+
+ animateOffset(
+ transition = transition,
+ layoutImpl = layoutImpl,
+ initialVelocity = velocityAmount,
+ targetOffset = 0f,
+ targetScene = fromScene.key
+ )
+
+ // The animateOffset animation consumes any remaining velocity.
+ return velocity
+}
+
/**
* The number of pixels below which there won't be a visible difference in the transition and from
* which the animation can stop.
*/
private const val OffsetVisibilityThreshold = 0.5f
+
+@Composable
+private fun rememberSwipeToSceneNestedScrollConnection(
+ orientation: Orientation,
+ coroutineScope: CoroutineScope,
+ draggableState: DraggableState,
+ transition: SwipeTransition,
+ layoutImpl: SceneTransitionLayoutImpl,
+ velocityThreshold: Float,
+ positionalThreshold: Float,
+): PriorityPostNestedScrollConnection {
+ val density = LocalDensity.current
+ val scrollConnection =
+ remember(
+ orientation,
+ coroutineScope,
+ draggableState,
+ transition,
+ layoutImpl,
+ velocityThreshold,
+ positionalThreshold,
+ density,
+ ) {
+ fun Offset.toAmount() =
+ when (orientation) {
+ Orientation.Horizontal -> x
+ Orientation.Vertical -> y
+ }
+
+ fun Velocity.toAmount() =
+ when (orientation) {
+ Orientation.Horizontal -> x
+ Orientation.Vertical -> y
+ }
+
+ fun Float.toOffset() =
+ when (orientation) {
+ Orientation.Horizontal -> Offset(x = this, y = 0f)
+ Orientation.Vertical -> Offset(x = 0f, y = this)
+ }
+
+ // The next potential scene is calculated during the canStart
+ var nextScene: SceneKey? = null
+
+ // This is the scene on which we will have priority during the scroll gesture.
+ var priorityScene: SceneKey? = null
+
+ // If we performed a long gesture before entering priority mode, we would have to avoid
+ // moving on to the next scene.
+ var gestureStartedOnNestedChild = false
+
+ PriorityPostNestedScrollConnection(
+ canStart = { offsetAvailable, offsetBeforeStart ->
+ val amount = offsetAvailable.toAmount()
+ if (amount == 0f) return@PriorityPostNestedScrollConnection false
+
+ gestureStartedOnNestedChild = offsetBeforeStart != Offset.Zero
+
+ val fromScene = layoutImpl.scene(layoutImpl.state.transitionState.currentScene)
+ nextScene =
+ when {
+ amount < 0f -> fromScene.upOrLeft(orientation)
+ amount > 0f -> fromScene.downOrRight(orientation)
+ else -> null
+ }
+
+ nextScene != null
+ },
+ canContinueScroll = { priorityScene == transition._toScene.key },
+ onStart = {
+ priorityScene = nextScene
+ onDragStarted(layoutImpl, transition, orientation)
+ },
+ onScroll = { offsetAvailable ->
+ val amount = offsetAvailable.toAmount()
+
+ // TODO(b/297842071) We should handle the overscroll or slow drag if the gesture
+ // is initiated in a nested child.
+
+ // Appends a new coroutine to attempt to drag by [amount] px. In this case we
+ // are assuming that the [coroutineScope] is tied to the main thread and that
+ // calls to [launch] are therefore queued.
+ coroutineScope.launch { draggableState.drag { dragBy(amount) } }
+
+ amount.toOffset()
+ },
+ onStop = { velocityAvailable ->
+ priorityScene = null
+
+ coroutineScope.onDragStopped(
+ layoutImpl = layoutImpl,
+ transition = transition,
+ velocity = velocityAvailable.toAmount(),
+ velocityThreshold = velocityThreshold,
+ positionalThreshold = positionalThreshold,
+ canChangeScene = !gestureStartedOnNestedChild
+ )
+
+ // The onDragStopped animation consumes any remaining velocity.
+ velocityAvailable
+ },
+ onPostFling = { velocityAvailable ->
+ // If there is any velocity left, we can try running an overscroll animation
+ // between scenes.
+ coroutineScope.animateOverscroll(
+ layoutImpl = layoutImpl,
+ transition = transition,
+ velocity = velocityAvailable,
+ orientation = orientation
+ )
+ },
+ )
+ }
+ DisposableEffect(scrollConnection) {
+ onDispose {
+ coroutineScope.launch {
+ // This should ensure that the draggableState is in a consistent state and that it
+ // does not cause any unexpected behavior.
+ scrollConnection.reset()
+ }
+ }
+ }
+ return scrollConnection
+}
diff --git a/packages/SystemUI/compose/core/src/com/android/compose/nestedscroll/LargeTopAppBarNestedScrollConnection.kt b/packages/SystemUI/compose/core/src/com/android/compose/nestedscroll/LargeTopAppBarNestedScrollConnection.kt
new file mode 100644
index 0000000..cea8d9a
--- /dev/null
+++ b/packages/SystemUI/compose/core/src/com/android/compose/nestedscroll/LargeTopAppBarNestedScrollConnection.kt
@@ -0,0 +1,92 @@
+/*
+ * 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.compose.nestedscroll
+
+import androidx.compose.ui.geometry.Offset
+import androidx.compose.ui.input.nestedscroll.NestedScrollConnection
+import androidx.compose.ui.input.nestedscroll.NestedScrollSource
+
+/**
+ * A [NestedScrollConnection] that listens for all vertical scroll events and responds in the
+ * following way:
+ * - If you **scroll up**, it **first brings the [height]** back to the [minHeight] and then allows
+ * scrolling of the children (usually the content).
+ * - If you **scroll down**, it **first allows scrolling of the children** (usually the content) and
+ * then resets the [height] to [maxHeight].
+ *
+ * This behavior is useful for implementing a
+ * [Large top app bar](https://m3.material.io/components/top-app-bar/specs) effect or something
+ * similar.
+ *
+ * @sample com.android.compose.animation.scene.demo.Shade
+ */
+class LargeTopAppBarNestedScrollConnection(
+ private val height: () -> Float,
+ private val onChangeHeight: (Float) -> Unit,
+ private val minHeight: Float,
+ private val maxHeight: Float,
+) : NestedScrollConnection {
+
+ constructor(
+ height: () -> Float,
+ onHeightChanged: (Float) -> Unit,
+ heightRange: ClosedFloatingPointRange<Float>,
+ ) : this(
+ height = height,
+ onChangeHeight = onHeightChanged,
+ minHeight = heightRange.start,
+ maxHeight = heightRange.endInclusive,
+ )
+
+ /**
+ * When swiping up, the LargeTopAppBar will shrink (to [minHeight]) and the content will expand.
+ * Then, you can then scroll down the content.
+ */
+ override fun onPreScroll(available: Offset, source: NestedScrollSource): Offset {
+ val y = available.y
+ val currentHeight = height()
+ if (y >= 0 || currentHeight <= minHeight) {
+ return Offset.Zero
+ }
+
+ val amountLeft = minHeight - currentHeight
+ val amountConsumed = y.coerceAtLeast(amountLeft)
+ onChangeHeight(currentHeight + amountConsumed)
+ return Offset(0f, amountConsumed)
+ }
+
+ /**
+ * When swiping down, the content will scroll up until it reaches the top. Then, the
+ * LargeTopAppBar will expand until it reaches its [maxHeight].
+ */
+ override fun onPostScroll(
+ consumed: Offset,
+ available: Offset,
+ source: NestedScrollSource
+ ): Offset {
+ val y = available.y
+ val currentHeight = height()
+ if (y <= 0 || currentHeight >= maxHeight) {
+ return Offset.Zero
+ }
+
+ val amountLeft = maxHeight - currentHeight
+ val amountConsumed = y.coerceAtMost(amountLeft)
+ onChangeHeight(currentHeight + amountConsumed)
+ return Offset(0f, amountConsumed)
+ }
+}
diff --git a/packages/SystemUI/compose/core/src/com/android/compose/nestedscroll/PriorityPostNestedScrollConnection.kt b/packages/SystemUI/compose/core/src/com/android/compose/nestedscroll/PriorityPostNestedScrollConnection.kt
new file mode 100644
index 0000000..793a9a5
--- /dev/null
+++ b/packages/SystemUI/compose/core/src/com/android/compose/nestedscroll/PriorityPostNestedScrollConnection.kt
@@ -0,0 +1,126 @@
+/*
+ * 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.compose.nestedscroll
+
+import androidx.compose.ui.geometry.Offset
+import androidx.compose.ui.input.nestedscroll.NestedScrollConnection
+import androidx.compose.ui.input.nestedscroll.NestedScrollSource
+import androidx.compose.ui.unit.Velocity
+
+/**
+ * This [NestedScrollConnection] waits for a child to scroll ([onPostScroll]), and then decides (via
+ * [canStart]) if it should take over scrolling. If it does, it will scroll before its children,
+ * until [canContinueScroll] allows it.
+ *
+ * Note: Call [reset] before destroying this object to make sure you always get a call to [onStop]
+ * after [onStart].
+ *
+ * @sample com.android.compose.animation.scene.rememberSwipeToSceneNestedScrollConnection
+ */
+class PriorityPostNestedScrollConnection(
+ private val canStart: (offsetAvailable: Offset, offsetBeforeStart: Offset) -> Boolean,
+ private val canContinueScroll: () -> Boolean,
+ private val onStart: () -> Unit,
+ private val onScroll: (offsetAvailable: Offset) -> Offset,
+ private val onStop: (velocityAvailable: Velocity) -> Velocity,
+ private val onPostFling: suspend (velocityAvailable: Velocity) -> Velocity,
+) : NestedScrollConnection {
+
+ /** In priority mode [onPreScroll] events are first consumed by the parent, via [onScroll]. */
+ private var isPriorityMode = false
+
+ private var offsetScrolledBeforePriorityMode = Offset.Zero
+
+ override fun onPostScroll(
+ consumed: Offset,
+ available: Offset,
+ source: NestedScrollSource,
+ ): Offset {
+ // The offset before the start takes into account the up and down movements, starting from
+ // the beginning or from the last fling gesture.
+ val offsetBeforeStart = offsetScrolledBeforePriorityMode - available
+
+ if (
+ isPriorityMode ||
+ source == NestedScrollSource.Fling ||
+ !canStart(available, offsetBeforeStart)
+ ) {
+ // The priority mode cannot start so we won't consume the available offset.
+ return Offset.Zero
+ }
+
+ // Step 1: It's our turn! We start capturing scroll events when one of our children has an
+ // available offset following a scroll event.
+ isPriorityMode = true
+
+ // Note: onStop will be called if we cannot continue to scroll (step 3a), or the finger is
+ // lifted (step 3b), or this object has been destroyed (step 3c).
+ onStart()
+
+ return onScroll(available)
+ }
+
+ override fun onPreScroll(available: Offset, source: NestedScrollSource): Offset {
+ if (!isPriorityMode) {
+ if (source != NestedScrollSource.Fling) {
+ // We want to track the amount of offset consumed before entering priority mode
+ offsetScrolledBeforePriorityMode += available
+ }
+
+ return Offset.Zero
+ }
+
+ if (!canContinueScroll()) {
+ // Step 3a: We have lost priority and we no longer need to intercept scroll events.
+ onPriorityStop(velocity = Velocity.Zero)
+ return Offset.Zero
+ }
+
+ // Step 2: We have the priority and can consume the scroll events.
+ return onScroll(available)
+ }
+
+ override suspend fun onPreFling(available: Velocity): Velocity {
+ // Step 3b: The finger is lifted, we can stop intercepting scroll events and use the speed
+ // of the fling gesture.
+ return onPriorityStop(velocity = available)
+ }
+
+ override suspend fun onPostFling(consumed: Velocity, available: Velocity): Velocity {
+ return onPostFling(available)
+ }
+
+ /** Method to call before destroying the object or to reset the initial state. */
+ fun reset() {
+ // Step 3c: To ensure that an onStop is always called for every onStart.
+ onPriorityStop(velocity = Velocity.Zero)
+ }
+
+ private fun onPriorityStop(velocity: Velocity): Velocity {
+
+ // We can restart tracking the consumed offsets from scratch.
+ offsetScrolledBeforePriorityMode = Offset.Zero
+
+ if (!isPriorityMode) {
+ return Velocity.Zero
+ }
+
+ isPriorityMode = false
+
+ return onStop(velocity)
+ }
+}
diff --git a/packages/SystemUI/compose/core/tests/src/com/android/compose/nestedscroll/LargeTopAppBarNestedScrollConnectionTest.kt b/packages/SystemUI/compose/core/tests/src/com/android/compose/nestedscroll/LargeTopAppBarNestedScrollConnectionTest.kt
new file mode 100644
index 0000000..03d231a
--- /dev/null
+++ b/packages/SystemUI/compose/core/tests/src/com/android/compose/nestedscroll/LargeTopAppBarNestedScrollConnectionTest.kt
@@ -0,0 +1,145 @@
+/*
+ * 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.compose.nestedscroll
+
+import androidx.compose.ui.geometry.Offset
+import androidx.compose.ui.input.nestedscroll.NestedScrollSource
+import com.google.common.truth.Truth.assertThat
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.junit.runners.Parameterized
+
+@RunWith(Parameterized::class)
+class LargeTopAppBarNestedScrollConnectionTest(testCase: TestCase) {
+ val scrollSource = testCase.scrollSource
+
+ private var height = 0f
+
+ private fun buildScrollConnection(heightRange: ClosedFloatingPointRange<Float>) =
+ LargeTopAppBarNestedScrollConnection(
+ height = { height },
+ onHeightChanged = { height = it },
+ heightRange = heightRange,
+ )
+
+ @Test
+ fun onScrollUp_consumeHeightFirst() {
+ val scrollConnection = buildScrollConnection(heightRange = 0f..2f)
+ height = 1f
+
+ val offsetConsumed =
+ scrollConnection.onPreScroll(available = Offset(x = 0f, y = -1f), source = scrollSource)
+
+ // It can decrease by 1 the height
+ assertThat(offsetConsumed).isEqualTo(Offset(0f, -1f))
+ assertThat(height).isEqualTo(0f)
+ }
+
+ @Test
+ fun onScrollUp_consumeDownToMin() {
+ val scrollConnection = buildScrollConnection(heightRange = 0f..2f)
+ height = 0f
+
+ val offsetConsumed =
+ scrollConnection.onPreScroll(available = Offset(x = 0f, y = -1f), source = scrollSource)
+
+ // It should not change the height (already at min)
+ assertThat(offsetConsumed).isEqualTo(Offset.Zero)
+ assertThat(height).isEqualTo(0f)
+ }
+
+ @Test
+ fun onScrollUp_ignorePostScroll() {
+ val scrollConnection = buildScrollConnection(heightRange = 0f..2f)
+ height = 1f
+
+ val offsetConsumed =
+ scrollConnection.onPostScroll(
+ consumed = Offset.Zero,
+ available = Offset(x = 0f, y = -1f),
+ source = scrollSource
+ )
+
+ // It should ignore all onPostScroll events
+ assertThat(offsetConsumed).isEqualTo(Offset.Zero)
+ assertThat(height).isEqualTo(1f)
+ }
+
+ @Test
+ fun onScrollDown_allowConsumeContentFirst() {
+ val scrollConnection = buildScrollConnection(heightRange = 0f..2f)
+ height = 1f
+
+ val offsetConsumed =
+ scrollConnection.onPreScroll(available = Offset(x = 0f, y = 1f), source = scrollSource)
+
+ // It should ignore all onPreScroll events
+ assertThat(offsetConsumed).isEqualTo(Offset.Zero)
+ assertThat(height).isEqualTo(1f)
+ }
+
+ @Test
+ fun onScrollDown_consumeHeightPostScroll() {
+ val scrollConnection = buildScrollConnection(heightRange = 0f..2f)
+ height = 1f
+
+ val offsetConsumed =
+ scrollConnection.onPostScroll(
+ consumed = Offset.Zero,
+ available = Offset(x = 0f, y = 1f),
+ source = scrollSource
+ )
+
+ // It can increase by 1 the height
+ assertThat(offsetConsumed).isEqualTo(Offset(0f, 1f))
+ assertThat(height).isEqualTo(2f)
+ }
+
+ @Test
+ fun onScrollDown_consumeUpToMax() {
+ val scrollConnection = buildScrollConnection(heightRange = 0f..2f)
+ height = 2f
+
+ val offsetConsumed =
+ scrollConnection.onPostScroll(
+ consumed = Offset.Zero,
+ available = Offset(x = 0f, y = 1f),
+ source = scrollSource
+ )
+
+ // It should not change the height (already at max)
+ assertThat(offsetConsumed).isEqualTo(Offset.Zero)
+ assertThat(height).isEqualTo(2f)
+ }
+
+ // NestedScroll Source is a value/inline class and must be wrapped in a parameterized test
+ // https://youtrack.jetbrains.com/issue/KT-35523/Parameterized-JUnit-tests-with-inline-classes-throw-IllegalArgumentException
+ data class TestCase(val scrollSource: NestedScrollSource) {
+ override fun toString() = scrollSource.toString()
+ }
+
+ companion object {
+ @Parameterized.Parameters(name = "{0}")
+ @JvmStatic
+ fun data(): List<TestCase> =
+ listOf(
+ TestCase(NestedScrollSource.Drag),
+ TestCase(NestedScrollSource.Fling),
+ TestCase(NestedScrollSource.Wheel),
+ )
+ }
+}
diff --git a/packages/SystemUI/compose/core/tests/src/com/android/compose/nestedscroll/PriorityPostNestedScrollConnectionTest.kt b/packages/SystemUI/compose/core/tests/src/com/android/compose/nestedscroll/PriorityPostNestedScrollConnectionTest.kt
new file mode 100644
index 0000000..8e2b77a
--- /dev/null
+++ b/packages/SystemUI/compose/core/tests/src/com/android/compose/nestedscroll/PriorityPostNestedScrollConnectionTest.kt
@@ -0,0 +1,172 @@
+/*
+ * 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.
+ */
+
+@file:OptIn(ExperimentalCoroutinesApi::class)
+
+package com.android.compose.nestedscroll
+
+import androidx.compose.ui.geometry.Offset
+import androidx.compose.ui.input.nestedscroll.NestedScrollSource
+import androidx.compose.ui.unit.Velocity
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import com.google.common.truth.Truth.assertThat
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.test.runTest
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@RunWith(AndroidJUnit4::class)
+class PriorityPostNestedScrollConnectionTest {
+ private var canStart = false
+ private var canContinueScroll = false
+ private var isStarted = false
+ private var lastScroll: Offset? = null
+ private var returnOnScroll = Offset.Zero
+ private var lastStop: Velocity? = null
+ private var returnOnStop = Velocity.Zero
+ private var lastOnPostFling: Velocity? = null
+ private var returnOnPostFling = Velocity.Zero
+
+ private val scrollConnection =
+ PriorityPostNestedScrollConnection(
+ canStart = { _, _ -> canStart },
+ canContinueScroll = { canContinueScroll },
+ onStart = { isStarted = true },
+ onScroll = {
+ lastScroll = it
+ returnOnScroll
+ },
+ onStop = {
+ lastStop = it
+ returnOnStop
+ },
+ onPostFling = {
+ lastOnPostFling = it
+ returnOnPostFling
+ },
+ )
+
+ private val offset1 = Offset(1f, 1f)
+ private val offset2 = Offset(2f, 2f)
+ private val velocity1 = Velocity(1f, 1f)
+ private val velocity2 = Velocity(2f, 2f)
+
+ private fun startPriorityMode() {
+ canStart = true
+ scrollConnection.onPostScroll(
+ consumed = Offset.Zero,
+ available = Offset.Zero,
+ source = NestedScrollSource.Drag
+ )
+ }
+
+ @Test
+ fun step1_priorityModeShouldStartOnlyOnPostScroll() = runTest {
+ canStart = true
+
+ scrollConnection.onPreScroll(available = Offset.Zero, source = NestedScrollSource.Drag)
+ assertThat(isStarted).isEqualTo(false)
+
+ scrollConnection.onPreFling(available = Velocity.Zero)
+ assertThat(isStarted).isEqualTo(false)
+
+ scrollConnection.onPostFling(consumed = Velocity.Zero, available = Velocity.Zero)
+ assertThat(isStarted).isEqualTo(false)
+
+ startPriorityMode()
+ assertThat(isStarted).isEqualTo(true)
+ }
+
+ @Test
+ fun step1_priorityModeShouldStartOnlyIfAllowed() {
+ scrollConnection.onPostScroll(
+ consumed = Offset.Zero,
+ available = Offset.Zero,
+ source = NestedScrollSource.Drag
+ )
+ assertThat(isStarted).isEqualTo(false)
+
+ startPriorityMode()
+ assertThat(isStarted).isEqualTo(true)
+ }
+
+ @Test
+ fun step1_onPriorityModeStarted_receiveAvailableOffset() {
+ canStart = true
+
+ scrollConnection.onPostScroll(
+ consumed = offset1,
+ available = offset2,
+ source = NestedScrollSource.Drag
+ )
+
+ assertThat(lastScroll).isEqualTo(offset2)
+ }
+
+ @Test
+ fun step2_onPriorityMode_shouldContinueIfAllowed() {
+ startPriorityMode()
+ canContinueScroll = true
+
+ scrollConnection.onPreScroll(available = offset1, source = NestedScrollSource.Drag)
+ assertThat(lastScroll).isEqualTo(offset1)
+
+ canContinueScroll = false
+ scrollConnection.onPreScroll(available = offset2, source = NestedScrollSource.Drag)
+ assertThat(lastScroll).isNotEqualTo(offset2)
+ assertThat(lastScroll).isEqualTo(offset1)
+ }
+
+ @Test
+ fun step3a_onPriorityMode_shouldStopIfCannotContinue() {
+ startPriorityMode()
+ canContinueScroll = false
+
+ scrollConnection.onPreScroll(available = Offset.Zero, source = NestedScrollSource.Drag)
+
+ assertThat(lastStop).isNotNull()
+ }
+
+ @Test
+ fun step3b_onPriorityMode_shouldStopOnFling() = runTest {
+ startPriorityMode()
+ canContinueScroll = true
+
+ scrollConnection.onPreFling(available = Velocity.Zero)
+
+ assertThat(lastStop).isNotNull()
+ }
+
+ @Test
+ fun step3c_onPriorityMode_shouldStopOnReset() {
+ startPriorityMode()
+ canContinueScroll = true
+
+ scrollConnection.reset()
+
+ assertThat(lastStop).isNotNull()
+ }
+
+ @Test
+ fun receive_onPostFling() = runTest {
+ scrollConnection.onPostFling(
+ consumed = velocity1,
+ available = velocity2,
+ )
+
+ assertThat(lastOnPostFling).isEqualTo(velocity2)
+ }
+}
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 7545ff4..8a8557a 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
@@ -21,7 +21,6 @@
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.text.KeyboardActions
import androidx.compose.foundation.text.KeyboardOptions
-import androidx.compose.material3.ExperimentalMaterial3Api
import androidx.compose.material3.LocalTextStyle
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.TextField
@@ -45,7 +44,6 @@
import com.android.systemui.bouncer.ui.viewmodel.PasswordBouncerViewModel
/** UI for the input part of a password-requiring version of the bouncer. */
-@OptIn(ExperimentalMaterial3Api::class)
@Composable
internal fun PasswordBouncer(
viewModel: PasswordBouncerViewModel,
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 64227b8..03efbe0 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
@@ -21,6 +21,8 @@
import androidx.compose.animation.core.AnimationVector1D
import androidx.compose.animation.core.tween
import androidx.compose.foundation.Canvas
+import androidx.compose.foundation.gestures.awaitEachGesture
+import androidx.compose.foundation.gestures.awaitFirstDown
import androidx.compose.foundation.gestures.detectDragGestures
import androidx.compose.material3.MaterialTheme
import androidx.compose.runtime.Composable
@@ -199,33 +201,39 @@
.onSizeChanged { containerSize = it }
.thenIf(isInputEnabled) {
Modifier.pointerInput(Unit) {
- detectDragGestures(
- onDragStart = { start ->
- inputPosition = start
- viewModel.onDragStart()
- },
- onDragEnd = {
- inputPosition = null
- if (isAnimationEnabled) {
- lineFadeOutAnimatables.values.forEach { animatable ->
- // Launch using the longer-lived scope because we want these
- // animations to proceed to completion even if the surrounding
- // scope is canceled.
- scope.launch { animatable.animateTo(1f) }
- }
- }
- viewModel.onDragEnd()
- },
- ) { change, _ ->
- inputPosition = change.position
- viewModel.onDrag(
- xPx = change.position.x,
- yPx = change.position.y,
- containerSizePx = containerSize.width,
- verticalOffsetPx = verticalOffset,
- )
+ awaitEachGesture {
+ awaitFirstDown()
+ viewModel.onDown()
+ }
}
- }
+ .pointerInput(Unit) {
+ detectDragGestures(
+ onDragStart = { start ->
+ inputPosition = start
+ viewModel.onDragStart()
+ },
+ onDragEnd = {
+ inputPosition = null
+ if (isAnimationEnabled) {
+ lineFadeOutAnimatables.values.forEach { animatable ->
+ // Launch using the longer-lived scope because we want these
+ // animations to proceed to completion even if the
+ // surrounding scope is canceled.
+ scope.launch { animatable.animateTo(1f) }
+ }
+ }
+ viewModel.onDragEnd()
+ },
+ ) { change, _ ->
+ inputPosition = change.position
+ viewModel.onDrag(
+ xPx = change.position.x,
+ yPx = change.position.y,
+ containerSizePx = containerSize.width,
+ verticalOffsetPx = verticalOffset,
+ )
+ }
+ }
}
) {
if (isAnimationEnabled) {
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 ec6e5ed..e5c6977 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
@@ -24,6 +24,8 @@
import androidx.compose.animation.core.animateDpAsState
import androidx.compose.animation.core.animateFloatAsState
import androidx.compose.animation.core.tween
+import androidx.compose.foundation.gestures.awaitEachGesture
+import androidx.compose.foundation.gestures.awaitFirstDown
import androidx.compose.foundation.gestures.detectTapGestures
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column
@@ -76,7 +78,13 @@
Column(
horizontalAlignment = Alignment.CenterHorizontally,
- modifier = modifier,
+ modifier =
+ modifier.pointerInput(Unit) {
+ awaitEachGesture {
+ awaitFirstDown()
+ viewModel.onDown()
+ }
+ }
) {
PinInputDisplay(viewModel)
Spacer(Modifier.height(100.dp))
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/dialog/ui/composable/AlertDialogContent.kt b/packages/SystemUI/compose/features/src/com/android/systemui/dialog/ui/composable/AlertDialogContent.kt
new file mode 100644
index 0000000..418df5c
--- /dev/null
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/dialog/ui/composable/AlertDialogContent.kt
@@ -0,0 +1,204 @@
+/*
+ * 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.dialog.ui.composable
+
+import androidx.compose.foundation.layout.Box
+import androidx.compose.foundation.layout.Column
+import androidx.compose.foundation.layout.PaddingValues
+import androidx.compose.foundation.layout.Spacer
+import androidx.compose.foundation.layout.defaultMinSize
+import androidx.compose.foundation.layout.fillMaxWidth
+import androidx.compose.foundation.layout.height
+import androidx.compose.foundation.layout.padding
+import androidx.compose.foundation.rememberScrollState
+import androidx.compose.foundation.verticalScroll
+import androidx.compose.material3.LocalContentColor
+import androidx.compose.material3.MaterialTheme
+import androidx.compose.material3.ProvideTextStyle
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.CompositionLocalProvider
+import androidx.compose.ui.Alignment
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.layout.Layout
+import androidx.compose.ui.layout.Placeable
+import androidx.compose.ui.layout.layoutId
+import androidx.compose.ui.text.style.TextAlign
+import androidx.compose.ui.unit.dp
+import com.android.compose.theme.LocalAndroidColorScheme
+import kotlin.math.roundToInt
+
+/**
+ * The content of an AlertDialog which can be used together with
+ * [SystemUIDialogFactory.create][com.android.systemui.statusbar.phone.create] to create an alert
+ * dialog in Compose.
+ *
+ * @see com.android.systemui.statusbar.phone.create
+ */
+@Composable
+fun AlertDialogContent(
+ title: @Composable () -> Unit,
+ content: @Composable () -> Unit,
+ modifier: Modifier = Modifier,
+ icon: (@Composable () -> Unit)? = null,
+ positiveButton: (@Composable () -> Unit)? = null,
+ negativeButton: (@Composable () -> Unit)? = null,
+ neutralButton: (@Composable () -> Unit)? = null,
+) {
+ Column(
+ modifier.fillMaxWidth().verticalScroll(rememberScrollState()).padding(DialogPaddings),
+ horizontalAlignment = Alignment.CenterHorizontally,
+ ) {
+ // Icon.
+ if (icon != null) {
+ val defaultSize = 32.dp
+ Box(
+ Modifier.defaultMinSize(minWidth = defaultSize, minHeight = defaultSize),
+ propagateMinConstraints = true,
+ ) {
+ val iconColor = LocalAndroidColorScheme.current.primary
+ CompositionLocalProvider(LocalContentColor provides iconColor) { icon() }
+ }
+
+ Spacer(Modifier.height(16.dp))
+ }
+
+ // Title.
+ val titleColor = LocalAndroidColorScheme.current.onSurface
+ CompositionLocalProvider(LocalContentColor provides titleColor) {
+ ProvideTextStyle(
+ MaterialTheme.typography.headlineSmall.copy(textAlign = TextAlign.Center)
+ ) {
+ title()
+ }
+ }
+ Spacer(Modifier.height(16.dp))
+
+ // Content.
+ val contentColor = LocalAndroidColorScheme.current.onSurfaceVariant
+ Box(Modifier.defaultMinSize(minHeight = 48.dp)) {
+ CompositionLocalProvider(LocalContentColor provides contentColor) {
+ ProvideTextStyle(
+ MaterialTheme.typography.bodyMedium.copy(textAlign = TextAlign.Center)
+ ) {
+ content()
+ }
+ }
+ }
+ Spacer(Modifier.height(32.dp))
+
+ // Buttons.
+ if (positiveButton != null || negativeButton != null || neutralButton != null) {
+ AlertDialogButtons(
+ positiveButton = positiveButton,
+ negativeButton = negativeButton,
+ neutralButton = neutralButton,
+ )
+ }
+ }
+}
+
+@Composable
+private fun AlertDialogButtons(
+ positiveButton: (@Composable () -> Unit)?,
+ negativeButton: (@Composable () -> Unit)?,
+ neutralButton: (@Composable () -> Unit)?,
+ modifier: Modifier = Modifier,
+) {
+ Layout(
+ content = {
+ positiveButton?.let { Box(Modifier.layoutId("positive")) { it() } }
+ negativeButton?.let { Box(Modifier.layoutId("negative")) { it() } }
+ neutralButton?.let { Box(Modifier.layoutId("neutral")) { it() } }
+ },
+ modifier,
+ ) { measurables, constraints ->
+ check(constraints.hasBoundedWidth) {
+ "AlertDialogButtons should not be composed in an horizontally scrollable layout"
+ }
+ val maxWidth = constraints.maxWidth
+
+ // Measure the buttons.
+ var positive: Placeable? = null
+ var negative: Placeable? = null
+ var neutral: Placeable? = null
+ for (i in measurables.indices) {
+ val measurable = measurables[i]
+ when (val layoutId = measurable.layoutId) {
+ "positive" -> positive = measurable.measure(constraints)
+ "negative" -> negative = measurable.measure(constraints)
+ "neutral" -> neutral = measurable.measure(constraints)
+ else -> error("Unexpected layoutId=$layoutId")
+ }
+ }
+
+ fun Placeable?.width() = this?.width ?: 0
+ fun Placeable?.height() = this?.height ?: 0
+
+ // The min horizontal spacing between buttons.
+ val horizontalSpacing = 8.dp.toPx()
+ val totalHorizontalSpacing = (measurables.size - 1) * horizontalSpacing
+ val requiredWidth =
+ positive.width() + negative.width() + neutral.width() + totalHorizontalSpacing
+
+ if (requiredWidth <= maxWidth) {
+ // Stack horizontally: [neutral][flexSpace][negative][positive].
+ val height = maxOf(positive.height(), negative.height(), neutral.height())
+ layout(maxWidth, height) {
+ positive?.let { it.placeRelative(maxWidth - it.width, 0) }
+
+ negative?.let { negative ->
+ if (positive == null) {
+ negative.placeRelative(maxWidth - negative.width, 0)
+ } else {
+ negative.placeRelative(
+ maxWidth -
+ negative.width -
+ positive.width -
+ horizontalSpacing.roundToInt(),
+ 0
+ )
+ }
+ }
+
+ neutral?.placeRelative(0, 0)
+ }
+ } else {
+ // Stack vertically, aligned on the right (in LTR layouts):
+ // [positive]
+ // [negative]
+ // [neutral]
+ //
+ // TODO(b/283817398): Introduce a ResponsiveDialogButtons composable to create buttons
+ // that have different styles when stacked horizontally, as shown in
+ // go/sysui-dialog-styling.
+ val height = positive.height() + negative.height() + neutral.height()
+ layout(maxWidth, height) {
+ var y = 0
+ fun Placeable.place() {
+ placeRelative(maxWidth - width, y)
+ y += this.height
+ }
+
+ positive?.place()
+ negative?.place()
+ neutral?.place()
+ }
+ }
+ }
+}
+
+private val DialogPaddings = PaddingValues(start = 24.dp, end = 24.dp, top = 24.dp, bottom = 18.dp)
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/LockscreenScene.kt b/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/LockscreenScene.kt
index 0a100ba..b3b44cb 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/LockscreenScene.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/LockscreenScene.kt
@@ -14,20 +14,33 @@
* limitations under the License.
*/
-@file:OptIn(ExperimentalCoroutinesApi::class)
+@file:OptIn(ExperimentalCoroutinesApi::class, ExperimentalFoundationApi::class)
package com.android.systemui.keyguard.ui.composable
import android.view.View
import android.view.ViewGroup
+import androidx.compose.foundation.ExperimentalFoundationApi
+import androidx.compose.foundation.combinedClickable
+import androidx.compose.foundation.gestures.awaitEachGesture
+import androidx.compose.foundation.gestures.awaitFirstDown
+import androidx.compose.foundation.layout.Box
+import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.runtime.Composable
+import androidx.compose.runtime.collectAsState
+import androidx.compose.runtime.getValue
import androidx.compose.ui.Modifier
+import androidx.compose.ui.geometry.Rect
+import androidx.compose.ui.graphics.toComposeRect
+import androidx.compose.ui.input.pointer.pointerInput
import androidx.compose.ui.viewinterop.AndroidView
+import androidx.core.view.isVisible
import com.android.compose.animation.scene.SceneScope
import com.android.systemui.R
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Application
import com.android.systemui.keyguard.qualifiers.KeyguardRootView
+import com.android.systemui.keyguard.ui.viewmodel.KeyguardLongPressViewModel
import com.android.systemui.keyguard.ui.viewmodel.LockscreenSceneViewModel
import com.android.systemui.scene.shared.model.Direction
import com.android.systemui.scene.shared.model.SceneKey
@@ -67,8 +80,8 @@
modifier: Modifier,
) {
LockscreenScene(
- viewModel = viewModel,
viewProvider = viewProvider,
+ longPressViewModel = viewModel.longPress,
modifier = modifier,
)
}
@@ -85,23 +98,70 @@
@Composable
private fun LockscreenScene(
- viewModel: LockscreenSceneViewModel,
viewProvider: () -> View,
+ longPressViewModel: KeyguardLongPressViewModel,
modifier: Modifier = Modifier,
) {
- AndroidView(
- factory = { _ ->
- val keyguardRootView = viewProvider()
- // Remove the KeyguardRootView from any parent it might already have in legacy code just
- // in case (a view can't have two parents).
- (keyguardRootView.parent as? ViewGroup)?.removeView(keyguardRootView)
- keyguardRootView
- },
- update = { keyguardRootView ->
- keyguardRootView.requireViewById<View>(R.id.lock_icon_view).setOnClickListener {
- viewModel.onLockButtonClicked()
- }
- },
+ var settingsMenu: View? = null
+
+ Box(
modifier = modifier,
+ ) {
+ LongPressSurface(
+ viewModel = longPressViewModel,
+ isSettingsMenuVisible = { settingsMenu?.isVisible == true },
+ settingsMenuBounds = {
+ val bounds = android.graphics.Rect()
+ settingsMenu?.getHitRect(bounds)
+ bounds.toComposeRect()
+ },
+ modifier = Modifier.fillMaxSize(),
+ )
+
+ AndroidView(
+ factory = { _ ->
+ val keyguardRootView = viewProvider()
+ // Remove the KeyguardRootView from any parent it might already have in legacy code
+ // just in case (a view can't have two parents).
+ (keyguardRootView.parent as? ViewGroup)?.removeView(keyguardRootView)
+ settingsMenu = keyguardRootView.requireViewById(R.id.keyguard_settings_button)
+ keyguardRootView
+ },
+ update = { keyguardRootView ->
+ keyguardRootView.requireViewById<View>(R.id.lock_icon_view)
+ },
+ modifier = Modifier.fillMaxSize(),
+ )
+ }
+}
+
+@Composable
+private fun LongPressSurface(
+ viewModel: KeyguardLongPressViewModel,
+ isSettingsMenuVisible: () -> Boolean,
+ settingsMenuBounds: () -> Rect,
+ modifier: Modifier = Modifier,
+) {
+ val isEnabled: Boolean by viewModel.isLongPressHandlingEnabled.collectAsState(initial = false)
+
+ Box(
+ modifier =
+ modifier
+ .combinedClickable(
+ enabled = isEnabled,
+ onLongClick = viewModel::onLongPress,
+ onClick = {},
+ )
+ .pointerInput(Unit) {
+ awaitEachGesture {
+ val pointerInputChange = awaitFirstDown()
+ if (
+ isSettingsMenuVisible() &&
+ !settingsMenuBounds().contains(pointerInputChange.position)
+ ) {
+ viewModel.onTouchedOutside()
+ }
+ }
+ },
)
}
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/GoneScene.kt b/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/GoneScene.kt
index 774c409..40b0b4a 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/GoneScene.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/GoneScene.kt
@@ -17,11 +17,7 @@
package com.android.systemui.scene.ui.composable
import androidx.compose.foundation.layout.Box
-import androidx.compose.foundation.layout.Column
-import androidx.compose.material3.MaterialTheme
-import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
-import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import com.android.compose.animation.scene.SceneScope
import com.android.systemui.dagger.SysUISingleton
@@ -54,17 +50,6 @@
override fun SceneScope.Content(
modifier: Modifier,
) {
- /*
- * TODO(b/279501596): once we start testing with the real Content Dynamics Framework,
- * replace this with an error to make sure it doesn't get rendered.
- */
- Box(modifier = modifier) {
- Column(
- horizontalAlignment = Alignment.CenterHorizontally,
- modifier = Modifier.align(Alignment.Center)
- ) {
- Text("Gone", style = MaterialTheme.typography.headlineMedium)
- }
- }
+ Box(modifier = modifier)
}
}
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 c865070..31cbcb9 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
@@ -14,6 +14,8 @@
* limitations under the License.
*/
+@file:OptIn(ExperimentalComposeUiApi::class)
+
package com.android.systemui.scene.ui.composable
import androidx.compose.foundation.layout.fillMaxSize
@@ -22,7 +24,12 @@
import androidx.compose.runtime.collectAsState
import androidx.compose.runtime.getValue
import androidx.compose.runtime.remember
+import androidx.compose.ui.ExperimentalComposeUiApi
import androidx.compose.ui.Modifier
+import androidx.compose.ui.input.pointer.motionEventSpy
+import androidx.compose.ui.input.pointer.PointerEventPass
+import androidx.compose.ui.input.pointer.motionEventSpy
+import androidx.compose.ui.input.pointer.pointerInput
import com.android.compose.animation.scene.Back
import com.android.compose.animation.scene.ObservableTransitionState as SceneTransitionObservableTransitionState
import com.android.compose.animation.scene.SceneKey as SceneTransitionSceneKey
@@ -56,6 +63,7 @@
* must have entries in this map.
* @param modifier A modifier.
*/
+@OptIn(ExperimentalComposeUiApi::class)
@Composable
fun SceneContainer(
viewModel: SceneContainerViewModel,
@@ -79,7 +87,18 @@
onChangeScene = viewModel::onSceneChanged,
transitions = SceneContainerTransitions,
state = state,
- modifier = modifier.fillMaxSize(),
+ modifier =
+ modifier
+ .fillMaxSize()
+ .motionEventSpy { event -> viewModel.onMotionEvent(event) }
+ .pointerInput(Unit) {
+ awaitPointerEventScope {
+ while (true) {
+ awaitPointerEvent(PointerEventPass.Final)
+ viewModel.onMotionEventComplete()
+ }
+ }
+ }
) {
sceneByKey.forEach { (sceneKey, composableScene) ->
scene(
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/statusbar/phone/SystemUIDialogFactoryExt.kt b/packages/SystemUI/compose/features/src/com/android/systemui/statusbar/phone/SystemUIDialogFactoryExt.kt
new file mode 100644
index 0000000..5d6dd3b
--- /dev/null
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/statusbar/phone/SystemUIDialogFactoryExt.kt
@@ -0,0 +1,76 @@
+/*
+ * 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.statusbar.phone
+
+import android.content.Context
+import androidx.compose.material3.LocalContentColor
+import androidx.compose.material3.MaterialTheme
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.CompositionLocalProvider
+import androidx.compose.ui.platform.ComposeView
+import com.android.compose.theme.PlatformTheme
+
+/**
+ * Create a [SystemUIDialog] with the given [content].
+ *
+ * Note that the returned dialog will already have a background so the content should not draw an
+ * additional background.
+ *
+ * Example:
+ * ```
+ * val dialog = systemUiDialogFactory.create {
+ * AlertDialogContent(
+ * title = { Text("My title") },
+ * content = { Text("My content") },
+ * )
+ * }
+ *
+ * dialogLaunchAnimator.showFromView(dialog, viewThatWasClicked)
+ * ```
+ *
+ * @param context the [Context] in which the dialog will be constructed.
+ * @param dismissOnDeviceLock whether the dialog should be automatically dismissed when the device
+ * is locked (true by default).
+ */
+fun SystemUIDialogFactory.create(
+ context: Context = this.applicationContext,
+ dismissOnDeviceLock: Boolean = SystemUIDialog.DEFAULT_DISMISS_ON_DEVICE_LOCK,
+ content: @Composable (SystemUIDialog) -> Unit,
+): ComponentSystemUIDialog {
+ val dialog = create(context, dismissOnDeviceLock)
+
+ // Create the dialog so that it is properly constructed before we set the Compose content.
+ // Otherwise, the ComposeView won't render properly.
+ dialog.create()
+
+ // Set the content. Note that the background of the dialog is drawn on the DecorView of the
+ // dialog directly, which makes it automatically work nicely with DialogLaunchAnimator.
+ dialog.setContentView(
+ ComposeView(context).apply {
+ setContent {
+ PlatformTheme {
+ val defaultContentColor = MaterialTheme.colorScheme.onSurfaceVariant
+ CompositionLocalProvider(LocalContentColor provides defaultContentColor) {
+ content(dialog)
+ }
+ }
+ }
+ }
+ )
+
+ return dialog
+}
diff --git a/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/ClockRegistry.kt b/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/ClockRegistry.kt
index 966e183..054f9ec 100644
--- a/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/ClockRegistry.kt
+++ b/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/ClockRegistry.kt
@@ -68,11 +68,11 @@
private fun <TKey : Any, TVal : Any> ConcurrentHashMap<TKey, TVal>.concurrentGetOrPut(
key: TKey,
value: TVal,
- onNew: () -> Unit
+ onNew: (TVal) -> Unit
): TVal {
val result = this.putIfAbsent(key, value)
if (result == null) {
- onNew()
+ onNew(value)
}
return result ?: value
}
@@ -148,6 +148,8 @@
override fun onPluginAttached(
manager: PluginLifecycleManager<ClockProviderPlugin>
): Boolean {
+ manager.isDebug = true
+
if (keepAllLoaded) {
// Always load new plugins if requested
return true
@@ -177,15 +179,22 @@
val info =
availableClocks.concurrentGetOrPut(id, ClockInfo(metadata, null, manager)) {
isClockListChanged = true
- onConnected(id)
+ onConnected(it)
}
if (manager != info.manager) {
logger.tryLog(
TAG,
LogLevel.ERROR,
- { str1 = id },
- { "Clock Id conflict on known attach: $str1 is double registered" }
+ {
+ str1 = id
+ str2 = info.manager.toString()
+ str3 = manager.toString()
+ },
+ {
+ "Clock Id conflict on attach: " +
+ "$str1 is double registered by $str2 and $str3"
+ }
)
continue
}
@@ -217,22 +226,29 @@
val info =
availableClocks.concurrentGetOrPut(id, ClockInfo(clock, plugin, manager)) {
isClockListChanged = true
- onConnected(id)
+ onConnected(it)
}
if (manager != info.manager) {
logger.tryLog(
TAG,
LogLevel.ERROR,
- { str1 = id },
- { "Clock Id conflict on load: $str1 is double registered" }
+ {
+ str1 = id
+ str2 = info.manager.toString()
+ str3 = manager.toString()
+ },
+ {
+ "Clock Id conflict on load: " +
+ "$str1 is double registered by $str2 and $str3"
+ }
)
manager.unloadPlugin()
continue
}
info.provider = plugin
- onLoaded(id)
+ onLoaded(info)
}
if (isClockListChanged) {
@@ -252,26 +268,33 @@
logger.tryLog(
TAG,
LogLevel.ERROR,
- { str1 = id },
- { "Clock Id conflict on unload: $str1 is double registered" }
+ {
+ str1 = id
+ str2 = info?.manager.toString()
+ str3 = manager.toString()
+ },
+ {
+ "Clock Id conflict on unload: " +
+ "$str1 is double registered by $str2 and $str3"
+ }
)
continue
}
info.provider = null
- onUnloaded(id)
+ onUnloaded(info)
}
verifyLoadedProviders()
}
override fun onPluginDetached(manager: PluginLifecycleManager<ClockProviderPlugin>) {
- val removed = mutableListOf<ClockId>()
+ val removed = mutableListOf<ClockInfo>()
availableClocks.entries.removeAll {
if (it.value.manager != manager) {
return@removeAll false
}
- removed.add(it.key)
+ removed.add(it.value)
return@removeAll true
}
@@ -493,6 +516,12 @@
scope.launch(bgDispatcher) {
if (keepAllLoaded) {
+ logger.tryLog(
+ TAG,
+ LogLevel.INFO,
+ {},
+ { "verifyLoadedProviders: keepAllLoaded=true" }
+ )
// Enforce that all plugins are loaded if requested
for ((_, info) in availableClocks) {
info.manager?.loadPlugin()
@@ -503,6 +532,12 @@
val currentClock = availableClocks[currentClockId]
if (currentClock == null) {
+ logger.tryLog(
+ TAG,
+ LogLevel.INFO,
+ {},
+ { "verifyLoadedProviders: currentClock=null" }
+ )
// Current Clock missing, load no plugins and use default
for ((_, info) in availableClocks) {
info.manager?.unloadPlugin()
@@ -511,12 +546,13 @@
return@launch
}
+ logger.tryLog(TAG, LogLevel.INFO, {}, { "verifyLoadedProviders: load currentClock" })
val currentManager = currentClock.manager
currentManager?.loadPlugin()
for ((_, info) in availableClocks) {
val manager = info.manager
- if (manager != null && manager.isLoaded && currentManager != manager) {
+ if (manager != null && currentManager != manager) {
manager.unloadPlugin()
}
}
@@ -524,57 +560,68 @@
}
}
- private fun onConnected(clockId: ClockId) {
- logger.tryLog(TAG, LogLevel.DEBUG, { str1 = clockId }, { "Connected $str1" })
- if (currentClockId == clockId) {
- logger.tryLog(
- TAG,
- LogLevel.INFO,
- { str1 = clockId },
- { "Current clock ($str1) was connected" }
- )
- }
+ private fun onConnected(info: ClockInfo) {
+ val isCurrent = currentClockId == info.metadata.clockId
+ logger.tryLog(
+ TAG,
+ if (isCurrent) LogLevel.INFO else LogLevel.DEBUG,
+ {
+ str1 = info.metadata.clockId
+ str2 = info.manager.toString()
+ bool1 = isCurrent
+ },
+ { "Connected $str1 @$str2" + if (bool1) " (Current Clock)" else "" }
+ )
}
- private fun onLoaded(clockId: ClockId) {
- logger.tryLog(TAG, LogLevel.DEBUG, { str1 = clockId }, { "Loaded $str1" })
+ private fun onLoaded(info: ClockInfo) {
+ val isCurrent = currentClockId == info.metadata.clockId
+ logger.tryLog(
+ TAG,
+ if (isCurrent) LogLevel.INFO else LogLevel.DEBUG,
+ {
+ str1 = info.metadata.clockId
+ str2 = info.manager.toString()
+ bool1 = isCurrent
+ },
+ { "Loaded $str1 @$str2" + if (bool1) " (Current Clock)" else "" }
+ )
- if (currentClockId == clockId) {
- logger.tryLog(
- TAG,
- LogLevel.INFO,
- { str1 = clockId },
- { "Current clock ($str1) was loaded" }
- )
+ if (isCurrent) {
triggerOnCurrentClockChanged()
}
}
- private fun onUnloaded(clockId: ClockId) {
- logger.tryLog(TAG, LogLevel.DEBUG, { str1 = clockId }, { "Unloaded $str1" })
+ private fun onUnloaded(info: ClockInfo) {
+ val isCurrent = currentClockId == info.metadata.clockId
+ logger.tryLog(
+ TAG,
+ if (isCurrent) LogLevel.WARNING else LogLevel.DEBUG,
+ {
+ str1 = info.metadata.clockId
+ str2 = info.manager.toString()
+ bool1 = isCurrent
+ },
+ { "Unloaded $str1 @$str2" + if (bool1) " (Current Clock)" else "" }
+ )
- if (currentClockId == clockId) {
- logger.tryLog(
- TAG,
- LogLevel.WARNING,
- { str1 = clockId },
- { "Current clock ($str1) was unloaded" }
- )
+ if (isCurrent) {
triggerOnCurrentClockChanged()
}
}
- private fun onDisconnected(clockId: ClockId) {
- logger.tryLog(TAG, LogLevel.DEBUG, { str1 = clockId }, { "Disconnected $str1" })
-
- if (currentClockId == clockId) {
- logger.tryLog(
- TAG,
- LogLevel.WARNING,
- { str1 = clockId },
- { "Current clock ($str1) was disconnected" }
- )
- }
+ private fun onDisconnected(info: ClockInfo) {
+ val isCurrent = currentClockId == info.metadata.clockId
+ logger.tryLog(
+ TAG,
+ if (isCurrent) LogLevel.INFO else LogLevel.DEBUG,
+ {
+ str1 = info.metadata.clockId
+ str2 = info.manager.toString()
+ bool1 = isCurrent
+ },
+ { "Disconnected $str1 @$str2" + if (bool1) " (Current Clock)" else "" }
+ )
}
fun getClocks(): List<ClockMetadata> {
diff --git a/packages/SystemUI/ktfmt_includes.txt b/packages/SystemUI/ktfmt_includes.txt
index 785177e..d3254b7 100644
--- a/packages/SystemUI/ktfmt_includes.txt
+++ b/packages/SystemUI/ktfmt_includes.txt
@@ -303,7 +303,6 @@
-packages/SystemUI/src/com/android/systemui/statusbar/notification/LaunchAnimationParameters.kt
-packages/SystemUI/src/com/android/systemui/statusbar/notification/NotifPipelineFlags.kt
-packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationClickerLogger.kt
--packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationLaunchAnimatorController.kt
-packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationSectionsFeatureManager.kt
-packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationUtils.kt
-packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationWakeUpCoordinator.kt
diff --git a/packages/SystemUI/plugin_core/src/com/android/systemui/plugins/PluginLifecycleManager.java b/packages/SystemUI/plugin_core/src/com/android/systemui/plugins/PluginLifecycleManager.java
index 56c3f93..3e5e8a0 100644
--- a/packages/SystemUI/plugin_core/src/com/android/systemui/plugins/PluginLifecycleManager.java
+++ b/packages/SystemUI/plugin_core/src/com/android/systemui/plugins/PluginLifecycleManager.java
@@ -33,6 +33,12 @@
/** Returns the currently loaded plugin instance (if plugin is loaded) */
T getPlugin();
+ /** Returns true if the lifecycle manager should log debug messages */
+ boolean getIsDebug();
+
+ /** Sets whether or not hte lifecycle manager should log debug messages */
+ void setIsDebug(boolean debug);
+
/** returns true if the plugin is currently loaded */
default boolean isLoaded() {
return getPlugin() != null;
diff --git a/packages/SystemUI/proguard_common.flags b/packages/SystemUI/proguard_common.flags
index 3194815..75de943 100644
--- a/packages/SystemUI/proguard_common.flags
+++ b/packages/SystemUI/proguard_common.flags
@@ -70,9 +70,6 @@
-keep class com.android.systemui.log.core.** {
*;
}
--keep class com.android.systemui.fragments.FragmentService$FragmentCreator {
- *;
-}
-keep class androidx.core.app.CoreComponentFactory
# Keep the wm shell lib
diff --git a/packages/SystemUI/res/layout/connected_display_dialog.xml b/packages/SystemUI/res/layout/connected_display_dialog.xml
new file mode 100644
index 0000000..569dd4c
--- /dev/null
+++ b/packages/SystemUI/res/layout/connected_display_dialog.xml
@@ -0,0 +1,69 @@
+<!--
+ 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="match_parent"
+ android:gravity="center_horizontal"
+ android:orientation="vertical"
+ android:paddingHorizontal="@dimen/dialog_side_padding"
+ android:paddingTop="@dimen/dialog_top_padding"
+ android:background="@*android:drawable/bottomsheet_background"
+ android:paddingBottom="@dimen/dialog_bottom_padding">
+
+ <ImageView
+ android:id="@+id/connected_display_dialog_icon"
+ android:layout_width="@dimen/screenrecord_logo_size"
+ android:layout_height="@dimen/screenrecord_logo_size"
+ android:importantForAccessibility="no"
+ android:src="@drawable/stat_sys_connected_display"
+ android:tint="?androidprv:attr/materialColorPrimary" />
+
+ <TextView
+ android:id="@+id/connected_display_dialog_title"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_marginTop="@dimen/screenrecord_title_margin_top"
+ android:gravity="center"
+ android:text="@string/connected_display_dialog_start_mirroring"
+ android:textAppearance="@style/TextAppearance.Dialog.Title" />
+
+ <LinearLayout
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_marginTop="@dimen/screenrecord_buttons_margin_top"
+ android:orientation="horizontal">
+
+ <Button
+ android:id="@+id/cancel"
+ style="@style/Widget.Dialog.Button.BorderButton"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="@string/cancel" />
+
+ <Space
+ android:layout_width="0dp"
+ android:layout_height="match_parent"
+ android:layout_weight="1" />
+
+ <Button
+ android:id="@+id/enable_display"
+ style="@style/Widget.Dialog.Button"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="@string/enable_display" />
+ </LinearLayout>
+</LinearLayout>
\ No newline at end of file
diff --git a/packages/SystemUI/res/layout/status_bar_notification_footer.xml b/packages/SystemUI/res/layout/status_bar_notification_footer.xml
index 909048e..b00908f 100644
--- a/packages/SystemUI/res/layout/status_bar_notification_footer.xml
+++ b/packages/SystemUI/res/layout/status_bar_notification_footer.xml
@@ -47,7 +47,7 @@
android:layout_height="48dp"
android:layout_marginTop="12dp"
android:layout_marginStart="16dp"
- app:layout_constraintVertical_bias="0.0"
+ app:layout_constraintHorizontal_bias="0.0"
app:layout_constraintHorizontal_chainStyle="spread_inside"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
@@ -65,7 +65,6 @@
android:layout_height="48dp"
android:layout_marginTop="12dp"
android:layout_marginEnd="16dp"
- app:layout_constraintVertical_bias="1.0"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintStart_toEndOf="@id/manage_text"
diff --git a/packages/SystemUI/res/values-af/strings.xml b/packages/SystemUI/res/values-af/strings.xml
index fe80355..a1db50d 100644
--- a/packages/SystemUI/res/values-af/strings.xml
+++ b/packages/SystemUI/res/values-af/strings.xml
@@ -57,6 +57,8 @@
<string name="hdmi_cec_set_menu_language_description" msgid="8176716678074126619">"\'n Ander toestel het versoek om die stelseltaal te verander"</string>
<string name="hdmi_cec_set_menu_language_accept" msgid="2513689457281009578">"Verander taal"</string>
<string name="hdmi_cec_set_menu_language_decline" msgid="7650721096558646011">"Hou huidige taal"</string>
+ <!-- no translation found for share_wifi_button_text (1285273973812029240) -->
+ <skip />
<string name="wifi_debugging_title" msgid="7300007687492186076">"Laat draadlose ontfouting op hierdie netwerk toe?"</string>
<string name="wifi_debugging_message" msgid="5461204211731802995">"Netwerknaam (SSID)\n<xliff:g id="SSID_0">%1$s</xliff:g>\n\nWi‑Fi-adres (BSSID)\n<xliff:g id="BSSID_1">%2$s</xliff:g>"</string>
<string name="wifi_debugging_always" msgid="2968383799517975155">"Laat altyd toe op hierdie netwerk"</string>
@@ -158,6 +160,8 @@
<string name="biometric_dialog_wrong_pattern" msgid="8954812279840889029">"Verkeerde patroon"</string>
<string name="biometric_dialog_wrong_password" msgid="69477929306843790">"Verkeerde wagwoord"</string>
<string name="biometric_dialog_credential_too_many_attempts" msgid="3083141271737748716">"Te veel verkeerde pogings.\nProbeer oor <xliff:g id="NUMBER">%d</xliff:g> sekondes weer."</string>
+ <!-- no translation found for work_challenge_emergency_button_text (8946588434515599288) -->
+ <skip />
<string name="biometric_dialog_credential_attempts_before_wipe" msgid="6751859711975516999">"Probeer weer. Poging <xliff:g id="ATTEMPTS_0">%1$d</xliff:g> van <xliff:g id="MAX_ATTEMPTS">%2$d</xliff:g>."</string>
<string name="biometric_dialog_last_attempt_before_wipe_dialog_title" msgid="2874250099278693477">"Jou data sal uitgevee word"</string>
<string name="biometric_dialog_last_pattern_attempt_before_wipe_device" msgid="6562299244825817598">"As jy met jou volgende poging \'n verkeerde patroon invoer, sal hierdie toestel se data uitgevee word."</string>
@@ -411,6 +415,8 @@
<string name="media_projection_entry_app_permission_dialog_warning_entire_screen" msgid="8736391633234144237">"Wanneer jy deel, opneem of uitsaai, het <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> toegang tot enigiets wat op jou skerm sigbaar is of op jou toestel gespeel word. Wees dus versigtig met dinge soos wagwoorde, betalingbesonderhede, boodskappe, foto’s, en oudio en video."</string>
<string name="media_projection_entry_app_permission_dialog_warning_single_app" msgid="5211695779082563959">"Wanneer jy ’n app deel, opneem of uitsaai, het <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> toegang tot enigiets wat in daardie app gewys of gespeel word. Wees dus versigtig met dinge soos wagwoorde, betalingbesonderhede, boodskappe, foto’s, en oudio en video."</string>
<string name="media_projection_entry_app_permission_dialog_continue" msgid="295463518195075840">"Begin"</string>
+ <!-- no translation found for media_projection_entry_app_permission_dialog_single_app_disabled (8999903044874669995) -->
+ <skip />
<string name="media_projection_entry_cast_permission_dialog_title" msgid="8860150223172993547">"Begin uitsaai?"</string>
<string name="media_projection_entry_cast_permission_dialog_warning_entire_screen" msgid="1986212276016817231">"Wanneer jy uitsaai, het Android toegang tot enigiets wat op jou skerm sigbaar is of op jou toestel gespeel word. Wees dus versigtig met dinge soos wagwoorde, betalingbesonderhede, boodskappe, foto’s, en oudio en video."</string>
<string name="media_projection_entry_cast_permission_dialog_warning_single_app" msgid="9900961380294292">"Wanneer jy ’n app uitsaai, het Android toegang tot enigiets wat in daardie app gewys of gespeel word. Wees dus versigtig met dinge soos wagwoorde, betalingbesonderhede, boodskappe, foto’s, en oudio en video."</string>
@@ -865,8 +871,7 @@
<string name="magnification_mode_switch_state_window" msgid="8597100249594076965">"Vergroot \'n deel van die skerm"</string>
<string name="magnification_open_settings_click_label" msgid="6151849212725923363">"Maak vergrotinginstellings oop"</string>
<string name="magnification_close_settings_click_label" msgid="4642477260651704517">"Maak vergrotinginstellings toe"</string>
- <!-- no translation found for magnification_exit_edit_mode_click_label (1664818325144887117) -->
- <skip />
+ <string name="magnification_exit_edit_mode_click_label" msgid="1664818325144887117">"Verlaat redigeermodus"</string>
<string name="magnification_drag_corner_to_resize" msgid="1249766311052418130">"Sleep hoek om grootte te verander"</string>
<string name="accessibility_allow_diagonal_scrolling" msgid="3258050349191496398">"Laat diagonale rollees toe"</string>
<string name="accessibility_resize" msgid="5733759136600611551">"Verander grootte"</string>
diff --git a/packages/SystemUI/res/values-am/strings.xml b/packages/SystemUI/res/values-am/strings.xml
index 63f1765..bc7273d 100644
--- a/packages/SystemUI/res/values-am/strings.xml
+++ b/packages/SystemUI/res/values-am/strings.xml
@@ -57,6 +57,8 @@
<string name="hdmi_cec_set_menu_language_description" msgid="8176716678074126619">"በሌላ መሣሪያ የተጠየቀ የስርዓት ቋንቋ ለውጥ"</string>
<string name="hdmi_cec_set_menu_language_accept" msgid="2513689457281009578">"ቋንቋ ቀይር"</string>
<string name="hdmi_cec_set_menu_language_decline" msgid="7650721096558646011">"አሁን ያለውን ቋንቋ አቆይ"</string>
+ <!-- no translation found for share_wifi_button_text (1285273973812029240) -->
+ <skip />
<string name="wifi_debugging_title" msgid="7300007687492186076">"በዚህ አውታረ መረብ ላይ ገመድ-አልባ debugging ይፈቀድ?"</string>
<string name="wifi_debugging_message" msgid="5461204211731802995">"የአውታረ መረብ ስም (SSID)\n<xliff:g id="SSID_0">%1$s</xliff:g>\n\nየWi‑Fi አድራሻ (BSSID)\n<xliff:g id="BSSID_1">%2$s</xliff:g>"</string>
<string name="wifi_debugging_always" msgid="2968383799517975155">"ሁልጊዜ በዚህ አውታረ መረብ ላይ ፍቀድ"</string>
@@ -68,14 +70,14 @@
<string name="usb_port_enabled" msgid="531823867664717018">"ኃይል መሙያዎችን እና ተጨማሪ መሣሪያዎችን ፈልጎ ለማግኘት የነቃ የዩኤስቢ ወደብ"</string>
<string name="usb_disable_contaminant_detection" msgid="3827082183595978641">"ዩኤስቢ አንቃ"</string>
<string name="learn_more" msgid="4690632085667273811">"የበለጠ ለመረዳት"</string>
- <string name="global_action_screenshot" msgid="2760267567509131654">"ቅጽበታዊ ገፅ እይታ"</string>
+ <string name="global_action_screenshot" msgid="2760267567509131654">"ቅጽበታዊ ገፅ ዕይታ"</string>
<string name="global_action_smart_lock_disabled" msgid="6286551337177954859">"Extend Unlock ተሰናክሏል"</string>
<string name="remote_input_image_insertion_text" msgid="4850791636452521123">"ምስል ተልኳል"</string>
- <string name="screenshot_saving_title" msgid="2298349784913287333">"ቅጽበታዊ ገፅ እይታ በማስቀመጥ ላይ..."</string>
+ <string name="screenshot_saving_title" msgid="2298349784913287333">"ቅጽበታዊ ገፅ ዕይታ በማስቀመጥ ላይ..."</string>
<string name="screenshot_saving_work_profile_title" msgid="5332829607308450880">"ቅጽበታዊ ገፅ እይታን ወደ የስራ መገለጫ በማስቀመጥ ላይ…"</string>
- <string name="screenshot_saved_title" msgid="8893267638659083153">"ቅጽበታዊ ገፅ እይታ ተቀምጧል"</string>
+ <string name="screenshot_saved_title" msgid="8893267638659083153">"ቅጽበታዊ ገፅ ዕይታ ተቀምጧል"</string>
<string name="screenshot_failed_title" msgid="3259148215671936891">"ቅጽበታዊ ገፅ ዕይታን ማስቀመጥ አልተቻለም"</string>
- <string name="screenshot_failed_to_save_user_locked_text" msgid="6156607948256936920">"ቅጽበታዊ ገፅ እይታ ከመቀመጡ በፊት መሣሪያ መከፈት አለበት"</string>
+ <string name="screenshot_failed_to_save_user_locked_text" msgid="6156607948256936920">"ቅጽበታዊ ገፅ ዕይታ ከመቀመጡ በፊት መሣሪያ መከፈት አለበት"</string>
<string name="screenshot_failed_to_save_unknown_text" msgid="1506621600548684129">"ቅጽበታዊ ገፅ ዕይታን እንደገና ማንሳት ይሞክሩ"</string>
<string name="screenshot_failed_to_save_text" msgid="7232739948999195960">"ቅጽበታዊ ገፅ እይታን ማስቀመጥ አልተቻለም"</string>
<string name="screenshot_failed_to_capture_text" msgid="7818288545874407451">"ቅጽበታዊ ገፅ እይታዎችን ማንሳት በመተግበሪያው ወይም በእርስዎ ድርጅት አይፈቀድም"</string>
@@ -93,8 +95,8 @@
<string name="screenshot_right_boundary_pct" msgid="1201150713021779321">"የቀኝ ወሰን <xliff:g id="PERCENT">%1$d</xliff:g> በመቶ"</string>
<string name="screenshot_work_profile_notification" msgid="203041724052970693">"<xliff:g id="APP">%1$s</xliff:g> ውስጥ የስራ መገለጫው ውስጥ ተቀምጧል"</string>
<string name="screenshot_default_files_app_name" msgid="8721579578575161912">"ፋይሎች"</string>
- <string name="screenshot_detected_template" msgid="7940376642921719915">"<xliff:g id="APPNAME">%1$s</xliff:g> ይህን ቅጽበታዊ ገፅ እይታ ለይቷል።"</string>
- <string name="screenshot_detected_multiple_template" msgid="7644827792093819241">"<xliff:g id="APPNAME">%1$s</xliff:g> እና ሌሎች ክፍት መተግበሪያዎች ይህን ቅጽበታዊ ገፅ እይታ ለይተዋል።"</string>
+ <string name="screenshot_detected_template" msgid="7940376642921719915">"<xliff:g id="APPNAME">%1$s</xliff:g> ይህን ቅጽበታዊ ገፅ ዕይታ ለይቷል።"</string>
+ <string name="screenshot_detected_multiple_template" msgid="7644827792093819241">"<xliff:g id="APPNAME">%1$s</xliff:g> እና ሌሎች ክፍት መተግበሪያዎች ይህን ቅጽበታዊ ገፅ ዕይታ ለይተዋል።"</string>
<string name="app_clips_save_add_to_note" msgid="3460200751278069445">"ወደ ማስታወሻ አክል"</string>
<string name="screenrecord_title" msgid="4257171601439507792">"የማያ መቅረጫ"</string>
<string name="screenrecord_background_processing_label" msgid="7244617554884238898">"የማያ ገፅ ቀረጻን በማሰናዳት ላይ"</string>
@@ -158,6 +160,8 @@
<string name="biometric_dialog_wrong_pattern" msgid="8954812279840889029">"የተሳሳተ ሥርዓተ ጥለት"</string>
<string name="biometric_dialog_wrong_password" msgid="69477929306843790">"የተሳሳተ የይለፍ ቃል"</string>
<string name="biometric_dialog_credential_too_many_attempts" msgid="3083141271737748716">"ከልክ በላይ ብዙ የተሳሳቱ ሙከራዎች።\nበ<xliff:g id="NUMBER">%d</xliff:g> ሰከንዶች ውስጥ እንደገና ይሞክሩ።"</string>
+ <!-- no translation found for work_challenge_emergency_button_text (8946588434515599288) -->
+ <skip />
<string name="biometric_dialog_credential_attempts_before_wipe" msgid="6751859711975516999">"እንደገና ይሞክሩ። ሙከራ <xliff:g id="ATTEMPTS_0">%1$d</xliff:g> ከ<xliff:g id="MAX_ATTEMPTS">%2$d</xliff:g>።"</string>
<string name="biometric_dialog_last_attempt_before_wipe_dialog_title" msgid="2874250099278693477">"የእርስዎ ውሂብ ይሰረዛል"</string>
<string name="biometric_dialog_last_pattern_attempt_before_wipe_device" msgid="6562299244825817598">"በሚቀጥለው ሙከራ ላይ ትክክል ያልሆነ ሥርዓተ ጥለት ካስገቡ የዚህ መሣሪያ ውሂብ ይሰረዛል።"</string>
@@ -411,6 +415,8 @@
<string name="media_projection_entry_app_permission_dialog_warning_entire_screen" msgid="8736391633234144237">"እርስዎ ሲያጋሩ፣ ሲቀርጹ ወይም cast ሲያደርጉ <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> በማያ ገጽዎ ላይ ለሚታይ ወይም በመሣሪያዎ ላይ ለሚጫወት ማንኛውም ነገር መዳረሻ አለው። ስለዚህ እንደ የይለፍ ቃላት፣ የክፍያ ዝርዝሮች፣ መልዕክቶች፣ ፎቶዎች እና ኦዲዮ እና ቪድዮ ላሉ ነገሮች ጥንቃቄ ያድርጉ።"</string>
<string name="media_projection_entry_app_permission_dialog_warning_single_app" msgid="5211695779082563959">"አንድን መተግበሪያ ሲያጋሩ፣ ሲቀርጹ ወይም cast ሲያደርጉ <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> በዚያ መተግበሪያ ላይ ለሚታይ ወይም ለሚጫወት ማንኛውም ነገር መዳረሻ አለው። ስለዚህ እንደ የይለፍ ቃላት፣ የክፍያ ዝርዝሮች፣ መልዕክቶች፣ ፎቶዎች እና ኦዲዮ እና ቪድዮ ላሉ ነገሮች ጥንቃቄ ያድርጉ።"</string>
<string name="media_projection_entry_app_permission_dialog_continue" msgid="295463518195075840">"ጀምር"</string>
+ <!-- no translation found for media_projection_entry_app_permission_dialog_single_app_disabled (8999903044874669995) -->
+ <skip />
<string name="media_projection_entry_cast_permission_dialog_title" msgid="8860150223172993547">"cast ማድረግ ይጀምር?"</string>
<string name="media_projection_entry_cast_permission_dialog_warning_entire_screen" msgid="1986212276016817231">"እርስዎ cast በሚያደርጉበት ጊዜ Android በማያ ገጽዎ ላይ ለሚታይ ወይም በመሣሪያዎ ላይ ለሚጫወት ማንኛውም ነገር መዳረሻ አለው። ስለዚህ እንደ የይለፍ ቃላት፣ የክፍያ ዝርዝሮች፣ መልዕክቶች፣ ፎቶዎች እና ኦዲዮ እና ቪድዮ ላሉ ነገሮች ጥንቃቄ ያድርጉ።"</string>
<string name="media_projection_entry_cast_permission_dialog_warning_single_app" msgid="9900961380294292">"አንድን መተግበሪያ cast ሲያደርጉ Android በዚያ መተግበሪያ ላይ ለሚታይ ወይም ለሚጫወት ማንኛውም ነገር መዳረሻ አለው። ስለዚህ እንደ ይለፍ ቃላት፣ የክፍያ ዝርዝሮች፣ መልዕክቶች፣ ፎቶዎች እና ኦዲዮ እና ቪድዮ ላሉ ነገሮች ጥንቃቄ ያድርጉ"</string>
@@ -660,11 +666,11 @@
<string name="keyboard_shortcut_search_category_open_apps" msgid="1450959949739257562">"መተግበሪያዎችን ይክፈቱ"</string>
<string name="keyboard_shortcut_search_category_current_app" msgid="2011953559133734491">"የአሁኑ መተግበሪያ"</string>
<string name="group_system_access_notification_shade" msgid="7116898151485382275">"የማሳወቂያ ጥላ መዳረሻ"</string>
- <string name="group_system_full_screenshot" msgid="7389040853798023211">"ሙሉ ቅጽበታዊ ገፅ እይታ ያነሳል"</string>
+ <string name="group_system_full_screenshot" msgid="7389040853798023211">"ሙሉ ቅጽበታዊ ገፅ ዕይታ ያነሳል"</string>
<string name="group_system_access_system_app_shortcuts" msgid="4421497579210445641">"የሥርዓት / የመተግበሪያ አቋራጮች ዝርዝር መዳረሻ"</string>
<string name="group_system_go_back" msgid="8838454003680364227">"ተመለስ፦ ወደ ቀዳሚው ሁኔታ ይመለሳል (ተመለስ አዝራር)"</string>
<string name="group_system_access_home_screen" msgid="1857344316928441909">"የመነሻ ማያ ገፅ መዳረሻ"</string>
- <string name="group_system_overview_open_apps" msgid="6897128761003265350">"የክፍት መተግበሪያዎች አጠቃላይ እይታ"</string>
+ <string name="group_system_overview_open_apps" msgid="6897128761003265350">"የክፍት መተግበሪያዎች አጠቃላይ ዕይታ"</string>
<string name="group_system_cycle_forward" msgid="9202444850838205990">"የቅርብ ጊዜ መተግበሪያዎች ላይ ዑደት ያደርጋል (ወደ ፊት)"</string>
<string name="group_system_cycle_back" msgid="5163464503638229131">"የቅርብ ጊዜ መተግበሪያዎች ላይ ዑደት ያደርጋል (ወደ ኋላ)"</string>
<string name="group_system_access_all_apps_search" msgid="488070738028991753">"የሁሉም መተግበሪያዎች ዝርዝር እና ፍለጋ መዳረሻ (ማለትም ፍለጋ/ማስጀመሪያ)"</string>
@@ -865,8 +871,7 @@
<string name="magnification_mode_switch_state_window" msgid="8597100249594076965">"የማያ ገጹን ክፍል አጉላ"</string>
<string name="magnification_open_settings_click_label" msgid="6151849212725923363">"የማጉያ ቅንብሮችን ክፈት"</string>
<string name="magnification_close_settings_click_label" msgid="4642477260651704517">"የማጉላት ቅንብሮችን ዝጋ"</string>
- <!-- no translation found for magnification_exit_edit_mode_click_label (1664818325144887117) -->
- <skip />
+ <string name="magnification_exit_edit_mode_click_label" msgid="1664818325144887117">"ከአርትዖት ሁነታ ውጣ"</string>
<string name="magnification_drag_corner_to_resize" msgid="1249766311052418130">"መጠን ለመቀየር ጠርዙን ይዘው ይጎትቱ"</string>
<string name="accessibility_allow_diagonal_scrolling" msgid="3258050349191496398">"ሰያፍ ሽብለላን ፍቀድ"</string>
<string name="accessibility_resize" msgid="5733759136600611551">"መጠን ቀይር"</string>
@@ -1103,7 +1108,7 @@
<string name="clipboard_content_copied" msgid="144452398567828145">"ይዘት ተቀድቷል"</string>
<string name="clipboard_editor" msgid="2971197550401892843">"የቅንጥብ ሰሌዳ አርታዒ"</string>
<string name="clipboard_overlay_window_name" msgid="6450043652167357664">"የቅንጥብ ሰሌዳ"</string>
- <string name="clipboard_image_preview" msgid="2156475174343538128">"የምስል ቅድመ-እይታ"</string>
+ <string name="clipboard_image_preview" msgid="2156475174343538128">"የምስል ቅድመ-ዕይታ"</string>
<string name="clipboard_edit" msgid="4500155216174011640">"አርትዕ"</string>
<string name="add" msgid="81036585205287996">"አክል"</string>
<string name="manage_users" msgid="1823875311934643849">"ተጠቃሚዎችን ያስተዳድሩ"</string>
diff --git a/packages/SystemUI/res/values-ar/strings.xml b/packages/SystemUI/res/values-ar/strings.xml
index 8128fae..7b8f23a 100644
--- a/packages/SystemUI/res/values-ar/strings.xml
+++ b/packages/SystemUI/res/values-ar/strings.xml
@@ -57,6 +57,8 @@
<string name="hdmi_cec_set_menu_language_description" msgid="8176716678074126619">"طلب جهاز آخر تغيير لغة النظام."</string>
<string name="hdmi_cec_set_menu_language_accept" msgid="2513689457281009578">"تغيير اللغة"</string>
<string name="hdmi_cec_set_menu_language_decline" msgid="7650721096558646011">"الإبقاء على اللغة الحالية"</string>
+ <!-- no translation found for share_wifi_button_text (1285273973812029240) -->
+ <skip />
<string name="wifi_debugging_title" msgid="7300007687492186076">"هل تريد السماح باستخدام ميزة \"تصحيح الأخطاء اللاسلكي\" على هذه الشبكة؟"</string>
<string name="wifi_debugging_message" msgid="5461204211731802995">"اسم الشبكة (SSID)\n<xliff:g id="SSID_0">%1$s</xliff:g>\n\nعنوان شبكة Wi‑Fi (BSSID)\n<xliff:g id="BSSID_1">%2$s</xliff:g>"</string>
<string name="wifi_debugging_always" msgid="2968383799517975155">"السماح باستخدام هذه الميزة على هذه الشبكة دائمًا"</string>
@@ -158,6 +160,8 @@
<string name="biometric_dialog_wrong_pattern" msgid="8954812279840889029">"النقش غير صحيح."</string>
<string name="biometric_dialog_wrong_password" msgid="69477929306843790">"كلمة مرور غير صحيحة"</string>
<string name="biometric_dialog_credential_too_many_attempts" msgid="3083141271737748716">"تم إجراء عدد كبير جدًا من المحاولات غير الصحيحة.\nأعد المحاولة خلال <xliff:g id="NUMBER">%d</xliff:g> ثانية."</string>
+ <!-- no translation found for work_challenge_emergency_button_text (8946588434515599288) -->
+ <skip />
<string name="biometric_dialog_credential_attempts_before_wipe" msgid="6751859711975516999">"يُرجى إعادة المحاولة. المحاولة <xliff:g id="ATTEMPTS_0">%1$d</xliff:g> من <xliff:g id="MAX_ATTEMPTS">%2$d</xliff:g>"</string>
<string name="biometric_dialog_last_attempt_before_wipe_dialog_title" msgid="2874250099278693477">"سيتم حذف بياناتك"</string>
<string name="biometric_dialog_last_pattern_attempt_before_wipe_device" msgid="6562299244825817598">"عند إدخال نقش غير صحيح في المحاولة التالية، سيتم حذف بيانات هذا الجهاز."</string>
@@ -411,6 +415,8 @@
<string name="media_projection_entry_app_permission_dialog_warning_entire_screen" msgid="8736391633234144237">"أثناء المشاركة أو التسجيل أو البثّ، يمكن لتطبيق \"<xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g>\" الوصول إلى كل المحتوى المعروض على شاشتك أو الذي يتم تشغيله على جهاز، لذا يُرجى توخي الحذر بشأن المعلومات، مثل كلمات المرور وتفاصيل الدفع والرسائل والصور وملفات الصوت والفيديو."</string>
<string name="media_projection_entry_app_permission_dialog_warning_single_app" msgid="5211695779082563959">"أثناء مشاركة محتوى تطبيق أو تسجيله أو بثّه، يمكن لتطبيق \"<xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g>\" الوصول إلى كل المحتوى المعروض أو الذي يتم تشغيله في ذلك التطبيق، لذا يُرجى توخي الحذر بشأن المعلومات، مثل كلمات المرور وتفاصيل الدفع والرسائل والصور وملفات الصوت والفيديو."</string>
<string name="media_projection_entry_app_permission_dialog_continue" msgid="295463518195075840">"بدء"</string>
+ <!-- no translation found for media_projection_entry_app_permission_dialog_single_app_disabled (8999903044874669995) -->
+ <skip />
<string name="media_projection_entry_cast_permission_dialog_title" msgid="8860150223172993547">"هل تريد بدء البثّ؟"</string>
<string name="media_projection_entry_cast_permission_dialog_warning_entire_screen" msgid="1986212276016817231">"أثناء البثّ، يمكن لنظام Android الوصول إلى كل المحتوى المعروض على شاشتك أو الذي يتم تشغيله على جهازك، لذا يُرجى توخي الحذر بشأن المعلومات، مثل كلمات المرور وتفاصيل الدفع والرسائل والصور وملفات الصوت والفيديو."</string>
<string name="media_projection_entry_cast_permission_dialog_warning_single_app" msgid="9900961380294292">"أثناء بثّ محتوى تطبيق، يمكن لنظام Android الوصول إلى كل المحتوى المعروض أو الذي يتم تشغيله في ذلك التطبيق، لذا يُرجى توخي الحذر بشأن المعلومات، مثل كلمات المرور وتفاصيل الدفع والرسائل والصور وملفات الصوت والفيديو."</string>
@@ -865,8 +871,7 @@
<string name="magnification_mode_switch_state_window" msgid="8597100249594076965">"تكبير جزء من الشاشة"</string>
<string name="magnification_open_settings_click_label" msgid="6151849212725923363">"فتح إعدادات التكبير"</string>
<string name="magnification_close_settings_click_label" msgid="4642477260651704517">"إغلاق إعدادات التكبير"</string>
- <!-- no translation found for magnification_exit_edit_mode_click_label (1664818325144887117) -->
- <skip />
+ <string name="magnification_exit_edit_mode_click_label" msgid="1664818325144887117">"الخروج من وضع التعديل"</string>
<string name="magnification_drag_corner_to_resize" msgid="1249766311052418130">"اسحب الزاوية لتغيير الحجم."</string>
<string name="accessibility_allow_diagonal_scrolling" msgid="3258050349191496398">"السماح بالتمرير القطري"</string>
<string name="accessibility_resize" msgid="5733759136600611551">"تغيير الحجم"</string>
diff --git a/packages/SystemUI/res/values-as/strings.xml b/packages/SystemUI/res/values-as/strings.xml
index 4915257..9b3be3c 100644
--- a/packages/SystemUI/res/values-as/strings.xml
+++ b/packages/SystemUI/res/values-as/strings.xml
@@ -57,6 +57,8 @@
<string name="hdmi_cec_set_menu_language_description" msgid="8176716678074126619">"অন্য এটা ডিভাইচে ছিষ্টেমৰ ভাষা সলনি কৰাৰ অনুৰোধ কৰিছে"</string>
<string name="hdmi_cec_set_menu_language_accept" msgid="2513689457281009578">"ভাষা সলনি কৰক"</string>
<string name="hdmi_cec_set_menu_language_decline" msgid="7650721096558646011">"বৰ্তমানৰ ভাষাটো ৰাখক"</string>
+ <!-- no translation found for share_wifi_button_text (1285273973812029240) -->
+ <skip />
<string name="wifi_debugging_title" msgid="7300007687492186076">"এই নেটৱৰ্কত ৱায়াৰলেচ ডি\'বাগিংৰ অনুমতি দিবনে?"</string>
<string name="wifi_debugging_message" msgid="5461204211731802995">"নেটৱৰ্কৰ নাম (SSID)\n<xliff:g id="SSID_0">%1$s</xliff:g>\n\nৱাই-ফাইৰ ঠিকনা (BSSID)\n<xliff:g id="BSSID_1">%2$s</xliff:g>"</string>
<string name="wifi_debugging_always" msgid="2968383799517975155">"এই নেটৱৰ্কত সদায় অনুমতি দিয়ক"</string>
@@ -158,6 +160,8 @@
<string name="biometric_dialog_wrong_pattern" msgid="8954812279840889029">"ভুল আৰ্হি"</string>
<string name="biometric_dialog_wrong_password" msgid="69477929306843790">"ভুল পাছৱৰ্ড"</string>
<string name="biometric_dialog_credential_too_many_attempts" msgid="3083141271737748716">"বহুসংখ্যক ভুল প্ৰয়াস।\n<xliff:g id="NUMBER">%d</xliff:g>ছেকেণ্ডত পুনৰ চেষ্টা কৰক।"</string>
+ <!-- no translation found for work_challenge_emergency_button_text (8946588434515599288) -->
+ <skip />
<string name="biometric_dialog_credential_attempts_before_wipe" msgid="6751859711975516999">"আকৌ চেষ্টা কৰক। <xliff:g id="MAX_ATTEMPTS">%2$d</xliff:g> টাৰ প্ৰয়াসৰ ভিতৰত <xliff:g id="ATTEMPTS_0">%1$d</xliff:g> টা প্ৰয়াস।"</string>
<string name="biometric_dialog_last_attempt_before_wipe_dialog_title" msgid="2874250099278693477">"আপোনাৰ ডেটা মচি পেলোৱা হ’ব"</string>
<string name="biometric_dialog_last_pattern_attempt_before_wipe_device" msgid="6562299244825817598">"আপুনি পৰৱৰ্তী প্ৰয়াসত এটা ভুল আৰ্হি দিলে, এই ডিভাইচটোৰ ডেটা মচি পেলোৱা হ’ব।"</string>
@@ -411,6 +415,8 @@
<string name="media_projection_entry_app_permission_dialog_warning_entire_screen" msgid="8736391633234144237">"আপুনি শ্বেয়াৰ কৰা, ৰেকৰ্ড কৰা অথবা কাষ্ট কৰাৰ সময়ত, আপোনাৰ স্ক্ৰীনখনত দৃশ্যমান হোৱা যিকোনো বস্তু অথবা আপোনাৰ ডিভাইচত প্লে’ কৰা যিকোনো সমললৈ <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g>ৰ এক্সেছ থাকে। গতিকে, পাছৱৰ্ড, পৰিশোধৰ সবিশেষ, বাৰ্তা, ফট’ আৰু অডিঅ’ আৰু ভিডিঅ’ৰ ক্ষেত্ৰত সাৱধান হওক।"</string>
<string name="media_projection_entry_app_permission_dialog_warning_single_app" msgid="5211695779082563959">"আপুনি শ্বেয়াৰ কৰা, ৰেকৰ্ড কৰা অথবা কাষ্ট কৰাৰ সময়ত, সেইটো এপত দৃশ্যমান যিকোনো বস্তু অথবা আপোনাৰ ডিভাইচত প্লে’ কৰা যিকোনো সমললৈ <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g>ৰ এক্সেছ থাকে। গতিকে, পাছৱৰ্ড, পৰিশোধৰ সবিশেষ, বাৰ্তা, ফট’ আৰু অডিঅ’ আৰু ভিডিঅ’ৰ ক্ষেত্ৰত সাৱধান হওক।"</string>
<string name="media_projection_entry_app_permission_dialog_continue" msgid="295463518195075840">"আৰম্ভ কৰক"</string>
+ <!-- no translation found for media_projection_entry_app_permission_dialog_single_app_disabled (8999903044874669995) -->
+ <skip />
<string name="media_projection_entry_cast_permission_dialog_title" msgid="8860150223172993547">"কাষ্ট কৰিবলৈ আৰম্ভ কৰিবনে?"</string>
<string name="media_projection_entry_cast_permission_dialog_warning_entire_screen" msgid="1986212276016817231">"আপুনি কাষ্ট কৰাৰ সময়ত, আপোনাৰ স্ক্ৰীনখনত দৃশ্যমান যিকোনো বস্তু অথবা আপোনাৰ ডিভাইচত প্লে’ কৰা যিকোনো সমললৈ Androidৰ এক্সেছ থাকে। গতিকে, পাছৱৰ্ড, পৰিশোধৰ সবিশেষ, বাৰ্তা, ফট’ আৰু অডিঅ’ আৰু ভিডিঅ’ৰ ক্ষেত্ৰত সাৱধান হওক।"</string>
<string name="media_projection_entry_cast_permission_dialog_warning_single_app" msgid="9900961380294292">"আপুনি এটা এপ্ কাষ্ট কৰাৰ সময়ত সেইটো এপত দৃশ্যমান হোৱা যিকোনো বস্তু অথবা আপোনাৰ ডিভাইচত প্লে’ কৰা যিকোনো সমললৈ Androidৰ এক্সেছ থাকে। গতিকে, পাছৱৰ্ড, পৰিশোধৰ সবিশেষ, বাৰ্তা, ফট’ আৰু অডিঅ’ আৰু ভিডিঅ’ৰ ক্ষেত্ৰত সাৱধান হওক।"</string>
@@ -426,7 +432,7 @@
<string name="screen_capturing_disabled_by_policy_dialog_title" msgid="2113331792064527203">"আপোনাৰ আইটি প্ৰশাসকে অৱৰোধ কৰিছে"</string>
<string name="screen_capturing_disabled_by_policy_dialog_description" msgid="6015975736747696431">"ডিভাইচ সম্পৰ্কীয় নীতিয়ে স্ক্ৰীন কেপশ্বাৰ কৰাটো অক্ষম কৰিছে"</string>
<string name="clear_all_notifications_text" msgid="348312370303046130">"আটাইবোৰ মচক"</string>
- <string name="manage_notifications_text" msgid="6885645344647733116">"পৰিচালনা"</string>
+ <string name="manage_notifications_text" msgid="6885645344647733116">"পৰিচালনা কৰক"</string>
<string name="manage_notifications_history_text" msgid="57055985396576230">"ইতিহাস"</string>
<string name="notification_section_header_incoming" msgid="850925217908095197">"নতুন"</string>
<string name="notification_section_header_gentle" msgid="6804099527336337197">"নীৰৱ"</string>
@@ -865,8 +871,7 @@
<string name="magnification_mode_switch_state_window" msgid="8597100249594076965">"স্ক্ৰীনৰ কিছু অংশ বিবৰ্ধন কৰক"</string>
<string name="magnification_open_settings_click_label" msgid="6151849212725923363">"বিবৰ্ধন কৰাৰ ছেটিং খোলক"</string>
<string name="magnification_close_settings_click_label" msgid="4642477260651704517">"বিবৰ্ধনৰ ছেটিং বন্ধ কৰক"</string>
- <!-- no translation found for magnification_exit_edit_mode_click_label (1664818325144887117) -->
- <skip />
+ <string name="magnification_exit_edit_mode_click_label" msgid="1664818325144887117">"সম্পাদনাৰ ম’ডৰ পৰা বাহিৰ হওক"</string>
<string name="magnification_drag_corner_to_resize" msgid="1249766311052418130">"আকাৰ সলনি কৰিবলৈ চুককেইটা টানি আনি এৰক"</string>
<string name="accessibility_allow_diagonal_scrolling" msgid="3258050349191496398">"কোণীয়াকৈ স্ক্ৰ’ল কৰাৰ অনুমতি দিয়ক"</string>
<string name="accessibility_resize" msgid="5733759136600611551">"আকাৰ সলনি কৰক"</string>
diff --git a/packages/SystemUI/res/values-az/strings.xml b/packages/SystemUI/res/values-az/strings.xml
index 8d9fcbd..af63640 100644
--- a/packages/SystemUI/res/values-az/strings.xml
+++ b/packages/SystemUI/res/values-az/strings.xml
@@ -57,6 +57,8 @@
<string name="hdmi_cec_set_menu_language_description" msgid="8176716678074126619">"Sistem dilinin dəyişdirilməsi başqa cihaz tərəfindən tələb olunur"</string>
<string name="hdmi_cec_set_menu_language_accept" msgid="2513689457281009578">"Dili dəyişin"</string>
<string name="hdmi_cec_set_menu_language_decline" msgid="7650721096558646011">"Cari dili saxlayın"</string>
+ <!-- no translation found for share_wifi_button_text (1285273973812029240) -->
+ <skip />
<string name="wifi_debugging_title" msgid="7300007687492186076">"Bu şəbəkədə WiFi sazlamasına icazə verilsin?"</string>
<string name="wifi_debugging_message" msgid="5461204211731802995">"Şəbəkə Adı (SSID)\n<xliff:g id="SSID_0">%1$s</xliff:g>\n\nWi‑Fi Ünvanı (BSSID)\n<xliff:g id="BSSID_1">%2$s</xliff:g>"</string>
<string name="wifi_debugging_always" msgid="2968383799517975155">"Bu şəbəkədə həmişə icazə verilsin"</string>
@@ -158,6 +160,8 @@
<string name="biometric_dialog_wrong_pattern" msgid="8954812279840889029">"Yanlış model"</string>
<string name="biometric_dialog_wrong_password" msgid="69477929306843790">"Yanlış parol"</string>
<string name="biometric_dialog_credential_too_many_attempts" msgid="3083141271737748716">"Həddindən çox yanlış cəhd.\n<xliff:g id="NUMBER">%d</xliff:g> saniyəyə yenidən cəhd edin."</string>
+ <!-- no translation found for work_challenge_emergency_button_text (8946588434515599288) -->
+ <skip />
<string name="biometric_dialog_credential_attempts_before_wipe" msgid="6751859711975516999">"Yenidən cəhd edin. Cəhd: <xliff:g id="ATTEMPTS_0">%1$d</xliff:g>/<xliff:g id="MAX_ATTEMPTS">%2$d</xliff:g>."</string>
<string name="biometric_dialog_last_attempt_before_wipe_dialog_title" msgid="2874250099278693477">"Data silinəcək"</string>
<string name="biometric_dialog_last_pattern_attempt_before_wipe_device" msgid="6562299244825817598">"Növbəti cəhddə yanlış model daxil etsəniz, bu cihazın datası silinəcək."</string>
@@ -411,6 +415,8 @@
<string name="media_projection_entry_app_permission_dialog_warning_entire_screen" msgid="8736391633234144237">"Paylaşım, qeydəalma və ya yayım zamanı <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> ekranda görünən, yaxud cihazda oxudulan məlumatlara giriş edə bilir. Parol, ödəniş detalları, mesaj, foto, habelə audio və video kimi məlumatlarla bağlı diqqətli olun."</string>
<string name="media_projection_entry_app_permission_dialog_warning_single_app" msgid="5211695779082563959">"Tətbiq paylaşdıqda, qeydə aldıqda və ya yayımladıqda <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> həmin tətbiqdə göstərilən, yaxud oxudulan məlumatlara giriş edə bilir. Parol, ödəniş detalları, mesaj, foto, habelə audio və video kimi məlumatlarla bağlı diqqətli olun."</string>
<string name="media_projection_entry_app_permission_dialog_continue" msgid="295463518195075840">"Başlayın"</string>
+ <!-- no translation found for media_projection_entry_app_permission_dialog_single_app_disabled (8999903044874669995) -->
+ <skip />
<string name="media_projection_entry_cast_permission_dialog_title" msgid="8860150223172993547">"Yayım başladılsın?"</string>
<string name="media_projection_entry_cast_permission_dialog_warning_entire_screen" msgid="1986212276016817231">"Yayım zamanı Android-in ekranda görünən, yaxud cihazda oxudulan məlumatlara girişi olur. Parol, ödəniş detalları, mesaj, foto, habelə audio və video kimi məlumatlarla bağlı diqqətli olun."</string>
<string name="media_projection_entry_cast_permission_dialog_warning_single_app" msgid="9900961380294292">"Tətbiq yayımladıqda Android-in həmin tətbiqdə göstərilən və ya oxudulan məlumatlara girişi olur. Parol, ödəniş detalları, mesaj, foto, habelə audio və video kimi məlumatlarla bağlı diqqətli olun."</string>
@@ -865,8 +871,7 @@
<string name="magnification_mode_switch_state_window" msgid="8597100249594076965">"Ekran hissəsinin böyüdülməsi"</string>
<string name="magnification_open_settings_click_label" msgid="6151849212725923363">"Böyütmə ayarlarını açın"</string>
<string name="magnification_close_settings_click_label" msgid="4642477260651704517">"Böyütmə ayarlarını bağlayın"</string>
- <!-- no translation found for magnification_exit_edit_mode_click_label (1664818325144887117) -->
- <skip />
+ <string name="magnification_exit_edit_mode_click_label" msgid="1664818325144887117">"Redaktə rejimindən çıxın"</string>
<string name="magnification_drag_corner_to_resize" msgid="1249766311052418130">"Ölçüsünü dəyişmək üçün küncündən sürüşdürün"</string>
<string name="accessibility_allow_diagonal_scrolling" msgid="3258050349191496398">"Diaqonal sürüşdürməyə icazə verin"</string>
<string name="accessibility_resize" msgid="5733759136600611551">"Ölçüsünü dəyişin"</string>
diff --git a/packages/SystemUI/res/values-b+sr+Latn/strings.xml b/packages/SystemUI/res/values-b+sr+Latn/strings.xml
index e1bd89d..94c5265 100644
--- a/packages/SystemUI/res/values-b+sr+Latn/strings.xml
+++ b/packages/SystemUI/res/values-b+sr+Latn/strings.xml
@@ -57,6 +57,8 @@
<string name="hdmi_cec_set_menu_language_description" msgid="8176716678074126619">"Drugi uređaj je zatražio promenu jezika sistema"</string>
<string name="hdmi_cec_set_menu_language_accept" msgid="2513689457281009578">"Promeni jezik"</string>
<string name="hdmi_cec_set_menu_language_decline" msgid="7650721096558646011">"Zadrži aktuelni jezik"</string>
+ <!-- no translation found for share_wifi_button_text (1285273973812029240) -->
+ <skip />
<string name="wifi_debugging_title" msgid="7300007687492186076">"Želite da dozvolite bežično otklanjanje grešaka na ovoj mreži?"</string>
<string name="wifi_debugging_message" msgid="5461204211731802995">"Naziv mreže (SSID)\n<xliff:g id="SSID_0">%1$s</xliff:g>\n\nWi‑Fi adresa (BSSID)\n<xliff:g id="BSSID_1">%2$s</xliff:g>"</string>
<string name="wifi_debugging_always" msgid="2968383799517975155">"Uvek dozvoli na ovoj mreži"</string>
@@ -158,6 +160,8 @@
<string name="biometric_dialog_wrong_pattern" msgid="8954812279840889029">"Pogrešan šablon"</string>
<string name="biometric_dialog_wrong_password" msgid="69477929306843790">"Pogrešna lozinka"</string>
<string name="biometric_dialog_credential_too_many_attempts" msgid="3083141271737748716">"Previše netačnih pokušaja.\n Probajte ponovo za <xliff:g id="NUMBER">%d</xliff:g> sek."</string>
+ <!-- no translation found for work_challenge_emergency_button_text (8946588434515599288) -->
+ <skip />
<string name="biometric_dialog_credential_attempts_before_wipe" msgid="6751859711975516999">"Probajte ponovo. <xliff:g id="ATTEMPTS_0">%1$d</xliff:g>. pokušaj od <xliff:g id="MAX_ATTEMPTS">%2$d</xliff:g>."</string>
<string name="biometric_dialog_last_attempt_before_wipe_dialog_title" msgid="2874250099278693477">"Podaci će se izbrisati"</string>
<string name="biometric_dialog_last_pattern_attempt_before_wipe_device" msgid="6562299244825817598">"Ako unesete netačan šablon pri sledećem pokušaju, izbrisaćemo podatke sa ovog uređaja."</string>
@@ -411,6 +415,8 @@
<string name="media_projection_entry_app_permission_dialog_warning_entire_screen" msgid="8736391633234144237">"Kada delite, snimate ili prebacujete, <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> ima pristup kompletnom sadržaju koji je vidljiv na ekranu ili se pušta na uređaju. Zato budite pažljivi sa lozinkama, informacijama o plaćanju, porukama, slikama i audio i video snimcima."</string>
<string name="media_projection_entry_app_permission_dialog_warning_single_app" msgid="5211695779082563959">"Kada delite, snimate ili prebacujete aplikaciju, <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> ima pristup kompletnom sadržaju koji je vidljiv ili se pušta u toj aplikaciji. Zato budite pažljivi sa lozinkama, informacijama o plaćanju, porukama, slikama i audio i video snimcima."</string>
<string name="media_projection_entry_app_permission_dialog_continue" msgid="295463518195075840">"Pokreni"</string>
+ <!-- no translation found for media_projection_entry_app_permission_dialog_single_app_disabled (8999903044874669995) -->
+ <skip />
<string name="media_projection_entry_cast_permission_dialog_title" msgid="8860150223172993547">"Želite da započnete prebacivanje?"</string>
<string name="media_projection_entry_cast_permission_dialog_warning_entire_screen" msgid="1986212276016817231">"Kada prebacujete, Android ima pristup kompletnom sadržaju koji je vidljiv na ekranu ili se pušta na uređaju. Zato budite pažljivi sa lozinkama, informacijama o plaćanju, porukama, slikama i audio i video snimcima."</string>
<string name="media_projection_entry_cast_permission_dialog_warning_single_app" msgid="9900961380294292">"Kada prebacujete aplikaciju, Android ima pristup kompletnom sadržaju koji je vidljiv ili se pušta u toj aplikaciji. Zato budite pažljivi sa lozinkama, informacijama o plaćanju, porukama, slikama i audio i video snimcima."</string>
@@ -426,7 +432,7 @@
<string name="screen_capturing_disabled_by_policy_dialog_title" msgid="2113331792064527203">"Blokira IT administrator"</string>
<string name="screen_capturing_disabled_by_policy_dialog_description" msgid="6015975736747696431">"Snimanje ekrana je onemogućeno smernicama za uređaj"</string>
<string name="clear_all_notifications_text" msgid="348312370303046130">"Obriši sve"</string>
- <string name="manage_notifications_text" msgid="6885645344647733116">"Upravljajte"</string>
+ <string name="manage_notifications_text" msgid="6885645344647733116">"Upravljaj"</string>
<string name="manage_notifications_history_text" msgid="57055985396576230">"Istorija"</string>
<string name="notification_section_header_incoming" msgid="850925217908095197">"Novo"</string>
<string name="notification_section_header_gentle" msgid="6804099527336337197">"Nečujno"</string>
diff --git a/packages/SystemUI/res/values-be/strings.xml b/packages/SystemUI/res/values-be/strings.xml
index 5d1b65c..e271408 100644
--- a/packages/SystemUI/res/values-be/strings.xml
+++ b/packages/SystemUI/res/values-be/strings.xml
@@ -57,6 +57,8 @@
<string name="hdmi_cec_set_menu_language_description" msgid="8176716678074126619">"Іншая прылада запытала змяненне мовы сістэмы"</string>
<string name="hdmi_cec_set_menu_language_accept" msgid="2513689457281009578">"Змяніць мову"</string>
<string name="hdmi_cec_set_menu_language_decline" msgid="7650721096558646011">"Захаваць бягучую мову"</string>
+ <!-- no translation found for share_wifi_button_text (1285273973812029240) -->
+ <skip />
<string name="wifi_debugging_title" msgid="7300007687492186076">"Дазволіць адладку па Wi-Fi у гэтай сетцы?"</string>
<string name="wifi_debugging_message" msgid="5461204211731802995">"Назва сеткі (SSID)\n<xliff:g id="SSID_0">%1$s</xliff:g>\n\nАдрас Wi‑Fi (BSSID)\n<xliff:g id="BSSID_1">%2$s</xliff:g>"</string>
<string name="wifi_debugging_always" msgid="2968383799517975155">"Заўсёды дазваляць у гэтай сетцы"</string>
@@ -158,6 +160,8 @@
<string name="biometric_dialog_wrong_pattern" msgid="8954812279840889029">"Няправільны ўзор разблакіроўкі"</string>
<string name="biometric_dialog_wrong_password" msgid="69477929306843790">"Няправільны пароль"</string>
<string name="biometric_dialog_credential_too_many_attempts" msgid="3083141271737748716">"Занадта шмат няўдалых спроб.\nПаспрабуйце зноў праз <xliff:g id="NUMBER">%d</xliff:g> с."</string>
+ <!-- no translation found for work_challenge_emergency_button_text (8946588434515599288) -->
+ <skip />
<string name="biometric_dialog_credential_attempts_before_wipe" msgid="6751859711975516999">"Паўтарыце спробу. Спроба <xliff:g id="ATTEMPTS_0">%1$d</xliff:g> з <xliff:g id="MAX_ATTEMPTS">%2$d</xliff:g>."</string>
<string name="biometric_dialog_last_attempt_before_wipe_dialog_title" msgid="2874250099278693477">"Вашы даныя будуць выдалены"</string>
<string name="biometric_dialog_last_pattern_attempt_before_wipe_device" msgid="6562299244825817598">"Калі вы ўведзяце няправільны ўзор разблакіроўкі яшчэ раз, даныя з гэтай прылады будуць выдалены."</string>
@@ -411,6 +415,8 @@
<string name="media_projection_entry_app_permission_dialog_warning_entire_screen" msgid="8736391633234144237">"Калі пачынаецца абагульванне, запіс ці трансляцыя, <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> атрымлівае доступ да ўсяго змесціва, якое паказваецца на экране ці прайграецца на прыладзе. Таму прадухіліце паказ пароляў, плацежных рэквізітаў, паведамленняў, фота, відэа і аўдыя."</string>
<string name="media_projection_entry_app_permission_dialog_warning_single_app" msgid="5211695779082563959">"Калі пачынаецца абагульванне, запіс ці трансляцыя змесціва праграмы, <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> атрымлівае доступ да ўсяго змесціва, якое паказваецца ці прайграецца ў праграме. Таму прадухіліце паказ пароляў, плацежных рэквізітаў, паведамленняў, фота, відэа і аўдыя."</string>
<string name="media_projection_entry_app_permission_dialog_continue" msgid="295463518195075840">"Пачаць"</string>
+ <!-- no translation found for media_projection_entry_app_permission_dialog_single_app_disabled (8999903044874669995) -->
+ <skip />
<string name="media_projection_entry_cast_permission_dialog_title" msgid="8860150223172993547">"Пачаць трансляцыю?"</string>
<string name="media_projection_entry_cast_permission_dialog_warning_entire_screen" msgid="1986212276016817231">"Калі адбываецца трансляцыя, Android мае доступ да ўсяго змесціва, якое паказваецца на экране ці прайграецца на прыладзе. Таму прадухіліце паказ пароляў, плацежных рэквізітаў, паведамленняў, фота, відэа і аўдыя."</string>
<string name="media_projection_entry_cast_permission_dialog_warning_single_app" msgid="9900961380294292">"Калі адбываецца трансляцыя змесціва праграмы, Android мае доступ да ўсяго змесціва, якое паказваецца ці прайграецца ў праграме. Таму прадухіліце паказ пароляў, плацежных рэквізітаў, паведамленняў, фота, відэа і аўдыя."</string>
@@ -865,8 +871,7 @@
<string name="magnification_mode_switch_state_window" msgid="8597100249594076965">"Павялічыць частку экрана"</string>
<string name="magnification_open_settings_click_label" msgid="6151849212725923363">"Адкрыць налады павелічэння"</string>
<string name="magnification_close_settings_click_label" msgid="4642477260651704517">"Закрыць налады павелічэння"</string>
- <!-- no translation found for magnification_exit_edit_mode_click_label (1664818325144887117) -->
- <skip />
+ <string name="magnification_exit_edit_mode_click_label" msgid="1664818325144887117">"Выйсці з рэжыму рэдагавання"</string>
<string name="magnification_drag_corner_to_resize" msgid="1249766311052418130">"Каб змяніць памер, перацягніце вугал"</string>
<string name="accessibility_allow_diagonal_scrolling" msgid="3258050349191496398">"Дазволіць прагортванне па дыяганалі"</string>
<string name="accessibility_resize" msgid="5733759136600611551">"Змяніць памер"</string>
@@ -1181,7 +1186,7 @@
<string name="privacy_dialog_active_call_usage" msgid="7858746847946397562">"Зараз выкарыстоўваецца для тэлефоннага выкліку"</string>
<string name="privacy_dialog_recent_call_usage" msgid="1214810644978167344">"Нядаўна выкарыстоўваўся для тэлефоннага выкліку"</string>
<string name="privacy_dialog_active_app_usage" msgid="631997836335929880">"Зараз выкарыстоўваецца праграмай \"<xliff:g id="APP_NAME">%1$s</xliff:g>\""</string>
- <string name="privacy_dialog_recent_app_usage" msgid="4883417856848222450">"Нядаўна выкарыстоўваўся праграмай \"<xliff:g id="APP_NAME">%1$s</xliff:g>\""</string>
+ <string name="privacy_dialog_recent_app_usage" msgid="4883417856848222450">"Нядаўна выкарыстоўвалася праграмай \"<xliff:g id="APP_NAME">%1$s</xliff:g>\""</string>
<string name="privacy_dialog_active_app_usage_1" msgid="9047570143069220911">"Зараз выкарыстоўваецца праграмай \"<xliff:g id="APP_NAME">%1$s</xliff:g>\" (<xliff:g id="ATTRIBUTION_LABEL">%2$s</xliff:g>)"</string>
<string name="privacy_dialog_recent_app_usage_1" msgid="2551340497722370109">"Нядаўна выкарыстоўваўся праграмай \"<xliff:g id="APP_NAME">%1$s</xliff:g>\" (<xliff:g id="ATTRIBUTION_LABEL">%2$s</xliff:g>)"</string>
<string name="privacy_dialog_active_app_usage_2" msgid="2770926061339921767">"Зараз выкарыстоўваецца праграмай \"<xliff:g id="APP_NAME">%1$s</xliff:g>\" (<xliff:g id="ATTRIBUTION_LABEL">%2$s</xliff:g> • <xliff:g id="PROXY_LABEL">%3$s</xliff:g>)"</string>
diff --git a/packages/SystemUI/res/values-bg/strings.xml b/packages/SystemUI/res/values-bg/strings.xml
index 94ab2c9..38781a4 100644
--- a/packages/SystemUI/res/values-bg/strings.xml
+++ b/packages/SystemUI/res/values-bg/strings.xml
@@ -57,6 +57,8 @@
<string name="hdmi_cec_set_menu_language_description" msgid="8176716678074126619">"Друго устройство е заявило промяна на езика на системата"</string>
<string name="hdmi_cec_set_menu_language_accept" msgid="2513689457281009578">"Промяна на езика"</string>
<string name="hdmi_cec_set_menu_language_decline" msgid="7650721096558646011">"Текущ език: Запазване"</string>
+ <!-- no translation found for share_wifi_button_text (1285273973812029240) -->
+ <skip />
<string name="wifi_debugging_title" msgid="7300007687492186076">"Разрешаване на безжичното отстраняване на грешки?"</string>
<string name="wifi_debugging_message" msgid="5461204211731802995">"Име на мрежата (SSID)\n<xliff:g id="SSID_0">%1$s</xliff:g>\n\nАдрес на Wi‑Fi мрежата (BSSID)\n<xliff:g id="BSSID_1">%2$s</xliff:g>"</string>
<string name="wifi_debugging_always" msgid="2968383799517975155">"Винаги да се разрешава в тази мрежа"</string>
@@ -158,6 +160,8 @@
<string name="biometric_dialog_wrong_pattern" msgid="8954812279840889029">"Грешна фигура"</string>
<string name="biometric_dialog_wrong_password" msgid="69477929306843790">"Грешна парола"</string>
<string name="biometric_dialog_credential_too_many_attempts" msgid="3083141271737748716">"Твърде много неправилни опити.\nОпитайте отново след <xliff:g id="NUMBER">%d</xliff:g> секунди."</string>
+ <!-- no translation found for work_challenge_emergency_button_text (8946588434515599288) -->
+ <skip />
<string name="biometric_dialog_credential_attempts_before_wipe" msgid="6751859711975516999">"Опитайте отново. Опит <xliff:g id="ATTEMPTS_0">%1$d</xliff:g> от <xliff:g id="MAX_ATTEMPTS">%2$d</xliff:g>."</string>
<string name="biometric_dialog_last_attempt_before_wipe_dialog_title" msgid="2874250099278693477">"Данните ви ще бъдат изтрити"</string>
<string name="biometric_dialog_last_pattern_attempt_before_wipe_device" msgid="6562299244825817598">"Ако въведете неправилна фигура при следващия опит, данните от това устройство ще бъдат изтрити."</string>
@@ -411,6 +415,8 @@
<string name="media_projection_entry_app_permission_dialog_warning_entire_screen" msgid="8736391633234144237">"Когато споделяте, записвате или предавате, <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> има достъп до всичко, което се вижда на екрана ви или се възпроизвежда на устройството ви. Затова бъдете внимателни с неща като пароли, подробности за начини на плащане, съобщения, снимки, аудио и видео."</string>
<string name="media_projection_entry_app_permission_dialog_warning_single_app" msgid="5211695779082563959">"Когато споделяте, записвате или предавате дадено приложение, <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> има достъп до всичко, което се показва или възпроизвежда в него. Затова бъдете внимателни с неща като пароли, подробности за начини на плащане, съобщения, снимки, аудио и видео."</string>
<string name="media_projection_entry_app_permission_dialog_continue" msgid="295463518195075840">"Стартиране"</string>
+ <!-- no translation found for media_projection_entry_app_permission_dialog_single_app_disabled (8999903044874669995) -->
+ <skip />
<string name="media_projection_entry_cast_permission_dialog_title" msgid="8860150223172993547">"Искате ли да стартирате предаване?"</string>
<string name="media_projection_entry_cast_permission_dialog_warning_entire_screen" msgid="1986212276016817231">"Когато предавате, Android има достъп до всичко, което се вижда на екрана ви или се възпроизвежда на устройството ви. Затова бъдете внимателни с неща като пароли, подробности за начини на плащане, съобщения, снимки, аудио и видео."</string>
<string name="media_projection_entry_cast_permission_dialog_warning_single_app" msgid="9900961380294292">"Когато предавате дадено приложение, Android има достъп до всичко, което се показва или възпроизвежда в него. Затова бъдете внимателни с неща като пароли, подробности за начини на плащане, съобщения, снимки, аудио и видео."</string>
diff --git a/packages/SystemUI/res/values-bn/strings.xml b/packages/SystemUI/res/values-bn/strings.xml
index f80c301..b6b539c 100644
--- a/packages/SystemUI/res/values-bn/strings.xml
+++ b/packages/SystemUI/res/values-bn/strings.xml
@@ -57,6 +57,8 @@
<string name="hdmi_cec_set_menu_language_description" msgid="8176716678074126619">"অন্য ডিভাইসের দ্বারা সিস্টেমের ভাষা পরিবর্তনের অনুরোধ করা হয়েছে"</string>
<string name="hdmi_cec_set_menu_language_accept" msgid="2513689457281009578">"ভাষা পরিবর্তন করুন"</string>
<string name="hdmi_cec_set_menu_language_decline" msgid="7650721096558646011">"বর্তমান ভাষা রাখুন"</string>
+ <!-- no translation found for share_wifi_button_text (1285273973812029240) -->
+ <skip />
<string name="wifi_debugging_title" msgid="7300007687492186076">"এই নেটওয়ার্কে ওয়্যারলেস ডিবাগিংয়ের অনুমতি দেবেন?"</string>
<string name="wifi_debugging_message" msgid="5461204211731802995">"নেটওয়ার্কের নাম (SSID)\n<xliff:g id="SSID_0">%1$s</xliff:g>\n\nওয়াই-ফাই অ্যাড্রেস (BSSID)\n<xliff:g id="BSSID_1">%2$s</xliff:g>"</string>
<string name="wifi_debugging_always" msgid="2968383799517975155">"এই নেটওয়ার্কে সবসময় অনুমতি দিন"</string>
@@ -158,6 +160,8 @@
<string name="biometric_dialog_wrong_pattern" msgid="8954812279840889029">"ভুল প্যাটার্ন"</string>
<string name="biometric_dialog_wrong_password" msgid="69477929306843790">"ভুল পাসওয়ার্ড"</string>
<string name="biometric_dialog_credential_too_many_attempts" msgid="3083141271737748716">"অনেকবার ভুল চেষ্টা করা হয়েছে। \n<xliff:g id="NUMBER">%d</xliff:g> সেকেন্ডের মধ্যে আবার চেষ্টা করুন।"</string>
+ <!-- no translation found for work_challenge_emergency_button_text (8946588434515599288) -->
+ <skip />
<string name="biometric_dialog_credential_attempts_before_wipe" msgid="6751859711975516999">"আবার চেষ্টা করুন। <xliff:g id="MAX_ATTEMPTS">%2$d</xliff:g> বারের মধ্যে <xliff:g id="ATTEMPTS_0">%1$d</xliff:g> বার চেষ্টা করা হয়েছে।"</string>
<string name="biometric_dialog_last_attempt_before_wipe_dialog_title" msgid="2874250099278693477">"আপনার ডেটা মুছে দেওয়া হবে"</string>
<string name="biometric_dialog_last_pattern_attempt_before_wipe_device" msgid="6562299244825817598">"আপনি পরের বারও ভুল প্যাটার্ন আঁকলে এই ডিভাইসের ডেটা মুছে দেওয়া হবে।"</string>
@@ -295,8 +299,7 @@
<string name="quick_settings_cellular_detail_data_limit" msgid="1791389609409211628">"সীমা <xliff:g id="DATA_LIMIT">%s</xliff:g>"</string>
<string name="quick_settings_cellular_detail_data_warning" msgid="7957253810481086455">"<xliff:g id="DATA_LIMIT">%s</xliff:g> সতর্কতা"</string>
<string name="quick_settings_work_mode_label" msgid="6440531507319809121">"অফিসের অ্যাপ"</string>
- <!-- no translation found for quick_settings_work_mode_paused_state (6681788236383735976) -->
- <skip />
+ <string name="quick_settings_work_mode_paused_state" msgid="6681788236383735976">"পজ করা আছে"</string>
<string name="quick_settings_night_display_label" msgid="8180030659141778180">"নাইট লাইট"</string>
<string name="quick_settings_night_secondary_label_on_at_sunset" msgid="3358706312129866626">"সূর্যাস্তে চালু হবে"</string>
<string name="quick_settings_night_secondary_label_until_sunrise" msgid="4063448287758262485">"সূর্যোদয় পর্যন্ত"</string>
@@ -412,6 +415,8 @@
<string name="media_projection_entry_app_permission_dialog_warning_entire_screen" msgid="8736391633234144237">"আপনি শেয়ার, রেকর্ড বা কাস্ট করার সময়, স্ক্রিনে দৃশ্যমান বা ডিভাইসে চালানো সব কিছুই <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> অ্যাক্সেস করতে পারবে। তাই পাসওয়ার্ড, পেমেন্টের বিবরণ, মেসেজ, ফটো এবং অডিও ও ভিডিওর মতো বিষয়ে সতর্ক থাকুন।"</string>
<string name="media_projection_entry_app_permission_dialog_warning_single_app" msgid="5211695779082563959">"আপনি কোনও অ্যাপ শেয়ার, রেকর্ড বা কাস্ট করার সময়, সেই অ্যাপে দেখা যায় বা চালানো হয় এমন সব কিছু <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> অ্যাক্সেস করতে পারবে। তাই পাসওয়ার্ড, পেমেন্টের বিবরণ, মেসেজ, ফটো এবং অডিও ও ভিডিওর মতো বিষয়ে সতর্ক থাকুন।"</string>
<string name="media_projection_entry_app_permission_dialog_continue" msgid="295463518195075840">"শুরু করুন"</string>
+ <!-- no translation found for media_projection_entry_app_permission_dialog_single_app_disabled (8999903044874669995) -->
+ <skip />
<string name="media_projection_entry_cast_permission_dialog_title" msgid="8860150223172993547">"কাস্ট করা শুরু করবেন?"</string>
<string name="media_projection_entry_cast_permission_dialog_warning_entire_screen" msgid="1986212276016817231">"আপনি কাস্ট করার সময়, স্ক্রিনে দৃশ্যমান বা ডিভাইসে চালানো সবকিছুই Android অ্যাক্সেস করতে পারবে। তাই পাসওয়ার্ড, পেমেন্টের বিবরণ, মেসেজ, ফটো এবং অডিও ও ভিডিওর মতো বিষয়ে সতর্ক থাকুন।"</string>
<string name="media_projection_entry_cast_permission_dialog_warning_single_app" msgid="9900961380294292">"আপনি কোনও অ্যাপ কাস্ট করার সময়, ওই অ্যাপে দেখানো বা চালানো হয় এমন সবকিছুই Android অ্যাক্সেস করতে পারবে। তাই পাসওয়ার্ড, পেমেন্টের বিবরণ, মেসেজ, ফটো এবং অডিও ও ভিডিওর মতো বিষয়ে সতর্ক থাকুন।"</string>
@@ -866,8 +871,7 @@
<string name="magnification_mode_switch_state_window" msgid="8597100249594076965">"স্ক্রিনের কিছুটা অংশ বড় করুন"</string>
<string name="magnification_open_settings_click_label" msgid="6151849212725923363">"বড় করে দেখার সেটিংস খুলুন"</string>
<string name="magnification_close_settings_click_label" msgid="4642477260651704517">"\'বড় করে দেখা\' সেটিংস বন্ধ করুন"</string>
- <!-- no translation found for magnification_exit_edit_mode_click_label (1664818325144887117) -->
- <skip />
+ <string name="magnification_exit_edit_mode_click_label" msgid="1664818325144887117">"এডিট মোড থেকে বেরিয়ে আসুন"</string>
<string name="magnification_drag_corner_to_resize" msgid="1249766311052418130">"ছোট বড় করার জন্য কোণ টেনে আনুন"</string>
<string name="accessibility_allow_diagonal_scrolling" msgid="3258050349191496398">"কোনাকুনি স্ক্রল করার অনুমতি দেওয়া"</string>
<string name="accessibility_resize" msgid="5733759136600611551">"ছোট বড় করা"</string>
diff --git a/packages/SystemUI/res/values-bs/strings.xml b/packages/SystemUI/res/values-bs/strings.xml
index 364e847..b5fa00e 100644
--- a/packages/SystemUI/res/values-bs/strings.xml
+++ b/packages/SystemUI/res/values-bs/strings.xml
@@ -57,6 +57,8 @@
<string name="hdmi_cec_set_menu_language_description" msgid="8176716678074126619">"Promjenu jezika sistema je zatražio drugi uređaj"</string>
<string name="hdmi_cec_set_menu_language_accept" msgid="2513689457281009578">"Promijeni jezik"</string>
<string name="hdmi_cec_set_menu_language_decline" msgid="7650721096558646011">"Zadrži trenutni jezik"</string>
+ <!-- no translation found for share_wifi_button_text (1285273973812029240) -->
+ <skip />
<string name="wifi_debugging_title" msgid="7300007687492186076">"Dozvoliti bežično otklanjanje grešaka na ovoj mreži?"</string>
<string name="wifi_debugging_message" msgid="5461204211731802995">"Naziv mreže (SSID)\n<xliff:g id="SSID_0">%1$s</xliff:g>\n\nAdresa WiFi mreže (BSSID)\n<xliff:g id="BSSID_1">%2$s</xliff:g>"</string>
<string name="wifi_debugging_always" msgid="2968383799517975155">"Uvijek dozvoli na ovoj mreži"</string>
@@ -158,6 +160,8 @@
<string name="biometric_dialog_wrong_pattern" msgid="8954812279840889029">"Pogrešan uzorak"</string>
<string name="biometric_dialog_wrong_password" msgid="69477929306843790">"Pogrešna lozinka"</string>
<string name="biometric_dialog_credential_too_many_attempts" msgid="3083141271737748716">"Previše pogrešnih pokušaja.\n Pokušajte ponovo za <xliff:g id="NUMBER">%d</xliff:g> s."</string>
+ <!-- no translation found for work_challenge_emergency_button_text (8946588434515599288) -->
+ <skip />
<string name="biometric_dialog_credential_attempts_before_wipe" msgid="6751859711975516999">"Pokušajte ponovo. <xliff:g id="ATTEMPTS_0">%1$d</xliff:g>. pokušaj od <xliff:g id="MAX_ATTEMPTS">%2$d</xliff:g>."</string>
<string name="biometric_dialog_last_attempt_before_wipe_dialog_title" msgid="2874250099278693477">"Vaši podaci će se izbrisati"</string>
<string name="biometric_dialog_last_pattern_attempt_before_wipe_device" msgid="6562299244825817598">"Ako u sljedećem pokušaju unesete neispravan uzorak, podaci ovog uređaja će se izbrisati."</string>
@@ -411,6 +415,8 @@
<string name="media_projection_entry_app_permission_dialog_warning_entire_screen" msgid="8736391633234144237">"Kada dijelite, snimate ili emitirate, aplikacija <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> ima pristup svemu što je vidljivo na ekranu ili što se reproducira na uređaju. Stoga budite oprezni s informacijama kao što su lozinke, podaci o plaćanju, poruke, fotografije, zvukovi i videozapisi."</string>
<string name="media_projection_entry_app_permission_dialog_warning_single_app" msgid="5211695779082563959">"Kada dijelite, snimate ili emitirate aplikaciju, aplikacija <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> ima pristup svemu što se prikazuje ili reproducira u toj aplikaciji. Stoga budite oprezni s informacijama kao što su lozinke, podaci o plaćanju, poruke, fotografije, zvukovi i videozapisi."</string>
<string name="media_projection_entry_app_permission_dialog_continue" msgid="295463518195075840">"Pokreni"</string>
+ <!-- no translation found for media_projection_entry_app_permission_dialog_single_app_disabled (8999903044874669995) -->
+ <skip />
<string name="media_projection_entry_cast_permission_dialog_title" msgid="8860150223172993547">"Pokrenuti emitiranje?"</string>
<string name="media_projection_entry_cast_permission_dialog_warning_entire_screen" msgid="1986212276016817231">"Kada emitirate, Android ima pristup svemu što je vidljivo na ekranu ili što se reproducira na uređaju. Stoga budite oprezni s informacijama kao što su lozinke, podaci o plaćanju, poruke, fotografije, zvukovi i videozapisi."</string>
<string name="media_projection_entry_cast_permission_dialog_warning_single_app" msgid="9900961380294292">"Kada emitirate aplikaciju, Android ima pristup svemu što se prikazuje ili reproducira u toj aplikaciji. Stoga budite oprezni s informacijama kao što su lozinke, podaci o plaćanju, poruke, fotografije, zvukovi i videozapisi."</string>
@@ -1168,7 +1174,7 @@
<string name="set_default_notes_app_toast_content" msgid="2812374329662610753">"Postavite zadanu aplikaciju za bilješke u Postavkama"</string>
<string name="install_app" msgid="5066668100199613936">"Instaliraj aplikaciju"</string>
<string name="privacy_dialog_title" msgid="7839968133469098311">"Mikrofon i kamera"</string>
- <string name="privacy_dialog_summary" msgid="2458769652125995409">"Nedavno koristila aplikacija"</string>
+ <string name="privacy_dialog_summary" msgid="2458769652125995409">"Nedavno korištenje aplikacije"</string>
<string name="privacy_dialog_more_button" msgid="7610604080293562345">"Prikaži nedavni pristup"</string>
<string name="privacy_dialog_done_button" msgid="4504330708531434263">"Gotovo"</string>
<string name="privacy_dialog_expand_action" msgid="9129262348628331377">"Proširite i prikažite opcije"</string>
diff --git a/packages/SystemUI/res/values-ca/strings.xml b/packages/SystemUI/res/values-ca/strings.xml
index 557a7e6..fea2ee6 100644
--- a/packages/SystemUI/res/values-ca/strings.xml
+++ b/packages/SystemUI/res/values-ca/strings.xml
@@ -57,6 +57,8 @@
<string name="hdmi_cec_set_menu_language_description" msgid="8176716678074126619">"Un altre dispositiu ha sol·licitat canviar l\'idioma del sistema"</string>
<string name="hdmi_cec_set_menu_language_accept" msgid="2513689457281009578">"Canvia l\'idioma"</string>
<string name="hdmi_cec_set_menu_language_decline" msgid="7650721096558646011">"Mantén l\'idioma actual"</string>
+ <!-- no translation found for share_wifi_button_text (1285273973812029240) -->
+ <skip />
<string name="wifi_debugging_title" msgid="7300007687492186076">"Vols permetre la depuració sense fil en aquesta xarxa?"</string>
<string name="wifi_debugging_message" msgid="5461204211731802995">"Nom de la xarxa (SSID)\n<xliff:g id="SSID_0">%1$s</xliff:g>\n\nAdreça Wi‑Fi (BSSID)\n<xliff:g id="BSSID_1">%2$s</xliff:g>"</string>
<string name="wifi_debugging_always" msgid="2968383799517975155">"Permet sempre en aquesta xarxa"</string>
@@ -158,6 +160,8 @@
<string name="biometric_dialog_wrong_pattern" msgid="8954812279840889029">"Patró incorrecte"</string>
<string name="biometric_dialog_wrong_password" msgid="69477929306843790">"Contrasenya incorrecta"</string>
<string name="biometric_dialog_credential_too_many_attempts" msgid="3083141271737748716">"Has superat el nombre d\'intents incorrectes permesos.\nTorna-ho a provar d\'aquí a <xliff:g id="NUMBER">%d</xliff:g> segons."</string>
+ <!-- no translation found for work_challenge_emergency_button_text (8946588434515599288) -->
+ <skip />
<string name="biometric_dialog_credential_attempts_before_wipe" msgid="6751859711975516999">"Torna-ho a provar. Intent <xliff:g id="ATTEMPTS_0">%1$d</xliff:g> de <xliff:g id="MAX_ATTEMPTS">%2$d</xliff:g>."</string>
<string name="biometric_dialog_last_attempt_before_wipe_dialog_title" msgid="2874250099278693477">"Se suprimiran les teves dades"</string>
<string name="biometric_dialog_last_pattern_attempt_before_wipe_device" msgid="6562299244825817598">"Si tornes a introduir un patró incorrecte, se suprimiran les dades del dispositiu."</string>
@@ -411,6 +415,8 @@
<string name="media_projection_entry_app_permission_dialog_warning_entire_screen" msgid="8736391633234144237">"Quan comparteixes, graves o emets contingut, <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> té accés a qualsevol cosa que es vegi a la pantalla o que es reprodueixi al dispositiu. Per aquest motiu, ves amb compte amb les contrasenyes, les dades de pagament, els missatges, les fotos i l\'àudio i el vídeo."</string>
<string name="media_projection_entry_app_permission_dialog_warning_single_app" msgid="5211695779082563959">"Quan comparteixes, graves o emets contingut, <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> té accés a qualsevol cosa que es vegi a la pantalla o que es reprodueixi a l\'aplicació. Per aquest motiu, ves amb compte amb les contrasenyes, les dades de pagament, els missatges, les fotos i l\'àudio i el vídeo."</string>
<string name="media_projection_entry_app_permission_dialog_continue" msgid="295463518195075840">"Inicia"</string>
+ <!-- no translation found for media_projection_entry_app_permission_dialog_single_app_disabled (8999903044874669995) -->
+ <skip />
<string name="media_projection_entry_cast_permission_dialog_title" msgid="8860150223172993547">"Vols iniciar una emissió?"</string>
<string name="media_projection_entry_cast_permission_dialog_warning_entire_screen" msgid="1986212276016817231">"Quan emets contingut, Android té accés a qualsevol cosa que es vegi a la pantalla o que es reprodueixi al dispositiu. Per aquest motiu, ves amb compte amb les contrasenyes, les dades de pagament, els missatges, les fotos i l\'àudio i el vídeo."</string>
<string name="media_projection_entry_cast_permission_dialog_warning_single_app" msgid="9900961380294292">"Quan emets una aplicació, Android té accés a qualsevol cosa que es mostri o que es reprodueixi en aquella aplicació. Per aquest motiu, ves amb compte amb les contrasenyes, les dades de pagament, els missatges, les fotos i l\'àudio i el vídeo."</string>
@@ -865,8 +871,7 @@
<string name="magnification_mode_switch_state_window" msgid="8597100249594076965">"Amplia una part de la pantalla"</string>
<string name="magnification_open_settings_click_label" msgid="6151849212725923363">"Obre la configuració de l\'ampliació"</string>
<string name="magnification_close_settings_click_label" msgid="4642477260651704517">"Tanca la configuració de l\'ampliació"</string>
- <!-- no translation found for magnification_exit_edit_mode_click_label (1664818325144887117) -->
- <skip />
+ <string name="magnification_exit_edit_mode_click_label" msgid="1664818325144887117">"Surt del mode d\'edició"</string>
<string name="magnification_drag_corner_to_resize" msgid="1249766311052418130">"Arrossega el cantó per canviar la mida"</string>
<string name="accessibility_allow_diagonal_scrolling" msgid="3258050349191496398">"Permet el desplaçament en diagonal"</string>
<string name="accessibility_resize" msgid="5733759136600611551">"Canvia la mida"</string>
diff --git a/packages/SystemUI/res/values-cs/strings.xml b/packages/SystemUI/res/values-cs/strings.xml
index 0a83c86..339ba71 100644
--- a/packages/SystemUI/res/values-cs/strings.xml
+++ b/packages/SystemUI/res/values-cs/strings.xml
@@ -57,6 +57,8 @@
<string name="hdmi_cec_set_menu_language_description" msgid="8176716678074126619">"Jiné zařízení požádalo o změnu systémového jazyka"</string>
<string name="hdmi_cec_set_menu_language_accept" msgid="2513689457281009578">"Změnit jazyk"</string>
<string name="hdmi_cec_set_menu_language_decline" msgid="7650721096558646011">"Zachovat stávající jazyk"</string>
+ <!-- no translation found for share_wifi_button_text (1285273973812029240) -->
+ <skip />
<string name="wifi_debugging_title" msgid="7300007687492186076">"Povolit v této síti bezdrátové ladění?"</string>
<string name="wifi_debugging_message" msgid="5461204211731802995">"Název sítě (SSID)\n<xliff:g id="SSID_0">%1$s</xliff:g>\n\nAdresa Wi‑Fi (BSSID)\n<xliff:g id="BSSID_1">%2$s</xliff:g>"</string>
<string name="wifi_debugging_always" msgid="2968383799517975155">"V této síti vždy povolit"</string>
@@ -158,6 +160,8 @@
<string name="biometric_dialog_wrong_pattern" msgid="8954812279840889029">"Nesprávné gesto"</string>
<string name="biometric_dialog_wrong_password" msgid="69477929306843790">"Nesprávné heslo"</string>
<string name="biometric_dialog_credential_too_many_attempts" msgid="3083141271737748716">"Příliš mnoho neplatných pokusů.\nZkuste to znovu za <xliff:g id="NUMBER">%d</xliff:g> s."</string>
+ <!-- no translation found for work_challenge_emergency_button_text (8946588434515599288) -->
+ <skip />
<string name="biometric_dialog_credential_attempts_before_wipe" msgid="6751859711975516999">"Zkuste to znovu. Pokus <xliff:g id="ATTEMPTS_0">%1$d</xliff:g> z <xliff:g id="MAX_ATTEMPTS">%2$d</xliff:g>."</string>
<string name="biometric_dialog_last_attempt_before_wipe_dialog_title" msgid="2874250099278693477">"Vaše data budou smazána"</string>
<string name="biometric_dialog_last_pattern_attempt_before_wipe_device" msgid="6562299244825817598">"Pokud při příštím pokusu zadáte nesprávné gesto, data v tomto zařízení budou smazána."</string>
@@ -411,6 +415,8 @@
<string name="media_projection_entry_app_permission_dialog_warning_entire_screen" msgid="8736391633234144237">"Během sdílení, nahrávání nebo odesílání má <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> přístup k veškerému obsahu, který je viditelný na obrazovce nebo se přehrává v zařízení. Buďte proto opatrní s věcmi, jako jsou hesla, platební údaje, zprávy, fotografie, zvuk a video."</string>
<string name="media_projection_entry_app_permission_dialog_warning_single_app" msgid="5211695779082563959">"Během sdílení, nahrávání nebo odesílání aplikace má <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> přístup k veškerému obsahu, který je v dané aplikaci zobrazen nebo přehráván. Buďte proto opatrní s věcmi, jako jsou hesla, platební údaje, zprávy, fotografie, zvuk a video."</string>
<string name="media_projection_entry_app_permission_dialog_continue" msgid="295463518195075840">"Začít"</string>
+ <!-- no translation found for media_projection_entry_app_permission_dialog_single_app_disabled (8999903044874669995) -->
+ <skip />
<string name="media_projection_entry_cast_permission_dialog_title" msgid="8860150223172993547">"Začít odesílat?"</string>
<string name="media_projection_entry_cast_permission_dialog_warning_entire_screen" msgid="1986212276016817231">"Během odesílání má Android přístup ke všemu, co je viditelné na obrazovce nebo se přehrává v zařízení. Buďte proto opatrní s věcmi, jako jsou hesla, platební údaje, zprávy, fotografie, zvuk a video."</string>
<string name="media_projection_entry_cast_permission_dialog_warning_single_app" msgid="9900961380294292">"Během odesílání aplikace má Android přístup k veškerému obsahu, který je v dané aplikaci zobrazen nebo přehráván. Buďte proto opatrní s věcmi, jako jsou hesla, platební údaje, zprávy, fotografie, zvuk a video."</string>
diff --git a/packages/SystemUI/res/values-da/strings.xml b/packages/SystemUI/res/values-da/strings.xml
index 9ebbebb..79ba451 100644
--- a/packages/SystemUI/res/values-da/strings.xml
+++ b/packages/SystemUI/res/values-da/strings.xml
@@ -57,6 +57,8 @@
<string name="hdmi_cec_set_menu_language_description" msgid="8176716678074126619">"En anden enhed har anmodet om en ændring af systemsproget"</string>
<string name="hdmi_cec_set_menu_language_accept" msgid="2513689457281009578">"Skift sprog"</string>
<string name="hdmi_cec_set_menu_language_decline" msgid="7650721096558646011">"Behold nuværende sprog"</string>
+ <!-- no translation found for share_wifi_button_text (1285273973812029240) -->
+ <skip />
<string name="wifi_debugging_title" msgid="7300007687492186076">"Vil du tillade trådløs fejlretning på dette netværk?"</string>
<string name="wifi_debugging_message" msgid="5461204211731802995">"Netværksnavn (SSID)\n<xliff:g id="SSID_0">%1$s</xliff:g>\n\nWi‑Fi-adresse (BSSID)\n<xliff:g id="BSSID_1">%2$s</xliff:g>"</string>
<string name="wifi_debugging_always" msgid="2968383799517975155">"Tillad altid på dette netværk"</string>
@@ -158,6 +160,8 @@
<string name="biometric_dialog_wrong_pattern" msgid="8954812279840889029">"Forkert mønster"</string>
<string name="biometric_dialog_wrong_password" msgid="69477929306843790">"Forkert adgangskode"</string>
<string name="biometric_dialog_credential_too_many_attempts" msgid="3083141271737748716">"For mange mislykkede forsøg. \nPrøv igen om <xliff:g id="NUMBER">%d</xliff:g> sekunder."</string>
+ <!-- no translation found for work_challenge_emergency_button_text (8946588434515599288) -->
+ <skip />
<string name="biometric_dialog_credential_attempts_before_wipe" msgid="6751859711975516999">"Prøv igen. <xliff:g id="ATTEMPTS_0">%1$d</xliff:g>. forsøg ud af <xliff:g id="MAX_ATTEMPTS">%2$d</xliff:g>."</string>
<string name="biometric_dialog_last_attempt_before_wipe_dialog_title" msgid="2874250099278693477">"Dine data bliver slettet"</string>
<string name="biometric_dialog_last_pattern_attempt_before_wipe_device" msgid="6562299244825817598">"Hvis du angiver et forkert mønster i næste forsøg, slettes dataene på denne enhed."</string>
@@ -411,6 +415,8 @@
<string name="media_projection_entry_app_permission_dialog_warning_entire_screen" msgid="8736391633234144237">"Når du deler, optager eller caster, har <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> adgang til alt, der er synligt på din skærm eller afspilles på din enhed. Vær derfor forsigtig med ting såsom adgangskoder, betalingsoplysninger, beskeder, billeder, lyd og video."</string>
<string name="media_projection_entry_app_permission_dialog_warning_single_app" msgid="5211695779082563959">"Når du deler, optager eller caster en app, har <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> adgang til alt, der vises eller afspilles i den pågældende app. Vær derfor forsigtig med ting såsom adgangskoder, betalingsoplysninger, beskeder, billeder, lyd og video."</string>
<string name="media_projection_entry_app_permission_dialog_continue" msgid="295463518195075840">"Start"</string>
+ <!-- no translation found for media_projection_entry_app_permission_dialog_single_app_disabled (8999903044874669995) -->
+ <skip />
<string name="media_projection_entry_cast_permission_dialog_title" msgid="8860150223172993547">"Vil du begynde at caste?"</string>
<string name="media_projection_entry_cast_permission_dialog_warning_entire_screen" msgid="1986212276016817231">"Når du caster, har Android adgang til alt, der vises på din skærm eller afspilles på din enhed. Vær derfor forsigtig med ting såsom adgangskoder, betalingsoplysninger, beskeder, billeder, lyd og video."</string>
<string name="media_projection_entry_cast_permission_dialog_warning_single_app" msgid="9900961380294292">"Når du caster en app, har Android adgang til alt, der vises eller afspilles i den pågældende app. Vær derfor forsigtig med ting såsom adgangskoder, betalingsoplysninger, beskeder, billeder, lyd og video."</string>
@@ -865,8 +871,7 @@
<string name="magnification_mode_switch_state_window" msgid="8597100249594076965">"Forstør en del af skærmen"</string>
<string name="magnification_open_settings_click_label" msgid="6151849212725923363">"Åbn indstillinger for forstørrelse"</string>
<string name="magnification_close_settings_click_label" msgid="4642477260651704517">"Luk indstillinger for forstørrelse"</string>
- <!-- no translation found for magnification_exit_edit_mode_click_label (1664818325144887117) -->
- <skip />
+ <string name="magnification_exit_edit_mode_click_label" msgid="1664818325144887117">"Afslut redigeringstilstand"</string>
<string name="magnification_drag_corner_to_resize" msgid="1249766311052418130">"Træk i hjørnet for at justere størrelsen"</string>
<string name="accessibility_allow_diagonal_scrolling" msgid="3258050349191496398">"Tillad diagonal rulning"</string>
<string name="accessibility_resize" msgid="5733759136600611551">"Juster"</string>
diff --git a/packages/SystemUI/res/values-de/strings.xml b/packages/SystemUI/res/values-de/strings.xml
index 206e832..4f08805 100644
--- a/packages/SystemUI/res/values-de/strings.xml
+++ b/packages/SystemUI/res/values-de/strings.xml
@@ -57,6 +57,8 @@
<string name="hdmi_cec_set_menu_language_description" msgid="8176716678074126619">"Von einem anderen Gerät wurde eine Änderung der Systemsprache angefordert"</string>
<string name="hdmi_cec_set_menu_language_accept" msgid="2513689457281009578">"Sprache ändern"</string>
<string name="hdmi_cec_set_menu_language_decline" msgid="7650721096558646011">"Aktuelle Sprache nutzen"</string>
+ <!-- no translation found for share_wifi_button_text (1285273973812029240) -->
+ <skip />
<string name="wifi_debugging_title" msgid="7300007687492186076">"Debugging über WLAN in diesem Netzwerk zulassen?"</string>
<string name="wifi_debugging_message" msgid="5461204211731802995">"Netzwerkname (SSID)\n<xliff:g id="SSID_0">%1$s</xliff:g>\n\nWLAN-Adresse (BSSID)\n<xliff:g id="BSSID_1">%2$s</xliff:g>"</string>
<string name="wifi_debugging_always" msgid="2968383799517975155">"Immer in diesem Netzwerk zulassen"</string>
@@ -158,6 +160,8 @@
<string name="biometric_dialog_wrong_pattern" msgid="8954812279840889029">"Falsches Muster"</string>
<string name="biometric_dialog_wrong_password" msgid="69477929306843790">"Falsches Passwort"</string>
<string name="biometric_dialog_credential_too_many_attempts" msgid="3083141271737748716">"Zu viele Fehlversuche.\nBitte probiere es in <xliff:g id="NUMBER">%d</xliff:g> Sekunden noch einmal."</string>
+ <!-- no translation found for work_challenge_emergency_button_text (8946588434515599288) -->
+ <skip />
<string name="biometric_dialog_credential_attempts_before_wipe" msgid="6751859711975516999">"Bitte probier es noch einmal. Versuch <xliff:g id="ATTEMPTS_0">%1$d</xliff:g> von <xliff:g id="MAX_ATTEMPTS">%2$d</xliff:g>."</string>
<string name="biometric_dialog_last_attempt_before_wipe_dialog_title" msgid="2874250099278693477">"Deine Daten werden gelöscht"</string>
<string name="biometric_dialog_last_pattern_attempt_before_wipe_device" msgid="6562299244825817598">"Wenn du beim nächsten Versuch ein falsches Muster eingibst, werden die Daten auf diesem Gerät gelöscht."</string>
@@ -411,6 +415,8 @@
<string name="media_projection_entry_app_permission_dialog_warning_entire_screen" msgid="8736391633234144237">"Beim Teilen, Aufnehmen oder Streamen hat <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> Zugriff auf alle Inhalte, die auf deinem Bildschirm sichtbar sind oder von deinem Gerät wiedergegeben werden. Sei also vorsichtig mit Informationen wie Passwörtern, Zahlungsdetails, Nachrichten, Fotos sowie Audio- und Videoinhalten."</string>
<string name="media_projection_entry_app_permission_dialog_warning_single_app" msgid="5211695779082563959">"Beim Teilen, Aufnehmen oder Streamen einer App hat <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> Zugriff auf alle Inhalte, die in dieser App sichtbar sind oder von ihr wiedergegeben werden. Sei also vorsichtig mit Informationen wie Passwörtern, Zahlungsdetails, Nachrichten, Fotos sowie Audio- und Videoinhalten."</string>
<string name="media_projection_entry_app_permission_dialog_continue" msgid="295463518195075840">"Starten"</string>
+ <!-- no translation found for media_projection_entry_app_permission_dialog_single_app_disabled (8999903044874669995) -->
+ <skip />
<string name="media_projection_entry_cast_permission_dialog_title" msgid="8860150223172993547">"Stream starten?"</string>
<string name="media_projection_entry_cast_permission_dialog_warning_entire_screen" msgid="1986212276016817231">"Beim Streamen hat Android Zugriff auf alle Inhalte, die auf deinem Bildschirm sichtbar sind oder von deinem Gerät wiedergegeben werden. Sei also vorsichtig mit Informationen wie Passwörtern, Zahlungsdetails, Nachrichten, Fotos sowie Audio- und Videoinhalten."</string>
<string name="media_projection_entry_cast_permission_dialog_warning_single_app" msgid="9900961380294292">"Beim Streamen einer App hat Android Zugriff auf alle Inhalte, die in dieser App sichtbar sind oder von ihr wiedergegeben werden. Sei also vorsichtig mit Informationen wie Passwörtern, Zahlungsdetails, Nachrichten, Fotos sowie Audio- und Videoinhalten."</string>
@@ -865,8 +871,7 @@
<string name="magnification_mode_switch_state_window" msgid="8597100249594076965">"Teil des Bildschirms vergrößern"</string>
<string name="magnification_open_settings_click_label" msgid="6151849212725923363">"Vergrößerungseinstellungen öffnen"</string>
<string name="magnification_close_settings_click_label" msgid="4642477260651704517">"Vergrößerungseinstellungen schließen"</string>
- <!-- no translation found for magnification_exit_edit_mode_click_label (1664818325144887117) -->
- <skip />
+ <string name="magnification_exit_edit_mode_click_label" msgid="1664818325144887117">"Bearbeitungsmodus beenden"</string>
<string name="magnification_drag_corner_to_resize" msgid="1249766311052418130">"Zum Anpassen der Größe Ecke ziehen"</string>
<string name="accessibility_allow_diagonal_scrolling" msgid="3258050349191496398">"Diagonales Scrollen erlauben"</string>
<string name="accessibility_resize" msgid="5733759136600611551">"Größe ändern"</string>
diff --git a/packages/SystemUI/res/values-el/strings.xml b/packages/SystemUI/res/values-el/strings.xml
index c32cd17..63337fd 100644
--- a/packages/SystemUI/res/values-el/strings.xml
+++ b/packages/SystemUI/res/values-el/strings.xml
@@ -57,6 +57,8 @@
<string name="hdmi_cec_set_menu_language_description" msgid="8176716678074126619">"Ζητήθηκε αλλαγή της γλώσσας συστήματος από άλλη συσκευή"</string>
<string name="hdmi_cec_set_menu_language_accept" msgid="2513689457281009578">"Αλλαγή γλώσσας"</string>
<string name="hdmi_cec_set_menu_language_decline" msgid="7650721096558646011">"Διατήρ. τρέχουσας γλώσσας"</string>
+ <!-- no translation found for share_wifi_button_text (1285273973812029240) -->
+ <skip />
<string name="wifi_debugging_title" msgid="7300007687492186076">"Να επιτρέπεται ασύρματος εντοπ. σφαλ. στο δίκτυο;"</string>
<string name="wifi_debugging_message" msgid="5461204211731802995">"Όνομα δικτύου (SSID)\n<xliff:g id="SSID_0">%1$s</xliff:g>\n\nΔιεύθυνση Wi‑Fi (BSSID)\n<xliff:g id="BSSID_1">%2$s</xliff:g>"</string>
<string name="wifi_debugging_always" msgid="2968383799517975155">"Να επιτρέπεται πάντα σε αυτό το δίκτυο"</string>
@@ -158,6 +160,8 @@
<string name="biometric_dialog_wrong_pattern" msgid="8954812279840889029">"Εσφαλμένο μοτίβο"</string>
<string name="biometric_dialog_wrong_password" msgid="69477929306843790">"Εσφαλμένος κωδικός πρόσβασης"</string>
<string name="biometric_dialog_credential_too_many_attempts" msgid="3083141271737748716">"Πάρα πολλές αποτυχημένες προσπάθειες.\nΔοκιμάστε ξανά σε <xliff:g id="NUMBER">%d</xliff:g> δευτερόλεπτα."</string>
+ <!-- no translation found for work_challenge_emergency_button_text (8946588434515599288) -->
+ <skip />
<string name="biometric_dialog_credential_attempts_before_wipe" msgid="6751859711975516999">"Προσπαθήστε ξανά. Προσπάθεια <xliff:g id="ATTEMPTS_0">%1$d</xliff:g> από <xliff:g id="MAX_ATTEMPTS">%2$d</xliff:g>."</string>
<string name="biometric_dialog_last_attempt_before_wipe_dialog_title" msgid="2874250099278693477">"Τα δεδομένα σας θα διαγραφούν"</string>
<string name="biometric_dialog_last_pattern_attempt_before_wipe_device" msgid="6562299244825817598">"Εάν εισαγάγετε εσφαλμένο μοτίβο στην επόμενη προσπάθεια, τα δεδομένα αυτής της συσκευής θα διαγραφούν."</string>
@@ -411,6 +415,8 @@
<string name="media_projection_entry_app_permission_dialog_warning_entire_screen" msgid="8736391633234144237">"Όταν κάνετε κοινή χρήση, εγγραφή ή μετάδοση, η εφαρμογή <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> έχει πρόσβαση σε οτιδήποτε είναι ορατό στην οθόνη σας ή αναπαράγεται στη συσκευή σας. Επομένως, να είστε προσεκτικοί με τους κωδικούς πρόσβασης, τα στοιχεία πληρωμής, τα μηνύματα, τις φωτογραφίες, τον ήχο και το βίντεο."</string>
<string name="media_projection_entry_app_permission_dialog_warning_single_app" msgid="5211695779082563959">"Όταν κάνετε κοινή χρήση, εγγραφή ή μετάδοση μιας εφαρμογής, η εφαρμογή <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> έχει πρόσβαση σε οτιδήποτε είναι ορατό ή αναπαράγεται στη συγκεκριμένη εφαρμογή. Επομένως, να είστε προσεκτικοί με τους κωδικούς πρόσβασης, τα στοιχεία πληρωμής, τα μηνύματα, τις φωτογραφίες, τον ήχο και το βίντεο."</string>
<string name="media_projection_entry_app_permission_dialog_continue" msgid="295463518195075840">"Έναρξη"</string>
+ <!-- no translation found for media_projection_entry_app_permission_dialog_single_app_disabled (8999903044874669995) -->
+ <skip />
<string name="media_projection_entry_cast_permission_dialog_title" msgid="8860150223172993547">"Έναρξη μετάδοσης;"</string>
<string name="media_projection_entry_cast_permission_dialog_warning_entire_screen" msgid="1986212276016817231">"Όταν κάνετε μετάδοση, το Android έχει πρόσβαση σε οτιδήποτε είναι ορατό στην οθόνη σας ή αναπαράγεται στη συσκευή σας. Επομένως, να είστε προσεκτικοί με τους κωδικούς πρόσβασης, τα στοιχεία πληρωμής, τα μηνύματα, τις φωτογραφίες, τον ήχο και το βίντεο."</string>
<string name="media_projection_entry_cast_permission_dialog_warning_single_app" msgid="9900961380294292">"Όταν κάνετε μετάδοση μιας εφαρμογής, το Android έχει πρόσβαση σε οτιδήποτε είναι ορατό ή αναπαράγεται στη συγκεκριμένη εφαρμογή. Επομένως, να είστε προσεκτικοί με τους κωδικούς πρόσβασης, τα στοιχεία πληρωμής, τα μηνύματα, τις φωτογραφίες, τον ήχο και το βίντεο."</string>
@@ -865,8 +871,7 @@
<string name="magnification_mode_switch_state_window" msgid="8597100249594076965">"Μεγέθυνση μέρους της οθόνης"</string>
<string name="magnification_open_settings_click_label" msgid="6151849212725923363">"Άνοιγμα ρυθμίσεων μεγιστοποίησης"</string>
<string name="magnification_close_settings_click_label" msgid="4642477260651704517">"Κλείσιμο ρυθμίσεων μεγιστοποίησης"</string>
- <!-- no translation found for magnification_exit_edit_mode_click_label (1664818325144887117) -->
- <skip />
+ <string name="magnification_exit_edit_mode_click_label" msgid="1664818325144887117">"Έξοδος από τη λειτουργία επεξεργασίας"</string>
<string name="magnification_drag_corner_to_resize" msgid="1249766311052418130">"Σύρετε τη γωνία για αλλαγή μεγέθους"</string>
<string name="accessibility_allow_diagonal_scrolling" msgid="3258050349191496398">"Να επιτρέπεται η διαγώνια κύλιση"</string>
<string name="accessibility_resize" msgid="5733759136600611551">"Αλλαγή μεγέθους"</string>
diff --git a/packages/SystemUI/res/values-en-rAU/strings.xml b/packages/SystemUI/res/values-en-rAU/strings.xml
index 313f617..7fbd4bf 100644
--- a/packages/SystemUI/res/values-en-rAU/strings.xml
+++ b/packages/SystemUI/res/values-en-rAU/strings.xml
@@ -57,6 +57,8 @@
<string name="hdmi_cec_set_menu_language_description" msgid="8176716678074126619">"System language change requested by another device"</string>
<string name="hdmi_cec_set_menu_language_accept" msgid="2513689457281009578">"Change language"</string>
<string name="hdmi_cec_set_menu_language_decline" msgid="7650721096558646011">"Keep current language"</string>
+ <!-- no translation found for share_wifi_button_text (1285273973812029240) -->
+ <skip />
<string name="wifi_debugging_title" msgid="7300007687492186076">"Allow wireless debugging on this network?"</string>
<string name="wifi_debugging_message" msgid="5461204211731802995">"Network Name (SSID)\n<xliff:g id="SSID_0">%1$s</xliff:g>\n\nWi‑Fi Address (BSSID)\n<xliff:g id="BSSID_1">%2$s</xliff:g>"</string>
<string name="wifi_debugging_always" msgid="2968383799517975155">"Always allow on this network"</string>
@@ -158,6 +160,8 @@
<string name="biometric_dialog_wrong_pattern" msgid="8954812279840889029">"Wrong pattern"</string>
<string name="biometric_dialog_wrong_password" msgid="69477929306843790">"Wrong password"</string>
<string name="biometric_dialog_credential_too_many_attempts" msgid="3083141271737748716">"Too many incorrect attempts.\nTry again in <xliff:g id="NUMBER">%d</xliff:g> seconds."</string>
+ <!-- no translation found for work_challenge_emergency_button_text (8946588434515599288) -->
+ <skip />
<string name="biometric_dialog_credential_attempts_before_wipe" msgid="6751859711975516999">"Try again. Attempt <xliff:g id="ATTEMPTS_0">%1$d</xliff:g> of <xliff:g id="MAX_ATTEMPTS">%2$d</xliff:g>."</string>
<string name="biometric_dialog_last_attempt_before_wipe_dialog_title" msgid="2874250099278693477">"Your data will be deleted"</string>
<string name="biometric_dialog_last_pattern_attempt_before_wipe_device" msgid="6562299244825817598">"If you enter an incorrect pattern on the next attempt, this device’s data will be deleted."</string>
@@ -295,8 +299,7 @@
<string name="quick_settings_cellular_detail_data_limit" msgid="1791389609409211628">"<xliff:g id="DATA_LIMIT">%s</xliff:g> limit"</string>
<string name="quick_settings_cellular_detail_data_warning" msgid="7957253810481086455">"<xliff:g id="DATA_LIMIT">%s</xliff:g> warning"</string>
<string name="quick_settings_work_mode_label" msgid="6440531507319809121">"Work apps"</string>
- <!-- no translation found for quick_settings_work_mode_paused_state (6681788236383735976) -->
- <skip />
+ <string name="quick_settings_work_mode_paused_state" msgid="6681788236383735976">"Paused"</string>
<string name="quick_settings_night_display_label" msgid="8180030659141778180">"Night Light"</string>
<string name="quick_settings_night_secondary_label_on_at_sunset" msgid="3358706312129866626">"On at sunset"</string>
<string name="quick_settings_night_secondary_label_until_sunrise" msgid="4063448287758262485">"Until sunrise"</string>
@@ -412,6 +415,8 @@
<string name="media_projection_entry_app_permission_dialog_warning_entire_screen" msgid="8736391633234144237">"When you’re sharing, recording or casting, <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> has access to anything visible on your screen or played on your device. So be careful with things like passwords, payment details, messages, photos, audio and video."</string>
<string name="media_projection_entry_app_permission_dialog_warning_single_app" msgid="5211695779082563959">"When you’re sharing, recording or casting an app, <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> has access to anything shown or played on that app. So be careful with things like passwords, payment details, messages, photos, audio and video."</string>
<string name="media_projection_entry_app_permission_dialog_continue" msgid="295463518195075840">"Start"</string>
+ <!-- no translation found for media_projection_entry_app_permission_dialog_single_app_disabled (8999903044874669995) -->
+ <skip />
<string name="media_projection_entry_cast_permission_dialog_title" msgid="8860150223172993547">"Start casting?"</string>
<string name="media_projection_entry_cast_permission_dialog_warning_entire_screen" msgid="1986212276016817231">"When you’re casting, Android has access to anything visible on your screen or played on your device. So be careful with things like passwords, payment details, messages, photos, audio and video."</string>
<string name="media_projection_entry_cast_permission_dialog_warning_single_app" msgid="9900961380294292">"When you’re casting an app, Android has access to anything shown or played on that app. So be careful with things like passwords, payment details, messages, photos, audio and video."</string>
@@ -598,8 +603,8 @@
<string name="notification_delegate_header" msgid="1264510071031479920">"Proxied notification"</string>
<string name="notification_channel_dialog_title" msgid="6856514143093200019">"All <xliff:g id="APP_NAME">%1$s</xliff:g> notifications"</string>
<string name="see_more_title" msgid="7409317011708185729">"See more"</string>
- <string name="feedback_alerted" msgid="5192459808484271208">"This notification was automatically <b>promoted to default</b> by the system."</string>
- <string name="feedback_silenced" msgid="9116540317466126457">"This notification was automatically <b>demoted to silent</b> by the system."</string>
+ <string name="feedback_alerted" msgid="5192459808484271208">"This notification was automatically <b>promoted to Default</b> by the system."</string>
+ <string name="feedback_silenced" msgid="9116540317466126457">"This notification was automatically <b>demoted to Silent</b> by the system."</string>
<string name="feedback_promoted" msgid="2125562787759780807">"This notification was automatically <b>ranked higher</b> in your shade."</string>
<string name="feedback_demoted" msgid="951884763467110604">"This notification was automatically <b>ranked lower</b> in your shade."</string>
<string name="feedback_prompt" msgid="3656728972307896379">"Let the developer know your feedback. Was this correct?"</string>
@@ -866,8 +871,7 @@
<string name="magnification_mode_switch_state_window" msgid="8597100249594076965">"Magnify part of screen"</string>
<string name="magnification_open_settings_click_label" msgid="6151849212725923363">"Open magnification settings"</string>
<string name="magnification_close_settings_click_label" msgid="4642477260651704517">"Close magnification settings"</string>
- <!-- no translation found for magnification_exit_edit_mode_click_label (1664818325144887117) -->
- <skip />
+ <string name="magnification_exit_edit_mode_click_label" msgid="1664818325144887117">"Exit edit mode"</string>
<string name="magnification_drag_corner_to_resize" msgid="1249766311052418130">"Drag corner to resize"</string>
<string name="accessibility_allow_diagonal_scrolling" msgid="3258050349191496398">"Allow diagonal scrolling"</string>
<string name="accessibility_resize" msgid="5733759136600611551">"Resize"</string>
diff --git a/packages/SystemUI/res/values-en-rCA/strings.xml b/packages/SystemUI/res/values-en-rCA/strings.xml
index 157570a..0e50776 100644
--- a/packages/SystemUI/res/values-en-rCA/strings.xml
+++ b/packages/SystemUI/res/values-en-rCA/strings.xml
@@ -57,6 +57,8 @@
<string name="hdmi_cec_set_menu_language_description" msgid="8176716678074126619">"System language change requested by another device"</string>
<string name="hdmi_cec_set_menu_language_accept" msgid="2513689457281009578">"Change language"</string>
<string name="hdmi_cec_set_menu_language_decline" msgid="7650721096558646011">"Keep current language"</string>
+ <!-- no translation found for share_wifi_button_text (1285273973812029240) -->
+ <skip />
<string name="wifi_debugging_title" msgid="7300007687492186076">"Allow wireless debugging on this network?"</string>
<string name="wifi_debugging_message" msgid="5461204211731802995">"Network Name (SSID)\n<xliff:g id="SSID_0">%1$s</xliff:g>\n\nWi‑Fi Address (BSSID)\n<xliff:g id="BSSID_1">%2$s</xliff:g>"</string>
<string name="wifi_debugging_always" msgid="2968383799517975155">"Always allow on this network"</string>
@@ -158,6 +160,8 @@
<string name="biometric_dialog_wrong_pattern" msgid="8954812279840889029">"Wrong pattern"</string>
<string name="biometric_dialog_wrong_password" msgid="69477929306843790">"Wrong password"</string>
<string name="biometric_dialog_credential_too_many_attempts" msgid="3083141271737748716">"Too many incorrect attempts.\nTry again in <xliff:g id="NUMBER">%d</xliff:g> seconds."</string>
+ <!-- no translation found for work_challenge_emergency_button_text (8946588434515599288) -->
+ <skip />
<string name="biometric_dialog_credential_attempts_before_wipe" msgid="6751859711975516999">"Try again. Attempt <xliff:g id="ATTEMPTS_0">%1$d</xliff:g> of <xliff:g id="MAX_ATTEMPTS">%2$d</xliff:g>."</string>
<string name="biometric_dialog_last_attempt_before_wipe_dialog_title" msgid="2874250099278693477">"Your data will be deleted"</string>
<string name="biometric_dialog_last_pattern_attempt_before_wipe_device" msgid="6562299244825817598">"If you enter an incorrect pattern on the next attempt, this device’s data will be deleted."</string>
@@ -411,6 +415,8 @@
<string name="media_projection_entry_app_permission_dialog_warning_entire_screen" msgid="8736391633234144237">"When you’re sharing, recording, or casting, <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> has access to anything visible on your screen or played on your device. So be careful with things like passwords, payment details, messages, photos, and audio and video."</string>
<string name="media_projection_entry_app_permission_dialog_warning_single_app" msgid="5211695779082563959">"When you’re sharing, recording, or casting an app, <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> has access to anything shown or played on that app. So be careful with things like passwords, payment details, messages, photos, and audio and video."</string>
<string name="media_projection_entry_app_permission_dialog_continue" msgid="295463518195075840">"Start"</string>
+ <!-- no translation found for media_projection_entry_app_permission_dialog_single_app_disabled (8999903044874669995) -->
+ <skip />
<string name="media_projection_entry_cast_permission_dialog_title" msgid="8860150223172993547">"Start casting?"</string>
<string name="media_projection_entry_cast_permission_dialog_warning_entire_screen" msgid="1986212276016817231">"When you’re casting, Android has access to anything visible on your screen or played on your device. So be careful with things like passwords, payment details, messages, photos, and audio and video."</string>
<string name="media_projection_entry_cast_permission_dialog_warning_single_app" msgid="9900961380294292">"When you’re casting an app, Android has access to anything shown or played on that app. So be careful with things like passwords, payment details, messages, photos, and audio and video."</string>
diff --git a/packages/SystemUI/res/values-en-rGB/strings.xml b/packages/SystemUI/res/values-en-rGB/strings.xml
index 313f617..7fbd4bf 100644
--- a/packages/SystemUI/res/values-en-rGB/strings.xml
+++ b/packages/SystemUI/res/values-en-rGB/strings.xml
@@ -57,6 +57,8 @@
<string name="hdmi_cec_set_menu_language_description" msgid="8176716678074126619">"System language change requested by another device"</string>
<string name="hdmi_cec_set_menu_language_accept" msgid="2513689457281009578">"Change language"</string>
<string name="hdmi_cec_set_menu_language_decline" msgid="7650721096558646011">"Keep current language"</string>
+ <!-- no translation found for share_wifi_button_text (1285273973812029240) -->
+ <skip />
<string name="wifi_debugging_title" msgid="7300007687492186076">"Allow wireless debugging on this network?"</string>
<string name="wifi_debugging_message" msgid="5461204211731802995">"Network Name (SSID)\n<xliff:g id="SSID_0">%1$s</xliff:g>\n\nWi‑Fi Address (BSSID)\n<xliff:g id="BSSID_1">%2$s</xliff:g>"</string>
<string name="wifi_debugging_always" msgid="2968383799517975155">"Always allow on this network"</string>
@@ -158,6 +160,8 @@
<string name="biometric_dialog_wrong_pattern" msgid="8954812279840889029">"Wrong pattern"</string>
<string name="biometric_dialog_wrong_password" msgid="69477929306843790">"Wrong password"</string>
<string name="biometric_dialog_credential_too_many_attempts" msgid="3083141271737748716">"Too many incorrect attempts.\nTry again in <xliff:g id="NUMBER">%d</xliff:g> seconds."</string>
+ <!-- no translation found for work_challenge_emergency_button_text (8946588434515599288) -->
+ <skip />
<string name="biometric_dialog_credential_attempts_before_wipe" msgid="6751859711975516999">"Try again. Attempt <xliff:g id="ATTEMPTS_0">%1$d</xliff:g> of <xliff:g id="MAX_ATTEMPTS">%2$d</xliff:g>."</string>
<string name="biometric_dialog_last_attempt_before_wipe_dialog_title" msgid="2874250099278693477">"Your data will be deleted"</string>
<string name="biometric_dialog_last_pattern_attempt_before_wipe_device" msgid="6562299244825817598">"If you enter an incorrect pattern on the next attempt, this device’s data will be deleted."</string>
@@ -295,8 +299,7 @@
<string name="quick_settings_cellular_detail_data_limit" msgid="1791389609409211628">"<xliff:g id="DATA_LIMIT">%s</xliff:g> limit"</string>
<string name="quick_settings_cellular_detail_data_warning" msgid="7957253810481086455">"<xliff:g id="DATA_LIMIT">%s</xliff:g> warning"</string>
<string name="quick_settings_work_mode_label" msgid="6440531507319809121">"Work apps"</string>
- <!-- no translation found for quick_settings_work_mode_paused_state (6681788236383735976) -->
- <skip />
+ <string name="quick_settings_work_mode_paused_state" msgid="6681788236383735976">"Paused"</string>
<string name="quick_settings_night_display_label" msgid="8180030659141778180">"Night Light"</string>
<string name="quick_settings_night_secondary_label_on_at_sunset" msgid="3358706312129866626">"On at sunset"</string>
<string name="quick_settings_night_secondary_label_until_sunrise" msgid="4063448287758262485">"Until sunrise"</string>
@@ -412,6 +415,8 @@
<string name="media_projection_entry_app_permission_dialog_warning_entire_screen" msgid="8736391633234144237">"When you’re sharing, recording or casting, <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> has access to anything visible on your screen or played on your device. So be careful with things like passwords, payment details, messages, photos, audio and video."</string>
<string name="media_projection_entry_app_permission_dialog_warning_single_app" msgid="5211695779082563959">"When you’re sharing, recording or casting an app, <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> has access to anything shown or played on that app. So be careful with things like passwords, payment details, messages, photos, audio and video."</string>
<string name="media_projection_entry_app_permission_dialog_continue" msgid="295463518195075840">"Start"</string>
+ <!-- no translation found for media_projection_entry_app_permission_dialog_single_app_disabled (8999903044874669995) -->
+ <skip />
<string name="media_projection_entry_cast_permission_dialog_title" msgid="8860150223172993547">"Start casting?"</string>
<string name="media_projection_entry_cast_permission_dialog_warning_entire_screen" msgid="1986212276016817231">"When you’re casting, Android has access to anything visible on your screen or played on your device. So be careful with things like passwords, payment details, messages, photos, audio and video."</string>
<string name="media_projection_entry_cast_permission_dialog_warning_single_app" msgid="9900961380294292">"When you’re casting an app, Android has access to anything shown or played on that app. So be careful with things like passwords, payment details, messages, photos, audio and video."</string>
@@ -598,8 +603,8 @@
<string name="notification_delegate_header" msgid="1264510071031479920">"Proxied notification"</string>
<string name="notification_channel_dialog_title" msgid="6856514143093200019">"All <xliff:g id="APP_NAME">%1$s</xliff:g> notifications"</string>
<string name="see_more_title" msgid="7409317011708185729">"See more"</string>
- <string name="feedback_alerted" msgid="5192459808484271208">"This notification was automatically <b>promoted to default</b> by the system."</string>
- <string name="feedback_silenced" msgid="9116540317466126457">"This notification was automatically <b>demoted to silent</b> by the system."</string>
+ <string name="feedback_alerted" msgid="5192459808484271208">"This notification was automatically <b>promoted to Default</b> by the system."</string>
+ <string name="feedback_silenced" msgid="9116540317466126457">"This notification was automatically <b>demoted to Silent</b> by the system."</string>
<string name="feedback_promoted" msgid="2125562787759780807">"This notification was automatically <b>ranked higher</b> in your shade."</string>
<string name="feedback_demoted" msgid="951884763467110604">"This notification was automatically <b>ranked lower</b> in your shade."</string>
<string name="feedback_prompt" msgid="3656728972307896379">"Let the developer know your feedback. Was this correct?"</string>
@@ -866,8 +871,7 @@
<string name="magnification_mode_switch_state_window" msgid="8597100249594076965">"Magnify part of screen"</string>
<string name="magnification_open_settings_click_label" msgid="6151849212725923363">"Open magnification settings"</string>
<string name="magnification_close_settings_click_label" msgid="4642477260651704517">"Close magnification settings"</string>
- <!-- no translation found for magnification_exit_edit_mode_click_label (1664818325144887117) -->
- <skip />
+ <string name="magnification_exit_edit_mode_click_label" msgid="1664818325144887117">"Exit edit mode"</string>
<string name="magnification_drag_corner_to_resize" msgid="1249766311052418130">"Drag corner to resize"</string>
<string name="accessibility_allow_diagonal_scrolling" msgid="3258050349191496398">"Allow diagonal scrolling"</string>
<string name="accessibility_resize" msgid="5733759136600611551">"Resize"</string>
diff --git a/packages/SystemUI/res/values-en-rIN/strings.xml b/packages/SystemUI/res/values-en-rIN/strings.xml
index 313f617..7fbd4bf 100644
--- a/packages/SystemUI/res/values-en-rIN/strings.xml
+++ b/packages/SystemUI/res/values-en-rIN/strings.xml
@@ -57,6 +57,8 @@
<string name="hdmi_cec_set_menu_language_description" msgid="8176716678074126619">"System language change requested by another device"</string>
<string name="hdmi_cec_set_menu_language_accept" msgid="2513689457281009578">"Change language"</string>
<string name="hdmi_cec_set_menu_language_decline" msgid="7650721096558646011">"Keep current language"</string>
+ <!-- no translation found for share_wifi_button_text (1285273973812029240) -->
+ <skip />
<string name="wifi_debugging_title" msgid="7300007687492186076">"Allow wireless debugging on this network?"</string>
<string name="wifi_debugging_message" msgid="5461204211731802995">"Network Name (SSID)\n<xliff:g id="SSID_0">%1$s</xliff:g>\n\nWi‑Fi Address (BSSID)\n<xliff:g id="BSSID_1">%2$s</xliff:g>"</string>
<string name="wifi_debugging_always" msgid="2968383799517975155">"Always allow on this network"</string>
@@ -158,6 +160,8 @@
<string name="biometric_dialog_wrong_pattern" msgid="8954812279840889029">"Wrong pattern"</string>
<string name="biometric_dialog_wrong_password" msgid="69477929306843790">"Wrong password"</string>
<string name="biometric_dialog_credential_too_many_attempts" msgid="3083141271737748716">"Too many incorrect attempts.\nTry again in <xliff:g id="NUMBER">%d</xliff:g> seconds."</string>
+ <!-- no translation found for work_challenge_emergency_button_text (8946588434515599288) -->
+ <skip />
<string name="biometric_dialog_credential_attempts_before_wipe" msgid="6751859711975516999">"Try again. Attempt <xliff:g id="ATTEMPTS_0">%1$d</xliff:g> of <xliff:g id="MAX_ATTEMPTS">%2$d</xliff:g>."</string>
<string name="biometric_dialog_last_attempt_before_wipe_dialog_title" msgid="2874250099278693477">"Your data will be deleted"</string>
<string name="biometric_dialog_last_pattern_attempt_before_wipe_device" msgid="6562299244825817598">"If you enter an incorrect pattern on the next attempt, this device’s data will be deleted."</string>
@@ -295,8 +299,7 @@
<string name="quick_settings_cellular_detail_data_limit" msgid="1791389609409211628">"<xliff:g id="DATA_LIMIT">%s</xliff:g> limit"</string>
<string name="quick_settings_cellular_detail_data_warning" msgid="7957253810481086455">"<xliff:g id="DATA_LIMIT">%s</xliff:g> warning"</string>
<string name="quick_settings_work_mode_label" msgid="6440531507319809121">"Work apps"</string>
- <!-- no translation found for quick_settings_work_mode_paused_state (6681788236383735976) -->
- <skip />
+ <string name="quick_settings_work_mode_paused_state" msgid="6681788236383735976">"Paused"</string>
<string name="quick_settings_night_display_label" msgid="8180030659141778180">"Night Light"</string>
<string name="quick_settings_night_secondary_label_on_at_sunset" msgid="3358706312129866626">"On at sunset"</string>
<string name="quick_settings_night_secondary_label_until_sunrise" msgid="4063448287758262485">"Until sunrise"</string>
@@ -412,6 +415,8 @@
<string name="media_projection_entry_app_permission_dialog_warning_entire_screen" msgid="8736391633234144237">"When you’re sharing, recording or casting, <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> has access to anything visible on your screen or played on your device. So be careful with things like passwords, payment details, messages, photos, audio and video."</string>
<string name="media_projection_entry_app_permission_dialog_warning_single_app" msgid="5211695779082563959">"When you’re sharing, recording or casting an app, <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> has access to anything shown or played on that app. So be careful with things like passwords, payment details, messages, photos, audio and video."</string>
<string name="media_projection_entry_app_permission_dialog_continue" msgid="295463518195075840">"Start"</string>
+ <!-- no translation found for media_projection_entry_app_permission_dialog_single_app_disabled (8999903044874669995) -->
+ <skip />
<string name="media_projection_entry_cast_permission_dialog_title" msgid="8860150223172993547">"Start casting?"</string>
<string name="media_projection_entry_cast_permission_dialog_warning_entire_screen" msgid="1986212276016817231">"When you’re casting, Android has access to anything visible on your screen or played on your device. So be careful with things like passwords, payment details, messages, photos, audio and video."</string>
<string name="media_projection_entry_cast_permission_dialog_warning_single_app" msgid="9900961380294292">"When you’re casting an app, Android has access to anything shown or played on that app. So be careful with things like passwords, payment details, messages, photos, audio and video."</string>
@@ -598,8 +603,8 @@
<string name="notification_delegate_header" msgid="1264510071031479920">"Proxied notification"</string>
<string name="notification_channel_dialog_title" msgid="6856514143093200019">"All <xliff:g id="APP_NAME">%1$s</xliff:g> notifications"</string>
<string name="see_more_title" msgid="7409317011708185729">"See more"</string>
- <string name="feedback_alerted" msgid="5192459808484271208">"This notification was automatically <b>promoted to default</b> by the system."</string>
- <string name="feedback_silenced" msgid="9116540317466126457">"This notification was automatically <b>demoted to silent</b> by the system."</string>
+ <string name="feedback_alerted" msgid="5192459808484271208">"This notification was automatically <b>promoted to Default</b> by the system."</string>
+ <string name="feedback_silenced" msgid="9116540317466126457">"This notification was automatically <b>demoted to Silent</b> by the system."</string>
<string name="feedback_promoted" msgid="2125562787759780807">"This notification was automatically <b>ranked higher</b> in your shade."</string>
<string name="feedback_demoted" msgid="951884763467110604">"This notification was automatically <b>ranked lower</b> in your shade."</string>
<string name="feedback_prompt" msgid="3656728972307896379">"Let the developer know your feedback. Was this correct?"</string>
@@ -866,8 +871,7 @@
<string name="magnification_mode_switch_state_window" msgid="8597100249594076965">"Magnify part of screen"</string>
<string name="magnification_open_settings_click_label" msgid="6151849212725923363">"Open magnification settings"</string>
<string name="magnification_close_settings_click_label" msgid="4642477260651704517">"Close magnification settings"</string>
- <!-- no translation found for magnification_exit_edit_mode_click_label (1664818325144887117) -->
- <skip />
+ <string name="magnification_exit_edit_mode_click_label" msgid="1664818325144887117">"Exit edit mode"</string>
<string name="magnification_drag_corner_to_resize" msgid="1249766311052418130">"Drag corner to resize"</string>
<string name="accessibility_allow_diagonal_scrolling" msgid="3258050349191496398">"Allow diagonal scrolling"</string>
<string name="accessibility_resize" msgid="5733759136600611551">"Resize"</string>
diff --git a/packages/SystemUI/res/values-en-rXC/strings.xml b/packages/SystemUI/res/values-en-rXC/strings.xml
index db7133c..876bb0e 100644
--- a/packages/SystemUI/res/values-en-rXC/strings.xml
+++ b/packages/SystemUI/res/values-en-rXC/strings.xml
@@ -57,6 +57,8 @@
<string name="hdmi_cec_set_menu_language_description" msgid="8176716678074126619">"System language change requested by another device"</string>
<string name="hdmi_cec_set_menu_language_accept" msgid="2513689457281009578">"Change language"</string>
<string name="hdmi_cec_set_menu_language_decline" msgid="7650721096558646011">"Keep current language"</string>
+ <!-- no translation found for share_wifi_button_text (1285273973812029240) -->
+ <skip />
<string name="wifi_debugging_title" msgid="7300007687492186076">"Allow wireless debugging on this network?"</string>
<string name="wifi_debugging_message" msgid="5461204211731802995">"Network Name (SSID)\n<xliff:g id="SSID_0">%1$s</xliff:g>\n\nWi‑Fi Address (BSSID)\n<xliff:g id="BSSID_1">%2$s</xliff:g>"</string>
<string name="wifi_debugging_always" msgid="2968383799517975155">"Always allow on this network"</string>
@@ -158,6 +160,8 @@
<string name="biometric_dialog_wrong_pattern" msgid="8954812279840889029">"Wrong pattern"</string>
<string name="biometric_dialog_wrong_password" msgid="69477929306843790">"Wrong password"</string>
<string name="biometric_dialog_credential_too_many_attempts" msgid="3083141271737748716">"Too many incorrect attempts.\nTry again in <xliff:g id="NUMBER">%d</xliff:g> seconds."</string>
+ <!-- no translation found for work_challenge_emergency_button_text (8946588434515599288) -->
+ <skip />
<string name="biometric_dialog_credential_attempts_before_wipe" msgid="6751859711975516999">"Try again. Attempt <xliff:g id="ATTEMPTS_0">%1$d</xliff:g> of <xliff:g id="MAX_ATTEMPTS">%2$d</xliff:g>."</string>
<string name="biometric_dialog_last_attempt_before_wipe_dialog_title" msgid="2874250099278693477">"Your data will be deleted"</string>
<string name="biometric_dialog_last_pattern_attempt_before_wipe_device" msgid="6562299244825817598">"If you enter an incorrect pattern on the next attempt, this device’s data will be deleted."</string>
@@ -411,6 +415,8 @@
<string name="media_projection_entry_app_permission_dialog_warning_entire_screen" msgid="8736391633234144237">"When you’re sharing, recording, or casting, <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> has access to anything visible on your screen or played on your device. So be careful with things like passwords, payment details, messages, photos, and audio and video."</string>
<string name="media_projection_entry_app_permission_dialog_warning_single_app" msgid="5211695779082563959">"When you’re sharing, recording, or casting an app, <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> has access to anything shown or played on that app. So be careful with things like passwords, payment details, messages, photos, and audio and video."</string>
<string name="media_projection_entry_app_permission_dialog_continue" msgid="295463518195075840">"Start"</string>
+ <!-- no translation found for media_projection_entry_app_permission_dialog_single_app_disabled (8999903044874669995) -->
+ <skip />
<string name="media_projection_entry_cast_permission_dialog_title" msgid="8860150223172993547">"Start casting?"</string>
<string name="media_projection_entry_cast_permission_dialog_warning_entire_screen" msgid="1986212276016817231">"When you’re casting, Android has access to anything visible on your screen or played on your device. So be careful with things like passwords, payment details, messages, photos, and audio and video."</string>
<string name="media_projection_entry_cast_permission_dialog_warning_single_app" msgid="9900961380294292">"When you’re casting an app, Android has access to anything shown or played on that app. So be careful with things like passwords, payment details, messages, photos, and audio and video."</string>
diff --git a/packages/SystemUI/res/values-es-rUS/strings.xml b/packages/SystemUI/res/values-es-rUS/strings.xml
index 23d2edc..e5d197d 100644
--- a/packages/SystemUI/res/values-es-rUS/strings.xml
+++ b/packages/SystemUI/res/values-es-rUS/strings.xml
@@ -57,6 +57,8 @@
<string name="hdmi_cec_set_menu_language_description" msgid="8176716678074126619">"Cambio de idioma del sistema solicitado por otro dispositivo"</string>
<string name="hdmi_cec_set_menu_language_accept" msgid="2513689457281009578">"Cambiar idioma"</string>
<string name="hdmi_cec_set_menu_language_decline" msgid="7650721096558646011">"Mantener el idioma actual"</string>
+ <!-- no translation found for share_wifi_button_text (1285273973812029240) -->
+ <skip />
<string name="wifi_debugging_title" msgid="7300007687492186076">"¿Quieres permitir la depuración inalámbrica en esta red?"</string>
<string name="wifi_debugging_message" msgid="5461204211731802995">"Nombre de red (SSID)\n<xliff:g id="SSID_0">%1$s</xliff:g>\n\nDirección Wi‑Fi (BSSID)\n<xliff:g id="BSSID_1">%2$s</xliff:g>"</string>
<string name="wifi_debugging_always" msgid="2968383799517975155">"Permitir siempre en esta red"</string>
@@ -158,6 +160,8 @@
<string name="biometric_dialog_wrong_pattern" msgid="8954812279840889029">"Patrón incorrecto"</string>
<string name="biometric_dialog_wrong_password" msgid="69477929306843790">"Contraseña incorrecta"</string>
<string name="biometric_dialog_credential_too_many_attempts" msgid="3083141271737748716">"Demasiados intentos incorrectos.\nVuelve a intentarlo en <xliff:g id="NUMBER">%d</xliff:g> segundos."</string>
+ <!-- no translation found for work_challenge_emergency_button_text (8946588434515599288) -->
+ <skip />
<string name="biometric_dialog_credential_attempts_before_wipe" msgid="6751859711975516999">"Vuelve a intentarlo. Intento <xliff:g id="ATTEMPTS_0">%1$d</xliff:g> de <xliff:g id="MAX_ATTEMPTS">%2$d</xliff:g>."</string>
<string name="biometric_dialog_last_attempt_before_wipe_dialog_title" msgid="2874250099278693477">"Se borrarán tus datos"</string>
<string name="biometric_dialog_last_pattern_attempt_before_wipe_device" msgid="6562299244825817598">"Si ingresas un patrón incorrecto en el próximo intento, se borrarán los datos de este dispositivo."</string>
@@ -411,6 +415,8 @@
<string name="media_projection_entry_app_permission_dialog_warning_entire_screen" msgid="8736391633234144237">"Cuando compartas, grabes o transmitas contenido, <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> podrá acceder a todo lo que sea visible en la pantalla o que reproduzcas en el dispositivo. Por lo tanto, debes tener cuidado con contraseñas, detalles de pagos, mensajes, fotos, audios y videos."</string>
<string name="media_projection_entry_app_permission_dialog_warning_single_app" msgid="5211695779082563959">"Cuando compartas, grabes o transmitas una app, <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> podrá acceder a todo el contenido que se muestre o que reproduzcas en ella. Por lo tanto, debes tener cuidado con contraseñas, detalles de pagos, mensajes, fotos, audios y videos."</string>
<string name="media_projection_entry_app_permission_dialog_continue" msgid="295463518195075840">"Iniciar"</string>
+ <!-- no translation found for media_projection_entry_app_permission_dialog_single_app_disabled (8999903044874669995) -->
+ <skip />
<string name="media_projection_entry_cast_permission_dialog_title" msgid="8860150223172993547">"¿Quieres comenzar a transmitir contenido?"</string>
<string name="media_projection_entry_cast_permission_dialog_warning_entire_screen" msgid="1986212276016817231">"Cuando transmitas contenido, Android podrá acceder a todo lo que sea visible en la pantalla o que reproduzcas en el dispositivo. Por lo tanto, debes tener cuidado con contraseñas, detalles de pagos, mensajes, fotos, audios y videos."</string>
<string name="media_projection_entry_cast_permission_dialog_warning_single_app" msgid="9900961380294292">"Cuando transmitas una app, Android podrá acceder a todo el contenido que se muestre o que reproduzcas en ella. Por lo tanto, debes tener cuidado con contraseñas, detalles de pagos, mensajes, fotos, audios y videos."</string>
@@ -1168,7 +1174,7 @@
<string name="set_default_notes_app_toast_content" msgid="2812374329662610753">"Configura la app de notas predeterminada en Configuración"</string>
<string name="install_app" msgid="5066668100199613936">"Instalar app"</string>
<string name="privacy_dialog_title" msgid="7839968133469098311">"Micrófono y cámara"</string>
- <string name="privacy_dialog_summary" msgid="2458769652125995409">"Uso reciente de una app"</string>
+ <string name="privacy_dialog_summary" msgid="2458769652125995409">"Uso reciente en apps"</string>
<string name="privacy_dialog_more_button" msgid="7610604080293562345">"Ver accesos recientes"</string>
<string name="privacy_dialog_done_button" msgid="4504330708531434263">"Listo"</string>
<string name="privacy_dialog_expand_action" msgid="9129262348628331377">"Expandir y mostrar opciones"</string>
diff --git a/packages/SystemUI/res/values-es/strings.xml b/packages/SystemUI/res/values-es/strings.xml
index 1eb0abc..c2a6b71 100644
--- a/packages/SystemUI/res/values-es/strings.xml
+++ b/packages/SystemUI/res/values-es/strings.xml
@@ -57,6 +57,8 @@
<string name="hdmi_cec_set_menu_language_description" msgid="8176716678074126619">"Otro dispositivo ha solicitado un cambio en el idioma del sistema"</string>
<string name="hdmi_cec_set_menu_language_accept" msgid="2513689457281009578">"Cambiar idioma"</string>
<string name="hdmi_cec_set_menu_language_decline" msgid="7650721096558646011">"Seguir en este idioma"</string>
+ <!-- no translation found for share_wifi_button_text (1285273973812029240) -->
+ <skip />
<string name="wifi_debugging_title" msgid="7300007687492186076">"¿Permitir la depuración inalámbrica en esta red?"</string>
<string name="wifi_debugging_message" msgid="5461204211731802995">"Nombre de la red (SSID)\n<xliff:g id="SSID_0">%1$s</xliff:g>\n\nDirección Wi‑Fi (BSSID)\n<xliff:g id="BSSID_1">%2$s</xliff:g>"</string>
<string name="wifi_debugging_always" msgid="2968383799517975155">"Permitir siempre en esta red"</string>
@@ -158,6 +160,8 @@
<string name="biometric_dialog_wrong_pattern" msgid="8954812279840889029">"Patrón incorrecto"</string>
<string name="biometric_dialog_wrong_password" msgid="69477929306843790">"Contraseña incorrecta"</string>
<string name="biometric_dialog_credential_too_many_attempts" msgid="3083141271737748716">"Demasiados intentos fallidos.\n Vuelve a intentarlo en <xliff:g id="NUMBER">%d</xliff:g> segundos."</string>
+ <!-- no translation found for work_challenge_emergency_button_text (8946588434515599288) -->
+ <skip />
<string name="biometric_dialog_credential_attempts_before_wipe" msgid="6751859711975516999">"Vuelve a intentarlo. Intento <xliff:g id="ATTEMPTS_0">%1$d</xliff:g> de <xliff:g id="MAX_ATTEMPTS">%2$d</xliff:g>."</string>
<string name="biometric_dialog_last_attempt_before_wipe_dialog_title" msgid="2874250099278693477">"Tus datos se eliminarán"</string>
<string name="biometric_dialog_last_pattern_attempt_before_wipe_device" msgid="6562299244825817598">"Si vuelves a introducir un patrón incorrecto, los datos de este dispositivo se eliminarán."</string>
@@ -411,6 +415,8 @@
<string name="media_projection_entry_app_permission_dialog_warning_entire_screen" msgid="8736391633234144237">"Cuando compartes, grabas o envías contenido, <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> puede acceder a todo lo que se muestre en la pantalla o se reproduzca en tu dispositivo. Debes tener cuidado con elementos como contraseñas, detalles de pagos, mensajes, fotos, audio y vídeo."</string>
<string name="media_projection_entry_app_permission_dialog_warning_single_app" msgid="5211695779082563959">"Cuando compartes, grabas o envías una aplicación, <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> puede acceder a todo lo que se muestre o se reproduzca en ella. Debes tener cuidado con elementos como contraseñas, detalles de pagos, mensajes, fotos, audio y vídeo."</string>
<string name="media_projection_entry_app_permission_dialog_continue" msgid="295463518195075840">"Empezar"</string>
+ <!-- no translation found for media_projection_entry_app_permission_dialog_single_app_disabled (8999903044874669995) -->
+ <skip />
<string name="media_projection_entry_cast_permission_dialog_title" msgid="8860150223172993547">"¿Empezar a enviar contenido?"</string>
<string name="media_projection_entry_cast_permission_dialog_warning_entire_screen" msgid="1986212276016817231">"Cuando envías contenido, Android puede acceder a todo lo que se muestre en la pantalla o se reproduzca en tu dispositivo. Debes tener cuidado con elementos como contraseñas, detalles de pagos, mensajes, fotos, audio y vídeo."</string>
<string name="media_projection_entry_cast_permission_dialog_warning_single_app" msgid="9900961380294292">"Cuando envías una aplicación, Android puede acceder a todo lo que se muestre o se reproduzca en ella. Debes tener cuidado con elementos como contraseñas, detalles de pagos, mensajes, fotos, audio y vídeo."</string>
@@ -865,8 +871,7 @@
<string name="magnification_mode_switch_state_window" msgid="8597100249594076965">"Ampliar parte de la pantalla"</string>
<string name="magnification_open_settings_click_label" msgid="6151849212725923363">"Abrir ajustes de ampliación"</string>
<string name="magnification_close_settings_click_label" msgid="4642477260651704517">"Cerrar ajustes de ampliación"</string>
- <!-- no translation found for magnification_exit_edit_mode_click_label (1664818325144887117) -->
- <skip />
+ <string name="magnification_exit_edit_mode_click_label" msgid="1664818325144887117">"Salir del modo Edición"</string>
<string name="magnification_drag_corner_to_resize" msgid="1249766311052418130">"Arrastra la esquina para cambiar el tamaño"</string>
<string name="accessibility_allow_diagonal_scrolling" msgid="3258050349191496398">"Permitir ir en diagonal"</string>
<string name="accessibility_resize" msgid="5733759136600611551">"Cambiar tamaño"</string>
@@ -1181,7 +1186,7 @@
<string name="privacy_dialog_active_call_usage" msgid="7858746847946397562">"En uso por llamada telefónica"</string>
<string name="privacy_dialog_recent_call_usage" msgid="1214810644978167344">"Usado recientemente en llamada telefónica"</string>
<string name="privacy_dialog_active_app_usage" msgid="631997836335929880">"En uso por <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
- <string name="privacy_dialog_recent_app_usage" msgid="4883417856848222450">"Usado recientemente por <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
+ <string name="privacy_dialog_recent_app_usage" msgid="4883417856848222450">"Uso reciente por <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
<string name="privacy_dialog_active_app_usage_1" msgid="9047570143069220911">"En uso por <xliff:g id="APP_NAME">%1$s</xliff:g> (<xliff:g id="ATTRIBUTION_LABEL">%2$s</xliff:g>)"</string>
<string name="privacy_dialog_recent_app_usage_1" msgid="2551340497722370109">"Usado recientemente por <xliff:g id="APP_NAME">%1$s</xliff:g> (<xliff:g id="ATTRIBUTION_LABEL">%2$s</xliff:g>)"</string>
<string name="privacy_dialog_active_app_usage_2" msgid="2770926061339921767">"En uso por <xliff:g id="APP_NAME">%1$s</xliff:g> (<xliff:g id="ATTRIBUTION_LABEL">%2$s</xliff:g> • <xliff:g id="PROXY_LABEL">%3$s</xliff:g>)"</string>
diff --git a/packages/SystemUI/res/values-et/strings.xml b/packages/SystemUI/res/values-et/strings.xml
index 3881635..3817e78 100644
--- a/packages/SystemUI/res/values-et/strings.xml
+++ b/packages/SystemUI/res/values-et/strings.xml
@@ -57,6 +57,8 @@
<string name="hdmi_cec_set_menu_language_description" msgid="8176716678074126619">"Teine seade taotles süsteemi keele muutmist"</string>
<string name="hdmi_cec_set_menu_language_accept" msgid="2513689457281009578">"Muuda keelt"</string>
<string name="hdmi_cec_set_menu_language_decline" msgid="7650721096558646011">"Kasuta praegust keelt"</string>
+ <!-- no translation found for share_wifi_button_text (1285273973812029240) -->
+ <skip />
<string name="wifi_debugging_title" msgid="7300007687492186076">"Kas lubada selles võrgus juhtmevaba silumine?"</string>
<string name="wifi_debugging_message" msgid="5461204211731802995">"Võrgu nimi (SSID)\n<xliff:g id="SSID_0">%1$s</xliff:g>\n\nWiFi-võrgu aadress (BSSID)\n<xliff:g id="BSSID_1">%2$s</xliff:g>"</string>
<string name="wifi_debugging_always" msgid="2968383799517975155">"Luba selles võrgus alati"</string>
@@ -158,6 +160,8 @@
<string name="biometric_dialog_wrong_pattern" msgid="8954812279840889029">"Vale muster"</string>
<string name="biometric_dialog_wrong_password" msgid="69477929306843790">"Vale parool"</string>
<string name="biometric_dialog_credential_too_many_attempts" msgid="3083141271737748716">"Liiga palju valesid katseid.\nProovige <xliff:g id="NUMBER">%d</xliff:g> sekundi pärast uuesti."</string>
+ <!-- no translation found for work_challenge_emergency_button_text (8946588434515599288) -->
+ <skip />
<string name="biometric_dialog_credential_attempts_before_wipe" msgid="6751859711975516999">"Proovige uuesti. Katse <xliff:g id="ATTEMPTS_0">%1$d</xliff:g>/<xliff:g id="MAX_ATTEMPTS">%2$d</xliff:g>."</string>
<string name="biometric_dialog_last_attempt_before_wipe_dialog_title" msgid="2874250099278693477">"Teie andmed kustutatakse"</string>
<string name="biometric_dialog_last_pattern_attempt_before_wipe_device" msgid="6562299244825817598">"Kui sisestate järgmisel katsel vale mustri, kustutatakse selle seadme andmed."</string>
@@ -411,6 +415,8 @@
<string name="media_projection_entry_app_permission_dialog_warning_entire_screen" msgid="8736391633234144237">"Kui jagate, salvestate või kannate üle, on rakendusel <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> juurdepääs kõigele, mis on teie ekraanikuval nähtaval või mida teie seadmes esitatakse. Seega olge ettevaatlik selliste andmetega nagu paroolid, makseteave, sõnumid, fotod ning heli ja video."</string>
<string name="media_projection_entry_app_permission_dialog_warning_single_app" msgid="5211695779082563959">"Kui jagate, salvestate või kannate rakendust üle, on rakendusel <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> juurdepääs kõigele, mida selles rakenduses kuvatakse või esitatakse. Seega olge paroolide, makseteabe, sõnumite, fotode, heli ja videoga ettevaatlik."</string>
<string name="media_projection_entry_app_permission_dialog_continue" msgid="295463518195075840">"Alusta"</string>
+ <!-- no translation found for media_projection_entry_app_permission_dialog_single_app_disabled (8999903044874669995) -->
+ <skip />
<string name="media_projection_entry_cast_permission_dialog_title" msgid="8860150223172993547">"Kas alustada ülekandmist?"</string>
<string name="media_projection_entry_cast_permission_dialog_warning_entire_screen" msgid="1986212276016817231">"Kui kannate üle, on Androidil juurdepääs kõigele, mis on teie ekraanikuval nähtaval või mida teie seadmes esitatakse. Seega olge ettevaatlik selliste andmetega nagu paroolid, makseteave, sõnumid, fotod ning heli ja video."</string>
<string name="media_projection_entry_cast_permission_dialog_warning_single_app" msgid="9900961380294292">"Rakenduse ülekandmise ajal on Androidil juurdepääs kõigele, mis on selles rakenduses nähtaval või mida selles esitatakse. Seega olge paroolide, makseteabe, sõnumite, fotode, heli ja videoga ettevaatlik."</string>
@@ -865,8 +871,7 @@
<string name="magnification_mode_switch_state_window" msgid="8597100249594076965">"Ekraanikuva osa suurendamine"</string>
<string name="magnification_open_settings_click_label" msgid="6151849212725923363">"Ava suurendamisseaded"</string>
<string name="magnification_close_settings_click_label" msgid="4642477260651704517">"Sule suurendamisseaded"</string>
- <!-- no translation found for magnification_exit_edit_mode_click_label (1664818325144887117) -->
- <skip />
+ <string name="magnification_exit_edit_mode_click_label" msgid="1664818325144887117">"Välju muutmisrežiimist"</string>
<string name="magnification_drag_corner_to_resize" msgid="1249766311052418130">"Suuruse muutmiseks lohistage nurka"</string>
<string name="accessibility_allow_diagonal_scrolling" msgid="3258050349191496398">"Luba diagonaalne kerimine"</string>
<string name="accessibility_resize" msgid="5733759136600611551">"Muuda suurust"</string>
diff --git a/packages/SystemUI/res/values-eu/strings.xml b/packages/SystemUI/res/values-eu/strings.xml
index 0121abc..2ad43cc 100644
--- a/packages/SystemUI/res/values-eu/strings.xml
+++ b/packages/SystemUI/res/values-eu/strings.xml
@@ -57,6 +57,8 @@
<string name="hdmi_cec_set_menu_language_description" msgid="8176716678074126619">"Beste gailu batek sistemaren hizkuntza aldatzeko eskatu du"</string>
<string name="hdmi_cec_set_menu_language_accept" msgid="2513689457281009578">"Aldatu hizkuntza"</string>
<string name="hdmi_cec_set_menu_language_decline" msgid="7650721096558646011">"Mantendu oraingo hizkuntza"</string>
+ <!-- no translation found for share_wifi_button_text (1285273973812029240) -->
+ <skip />
<string name="wifi_debugging_title" msgid="7300007687492186076">"Hari gabeko arazketa sare honetan erabiltzeko baimena eman nahi duzu?"</string>
<string name="wifi_debugging_message" msgid="5461204211731802995">"Sarearen izena (SSID)\n<xliff:g id="SSID_0">%1$s</xliff:g>\n\nWifi-helbidea (BSSID)\n<xliff:g id="BSSID_1">%2$s</xliff:g>"</string>
<string name="wifi_debugging_always" msgid="2968383799517975155">"Eman baimena beti sare honetan"</string>
@@ -158,6 +160,8 @@
<string name="biometric_dialog_wrong_pattern" msgid="8954812279840889029">"Eredua ez da zuzena"</string>
<string name="biometric_dialog_wrong_password" msgid="69477929306843790">"Pasahitza ez da zuzena"</string>
<string name="biometric_dialog_credential_too_many_attempts" msgid="3083141271737748716">"Saiakera oker gehiegi egin dituzu.\nSaiatu berriro <xliff:g id="NUMBER">%d</xliff:g> segundo barru."</string>
+ <!-- no translation found for work_challenge_emergency_button_text (8946588434515599288) -->
+ <skip />
<string name="biometric_dialog_credential_attempts_before_wipe" msgid="6751859711975516999">"Saiatu berriro. <xliff:g id="ATTEMPTS_0">%1$d</xliff:g>/<xliff:g id="MAX_ATTEMPTS">%2$d</xliff:g> saiakera."</string>
<string name="biometric_dialog_last_attempt_before_wipe_dialog_title" msgid="2874250099278693477">"Datuak ezabatuko dira"</string>
<string name="biometric_dialog_last_pattern_attempt_before_wipe_device" msgid="6562299244825817598">"Hurrengo saiakeran eredua oker marrazten baduzu, gailuko datuak ezabatuko dira."</string>
@@ -411,6 +415,8 @@
<string name="media_projection_entry_app_permission_dialog_warning_entire_screen" msgid="8736391633234144237">"Edukia partekatzen, grabatzen edo igortzen ari zarenean, pantailan ikusgai dagoen edo gailuan erreproduzitzen ari den guztia atzi dezake <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> aplikazioak. Beraz, kontuz ibili pasahitzekin, ordainketen xehetasunekin, mezuekin, argazkiekin, audioekin eta bideoekin, besteak beste."</string>
<string name="media_projection_entry_app_permission_dialog_warning_single_app" msgid="5211695779082563959">"Aplikazio bat partekatzen, grabatzen edo igortzen ari zarenean, aplikazio horretan ikusgai dagoen edo bertan erreproduzitzen ari den guztia atzi dezake <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> aplikazioak. Beraz, kontuz ibili pasahitzekin, ordainketen xehetasunekin, mezuekin, argazkiekin, audioekin eta bideoekin, besteak beste."</string>
<string name="media_projection_entry_app_permission_dialog_continue" msgid="295463518195075840">"Hasi"</string>
+ <!-- no translation found for media_projection_entry_app_permission_dialog_single_app_disabled (8999903044874669995) -->
+ <skip />
<string name="media_projection_entry_cast_permission_dialog_title" msgid="8860150223172993547">"Igortzen hasi nahi duzu?"</string>
<string name="media_projection_entry_cast_permission_dialog_warning_entire_screen" msgid="1986212276016817231">"Edukia igortzen ari zarenean, pantailan ikusgai dagoen edo gailuan erreproduzitzen ari den guztia atzi dezake Android-ek. Beraz, kontuz ibili pasahitzekin, ordainketen xehetasunekin, mezuekin, argazkiekin, audioekin eta bideoekin, besteak beste."</string>
<string name="media_projection_entry_cast_permission_dialog_warning_single_app" msgid="9900961380294292">"Aplikazio bat igortzen ari zarenean, aplikazio horretan ikusgai dagoen edo bertan erreproduzitzen ari den guztia atzi dezake Android-ek. Beraz, kontuz ibili pasahitzekin, ordainketen xehetasunekin, mezuekin, argazkiekin, audioekin eta bideoekin, besteak beste."</string>
@@ -865,8 +871,7 @@
<string name="magnification_mode_switch_state_window" msgid="8597100249594076965">"Handitu pantailaren zati bat"</string>
<string name="magnification_open_settings_click_label" msgid="6151849212725923363">"Ireki luparen ezarpenak"</string>
<string name="magnification_close_settings_click_label" msgid="4642477260651704517">"Itxi luparen ezarpenak"</string>
- <!-- no translation found for magnification_exit_edit_mode_click_label (1664818325144887117) -->
- <skip />
+ <string name="magnification_exit_edit_mode_click_label" msgid="1664818325144887117">"Irten editatzeko modutik"</string>
<string name="magnification_drag_corner_to_resize" msgid="1249766311052418130">"Arrastatu izkina bat tamaina aldatzeko"</string>
<string name="accessibility_allow_diagonal_scrolling" msgid="3258050349191496398">"Eman diagonalki gora eta behera egiteko aukera erabiltzeko baimena"</string>
<string name="accessibility_resize" msgid="5733759136600611551">"Aldatu tamaina"</string>
diff --git a/packages/SystemUI/res/values-fa/strings.xml b/packages/SystemUI/res/values-fa/strings.xml
index a22f6da..8263639 100644
--- a/packages/SystemUI/res/values-fa/strings.xml
+++ b/packages/SystemUI/res/values-fa/strings.xml
@@ -57,6 +57,8 @@
<string name="hdmi_cec_set_menu_language_description" msgid="8176716678074126619">"دستگاه دیگری درخواست کرده است زبان سیستم تغییر کند"</string>
<string name="hdmi_cec_set_menu_language_accept" msgid="2513689457281009578">"تغییر زبان"</string>
<string name="hdmi_cec_set_menu_language_decline" msgid="7650721096558646011">"حفظ زبان فعلی"</string>
+ <!-- no translation found for share_wifi_button_text (1285273973812029240) -->
+ <skip />
<string name="wifi_debugging_title" msgid="7300007687492186076">"اشکالزدایی بیسیم در این شبکه مجاز شود؟"</string>
<string name="wifi_debugging_message" msgid="5461204211731802995">"نام شبکه (SSID)\n<xliff:g id="SSID_0">%1$s</xliff:g>\n\nنشانی Wi‑Fi (BSSID)\n<xliff:g id="BSSID_1">%2$s</xliff:g>"</string>
<string name="wifi_debugging_always" msgid="2968383799517975155">"همیشه در این شبکه مجاز شود"</string>
@@ -158,6 +160,8 @@
<string name="biometric_dialog_wrong_pattern" msgid="8954812279840889029">"الگو اشتباه است"</string>
<string name="biometric_dialog_wrong_password" msgid="69477929306843790">"گذرواژه اشتباه است"</string>
<string name="biometric_dialog_credential_too_many_attempts" msgid="3083141271737748716">"تلاشهای نادرست بسیاری انجام شده است.\nپس از <xliff:g id="NUMBER">%d</xliff:g> ثانیه دوباره امتحان کنید."</string>
+ <!-- no translation found for work_challenge_emergency_button_text (8946588434515599288) -->
+ <skip />
<string name="biometric_dialog_credential_attempts_before_wipe" msgid="6751859711975516999">"دوباره امتحان کنید. تلاش <xliff:g id="ATTEMPTS_0">%1$d</xliff:g> از <xliff:g id="MAX_ATTEMPTS">%2$d</xliff:g>."</string>
<string name="biometric_dialog_last_attempt_before_wipe_dialog_title" msgid="2874250099278693477">"دادههایتان حذف خواهد شد"</string>
<string name="biometric_dialog_last_pattern_attempt_before_wipe_device" msgid="6562299244825817598">"اگر در تلاش بعدی الگوی نادرستی وارد کنید، دادههای این دستگاه حذف خواهد شد."</string>
@@ -411,6 +415,8 @@
<string name="media_projection_entry_app_permission_dialog_warning_entire_screen" msgid="8736391633234144237">"وقتی درحال همرسانی، ضبط، یا پخش محتوا هستید، <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> به همه محتوایی که در صفحهتان نمایان است یا در دستگاهتان پخش میشود دسترسی دارد. درنتیجه مراقب چیزهایی مثل گذرواژهها، جزئیات پرداخت، پیامها، عکسها، و صدا و تصویر باشید."</string>
<string name="media_projection_entry_app_permission_dialog_warning_single_app" msgid="5211695779082563959">"وقتی درحال همرسانی، ضبط، یا پخش محتوای برنامهای هستید، <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> به همه محتوایی که در آن برنامه نمایان است یا پخش میشود دسترسی دارد. درنتیجه مراقب چیزهایی مثل گذرواژهها، جزئیات پرداخت، پیامها، عکسها، و صدا و تصویر باشید."</string>
<string name="media_projection_entry_app_permission_dialog_continue" msgid="295463518195075840">"شروع"</string>
+ <!-- no translation found for media_projection_entry_app_permission_dialog_single_app_disabled (8999903044874669995) -->
+ <skip />
<string name="media_projection_entry_cast_permission_dialog_title" msgid="8860150223172993547">"پخش محتوا شروع شود؟"</string>
<string name="media_projection_entry_cast_permission_dialog_warning_entire_screen" msgid="1986212276016817231">"وقتی محتوا پخش میکنید، Android به همه محتوایی که در صفحهتان نمایان است یا در دستگاهتان پخش میشود دسترسی دارد. درنتیجه مراقب چیزهایی مثل گذرواژهها، جزئیات پرداخت، پیامها، عکسها، و صدا و تصویر باشید."</string>
<string name="media_projection_entry_cast_permission_dialog_warning_single_app" msgid="9900961380294292">"وقتی محتوای برنامهای را پخش میکنید، Android به همه محتوایی که در آن برنامه نمایان است یا پخش میشود دسترسی دارد. درنتیجه مراقب چیزهایی مثل گذرواژهها، جزئیات پرداخت، پیامها، عکسها، و صدا و تصویر باشید."</string>
@@ -865,8 +871,7 @@
<string name="magnification_mode_switch_state_window" msgid="8597100249594076965">"درشتنمایی بخشی از صفحه"</string>
<string name="magnification_open_settings_click_label" msgid="6151849212725923363">"باز کردن تنظیمات درشتنمایی"</string>
<string name="magnification_close_settings_click_label" msgid="4642477260651704517">"بستن تنظیمات درشتنمایی"</string>
- <!-- no translation found for magnification_exit_edit_mode_click_label (1664818325144887117) -->
- <skip />
+ <string name="magnification_exit_edit_mode_click_label" msgid="1664818325144887117">"خروج از حالت ویرایش"</string>
<string name="magnification_drag_corner_to_resize" msgid="1249766311052418130">"برای تغییر اندازه، گوشه را بکشید"</string>
<string name="accessibility_allow_diagonal_scrolling" msgid="3258050349191496398">"اجازه دادن برای پیمایش قطری"</string>
<string name="accessibility_resize" msgid="5733759136600611551">"تغییر اندازه"</string>
@@ -1169,7 +1174,7 @@
<string name="set_default_notes_app_toast_content" msgid="2812374329662610753">"برنامه پیشفرض یادداشت را در «تنظیمات» تنظیم کنید"</string>
<string name="install_app" msgid="5066668100199613936">"نصب برنامه"</string>
<string name="privacy_dialog_title" msgid="7839968133469098311">"میکروفون و دوربین"</string>
- <string name="privacy_dialog_summary" msgid="2458769652125995409">"استفاده اخیر برنامهها"</string>
+ <string name="privacy_dialog_summary" msgid="2458769652125995409">"استفاده اخیر از برنامه"</string>
<string name="privacy_dialog_more_button" msgid="7610604080293562345">"دیدن دسترسی اخیر"</string>
<string name="privacy_dialog_done_button" msgid="4504330708531434263">"تمام"</string>
<string name="privacy_dialog_expand_action" msgid="9129262348628331377">"ازهم بازکردن و نمایش گزینهها"</string>
@@ -1180,7 +1185,7 @@
<string name="privacy_dialog_manage_permissions" msgid="2543451567190470413">"مدیریت دسترسی"</string>
<string name="privacy_dialog_active_call_usage" msgid="7858746847946397562">"تماس تلفنی از آن استفاده میکند"</string>
<string name="privacy_dialog_recent_call_usage" msgid="1214810644978167344">"اخیراً تماس تلفنی از آن استفاده کرده است"</string>
- <string name="privacy_dialog_active_app_usage" msgid="631997836335929880">"<xliff:g id="APP_NAME">%1$s</xliff:g> از آن استفاده میکند"</string>
+ <string name="privacy_dialog_active_app_usage" msgid="631997836335929880">"<xliff:g id="APP_NAME">%1$s</xliff:g> از آن استفاده میکند"</string>
<string name="privacy_dialog_recent_app_usage" msgid="4883417856848222450">"اخیراً <xliff:g id="APP_NAME">%1$s</xliff:g> از آن استفاده کرده است"</string>
<string name="privacy_dialog_active_app_usage_1" msgid="9047570143069220911">"<xliff:g id="APP_NAME">%1$s</xliff:g> از آن استفاده میکند (<xliff:g id="ATTRIBUTION_LABEL">%2$s</xliff:g>)"</string>
<string name="privacy_dialog_recent_app_usage_1" msgid="2551340497722370109">"اخیراً <xliff:g id="APP_NAME">%1$s</xliff:g> از آن استفاده کرده است (<xliff:g id="ATTRIBUTION_LABEL">%2$s</xliff:g>)"</string>
diff --git a/packages/SystemUI/res/values-fi/strings.xml b/packages/SystemUI/res/values-fi/strings.xml
index d87f2f6..dc0e52d 100644
--- a/packages/SystemUI/res/values-fi/strings.xml
+++ b/packages/SystemUI/res/values-fi/strings.xml
@@ -57,6 +57,8 @@
<string name="hdmi_cec_set_menu_language_description" msgid="8176716678074126619">"Toiselta laitteelta pyydetty järjestelmän kielen vaihtamista"</string>
<string name="hdmi_cec_set_menu_language_accept" msgid="2513689457281009578">"Vaihda kieltä"</string>
<string name="hdmi_cec_set_menu_language_decline" msgid="7650721096558646011">"Pidä nykyinen kieli"</string>
+ <!-- no translation found for share_wifi_button_text (1285273973812029240) -->
+ <skip />
<string name="wifi_debugging_title" msgid="7300007687492186076">"Sallitaanko langaton virheenkorjaus tässä verkossa?"</string>
<string name="wifi_debugging_message" msgid="5461204211731802995">"Verkon nimi (SSID)\n<xliff:g id="SSID_0">%1$s</xliff:g>\n\nWi-Fin osoite (BSSID)\n<xliff:g id="BSSID_1">%2$s</xliff:g>"</string>
<string name="wifi_debugging_always" msgid="2968383799517975155">"Salli aina tässä verkossa"</string>
@@ -158,6 +160,8 @@
<string name="biometric_dialog_wrong_pattern" msgid="8954812279840889029">"Väärä kuvio"</string>
<string name="biometric_dialog_wrong_password" msgid="69477929306843790">"Väärä salasana"</string>
<string name="biometric_dialog_credential_too_many_attempts" msgid="3083141271737748716">"Liian monta virheellistä yritystä.\nYritä uudelleen <xliff:g id="NUMBER">%d</xliff:g> sekunnin kuluttua."</string>
+ <!-- no translation found for work_challenge_emergency_button_text (8946588434515599288) -->
+ <skip />
<string name="biometric_dialog_credential_attempts_before_wipe" msgid="6751859711975516999">"Yritä uudelleen. Yritys <xliff:g id="ATTEMPTS_0">%1$d</xliff:g>/<xliff:g id="MAX_ATTEMPTS">%2$d</xliff:g>."</string>
<string name="biometric_dialog_last_attempt_before_wipe_dialog_title" msgid="2874250099278693477">"Datasi poistetaan"</string>
<string name="biometric_dialog_last_pattern_attempt_before_wipe_device" msgid="6562299244825817598">"Jos annat väärän kuvion seuraavalla yrityskerralla, tämän laitteen data poistetaan."</string>
@@ -411,6 +415,8 @@
<string name="media_projection_entry_app_permission_dialog_warning_entire_screen" msgid="8736391633234144237">"Kun jaat, tallennat tai striimaat, <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> saa pääsyn kaikkeen näytölläsi näkyvään tai laitteellasi toistettuun sisältöön. Ole siis varovainen, kun lisäät salasanoja, maksutietoja, viestejä, kuvia, audiota tai videoita."</string>
<string name="media_projection_entry_app_permission_dialog_warning_single_app" msgid="5211695779082563959">"Kun jaat, tallennat tai striimaat sovellusta, <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> saa pääsyn kaikkeen sovelluksessa näkyvään tai toistettuun sisältöön. Ole siis varovainen, kun lisäät salasanoja, maksutietoja, viestejä, kuvia, audiota tai videoita."</string>
<string name="media_projection_entry_app_permission_dialog_continue" msgid="295463518195075840">"Aloita"</string>
+ <!-- no translation found for media_projection_entry_app_permission_dialog_single_app_disabled (8999903044874669995) -->
+ <skip />
<string name="media_projection_entry_cast_permission_dialog_title" msgid="8860150223172993547">"Aloitetaanko striimaus?"</string>
<string name="media_projection_entry_cast_permission_dialog_warning_entire_screen" msgid="1986212276016817231">"Kun striimaat, Android saa pääsyn kaikkeen näytölläsi näkyvään tai laitteellasi toistettuun sisältöön. Ole siis varovainen, kun lisäät salasanoja, maksutietoja, viestejä, kuvia, audiota tai videoita."</string>
<string name="media_projection_entry_cast_permission_dialog_warning_single_app" msgid="9900961380294292">"Kun striimaat sovellusta, Android saa pääsyn kaikkeen sovelluksessa näkyvään tai toistettuun sisältöön. Ole siis varovainen, kun lisäät salasanoja, maksutietoja, viestejä, kuvia, audiota tai videoita."</string>
@@ -865,8 +871,7 @@
<string name="magnification_mode_switch_state_window" msgid="8597100249594076965">"Suurenna osa näytöstä"</string>
<string name="magnification_open_settings_click_label" msgid="6151849212725923363">"Avaa suurennusasetukset"</string>
<string name="magnification_close_settings_click_label" msgid="4642477260651704517">"Sulje suurennusasetukset"</string>
- <!-- no translation found for magnification_exit_edit_mode_click_label (1664818325144887117) -->
- <skip />
+ <string name="magnification_exit_edit_mode_click_label" msgid="1664818325144887117">"Poistu muokkaustilasta"</string>
<string name="magnification_drag_corner_to_resize" msgid="1249766311052418130">"Muuta kokoa vetämällä kulmaa"</string>
<string name="accessibility_allow_diagonal_scrolling" msgid="3258050349191496398">"Salli diagonaalinen vierittäminen"</string>
<string name="accessibility_resize" msgid="5733759136600611551">"Muuta kokoa"</string>
diff --git a/packages/SystemUI/res/values-fr-rCA/strings.xml b/packages/SystemUI/res/values-fr-rCA/strings.xml
index 1c6b508..dcaaef0 100644
--- a/packages/SystemUI/res/values-fr-rCA/strings.xml
+++ b/packages/SystemUI/res/values-fr-rCA/strings.xml
@@ -57,6 +57,8 @@
<string name="hdmi_cec_set_menu_language_description" msgid="8176716678074126619">"Un autre appareil demande de changer la langue du système"</string>
<string name="hdmi_cec_set_menu_language_accept" msgid="2513689457281009578">"Changer la langue"</string>
<string name="hdmi_cec_set_menu_language_decline" msgid="7650721096558646011">"Garder la langue actuelle"</string>
+ <!-- no translation found for share_wifi_button_text (1285273973812029240) -->
+ <skip />
<string name="wifi_debugging_title" msgid="7300007687492186076">"Autoriser le débogage sans fil sur ce réseau?"</string>
<string name="wifi_debugging_message" msgid="5461204211731802995">"Nom du réseau (SSID)\n<xliff:g id="SSID_0">%1$s</xliff:g>\n\nAdresse Wi‑Fi (BSSID)\n<xliff:g id="BSSID_1">%2$s</xliff:g>"</string>
<string name="wifi_debugging_always" msgid="2968383799517975155">"Toujours autoriser sur ce réseau"</string>
@@ -158,6 +160,8 @@
<string name="biometric_dialog_wrong_pattern" msgid="8954812279840889029">"Schéma incorrect"</string>
<string name="biometric_dialog_wrong_password" msgid="69477929306843790">"Mot de passe incorrect"</string>
<string name="biometric_dialog_credential_too_many_attempts" msgid="3083141271737748716">"Trop de tentatives incorrectes. \nRéessayez dans <xliff:g id="NUMBER">%d</xliff:g> secondes."</string>
+ <!-- no translation found for work_challenge_emergency_button_text (8946588434515599288) -->
+ <skip />
<string name="biometric_dialog_credential_attempts_before_wipe" msgid="6751859711975516999">"Réessayez. Tentative <xliff:g id="ATTEMPTS_0">%1$d</xliff:g> sur <xliff:g id="MAX_ATTEMPTS">%2$d</xliff:g>."</string>
<string name="biometric_dialog_last_attempt_before_wipe_dialog_title" msgid="2874250099278693477">"Vos données seront supprimées"</string>
<string name="biometric_dialog_last_pattern_attempt_before_wipe_device" msgid="6562299244825817598">"Si vous entrez un schéma incorrect à la prochaine tentative, les données de cet appareil seront supprimées."</string>
@@ -411,6 +415,8 @@
<string name="media_projection_entry_app_permission_dialog_warning_entire_screen" msgid="8736391633234144237">"Lorsque vous partagez, enregistrez ou diffusez, <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> a accès à tout ce qui est visible sur votre écran ou lu sur votre appareil. Par conséquent, soyez prudent avec les mots de passe, les détails du paiement, les messages, les photos et les contenus audio et vidéo."</string>
<string name="media_projection_entry_app_permission_dialog_warning_single_app" msgid="5211695779082563959">"Lorsque vous partagez, enregistrez ou diffusez une application, <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> a accès à tout ce qui est visible sur votre écran ou lu sur cette application. Par conséquent, soyez prudent avec les mots de passe, les détails du paiement, les messages, les photos et les contenus audio et vidéo."</string>
<string name="media_projection_entry_app_permission_dialog_continue" msgid="295463518195075840">"Commencer"</string>
+ <!-- no translation found for media_projection_entry_app_permission_dialog_single_app_disabled (8999903044874669995) -->
+ <skip />
<string name="media_projection_entry_cast_permission_dialog_title" msgid="8860150223172993547">"Commencer la diffusion?"</string>
<string name="media_projection_entry_cast_permission_dialog_warning_entire_screen" msgid="1986212276016817231">"Lorsque vous diffusez, Android a accès à tout ce qui est visible sur votre écran ou lu sur votre appareil. Par conséquent, soyez prudent avec les mots de passe, les détails du paiement, les messages, les photos et les contenus audio et vidéo."</string>
<string name="media_projection_entry_cast_permission_dialog_warning_single_app" msgid="9900961380294292">"Lorsque vous diffusez une application, Android a accès à tout ce qui est visible sur votre écran ou lu sur cette application. Par conséquent, soyez prudent avec les mots de passe, les détails du paiement, les messages, les photos et les contenus audio et vidéo."</string>
@@ -865,8 +871,7 @@
<string name="magnification_mode_switch_state_window" msgid="8597100249594076965">"Agrandir une partie de l\'écran"</string>
<string name="magnification_open_settings_click_label" msgid="6151849212725923363">"Ouvrir les paramètres d\'agrandissement"</string>
<string name="magnification_close_settings_click_label" msgid="4642477260651704517">"Fermer les paramètres d\'agrandissement"</string>
- <!-- no translation found for magnification_exit_edit_mode_click_label (1664818325144887117) -->
- <skip />
+ <string name="magnification_exit_edit_mode_click_label" msgid="1664818325144887117">"Désactiver le mode de modification"</string>
<string name="magnification_drag_corner_to_resize" msgid="1249766311052418130">"Faire glisser le coin pour redimensionner"</string>
<string name="accessibility_allow_diagonal_scrolling" msgid="3258050349191496398">"Autoriser défilement diagonal"</string>
<string name="accessibility_resize" msgid="5733759136600611551">"Redimensionner"</string>
@@ -1169,7 +1174,7 @@
<string name="set_default_notes_app_toast_content" msgid="2812374329662610753">"Définir l\'application de prise de notes par défaut dans les Paramètres"</string>
<string name="install_app" msgid="5066668100199613936">"Installer l\'application"</string>
<string name="privacy_dialog_title" msgid="7839968133469098311">"Microphone et appareil photo"</string>
- <string name="privacy_dialog_summary" msgid="2458769652125995409">"Utilisation récente d\'application"</string>
+ <string name="privacy_dialog_summary" msgid="2458769652125995409">"Utilisation récente par les applications"</string>
<string name="privacy_dialog_more_button" msgid="7610604080293562345">"Afficher l\'accès récent"</string>
<string name="privacy_dialog_done_button" msgid="4504330708531434263">"OK"</string>
<string name="privacy_dialog_expand_action" msgid="9129262348628331377">"Développer et afficher les options"</string>
diff --git a/packages/SystemUI/res/values-fr/strings.xml b/packages/SystemUI/res/values-fr/strings.xml
index 420fd85..0fbf014 100644
--- a/packages/SystemUI/res/values-fr/strings.xml
+++ b/packages/SystemUI/res/values-fr/strings.xml
@@ -57,6 +57,8 @@
<string name="hdmi_cec_set_menu_language_description" msgid="8176716678074126619">"Changement de langue système demandé par un autre appareil"</string>
<string name="hdmi_cec_set_menu_language_accept" msgid="2513689457281009578">"Changer de langue"</string>
<string name="hdmi_cec_set_menu_language_decline" msgid="7650721096558646011">"Garder la langue actuelle"</string>
+ <!-- no translation found for share_wifi_button_text (1285273973812029240) -->
+ <skip />
<string name="wifi_debugging_title" msgid="7300007687492186076">"Autoriser le débogage sans fil sur ce réseau ?"</string>
<string name="wifi_debugging_message" msgid="5461204211731802995">"Nom du réseau (SSID)\n<xliff:g id="SSID_0">%1$s</xliff:g>\n\nAdresse Wi‑Fi (BSSID)\n<xliff:g id="BSSID_1">%2$s</xliff:g>"</string>
<string name="wifi_debugging_always" msgid="2968383799517975155">"Toujours autoriser sur ce réseau"</string>
@@ -158,6 +160,8 @@
<string name="biometric_dialog_wrong_pattern" msgid="8954812279840889029">"Schéma incorrect"</string>
<string name="biometric_dialog_wrong_password" msgid="69477929306843790">"Mot de passe incorrect"</string>
<string name="biometric_dialog_credential_too_many_attempts" msgid="3083141271737748716">"Trop de tentatives incorrectes.\nVeuillez réessayer dans <xliff:g id="NUMBER">%d</xliff:g> secondes."</string>
+ <!-- no translation found for work_challenge_emergency_button_text (8946588434515599288) -->
+ <skip />
<string name="biometric_dialog_credential_attempts_before_wipe" msgid="6751859711975516999">"Réessayez. Tentative <xliff:g id="ATTEMPTS_0">%1$d</xliff:g> sur <xliff:g id="MAX_ATTEMPTS">%2$d</xliff:g>."</string>
<string name="biometric_dialog_last_attempt_before_wipe_dialog_title" msgid="2874250099278693477">"Risque de perte des données"</string>
<string name="biometric_dialog_last_pattern_attempt_before_wipe_device" msgid="6562299244825817598">"Si vous dessinez un schéma incorrect lors de la prochaine tentative, les données de cet appareil seront supprimées."</string>
@@ -411,6 +415,8 @@
<string name="media_projection_entry_app_permission_dialog_warning_entire_screen" msgid="8736391633234144237">"Lorsque vous partagez, enregistrez ou castez, <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> a accès à tout ce qui est visible sur votre écran ou lu sur votre appareil. Faites donc attention aux éléments tels que les mots de passe, détails de mode de paiement, messages, photos et contenus audio et vidéo."</string>
<string name="media_projection_entry_app_permission_dialog_warning_single_app" msgid="5211695779082563959">"Lorsque vous partagez, enregistrez ou castez une appli, <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> a accès à tout ce qui est visible sur votre écran ou lu sur votre appareil. Faites donc attention aux éléments tels que les mots de passe, détails de mode de paiement, messages et contenus audio et vidéo."</string>
<string name="media_projection_entry_app_permission_dialog_continue" msgid="295463518195075840">"Commencer"</string>
+ <!-- no translation found for media_projection_entry_app_permission_dialog_single_app_disabled (8999903044874669995) -->
+ <skip />
<string name="media_projection_entry_cast_permission_dialog_title" msgid="8860150223172993547">"Commencer à caster ?"</string>
<string name="media_projection_entry_cast_permission_dialog_warning_entire_screen" msgid="1986212276016817231">"Lorsque vous castez, Android a accès à tout ce qui est visible sur votre écran ou lu sur votre appareil. Faites donc attention aux éléments tels que les mots de passe, détails de mode de paiement, messages, photos et contenus audio et vidéo."</string>
<string name="media_projection_entry_cast_permission_dialog_warning_single_app" msgid="9900961380294292">"Lorsque vous castez une appli, Android a accès à tout ce qui est visible sur votre écran ou lu sur votre appareil. Faites donc attention aux éléments tels que les mots de passe, détails de mode de paiement, messages et contenus audio et vidéo."</string>
@@ -865,8 +871,7 @@
<string name="magnification_mode_switch_state_window" msgid="8597100249594076965">"Agrandir une partie de l\'écran"</string>
<string name="magnification_open_settings_click_label" msgid="6151849212725923363">"Ouvrir les paramètres d\'agrandissement"</string>
<string name="magnification_close_settings_click_label" msgid="4642477260651704517">"Fermer les paramètres d\'agrandissement"</string>
- <!-- no translation found for magnification_exit_edit_mode_click_label (1664818325144887117) -->
- <skip />
+ <string name="magnification_exit_edit_mode_click_label" msgid="1664818325144887117">"Quitter le mode Édition"</string>
<string name="magnification_drag_corner_to_resize" msgid="1249766311052418130">"Faire glisser le coin pour redimensionner"</string>
<string name="accessibility_allow_diagonal_scrolling" msgid="3258050349191496398">"Autoriser le défilement diagonal"</string>
<string name="accessibility_resize" msgid="5733759136600611551">"Redimensionner"</string>
@@ -1169,7 +1174,7 @@
<string name="set_default_notes_app_toast_content" msgid="2812374329662610753">"Définir une appli de notes par défaut dans les paramètres"</string>
<string name="install_app" msgid="5066668100199613936">"Installer l\'appli"</string>
<string name="privacy_dialog_title" msgid="7839968133469098311">"Micro et caméra"</string>
- <string name="privacy_dialog_summary" msgid="2458769652125995409">"Utilisation récente par une appli"</string>
+ <string name="privacy_dialog_summary" msgid="2458769652125995409">"Utilisation récente par les applis"</string>
<string name="privacy_dialog_more_button" msgid="7610604080293562345">"Consulter les accès récents"</string>
<string name="privacy_dialog_done_button" msgid="4504330708531434263">"OK"</string>
<string name="privacy_dialog_expand_action" msgid="9129262348628331377">"Développer et afficher les options"</string>
@@ -1181,7 +1186,7 @@
<string name="privacy_dialog_active_call_usage" msgid="7858746847946397562">"En cours d\'utilisation par l\'appel téléphonique"</string>
<string name="privacy_dialog_recent_call_usage" msgid="1214810644978167344">"Récemment utilisé lors d\'un appel téléphonique"</string>
<string name="privacy_dialog_active_app_usage" msgid="631997836335929880">"En cours d\'utilisation par <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
- <string name="privacy_dialog_recent_app_usage" msgid="4883417856848222450">"Récemment utilisé par <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
+ <string name="privacy_dialog_recent_app_usage" msgid="4883417856848222450">"Utilisation récente par <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
<string name="privacy_dialog_active_app_usage_1" msgid="9047570143069220911">"En cours d\'utilisation par <xliff:g id="APP_NAME">%1$s</xliff:g> (<xliff:g id="ATTRIBUTION_LABEL">%2$s</xliff:g>)"</string>
<string name="privacy_dialog_recent_app_usage_1" msgid="2551340497722370109">"Récemment utilisé par <xliff:g id="APP_NAME">%1$s</xliff:g> (<xliff:g id="ATTRIBUTION_LABEL">%2$s</xliff:g>)"</string>
<string name="privacy_dialog_active_app_usage_2" msgid="2770926061339921767">"En cours d\'utilisation par <xliff:g id="APP_NAME">%1$s</xliff:g> (<xliff:g id="ATTRIBUTION_LABEL">%2$s</xliff:g> • <xliff:g id="PROXY_LABEL">%3$s</xliff:g>)"</string>
diff --git a/packages/SystemUI/res/values-gl/strings.xml b/packages/SystemUI/res/values-gl/strings.xml
index 3389dd8..27b57e4 100644
--- a/packages/SystemUI/res/values-gl/strings.xml
+++ b/packages/SystemUI/res/values-gl/strings.xml
@@ -57,6 +57,8 @@
<string name="hdmi_cec_set_menu_language_description" msgid="8176716678074126619">"Outro dispositivo solicitou un cambio do idioma do sistema"</string>
<string name="hdmi_cec_set_menu_language_accept" msgid="2513689457281009578">"Cambiar idioma"</string>
<string name="hdmi_cec_set_menu_language_decline" msgid="7650721096558646011">"Manter idioma actual"</string>
+ <!-- no translation found for share_wifi_button_text (1285273973812029240) -->
+ <skip />
<string name="wifi_debugging_title" msgid="7300007687492186076">"Queres permitir a depuración sen fíos nesta rede?"</string>
<string name="wifi_debugging_message" msgid="5461204211731802995">"Nome de rede (SSID)\n<xliff:g id="SSID_0">%1$s</xliff:g>\n\nEnderezo wifi (BSSID)\n<xliff:g id="BSSID_1">%2$s</xliff:g>"</string>
<string name="wifi_debugging_always" msgid="2968383799517975155">"Permitir sempre nesta rede"</string>
@@ -158,6 +160,8 @@
<string name="biometric_dialog_wrong_pattern" msgid="8954812279840889029">"O padrón é incorrecto"</string>
<string name="biometric_dialog_wrong_password" msgid="69477929306843790">"O contrasinal é incorrecto"</string>
<string name="biometric_dialog_credential_too_many_attempts" msgid="3083141271737748716">"Realizáronse demasiados intentos incorrectos.\nTéntao de novo en <xliff:g id="NUMBER">%d</xliff:g> segundos."</string>
+ <!-- no translation found for work_challenge_emergency_button_text (8946588434515599288) -->
+ <skip />
<string name="biometric_dialog_credential_attempts_before_wipe" msgid="6751859711975516999">"Téntao de novo. Intento <xliff:g id="ATTEMPTS_0">%1$d</xliff:g> de <xliff:g id="MAX_ATTEMPTS">%2$d</xliff:g>."</string>
<string name="biometric_dialog_last_attempt_before_wipe_dialog_title" msgid="2874250099278693477">"Eliminaranse os teus datos"</string>
<string name="biometric_dialog_last_pattern_attempt_before_wipe_device" msgid="6562299244825817598">"Se indicas un padrón incorrecto no seguinte intento, eliminaranse os datos deste dispositivo."</string>
@@ -411,6 +415,8 @@
<string name="media_projection_entry_app_permission_dialog_warning_entire_screen" msgid="8736391633234144237">"Cando compartes, gravas ou emites contido, <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> ten acceso a todo o que se vexa na pantalla ou se reproduza no teu dispositivo. Polo tanto, debes ter coidado con determinada información, como os contrasinais, os detalles de pago, as mensaxes e as fotos, así como o contido de audio e de vídeo."</string>
<string name="media_projection_entry_app_permission_dialog_warning_single_app" msgid="5211695779082563959">"Cando compartes, gravas ou emites unha aplicación, <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> ten acceso a todo o que se vexa ou se reproduza nela. Polo tanto, debes ter coidado con determinada información, como os contrasinais, os detalles de pago, as mensaxes e as fotos, así como o contido de audio e de vídeo."</string>
<string name="media_projection_entry_app_permission_dialog_continue" msgid="295463518195075840">"Iniciar"</string>
+ <!-- no translation found for media_projection_entry_app_permission_dialog_single_app_disabled (8999903044874669995) -->
+ <skip />
<string name="media_projection_entry_cast_permission_dialog_title" msgid="8860150223172993547">"Queres comezar a emitir contido?"</string>
<string name="media_projection_entry_cast_permission_dialog_warning_entire_screen" msgid="1986212276016817231">"Cando emites contido, Android ten acceso a todo o que se vexa na pantalla ou se reproduza no teu dispositivo. Polo tanto, debes ter coidado con determinada información, como os contrasinais, os detalles de pago, as mensaxes e as fotos, así como o contido de audio e de vídeo."</string>
<string name="media_projection_entry_cast_permission_dialog_warning_single_app" msgid="9900961380294292">"Cando emites unha aplicación, Android ten acceso a todo o que se vexa ou se reproduza nela. Polo tanto, debes ter coidado con determinada información, como os contrasinais, os detalles de pago, as mensaxes e as fotos, así como o contido de audio e de vídeo."</string>
@@ -865,8 +871,7 @@
<string name="magnification_mode_switch_state_window" msgid="8597100249594076965">"Amplía parte da pantalla"</string>
<string name="magnification_open_settings_click_label" msgid="6151849212725923363">"Abrir configuración da ampliación"</string>
<string name="magnification_close_settings_click_label" msgid="4642477260651704517">"Pechar configuración de ampliación"</string>
- <!-- no translation found for magnification_exit_edit_mode_click_label (1664818325144887117) -->
- <skip />
+ <string name="magnification_exit_edit_mode_click_label" msgid="1664818325144887117">"Saír do modo de edición"</string>
<string name="magnification_drag_corner_to_resize" msgid="1249766311052418130">"Arrastrar a esquina para cambiar o tamaño"</string>
<string name="accessibility_allow_diagonal_scrolling" msgid="3258050349191496398">"Permitir desprazamento diagonal"</string>
<string name="accessibility_resize" msgid="5733759136600611551">"Cambiar tamaño"</string>
diff --git a/packages/SystemUI/res/values-gu/strings.xml b/packages/SystemUI/res/values-gu/strings.xml
index 92ab77f..d62fd99 100644
--- a/packages/SystemUI/res/values-gu/strings.xml
+++ b/packages/SystemUI/res/values-gu/strings.xml
@@ -57,6 +57,8 @@
<string name="hdmi_cec_set_menu_language_description" msgid="8176716678074126619">"બીજા ડિવાઇસ દ્વારા સિસ્ટમની ભાષા બદલવાની વિનંતી કરવામાં આવી છે"</string>
<string name="hdmi_cec_set_menu_language_accept" msgid="2513689457281009578">"ભાષા બદલો"</string>
<string name="hdmi_cec_set_menu_language_decline" msgid="7650721096558646011">"વર્તમાન ભાષા રાખો"</string>
+ <!-- no translation found for share_wifi_button_text (1285273973812029240) -->
+ <skip />
<string name="wifi_debugging_title" msgid="7300007687492186076">"આ નેટવર્ક પર વાયરલેસ ડિબગીંગની મંજૂરી આપીએ?"</string>
<string name="wifi_debugging_message" msgid="5461204211731802995">"નેટવર્કનું નામ (SSID)\n<xliff:g id="SSID_0">%1$s</xliff:g>\n\nવાઇ-ફાઇ ઍડ્રેસ (BSSID)\n<xliff:g id="BSSID_1">%2$s</xliff:g>"</string>
<string name="wifi_debugging_always" msgid="2968383799517975155">"આ નેટવર્ક પર હંમેશા મંજૂરી આપો"</string>
@@ -158,6 +160,8 @@
<string name="biometric_dialog_wrong_pattern" msgid="8954812279840889029">"ખોટી પૅટર્ન"</string>
<string name="biometric_dialog_wrong_password" msgid="69477929306843790">"ખોટો પાસવર્ડ"</string>
<string name="biometric_dialog_credential_too_many_attempts" msgid="3083141271737748716">"ઘણા વધારે ખોટા પ્રયત્નો. \n <xliff:g id="NUMBER">%d</xliff:g> સેકંડમાં ફરી પ્રયાસ કરો."</string>
+ <!-- no translation found for work_challenge_emergency_button_text (8946588434515599288) -->
+ <skip />
<string name="biometric_dialog_credential_attempts_before_wipe" msgid="6751859711975516999">"ફરી પ્રયાસ કરો. <xliff:g id="MAX_ATTEMPTS">%2$d</xliff:g> માંથી <xliff:g id="ATTEMPTS_0">%1$d</xliff:g> પ્રયત્ન."</string>
<string name="biometric_dialog_last_attempt_before_wipe_dialog_title" msgid="2874250099278693477">"તમારો ડેટા ડિલીટ કરવામાં આવશે"</string>
<string name="biometric_dialog_last_pattern_attempt_before_wipe_device" msgid="6562299244825817598">"જો તમે આગલા પ્રયત્નમાં ખોટી પૅટર્ન દાખલ કરશો, તો આ ડિવાઇસનો ડેટા ડિલીટ કરવામાં આવશે."</string>
@@ -411,6 +415,8 @@
<string name="media_projection_entry_app_permission_dialog_warning_entire_screen" msgid="8736391633234144237">"જ્યારે તમે શેર, રેકોર્ડ અથવા કાસ્ટ કરી રહ્યાં હો, ત્યારે તમારી સ્ક્રીન પર દેખાતી હોય કે તમારા ડિવાઇસ પર ચલાવવામાં આવતી હોય તેવી બધી વસ્તુનો ઍક્સેસ <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> પાસે હોય છે. તેથી પાસવર્ડ, ચુકવણીની વિગતો, મેસેજ, ફોટા અને ડિવાઇસ પર વાગી રહેલા ઑડિયો તથા વીડિયો જેવી બાબતોને લઈને સાવચેત રહો."</string>
<string name="media_projection_entry_app_permission_dialog_warning_single_app" msgid="5211695779082563959">"જ્યારે તમે કોઈ ઍપ શેર, રેકોર્ડ અથવા કાસ્ટ કરી રહ્યાં હો, ત્યારે તે ઍપ પર બતાવવામાં કે ચલાવવામાં આવતી હોય તેવી બધી વસ્તુનો ઍક્સેસ <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> પાસે હોય છે. તેથી પાસવર્ડ, ચુકવણીની વિગતો, મેસેજ, ફોટા અને ડિવાઇસ પર વાગી રહેલા ઑડિયો તથા વીડિયો જેવી બાબતોને લઈને સાવચેત રહો."</string>
<string name="media_projection_entry_app_permission_dialog_continue" msgid="295463518195075840">"શરૂ કરો"</string>
+ <!-- no translation found for media_projection_entry_app_permission_dialog_single_app_disabled (8999903044874669995) -->
+ <skip />
<string name="media_projection_entry_cast_permission_dialog_title" msgid="8860150223172993547">"કાસ્ટ કરવાનું શરૂ કરીએ?"</string>
<string name="media_projection_entry_cast_permission_dialog_warning_entire_screen" msgid="1986212276016817231">"જ્યારે તમે કાસ્ટ કરી રહ્યાં હો, ત્યારે તમારી સ્ક્રીન પર દેખાતી કે તમારા ડિવાઇસ પર ચલાવવામાં આવતી બધી વસ્તુઓનો ઍક્સેસ Android પાસે હોય છે. તેથી પાસવર્ડ, ચુકવણીની વિગતો, મેસેજ, ફોટા અને ડિવાઇસ પર વાગી રહેલા ઑડિયો તથા વીડિયો જેવી બાબતોને લઈને સાવચેત રહો."</string>
<string name="media_projection_entry_cast_permission_dialog_warning_single_app" msgid="9900961380294292">"જ્યારે તમે ઍપને કાસ્ટ કરી રહ્યાં હો, ત્યારે તે ઍપ પર બતાવવામાં કે ચલાવવામાં આવતી હોય તેવી બધી વસ્તુનો ઍક્સેસ Android પાસે હોય છે. તેથી પાસવર્ડ, ચુકવણીની વિગતો, મેસેજ, ફોટા અને ડિવાઇસ પર વાગી રહેલા ઑડિયો તથા વીડિયો જેવી બાબતોને લઈને સાવચેત રહો."</string>
@@ -865,8 +871,7 @@
<string name="magnification_mode_switch_state_window" msgid="8597100249594076965">"સ્ક્રીનનો કોઈ ભાગ મોટો કરો"</string>
<string name="magnification_open_settings_click_label" msgid="6151849212725923363">"મોટા કરવાના સેટિંગ ખોલો"</string>
<string name="magnification_close_settings_click_label" msgid="4642477260651704517">"મોટા કરવાના સેટિંગ બંધ કરો"</string>
- <!-- no translation found for magnification_exit_edit_mode_click_label (1664818325144887117) -->
- <skip />
+ <string name="magnification_exit_edit_mode_click_label" msgid="1664818325144887117">"ફેરફાર કરવાના મોડમાંથી બહાર નીકળો"</string>
<string name="magnification_drag_corner_to_resize" msgid="1249766311052418130">"કદ બદલવા માટે ખૂણો ખેંચો"</string>
<string name="accessibility_allow_diagonal_scrolling" msgid="3258050349191496398">"ડાયગોનલ સ્ક્રોલિંગને મંજૂરી આપો"</string>
<string name="accessibility_resize" msgid="5733759136600611551">"કદ બદલો"</string>
diff --git a/packages/SystemUI/res/values-hi/strings.xml b/packages/SystemUI/res/values-hi/strings.xml
index 29f80aa..18774ae 100644
--- a/packages/SystemUI/res/values-hi/strings.xml
+++ b/packages/SystemUI/res/values-hi/strings.xml
@@ -57,6 +57,8 @@
<string name="hdmi_cec_set_menu_language_description" msgid="8176716678074126619">"किसी दूसरे डिवाइस से, सिस्टम की भाषा बदलने का अनुरोध किया गया"</string>
<string name="hdmi_cec_set_menu_language_accept" msgid="2513689457281009578">"भाषा बदलें"</string>
<string name="hdmi_cec_set_menu_language_decline" msgid="7650721096558646011">"मौजूदा भाषा रखें"</string>
+ <!-- no translation found for share_wifi_button_text (1285273973812029240) -->
+ <skip />
<string name="wifi_debugging_title" msgid="7300007687492186076">"क्या आप इस नेटवर्क पर वॉयरलेस डीबगिंग के इस्तेमाल की अनुमति देना चाहते हैं?"</string>
<string name="wifi_debugging_message" msgid="5461204211731802995">"नेटवर्क का नाम (SSID)\n<xliff:g id="SSID_0">%1$s</xliff:g>\n\nवाई-फ़ाई का पता (BSSID)\n<xliff:g id="BSSID_1">%2$s</xliff:g>"</string>
<string name="wifi_debugging_always" msgid="2968383799517975155">"इस नेटवर्क पर हमेशा अनुमति दें"</string>
@@ -158,6 +160,8 @@
<string name="biometric_dialog_wrong_pattern" msgid="8954812279840889029">"गलत पैटर्न"</string>
<string name="biometric_dialog_wrong_password" msgid="69477929306843790">"गलत पासवर्ड"</string>
<string name="biometric_dialog_credential_too_many_attempts" msgid="3083141271737748716">"आपकी कोशिशें बहुत बार गलत हुई हैं.\nआप <xliff:g id="NUMBER">%d</xliff:g> सेकंड में फिर से कोशिश कर सकते हैं."</string>
+ <!-- no translation found for work_challenge_emergency_button_text (8946588434515599288) -->
+ <skip />
<string name="biometric_dialog_credential_attempts_before_wipe" msgid="6751859711975516999">"फिर से कोशिश करें. आप <xliff:g id="MAX_ATTEMPTS">%2$d</xliff:g> में से <xliff:g id="ATTEMPTS_0">%1$d</xliff:g> बार कोशिश कर चुके हैं."</string>
<string name="biometric_dialog_last_attempt_before_wipe_dialog_title" msgid="2874250099278693477">"आपका डेटा मिटा दिया जाएगा"</string>
<string name="biometric_dialog_last_pattern_attempt_before_wipe_device" msgid="6562299244825817598">"अगर आप फिर से गलत पैटर्न डालते हैं, तो इस डिवाइस का डेटा मिटा दिया जाएगा."</string>
@@ -295,8 +299,7 @@
<string name="quick_settings_cellular_detail_data_limit" msgid="1791389609409211628">"<xliff:g id="DATA_LIMIT">%s</xliff:g> सीमा"</string>
<string name="quick_settings_cellular_detail_data_warning" msgid="7957253810481086455">"<xliff:g id="DATA_LIMIT">%s</xliff:g> चेतावनी"</string>
<string name="quick_settings_work_mode_label" msgid="6440531507319809121">"वर्क ऐप्लिकेशन"</string>
- <!-- no translation found for quick_settings_work_mode_paused_state (6681788236383735976) -->
- <skip />
+ <string name="quick_settings_work_mode_paused_state" msgid="6681788236383735976">"रोकी गई"</string>
<string name="quick_settings_night_display_label" msgid="8180030659141778180">"नाइट लाइट"</string>
<string name="quick_settings_night_secondary_label_on_at_sunset" msgid="3358706312129866626">"शाम को चालू की जाएगी"</string>
<string name="quick_settings_night_secondary_label_until_sunrise" msgid="4063448287758262485">"सुबह तक चालू रहेगी"</string>
@@ -412,6 +415,8 @@
<string name="media_projection_entry_app_permission_dialog_warning_entire_screen" msgid="8736391633234144237">"शेयर, रिकॉर्ड या कास्ट करते समय, <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> के पास स्क्रीन पर दिख रहे कॉन्टेंट या डिवाइस पर चल रहे हर मीडिया का ऐक्सेस होता है. इसलिए, पासवर्ड, पेमेंट के तरीके की जानकारी, मैसेज, फ़ोटो, और डिवाइस पर चल रहे ऑडियो और वीडियो को लेकर सावधानी बरतें."</string>
<string name="media_projection_entry_app_permission_dialog_warning_single_app" msgid="5211695779082563959">"किसी ऐप्लिकेशन को शेयर, रिकॉर्ड या कास्ट करते समय, <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> के पास उस ऐप्लिकेशन पर दिख रहे कॉन्टेंट या उस पर चल रहे हर मीडिया का ऐक्सेस होता है. इसलिए, पासवर्ड, पेमेंट के तरीके की जानकारी, मैसेज, फ़ोटो, और डिवाइस पर चल रहे ऑडियो और वीडियो को लेकर सावधानी बरतें."</string>
<string name="media_projection_entry_app_permission_dialog_continue" msgid="295463518195075840">"शुरू करें"</string>
+ <!-- no translation found for media_projection_entry_app_permission_dialog_single_app_disabled (8999903044874669995) -->
+ <skip />
<string name="media_projection_entry_cast_permission_dialog_title" msgid="8860150223172993547">"क्या मीडिया कास्ट करना है?"</string>
<string name="media_projection_entry_cast_permission_dialog_warning_entire_screen" msgid="1986212276016817231">"कास्ट करते समय, Android के पास स्क्रीन पर दिख रहे कॉन्टेंट या डिवाइस पर चल रहे हर मीडिया का ऐक्सेस होता है. इसलिए, पासवर्ड, पेमेंट के तरीके की जानकारी, मैसेज, फ़ोटो, और डिवाइस पर चल रहे ऑडियो और वीडियो को लेकर सावधानी बरतें."</string>
<string name="media_projection_entry_cast_permission_dialog_warning_single_app" msgid="9900961380294292">"किसी ऐप्लिकेशन को कास्ट करते समय, Android के पास उस ऐप्लिकेशन पर दिख रहे कॉन्टेंट या उस पर चल रहे हर मीडिया का ऐक्सेस होता है. इसलिए, पासवर्ड, पेमेंट के तरीके की जानकारी, मैसेज, फ़ोटो, और डिवाइस पर चल रहे ऑडियो और वीडियो को लेकर सावधानी बरतें."</string>
@@ -866,8 +871,7 @@
<string name="magnification_mode_switch_state_window" msgid="8597100249594076965">"स्क्रीन के किसी हिस्से को ज़ूम करें"</string>
<string name="magnification_open_settings_click_label" msgid="6151849212725923363">"ज़ूम करने की सुविधा वाली सेटिंग खोलें"</string>
<string name="magnification_close_settings_click_label" msgid="4642477260651704517">"ज़ूम करने की सुविधा वाली सेटिंग को बंद करें"</string>
- <!-- no translation found for magnification_exit_edit_mode_click_label (1664818325144887117) -->
- <skip />
+ <string name="magnification_exit_edit_mode_click_label" msgid="1664818325144887117">"बदलाव मोड से बाहर निकलें"</string>
<string name="magnification_drag_corner_to_resize" msgid="1249766311052418130">"साइज़ बदलने के लिए, कोने को खींचें और छोड़ें"</string>
<string name="accessibility_allow_diagonal_scrolling" msgid="3258050349191496398">"तिरछी दिशा में स्क्रोल करने की अनुमति दें"</string>
<string name="accessibility_resize" msgid="5733759136600611551">"साइज़ बदलें"</string>
diff --git a/packages/SystemUI/res/values-hr/strings.xml b/packages/SystemUI/res/values-hr/strings.xml
index e58bc76..b684038 100644
--- a/packages/SystemUI/res/values-hr/strings.xml
+++ b/packages/SystemUI/res/values-hr/strings.xml
@@ -57,6 +57,8 @@
<string name="hdmi_cec_set_menu_language_description" msgid="8176716678074126619">"Drugi uređaj zatražio je promjenu jezika sustava"</string>
<string name="hdmi_cec_set_menu_language_accept" msgid="2513689457281009578">"Promijeni jezik"</string>
<string name="hdmi_cec_set_menu_language_decline" msgid="7650721096558646011">"Zadrži trenutačni jezik"</string>
+ <!-- no translation found for share_wifi_button_text (1285273973812029240) -->
+ <skip />
<string name="wifi_debugging_title" msgid="7300007687492186076">"Dopustiti bežično otklanjanje pogrešaka na ovoj mreži?"</string>
<string name="wifi_debugging_message" msgid="5461204211731802995">"Naziv mreže (SSID)\n<xliff:g id="SSID_0">%1$s</xliff:g>\n\nAdresa Wi‑Fija (BSSID)\n<xliff:g id="BSSID_1">%2$s</xliff:g>"</string>
<string name="wifi_debugging_always" msgid="2968383799517975155">"Uvijek dopusti na ovoj mreži"</string>
@@ -158,6 +160,8 @@
<string name="biometric_dialog_wrong_pattern" msgid="8954812279840889029">"Pogrešan uzorak"</string>
<string name="biometric_dialog_wrong_password" msgid="69477929306843790">"Pogrešna zaporka"</string>
<string name="biometric_dialog_credential_too_many_attempts" msgid="3083141271737748716">"Previše netočnih pokušaja.\nPokušajte ponovo za <xliff:g id="NUMBER">%d</xliff:g> s."</string>
+ <!-- no translation found for work_challenge_emergency_button_text (8946588434515599288) -->
+ <skip />
<string name="biometric_dialog_credential_attempts_before_wipe" msgid="6751859711975516999">"Pokušajte ponovo. <xliff:g id="ATTEMPTS_0">%1$d</xliff:g>. pokušaj od <xliff:g id="MAX_ATTEMPTS">%2$d</xliff:g>."</string>
<string name="biometric_dialog_last_attempt_before_wipe_dialog_title" msgid="2874250099278693477">"Vaši će se podaci izbrisati"</string>
<string name="biometric_dialog_last_pattern_attempt_before_wipe_device" msgid="6562299244825817598">"Ako pri sljedećem pokušaju unesete netočan uzorak, izbrisat će se podaci s ovog uređaja."</string>
@@ -411,6 +415,8 @@
<string name="media_projection_entry_app_permission_dialog_warning_entire_screen" msgid="8736391633234144237">"Kad dijelite, snimate ili emitirate, <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> ima pristup svemu što je vidljivo na vašem zaslonu ili se reproducira na vašem uređaju. Stoga pazite na stvari kao što su zaporke, podaci o plaćanju, poruke, fotografije te audio i videozapisi."</string>
<string name="media_projection_entry_app_permission_dialog_warning_single_app" msgid="5211695779082563959">"Kad dijelite, snimate ili emitirate aplikaciju, <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> ima pristup svemu što se prikazuje ili reproducira u toj aplikaciji. Stoga pazite na stvari kao što su zaporke, podaci o plaćanju, poruke, fotografije te audio i videozapisi."</string>
<string name="media_projection_entry_app_permission_dialog_continue" msgid="295463518195075840">"Pokreni"</string>
+ <!-- no translation found for media_projection_entry_app_permission_dialog_single_app_disabled (8999903044874669995) -->
+ <skip />
<string name="media_projection_entry_cast_permission_dialog_title" msgid="8860150223172993547">"Želite li pokrenuti emitiranje?"</string>
<string name="media_projection_entry_cast_permission_dialog_warning_entire_screen" msgid="1986212276016817231">"Kad emitirate, Android ima pristup svemu što je vidljivo na vašem zaslonu ili se reproducira na vašem uređaju. Stoga pazite na stvari kao što su zaporke, podaci o plaćanju, poruke, fotografije te audio i videozapisi."</string>
<string name="media_projection_entry_cast_permission_dialog_warning_single_app" msgid="9900961380294292">"Kad emitirate aplikaciju, Android ima pristup svemu što se prikazuje ili reproducira u toj aplikaciji. Stoga pazite na stvari kao što su zaporke, podaci o plaćanju, poruke, fotografije te audio i videozapisi."</string>
diff --git a/packages/SystemUI/res/values-hu/strings.xml b/packages/SystemUI/res/values-hu/strings.xml
index 49ba82b..26f60f9 100644
--- a/packages/SystemUI/res/values-hu/strings.xml
+++ b/packages/SystemUI/res/values-hu/strings.xml
@@ -57,6 +57,8 @@
<string name="hdmi_cec_set_menu_language_description" msgid="8176716678074126619">"Egy másik eszköz a rendszer nyelvének módosítását kéri"</string>
<string name="hdmi_cec_set_menu_language_accept" msgid="2513689457281009578">"Nyelvmódosítás"</string>
<string name="hdmi_cec_set_menu_language_decline" msgid="7650721096558646011">"Aktuális nyelv megtartása"</string>
+ <!-- no translation found for share_wifi_button_text (1285273973812029240) -->
+ <skip />
<string name="wifi_debugging_title" msgid="7300007687492186076">"Engedélyezi a vezeték nélküli hibakeresést ezen a hálózaton?"</string>
<string name="wifi_debugging_message" msgid="5461204211731802995">"Hálózat neve (SSID):\n<xliff:g id="SSID_0">%1$s</xliff:g>\n\nWi‑Fi-cím (BSSID):\n<xliff:g id="BSSID_1">%2$s</xliff:g>"</string>
<string name="wifi_debugging_always" msgid="2968383799517975155">"Mindig engedélyezze ezen a hálózaton"</string>
@@ -158,6 +160,8 @@
<string name="biometric_dialog_wrong_pattern" msgid="8954812279840889029">"Helytelen minta"</string>
<string name="biometric_dialog_wrong_password" msgid="69477929306843790">"Helytelen jelszó"</string>
<string name="biometric_dialog_credential_too_many_attempts" msgid="3083141271737748716">"Túl sok helytelen próbálkozás.\nPróbálja újra <xliff:g id="NUMBER">%d</xliff:g> másodperc múlva."</string>
+ <!-- no translation found for work_challenge_emergency_button_text (8946588434515599288) -->
+ <skip />
<string name="biometric_dialog_credential_attempts_before_wipe" msgid="6751859711975516999">"Próbálja újra. <xliff:g id="ATTEMPTS_0">%1$d</xliff:g>. kísérlet, összesen: <xliff:g id="MAX_ATTEMPTS">%2$d</xliff:g>."</string>
<string name="biometric_dialog_last_attempt_before_wipe_dialog_title" msgid="2874250099278693477">"Adatai törlődni fognak"</string>
<string name="biometric_dialog_last_pattern_attempt_before_wipe_device" msgid="6562299244825817598">"Amennyiben helytelen mintát ad meg a következő kísérletnél, a rendszer törli az adatokat erről az eszközről."</string>
@@ -411,6 +415,8 @@
<string name="media_projection_entry_app_permission_dialog_warning_entire_screen" msgid="8736391633234144237">"Amikor Ön megosztást, rögzítést vagy átküldést végez, a(z) <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> a képernyőn látható vagy az eszközön lejátszott minden tartalomhoz hozzáfér. Ezért legyen elővigyázatos a jelszavakkal, a fizetési adatokkal, az üzenetekkel, a fotókkal, valamint a hang- és videófelvételekkel."</string>
<string name="media_projection_entry_app_permission_dialog_warning_single_app" msgid="5211695779082563959">"Amikor Ön megoszt, rögzít vagy átküld egy alkalmazást, a(z) <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> az adott appban látható vagy lejátszott minden tartalomhoz hozzáfér. Ezért legyen elővigyázatos a jelszavakkal, a fizetési adatokkal, az üzenetekkel, a fotókkal, valamint a hang- és videófelvételekkel."</string>
<string name="media_projection_entry_app_permission_dialog_continue" msgid="295463518195075840">"Indítás"</string>
+ <!-- no translation found for media_projection_entry_app_permission_dialog_single_app_disabled (8999903044874669995) -->
+ <skip />
<string name="media_projection_entry_cast_permission_dialog_title" msgid="8860150223172993547">"Elindítja az átküldést?"</string>
<string name="media_projection_entry_cast_permission_dialog_warning_entire_screen" msgid="1986212276016817231">"Amikor Ön átküldést végez, az Android a képernyőn látható vagy az eszközön lejátszott minden tartalomhoz hozzáfér. Ezért legyen elővigyázatos a jelszavakkal, a fizetési adatokkal, az üzenetekkel, a fotókkal, valamint a hang- és videófelvételekkel."</string>
<string name="media_projection_entry_cast_permission_dialog_warning_single_app" msgid="9900961380294292">"Alkalmazás átküldése közben az Android az adott appban látható vagy lejátszott minden tartalomhoz hozzáfér. Ezért legyen elővigyázatos a jelszavakkal, a fizetési adatokkal, az üzenetekkel, a fotókkal, valamint a hang- és videófelvételekkel."</string>
@@ -865,8 +871,7 @@
<string name="magnification_mode_switch_state_window" msgid="8597100249594076965">"Képernyő bizonyos részének nagyítása"</string>
<string name="magnification_open_settings_click_label" msgid="6151849212725923363">"Nagyítási beállítások megnyitása"</string>
<string name="magnification_close_settings_click_label" msgid="4642477260651704517">"Nagyítási beállítások bezárása"</string>
- <!-- no translation found for magnification_exit_edit_mode_click_label (1664818325144887117) -->
- <skip />
+ <string name="magnification_exit_edit_mode_click_label" msgid="1664818325144887117">"Kilépés a szerkesztési módból"</string>
<string name="magnification_drag_corner_to_resize" msgid="1249766311052418130">"Az átméretezéshez húzza a kívánt sarkot"</string>
<string name="accessibility_allow_diagonal_scrolling" msgid="3258050349191496398">"Átlós görgetés engedélyezése"</string>
<string name="accessibility_resize" msgid="5733759136600611551">"Átméretezés"</string>
diff --git a/packages/SystemUI/res/values-hy/strings.xml b/packages/SystemUI/res/values-hy/strings.xml
index 7b72456..da38ae1 100644
--- a/packages/SystemUI/res/values-hy/strings.xml
+++ b/packages/SystemUI/res/values-hy/strings.xml
@@ -57,6 +57,8 @@
<string name="hdmi_cec_set_menu_language_description" msgid="8176716678074126619">"Մեկ այլ սարք համակարգի լեզվի փոփոխության հարցում է ուղարկել"</string>
<string name="hdmi_cec_set_menu_language_accept" msgid="2513689457281009578">"Փոխել լեզուն"</string>
<string name="hdmi_cec_set_menu_language_decline" msgid="7650721096558646011">"Թողնել ընթացիկ լեզուն"</string>
+ <!-- no translation found for share_wifi_button_text (1285273973812029240) -->
+ <skip />
<string name="wifi_debugging_title" msgid="7300007687492186076">"Թույլատրե՞լ անլար վրիպազերծումն այս ցանցում"</string>
<string name="wifi_debugging_message" msgid="5461204211731802995">"Ցանցի անվանումը (SSID)\n<xliff:g id="SSID_0">%1$s</xliff:g>\n\nWi‑Fi-ի հասցեն (BSSID)\n<xliff:g id="BSSID_1">%2$s</xliff:g>"</string>
<string name="wifi_debugging_always" msgid="2968383799517975155">"Միշտ թույլատրել այս ցանցում"</string>
@@ -158,6 +160,8 @@
<string name="biometric_dialog_wrong_pattern" msgid="8954812279840889029">"Նախշը սխալ է"</string>
<string name="biometric_dialog_wrong_password" msgid="69477929306843790">"Գաղտնաբառը սխալ է"</string>
<string name="biometric_dialog_credential_too_many_attempts" msgid="3083141271737748716">"Չափից շատ սխալ փորձ է կատարվել:\nՆորից փորձեք <xliff:g id="NUMBER">%d</xliff:g> վայրկյանից:"</string>
+ <!-- no translation found for work_challenge_emergency_button_text (8946588434515599288) -->
+ <skip />
<string name="biometric_dialog_credential_attempts_before_wipe" msgid="6751859711975516999">"Փորձեք նորից։ Փորձ <xliff:g id="ATTEMPTS_0">%1$d</xliff:g>՝ <xliff:g id="MAX_ATTEMPTS">%2$d</xliff:g>-ից։"</string>
<string name="biometric_dialog_last_attempt_before_wipe_dialog_title" msgid="2874250099278693477">"Ձեր տվյալները կջնջվեն"</string>
<string name="biometric_dialog_last_pattern_attempt_before_wipe_device" msgid="6562299244825817598">"Հաջորդ փորձի ժամանակ սխալ նախշ մուտքագրելու դեպքում սարքի տվյալները կջնջվեն։"</string>
@@ -411,6 +415,8 @@
<string name="media_projection_entry_app_permission_dialog_warning_entire_screen" msgid="8736391633234144237">"Երբ դուք ցուցադրում, տեսագրում կամ հեռարձակում եք էկրանը, <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> հավելվածին հասանելի է լինում այն ամենը, ինչ տեսանելի է ձեր էկրանին և նվագարկվում է ձեր սարքում։ Ուստի ուշադիր եղեք այնպիսի բաների հետ, ինչպիսիք են գաղտնաբառերը, վճարային տվյալները, հաղորդագրությունները, լուսանկարները, աուդիո և վիդեո բովանդակությունը։"</string>
<string name="media_projection_entry_app_permission_dialog_warning_single_app" msgid="5211695779082563959">"Երբ դուք ցուցադրում, տեսագրում կամ հեռարձակում եք որևէ հավելվածի էկրանը, <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> հավելվածին հասանելի է լինում այն ամենը, ինչ ցուցադրվում կամ նվագարկվում է այդ հավելվածում։ Ուստի ուշադիր եղեք այնպիսի բաների հետ, ինչպիսիք են գաղտնաբառերը, վճարային տվյալները, հաղորդագրությունները, լուսանկարները, աուդիո և վիդեո բովանդակությունը։"</string>
<string name="media_projection_entry_app_permission_dialog_continue" msgid="295463518195075840">"Սկսել"</string>
+ <!-- no translation found for media_projection_entry_app_permission_dialog_single_app_disabled (8999903044874669995) -->
+ <skip />
<string name="media_projection_entry_cast_permission_dialog_title" msgid="8860150223172993547">"Սկսե՞լ հեռարձակումը"</string>
<string name="media_projection_entry_cast_permission_dialog_warning_entire_screen" msgid="1986212276016817231">"Երբ դուք հեռարձակում եք էկրանը, Android-ին հասանելի է լինում այն ամենը, ինչ տեսանելի է ձեր էկրանին կամ նվագարկվում է ձեր սարքում։ Ուստի ուշադիր եղեք այնպիսի բաների հետ, ինչպիսիք են գաղտնաբառերը, վճարային տվյալները, հաղորդագրությունները, լուսանկարները, աուդիո և վիդեո բովանդակությունը։"</string>
<string name="media_projection_entry_cast_permission_dialog_warning_single_app" msgid="9900961380294292">"Երբ դուք տեսագրում եք որևէ հավելվածի էկրանը, Android-ին հասանելի է լինում այն ամենը, ինչ ցուցադրվում կամ նվագարկվում է այդ հավելվածում։ Ուստի ուշադիր եղեք այնպիսի բաների հետ, ինչպիսիք են գաղտնաբառերը, վճարային տվյալները, հաղորդագրությունները, լուսանկարները, աուդիո և վիդեո բովանդակությունը։"</string>
@@ -865,8 +871,7 @@
<string name="magnification_mode_switch_state_window" msgid="8597100249594076965">"Խոշորացնել էկրանի որոշակի հատվածը"</string>
<string name="magnification_open_settings_click_label" msgid="6151849212725923363">"Բացել խոշորացման կարգավորումները"</string>
<string name="magnification_close_settings_click_label" msgid="4642477260651704517">"Փակել խոշորացման կարգավորումները"</string>
- <!-- no translation found for magnification_exit_edit_mode_click_label (1664818325144887117) -->
- <skip />
+ <string name="magnification_exit_edit_mode_click_label" msgid="1664818325144887117">"Դուրս գալ խմբագրման ռեժիմից"</string>
<string name="magnification_drag_corner_to_resize" msgid="1249766311052418130">"Քաշեք անկյունը՝ չափը փոխելու համար"</string>
<string name="accessibility_allow_diagonal_scrolling" msgid="3258050349191496398">"Թույլատրել անկյունագծով ոլորումը"</string>
<string name="accessibility_resize" msgid="5733759136600611551">"Փոխել չափը"</string>
@@ -1182,8 +1187,8 @@
<string name="privacy_dialog_recent_call_usage" msgid="1214810644978167344">"Վերջերս օգտագործվել է հեռախոսազանգի ժամանակ"</string>
<string name="privacy_dialog_active_app_usage" msgid="631997836335929880">"Օգտագործվում է <xliff:g id="APP_NAME">%1$s</xliff:g>-ի կողմից"</string>
<string name="privacy_dialog_recent_app_usage" msgid="4883417856848222450">"Վերջերս օգտագործվել է <xliff:g id="APP_NAME">%1$s</xliff:g> հավելվածի կողմից"</string>
- <string name="privacy_dialog_active_app_usage_1" msgid="9047570143069220911">"Օգտագործվում է <xliff:g id="APP_NAME">%1$s</xliff:g>-ի կողմից (<xliff:g id="ATTRIBUTION_LABEL">%2$s</xliff:g>)"</string>
+ <string name="privacy_dialog_active_app_usage_1" msgid="9047570143069220911">"Օգտագործվում է <xliff:g id="APP_NAME">%1$s</xliff:g>ի կողմից (<xliff:g id="ATTRIBUTION_LABEL">%2$s</xliff:g>)"</string>
<string name="privacy_dialog_recent_app_usage_1" msgid="2551340497722370109">"Վերջերս օգտագործվել է <xliff:g id="APP_NAME">%1$s</xliff:g> հավելվածի կողմից (<xliff:g id="ATTRIBUTION_LABEL">%2$s</xliff:g>)"</string>
- <string name="privacy_dialog_active_app_usage_2" msgid="2770926061339921767">"Օգտագործվում է <xliff:g id="APP_NAME">%1$s</xliff:g>-ի կողմից (<xliff:g id="ATTRIBUTION_LABEL">%2$s</xliff:g> • <xliff:g id="PROXY_LABEL">%3$s</xliff:g>)"</string>
+ <string name="privacy_dialog_active_app_usage_2" msgid="2770926061339921767">"Օգտագործվում է <xliff:g id="APP_NAME">%1$s</xliff:g>ի կողմից (<xliff:g id="ATTRIBUTION_LABEL">%2$s</xliff:g> • <xliff:g id="PROXY_LABEL">%3$s</xliff:g>)"</string>
<string name="privacy_dialog_recent_app_usage_2" msgid="2874689735085367167">"Վերջերս օգտագործվել է <xliff:g id="APP_NAME">%1$s</xliff:g> հավելվածի կողմից (<xliff:g id="ATTRIBUTION_LABEL">%2$s</xliff:g> • <xliff:g id="PROXY_LABEL">%3$s</xliff:g>)"</string>
</resources>
diff --git a/packages/SystemUI/res/values-in/strings.xml b/packages/SystemUI/res/values-in/strings.xml
index fc12a14..320e125 100644
--- a/packages/SystemUI/res/values-in/strings.xml
+++ b/packages/SystemUI/res/values-in/strings.xml
@@ -57,6 +57,8 @@
<string name="hdmi_cec_set_menu_language_description" msgid="8176716678074126619">"Perubahan bahasa sistem diminta oleh perangkat lain"</string>
<string name="hdmi_cec_set_menu_language_accept" msgid="2513689457281009578">"Ubah bahasa"</string>
<string name="hdmi_cec_set_menu_language_decline" msgid="7650721096558646011">"Pertahankan bahasa"</string>
+ <!-- no translation found for share_wifi_button_text (1285273973812029240) -->
+ <skip />
<string name="wifi_debugging_title" msgid="7300007687492186076">"Izinkan proses debug nirkabel di perangkat ini?"</string>
<string name="wifi_debugging_message" msgid="5461204211731802995">"Nama Jaringan (SSID)\n<xliff:g id="SSID_0">%1$s</xliff:g>\n\nAlamat Wi‑Fi (BSSID)\n<xliff:g id="BSSID_1">%2$s</xliff:g>"</string>
<string name="wifi_debugging_always" msgid="2968383799517975155">"Selalu izinkan di jaringan ini"</string>
@@ -158,6 +160,8 @@
<string name="biometric_dialog_wrong_pattern" msgid="8954812279840889029">"Pola salah"</string>
<string name="biometric_dialog_wrong_password" msgid="69477929306843790">"Sandi salah"</string>
<string name="biometric_dialog_credential_too_many_attempts" msgid="3083141271737748716">"Terlalu banyak kesalahan pola.\nCoba lagi dalam <xliff:g id="NUMBER">%d</xliff:g> detik."</string>
+ <!-- no translation found for work_challenge_emergency_button_text (8946588434515599288) -->
+ <skip />
<string name="biometric_dialog_credential_attempts_before_wipe" msgid="6751859711975516999">"Coba lagi. Percobaan <xliff:g id="ATTEMPTS_0">%1$d</xliff:g> dari <xliff:g id="MAX_ATTEMPTS">%2$d</xliff:g>."</string>
<string name="biometric_dialog_last_attempt_before_wipe_dialog_title" msgid="2874250099278693477">"Data akan dihapus"</string>
<string name="biometric_dialog_last_pattern_attempt_before_wipe_device" msgid="6562299244825817598">"Jika Anda memasukkan pola yang salah saat mencoba lagi, data perangkat ini akan dihapus."</string>
@@ -411,6 +415,8 @@
<string name="media_projection_entry_app_permission_dialog_warning_entire_screen" msgid="8736391633234144237">"Jika Anda membagikan, merekam, atau mentransmisikan, <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> akan memiliki akses ke semua hal yang ditampilkan di layar atau yang diputar di perangkat Anda. Jadi, berhati-hatilah saat memasukkan sandi, detail pembayaran, pesan, foto, audio, dan video."</string>
<string name="media_projection_entry_app_permission_dialog_warning_single_app" msgid="5211695779082563959">"Jika Anda membagikan, merekam, atau mentransmisikan suatu aplikasi, <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> akan memiliki akses ke semua hal yang ditampilkan atau yang diputar di aplikasi tersebut. Jadi, berhati-hatilah saat memasukkan sandi, detail pembayaran, pesan, foto, audio dan video."</string>
<string name="media_projection_entry_app_permission_dialog_continue" msgid="295463518195075840">"Mulai"</string>
+ <!-- no translation found for media_projection_entry_app_permission_dialog_single_app_disabled (8999903044874669995) -->
+ <skip />
<string name="media_projection_entry_cast_permission_dialog_title" msgid="8860150223172993547">"Mulai mentransmisikan?"</string>
<string name="media_projection_entry_cast_permission_dialog_warning_entire_screen" msgid="1986212276016817231">"Jika mentransmisikan, Android akan memiliki akses ke semua hal yang ditampilkan di layar atau yang diputar di perangkat Anda. Jadi, berhati-hatilah saat memasukkan sandi, detail pembayaran, pesan, foto, audio, dan video."</string>
<string name="media_projection_entry_cast_permission_dialog_warning_single_app" msgid="9900961380294292">"Jika Anda mentransmisikan aplikasi, Android akan memiliki akses ke semua hal yang ditampilkan atau yang diputar di aplikasi tersebut. Jadi, berhati-hatilah saat memasukkan sandi, detail pembayaran, pesan, foto, audio, dan video."</string>
@@ -865,8 +871,7 @@
<string name="magnification_mode_switch_state_window" msgid="8597100249594076965">"Perbesar sebagian layar"</string>
<string name="magnification_open_settings_click_label" msgid="6151849212725923363">"Buka setelan pembesaran"</string>
<string name="magnification_close_settings_click_label" msgid="4642477260651704517">"Tutup setelan pembesaran"</string>
- <!-- no translation found for magnification_exit_edit_mode_click_label (1664818325144887117) -->
- <skip />
+ <string name="magnification_exit_edit_mode_click_label" msgid="1664818325144887117">"Keluar dari mode edit"</string>
<string name="magnification_drag_corner_to_resize" msgid="1249766311052418130">"Tarik pojok persegi untuk mengubah ukuran"</string>
<string name="accessibility_allow_diagonal_scrolling" msgid="3258050349191496398">"Izinkan scrolling diagonal"</string>
<string name="accessibility_resize" msgid="5733759136600611551">"Ubah ukuran"</string>
diff --git a/packages/SystemUI/res/values-is/strings.xml b/packages/SystemUI/res/values-is/strings.xml
index 2c784a6..73f1745 100644
--- a/packages/SystemUI/res/values-is/strings.xml
+++ b/packages/SystemUI/res/values-is/strings.xml
@@ -57,6 +57,8 @@
<string name="hdmi_cec_set_menu_language_description" msgid="8176716678074126619">"Beiðni frá öðru tæki um að breyta tungumáli kerfis"</string>
<string name="hdmi_cec_set_menu_language_accept" msgid="2513689457281009578">"Breyta tungumáli"</string>
<string name="hdmi_cec_set_menu_language_decline" msgid="7650721096558646011">"Halda núverandi tungumáli"</string>
+ <!-- no translation found for share_wifi_button_text (1285273973812029240) -->
+ <skip />
<string name="wifi_debugging_title" msgid="7300007687492186076">"Viltu leyfa þráðlausa villuleit á þessu neti?"</string>
<string name="wifi_debugging_message" msgid="5461204211731802995">"Heiti netkerfis (SSID)\n<xliff:g id="SSID_0">%1$s</xliff:g>\n\nWi‑Fi vistfang (BSSID)\n<xliff:g id="BSSID_1">%2$s</xliff:g>"</string>
<string name="wifi_debugging_always" msgid="2968383799517975155">"Leyfa alltaf á þessu neti"</string>
@@ -158,6 +160,8 @@
<string name="biometric_dialog_wrong_pattern" msgid="8954812279840889029">"Rangt mynstur"</string>
<string name="biometric_dialog_wrong_password" msgid="69477929306843790">"Rangt aðgangsorð"</string>
<string name="biometric_dialog_credential_too_many_attempts" msgid="3083141271737748716">"Of margar misheppnaðar tilraunir.\nReyndu aftur eftir <xliff:g id="NUMBER">%d</xliff:g> sekúndur."</string>
+ <!-- no translation found for work_challenge_emergency_button_text (8946588434515599288) -->
+ <skip />
<string name="biometric_dialog_credential_attempts_before_wipe" msgid="6751859711975516999">"Reyndu aftur. Tilraun <xliff:g id="ATTEMPTS_0">%1$d</xliff:g> af <xliff:g id="MAX_ATTEMPTS">%2$d</xliff:g>."</string>
<string name="biometric_dialog_last_attempt_before_wipe_dialog_title" msgid="2874250099278693477">"Gögnunum þínum verður eytt"</string>
<string name="biometric_dialog_last_pattern_attempt_before_wipe_device" msgid="6562299244825817598">"Ef þú slærð inn rangt mynstur í næstu tilraun verður gögnum tækisins eytt."</string>
@@ -411,6 +415,8 @@
<string name="media_projection_entry_app_permission_dialog_warning_entire_screen" msgid="8736391633234144237">"Þegar þú deilir, tekur upp eða varpar hefur<xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> aðgang að öllu sem sést á skjánum eða spilast í tækinu. Passaðu því upp á aðgangsorð, greiðsluupplýsingar, skilaboð, myndir, hljóð og myndskeið."</string>
<string name="media_projection_entry_app_permission_dialog_warning_single_app" msgid="5211695779082563959">"Þegar þú deilir, tekur upp eða varpar forriti hefur <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> aðgang að öllu sem sést eða spilast í viðkomandi forriti. Passaðu því upp á aðgangsorð, greiðsluupplýsingar, skilaboð, myndir, hljóð og myndskeið."</string>
<string name="media_projection_entry_app_permission_dialog_continue" msgid="295463518195075840">"Byrja"</string>
+ <!-- no translation found for media_projection_entry_app_permission_dialog_single_app_disabled (8999903044874669995) -->
+ <skip />
<string name="media_projection_entry_cast_permission_dialog_title" msgid="8860150223172993547">"Byrja að varpa?"</string>
<string name="media_projection_entry_cast_permission_dialog_warning_entire_screen" msgid="1986212276016817231">"Þegar þú varpar hefur Android aðgang að öllu sem er sýnilegt á skjánum hjá þér eða spilast í tækinu þínu. Passaðu því upp á aðgangsorð, greiðsluupplýsingar, skilaboð, myndir, hljóð og myndskeið."</string>
<string name="media_projection_entry_cast_permission_dialog_warning_single_app" msgid="9900961380294292">"Þegar þú varpar forriti hefur Android aðgang að öllu sem sést eða spilast í viðkomandi forriti. Passaðu því upp á aðgangsorð, greiðsluupplýsingar, skilaboð, myndir, hljóð og myndskeið."</string>
@@ -865,8 +871,7 @@
<string name="magnification_mode_switch_state_window" msgid="8597100249594076965">"Stækka hluta skjásins"</string>
<string name="magnification_open_settings_click_label" msgid="6151849212725923363">"Opna stillingar stækkunar"</string>
<string name="magnification_close_settings_click_label" msgid="4642477260651704517">"Loka stillingum stækkunar"</string>
- <!-- no translation found for magnification_exit_edit_mode_click_label (1664818325144887117) -->
- <skip />
+ <string name="magnification_exit_edit_mode_click_label" msgid="1664818325144887117">"Loka breytingastillingu"</string>
<string name="magnification_drag_corner_to_resize" msgid="1249766311052418130">"Dragðu horn til að breyta stærð"</string>
<string name="accessibility_allow_diagonal_scrolling" msgid="3258050349191496398">"Leyfa skáflettingu"</string>
<string name="accessibility_resize" msgid="5733759136600611551">"Breyta stærð"</string>
diff --git a/packages/SystemUI/res/values-it/strings.xml b/packages/SystemUI/res/values-it/strings.xml
index 7c22297..0db6d7f 100644
--- a/packages/SystemUI/res/values-it/strings.xml
+++ b/packages/SystemUI/res/values-it/strings.xml
@@ -57,6 +57,8 @@
<string name="hdmi_cec_set_menu_language_description" msgid="8176716678074126619">"Cambio della lingua di sistema richiesto da un altro dispositivo"</string>
<string name="hdmi_cec_set_menu_language_accept" msgid="2513689457281009578">"Cambia lingua"</string>
<string name="hdmi_cec_set_menu_language_decline" msgid="7650721096558646011">"Mantieni lingua attuale"</string>
+ <!-- no translation found for share_wifi_button_text (1285273973812029240) -->
+ <skip />
<string name="wifi_debugging_title" msgid="7300007687492186076">"Consentire il debug wireless su questa rete?"</string>
<string name="wifi_debugging_message" msgid="5461204211731802995">"Nome della rete (SSID)\n<xliff:g id="SSID_0">%1$s</xliff:g>\n\nIndirizzo Wi‑Fi (BSSID)\n<xliff:g id="BSSID_1">%2$s</xliff:g>"</string>
<string name="wifi_debugging_always" msgid="2968383799517975155">"Consenti sempre su questa rete"</string>
@@ -158,6 +160,8 @@
<string name="biometric_dialog_wrong_pattern" msgid="8954812279840889029">"Sequenza errata"</string>
<string name="biometric_dialog_wrong_password" msgid="69477929306843790">"Password errata"</string>
<string name="biometric_dialog_credential_too_many_attempts" msgid="3083141271737748716">"Troppi tentativi errati.\nRiprova tra <xliff:g id="NUMBER">%d</xliff:g> secondi."</string>
+ <!-- no translation found for work_challenge_emergency_button_text (8946588434515599288) -->
+ <skip />
<string name="biometric_dialog_credential_attempts_before_wipe" msgid="6751859711975516999">"Riprova. Tentativo <xliff:g id="ATTEMPTS_0">%1$d</xliff:g> di <xliff:g id="MAX_ATTEMPTS">%2$d</xliff:g>."</string>
<string name="biometric_dialog_last_attempt_before_wipe_dialog_title" msgid="2874250099278693477">"I tuoi dati verranno eliminati"</string>
<string name="biometric_dialog_last_pattern_attempt_before_wipe_device" msgid="6562299244825817598">"Se al prossimo tentativo inserirai una sequenza sbagliata, i dati del dispositivo verranno eliminati."</string>
@@ -411,6 +415,8 @@
<string name="media_projection_entry_app_permission_dialog_warning_entire_screen" msgid="8736391633234144237">"Quando condividi, registri o trasmetti, <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> ha accesso a qualsiasi elemento visibile sul tuo schermo o in riproduzione sul tuo dispositivo. Presta quindi attenzione a password, dettagli sui pagamenti, messaggi, foto, audio e video."</string>
<string name="media_projection_entry_app_permission_dialog_warning_single_app" msgid="5211695779082563959">"Quando condividi, registri o trasmetti un\'app, <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> ha accesso a qualsiasi elemento visualizzato o riprodotto sull\'app. Presta quindi attenzione a password, dettagli sui pagamenti, messaggi, foto, audio e video."</string>
<string name="media_projection_entry_app_permission_dialog_continue" msgid="295463518195075840">"Inizia"</string>
+ <!-- no translation found for media_projection_entry_app_permission_dialog_single_app_disabled (8999903044874669995) -->
+ <skip />
<string name="media_projection_entry_cast_permission_dialog_title" msgid="8860150223172993547">"Iniziare a trasmettere?"</string>
<string name="media_projection_entry_cast_permission_dialog_warning_entire_screen" msgid="1986212276016817231">"Quando trasmetti, Android ha accesso a qualsiasi elemento visibile sul tuo schermo o in riproduzione sul tuo dispositivo. Presta quindi attenzione a password, dettagli sui pagamenti, messaggi, foto, audio e video."</string>
<string name="media_projection_entry_cast_permission_dialog_warning_single_app" msgid="9900961380294292">"Quando trasmetti un\'app, Android ha accesso a qualsiasi elemento visualizzato o riprodotto sull\'app. Presta quindi attenzione a password, dettagli sui pagamenti, messaggi, foto, audio e video."</string>
@@ -865,8 +871,7 @@
<string name="magnification_mode_switch_state_window" msgid="8597100249594076965">"Ingrandisci parte dello schermo"</string>
<string name="magnification_open_settings_click_label" msgid="6151849212725923363">"Apri le impostazioni di ingrandimento"</string>
<string name="magnification_close_settings_click_label" msgid="4642477260651704517">"Chiudi impostazioni di ingrandimento"</string>
- <!-- no translation found for magnification_exit_edit_mode_click_label (1664818325144887117) -->
- <skip />
+ <string name="magnification_exit_edit_mode_click_label" msgid="1664818325144887117">"Esci dalla modalità di modifica"</string>
<string name="magnification_drag_corner_to_resize" msgid="1249766311052418130">"Trascina l\'angolo per ridimensionare"</string>
<string name="accessibility_allow_diagonal_scrolling" msgid="3258050349191496398">"Scorrimento diagonale"</string>
<string name="accessibility_resize" msgid="5733759136600611551">"Ridimensiona"</string>
@@ -1168,7 +1173,7 @@
<string name="assistant_attention_content_description" msgid="6830215897604642875">"L\'assistente è attivo"</string>
<string name="set_default_notes_app_toast_content" msgid="2812374329662610753">"Imposta l\'app per le note predefinita nelle Impostazioni"</string>
<string name="install_app" msgid="5066668100199613936">"Installa app"</string>
- <string name="privacy_dialog_title" msgid="7839968133469098311">"Microfono e videocamera"</string>
+ <string name="privacy_dialog_title" msgid="7839968133469098311">"Microfono e fotocamera"</string>
<string name="privacy_dialog_summary" msgid="2458769652125995409">"Uso recente da app"</string>
<string name="privacy_dialog_more_button" msgid="7610604080293562345">"Vedi accesso recente"</string>
<string name="privacy_dialog_done_button" msgid="4504330708531434263">"Fine"</string>
diff --git a/packages/SystemUI/res/values-iw/strings.xml b/packages/SystemUI/res/values-iw/strings.xml
index f1dd497..0539635 100644
--- a/packages/SystemUI/res/values-iw/strings.xml
+++ b/packages/SystemUI/res/values-iw/strings.xml
@@ -57,6 +57,8 @@
<string name="hdmi_cec_set_menu_language_description" msgid="8176716678074126619">"התקבלה בקשה ממכשיר אחר לשינוי שפת המערכת"</string>
<string name="hdmi_cec_set_menu_language_accept" msgid="2513689457281009578">"שינוי שפה"</string>
<string name="hdmi_cec_set_menu_language_decline" msgid="7650721096558646011">"השארת השפה הנוכחית"</string>
+ <!-- no translation found for share_wifi_button_text (1285273973812029240) -->
+ <skip />
<string name="wifi_debugging_title" msgid="7300007687492186076">"לאשר ניפוי באגים אלחוטי ברשת הזו?"</string>
<string name="wifi_debugging_message" msgid="5461204211731802995">"שם הרשת (SSID)\n<xliff:g id="SSID_0">%1$s</xliff:g>\n\nכתובת Wi‑Fi (BSSID)\n<xliff:g id="BSSID_1">%2$s</xliff:g>"</string>
<string name="wifi_debugging_always" msgid="2968383799517975155">"לאשר תמיד ברשת הזו"</string>
@@ -158,6 +160,8 @@
<string name="biometric_dialog_wrong_pattern" msgid="8954812279840889029">"קו ביטול נעילה שגוי"</string>
<string name="biometric_dialog_wrong_password" msgid="69477929306843790">"סיסמה שגויה"</string>
<string name="biometric_dialog_credential_too_many_attempts" msgid="3083141271737748716">"יותר מדי ניסיונות שגויים.\nיש לנסות שוב בעוד <xliff:g id="NUMBER">%d</xliff:g> שניות."</string>
+ <!-- no translation found for work_challenge_emergency_button_text (8946588434515599288) -->
+ <skip />
<string name="biometric_dialog_credential_attempts_before_wipe" msgid="6751859711975516999">"יש לנסות שוב. ניסיון <xliff:g id="ATTEMPTS_0">%1$d</xliff:g> מתוך <xliff:g id="MAX_ATTEMPTS">%2$d</xliff:g>."</string>
<string name="biometric_dialog_last_attempt_before_wipe_dialog_title" msgid="2874250099278693477">"הנתונים שלך יימחקו"</string>
<string name="biometric_dialog_last_pattern_attempt_before_wipe_device" msgid="6562299244825817598">"הזנת קו ביטול נעילה שגוי בניסיון הבא תגרום למחיקת הנתונים במכשיר."</string>
@@ -411,6 +415,8 @@
<string name="media_projection_entry_app_permission_dialog_warning_entire_screen" msgid="8736391633234144237">"בזמן שיתוף, הקלטה או העברה (cast) תהיה ל-<xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> גישה לכל הפרטים שגלויים במסך שלך או מופעלים מהמכשיר שלך. מומלץ להיזהר עם סיסמאות, פרטי תשלום, הודעות, תמונות, אודיו וסרטונים."</string>
<string name="media_projection_entry_app_permission_dialog_warning_single_app" msgid="5211695779082563959">"בזמן שיתוף, הקלטה או העברה (cast) של אפליקציה, תהיה ל-<xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> גישה לכל מה שגלוי באפליקציה או מופעל מהאפליקציה. מומלץ להיזהר עם סיסמאות, פרטי תשלום, הודעות, תמונות, אודיו וסרטונים."</string>
<string name="media_projection_entry_app_permission_dialog_continue" msgid="295463518195075840">"התחלה"</string>
+ <!-- no translation found for media_projection_entry_app_permission_dialog_single_app_disabled (8999903044874669995) -->
+ <skip />
<string name="media_projection_entry_cast_permission_dialog_title" msgid="8860150223172993547">"להתחיל את ההעברה?"</string>
<string name="media_projection_entry_cast_permission_dialog_warning_entire_screen" msgid="1986212276016817231">"בזמן העברה (cast), תהיה ל-Android גישה לכל הפרטים שגלויים במסך שלך או מופעלים מהמכשיר שלך. מומלץ להיזהר עם סיסמאות, פרטי תשלום, הודעות, תמונות, אודיו וסרטונים."</string>
<string name="media_projection_entry_cast_permission_dialog_warning_single_app" msgid="9900961380294292">"בזמן העברה (cast) של אפליקציה, תהיה ל-Android גישה לכל מה שגלוי באפליקציה או מופעל מהאפליקציה. כדאי להיזהר עם סיסמאות, פרטי תשלום, הודעות, תמונות, אודיו וסרטונים."</string>
@@ -865,8 +871,7 @@
<string name="magnification_mode_switch_state_window" msgid="8597100249594076965">"הגדלת חלק מהמסך"</string>
<string name="magnification_open_settings_click_label" msgid="6151849212725923363">"פתיחת הגדרות ההגדלה"</string>
<string name="magnification_close_settings_click_label" msgid="4642477260651704517">"סגירת הגדרות ההגדלה"</string>
- <!-- no translation found for magnification_exit_edit_mode_click_label (1664818325144887117) -->
- <skip />
+ <string name="magnification_exit_edit_mode_click_label" msgid="1664818325144887117">"יציאה ממצב עריכה"</string>
<string name="magnification_drag_corner_to_resize" msgid="1249766311052418130">"צריך לגרור את הפינה כדי לשנות את הגודל"</string>
<string name="accessibility_allow_diagonal_scrolling" msgid="3258050349191496398">"הפעלת גלילה באלכסון"</string>
<string name="accessibility_resize" msgid="5733759136600611551">"שינוי גודל"</string>
@@ -1169,7 +1174,7 @@
<string name="set_default_notes_app_toast_content" msgid="2812374329662610753">"צריך להגדיר את אפליקציית ברירת המחדל לפתקים ב\'הגדרות\'"</string>
<string name="install_app" msgid="5066668100199613936">"התקנת האפליקציה"</string>
<string name="privacy_dialog_title" msgid="7839968133469098311">"מיקרופון ומצלמה"</string>
- <string name="privacy_dialog_summary" msgid="2458769652125995409">"נעשה שימוש לאחרונה באפליקציה"</string>
+ <string name="privacy_dialog_summary" msgid="2458769652125995409">"נעשה שימוש לאחרונה באפליקציות"</string>
<string name="privacy_dialog_more_button" msgid="7610604080293562345">"צפייה בהרשאות הגישה האחרונות"</string>
<string name="privacy_dialog_done_button" msgid="4504330708531434263">"סיום"</string>
<string name="privacy_dialog_expand_action" msgid="9129262348628331377">"הרחבה והצגת האפשרויות"</string>
diff --git a/packages/SystemUI/res/values-ja/strings.xml b/packages/SystemUI/res/values-ja/strings.xml
index 5d3919e..967d2d1 100644
--- a/packages/SystemUI/res/values-ja/strings.xml
+++ b/packages/SystemUI/res/values-ja/strings.xml
@@ -57,6 +57,8 @@
<string name="hdmi_cec_set_menu_language_description" msgid="8176716678074126619">"他のデバイスからシステム言語の変更が要求されました"</string>
<string name="hdmi_cec_set_menu_language_accept" msgid="2513689457281009578">"言語を変更"</string>
<string name="hdmi_cec_set_menu_language_decline" msgid="7650721096558646011">"現在の言語を変更しない"</string>
+ <!-- no translation found for share_wifi_button_text (1285273973812029240) -->
+ <skip />
<string name="wifi_debugging_title" msgid="7300007687492186076">"このネットワークでワイヤレス デバッグを許可しますか?"</string>
<string name="wifi_debugging_message" msgid="5461204211731802995">"ネットワーク名(SSID)\n<xliff:g id="SSID_0">%1$s</xliff:g>\n\nWi‑Fi アドレス(BSSID)\n<xliff:g id="BSSID_1">%2$s</xliff:g>"</string>
<string name="wifi_debugging_always" msgid="2968383799517975155">"このネットワークで常に許可する"</string>
@@ -158,6 +160,8 @@
<string name="biometric_dialog_wrong_pattern" msgid="8954812279840889029">"パターンが正しくありません"</string>
<string name="biometric_dialog_wrong_password" msgid="69477929306843790">"パスワードが正しくありません"</string>
<string name="biometric_dialog_credential_too_many_attempts" msgid="3083141271737748716">"間違えた回数が上限を超えました。\n<xliff:g id="NUMBER">%d</xliff:g> 秒後にもう一度お試しください。"</string>
+ <!-- no translation found for work_challenge_emergency_button_text (8946588434515599288) -->
+ <skip />
<string name="biometric_dialog_credential_attempts_before_wipe" msgid="6751859711975516999">"もう一度お試しください。入力回数: <xliff:g id="ATTEMPTS_0">%1$d</xliff:g>/<xliff:g id="MAX_ATTEMPTS">%2$d</xliff:g> 回"</string>
<string name="biometric_dialog_last_attempt_before_wipe_dialog_title" msgid="2874250099278693477">"データが削除されます"</string>
<string name="biometric_dialog_last_pattern_attempt_before_wipe_device" msgid="6562299244825817598">"パターンをあと 1 回間違えると、このデバイスのデータが削除されます。"</string>
@@ -411,6 +415,8 @@
<string name="media_projection_entry_app_permission_dialog_warning_entire_screen" msgid="8736391633234144237">"共有、録画、キャスト中は、画面に表示される内容やデバイスで再生される内容に <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> がアクセスできるため、パスワード、お支払いの詳細、メッセージ、写真、音声、動画などの情報にご注意ください。"</string>
<string name="media_projection_entry_app_permission_dialog_warning_single_app" msgid="5211695779082563959">"アプリの共有、録画、キャスト中は、そのアプリで表示または再生される内容に <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> がアクセスできるため、パスワード、お支払いの詳細、メッセージ、写真、音声、動画などの情報にご注意ください。"</string>
<string name="media_projection_entry_app_permission_dialog_continue" msgid="295463518195075840">"開始"</string>
+ <!-- no translation found for media_projection_entry_app_permission_dialog_single_app_disabled (8999903044874669995) -->
+ <skip />
<string name="media_projection_entry_cast_permission_dialog_title" msgid="8860150223172993547">"キャスト開始しますか?"</string>
<string name="media_projection_entry_cast_permission_dialog_warning_entire_screen" msgid="1986212276016817231">"キャスト中は、画面に表示される内容やデバイスで再生される内容に Android がアクセスできるため、パスワード、お支払いの詳細、メッセージ、写真、音声、動画などの情報にご注意ください。"</string>
<string name="media_projection_entry_cast_permission_dialog_warning_single_app" msgid="9900961380294292">"アプリのキャスト中は、そのアプリで表示または再生される内容に Android がアクセスできるため、パスワード、お支払いの詳細、メッセージ、写真、音声、動画などの情報にご注意ください。"</string>
diff --git a/packages/SystemUI/res/values-ka/strings.xml b/packages/SystemUI/res/values-ka/strings.xml
index daa0ae7..2a30da7 100644
--- a/packages/SystemUI/res/values-ka/strings.xml
+++ b/packages/SystemUI/res/values-ka/strings.xml
@@ -57,6 +57,8 @@
<string name="hdmi_cec_set_menu_language_description" msgid="8176716678074126619">"სისტემის ენის შეცვლა მოითხოვა სხვა მოწყობილობამ"</string>
<string name="hdmi_cec_set_menu_language_accept" msgid="2513689457281009578">"ენის შეცვლა"</string>
<string name="hdmi_cec_set_menu_language_decline" msgid="7650721096558646011">"მიმდინარე ენის დატოვება"</string>
+ <!-- no translation found for share_wifi_button_text (1285273973812029240) -->
+ <skip />
<string name="wifi_debugging_title" msgid="7300007687492186076">"დაუშვებთ ამ ქსელში შეცდომების უსადენო გამართვას?"</string>
<string name="wifi_debugging_message" msgid="5461204211731802995">"ქსელის სახელი (SSID)\n<xliff:g id="SSID_0">%1$s</xliff:g>\n\nWi‑Fi მისამართი (BSSID)\n<xliff:g id="BSSID_1">%2$s</xliff:g>"</string>
<string name="wifi_debugging_always" msgid="2968383799517975155">"ყოველთვის დაშვება ამ ქსელში"</string>
@@ -158,6 +160,8 @@
<string name="biometric_dialog_wrong_pattern" msgid="8954812279840889029">"ნიმუში არასწორია"</string>
<string name="biometric_dialog_wrong_password" msgid="69477929306843790">"პაროლი არასწორია"</string>
<string name="biometric_dialog_credential_too_many_attempts" msgid="3083141271737748716">"დაფიქსირდა ძალიან ბევრი არასწორი მცდელობა.\nცადეთ ხელახლა <xliff:g id="NUMBER">%d</xliff:g> წამში."</string>
+ <!-- no translation found for work_challenge_emergency_button_text (8946588434515599288) -->
+ <skip />
<string name="biometric_dialog_credential_attempts_before_wipe" msgid="6751859711975516999">"ცადეთ ხელახლა. მცდელობა <xliff:g id="ATTEMPTS_0">%1$d</xliff:g> / <xliff:g id="MAX_ATTEMPTS">%2$d</xliff:g>-დან."</string>
<string name="biometric_dialog_last_attempt_before_wipe_dialog_title" msgid="2874250099278693477">"თქვენი მონაცემები წაიშლება"</string>
<string name="biometric_dialog_last_pattern_attempt_before_wipe_device" msgid="6562299244825817598">"შემდეგი მცდელობისას განმბლოკავი ნიმუშის არასწორად შეყვანის შემთხვევაში, ამ მოწყობილობის მონაცემები წაიშლება."</string>
@@ -411,6 +415,8 @@
<string name="media_projection_entry_app_permission_dialog_warning_entire_screen" msgid="8736391633234144237">"გაზიარების, ჩაწერის ან ტრანსლირების დროს, <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g>-ს აქვს წვდომა ყველაფერზე, რაც ჩანს თქვენს ეკრანზე ან უკრავს თქვენს მოწყობილობაზე. ამიტომ იყავით ფრთხილად ისეთ ინფორმაციასთან, როგორიცაა პაროლები, გადახდის დეტალები, შეტყობინებები, ფოტოები, აუდიო და ვიდეო."</string>
<string name="media_projection_entry_app_permission_dialog_warning_single_app" msgid="5211695779082563959">"აპის გაზიარებისას, ჩაწერისას ან ტრანსლირებისას, <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g>-ს წვდომა აქვს ყველაფერზე, რაც ჩანს ან იკვრება აპში. ამიტომ იყავით ფრთხილად ისეთ ინფორმაციასთან, როგორიცაა პაროლები, გადახდის დეტალები, შეტყობინებები, ფოტოები, აუდიო და ვიდეო."</string>
<string name="media_projection_entry_app_permission_dialog_continue" msgid="295463518195075840">"დაწყება"</string>
+ <!-- no translation found for media_projection_entry_app_permission_dialog_single_app_disabled (8999903044874669995) -->
+ <skip />
<string name="media_projection_entry_cast_permission_dialog_title" msgid="8860150223172993547">"გსურთ ტრანსლირების დაწყება?"</string>
<string name="media_projection_entry_cast_permission_dialog_warning_entire_screen" msgid="1986212276016817231">"როდესაც თქვენ ტრანსლირებთ, ამ აპს აქვს წვდომა ყველაფერზე, რაც ჩანს თქვენს ეკრანზე ან უკრავს თქვენს მოწყობილობაზე. ამიტომ იყავით ფრთხილად ისეთ ინფორმაციასთან, როგორიცაა პაროლები, გადახდის დეტალები, შეტყობინებები, ფოტოები, აუდიო და ვიდეო."</string>
<string name="media_projection_entry_cast_permission_dialog_warning_single_app" msgid="9900961380294292">"როდესაც აპს ტრანსლირებთ, Android-ს წვდომა აქვს ყველაფერზე, რაც ჩანს ან იკვრება აპში. ამიტომ იყავით ფრთხილად ისეთ ინფორმაციასთან, როგორიცაა პაროლები, გადახდის დეტალები, შეტყობინებები, ფოტოები, აუდიო და ვიდეო."</string>
@@ -1169,7 +1175,7 @@
<string name="install_app" msgid="5066668100199613936">"აპის ინსტალაცია"</string>
<string name="privacy_dialog_title" msgid="7839968133469098311">"მიკროფონი და კამერა"</string>
<string name="privacy_dialog_summary" msgid="2458769652125995409">"აპის ბოლოდროინდელი გამოყენება"</string>
- <string name="privacy_dialog_more_button" msgid="7610604080293562345">"ბოლოდროინდელი წვდომის ნახვა"</string>
+ <string name="privacy_dialog_more_button" msgid="7610604080293562345">"ბოლო წვდომის ნახვა"</string>
<string name="privacy_dialog_done_button" msgid="4504330708531434263">"მზადაა"</string>
<string name="privacy_dialog_expand_action" msgid="9129262348628331377">"გაფართოება და ვარიანტების ჩვენება"</string>
<string name="privacy_dialog_collapse_action" msgid="277419962019466347">"ჩაკეცვა"</string>
diff --git a/packages/SystemUI/res/values-kk/strings.xml b/packages/SystemUI/res/values-kk/strings.xml
index 18f7855..1adda91 100644
--- a/packages/SystemUI/res/values-kk/strings.xml
+++ b/packages/SystemUI/res/values-kk/strings.xml
@@ -57,6 +57,8 @@
<string name="hdmi_cec_set_menu_language_description" msgid="8176716678074126619">"Басқа құрылғыдан жүйе тілін өзгерту туралы сұрау жіберілді."</string>
<string name="hdmi_cec_set_menu_language_accept" msgid="2513689457281009578">"Тілді өзгерту"</string>
<string name="hdmi_cec_set_menu_language_decline" msgid="7650721096558646011">"Қазіргі тіл тұра берсін"</string>
+ <!-- no translation found for share_wifi_button_text (1285273973812029240) -->
+ <skip />
<string name="wifi_debugging_title" msgid="7300007687492186076">"Бұл желіде сымсыз түзетуге рұқсат етілсін бе?"</string>
<string name="wifi_debugging_message" msgid="5461204211731802995">"Желі атауы (SSID)\n<xliff:g id="SSID_0">%1$s</xliff:g>\n\nWi‑Fi мекенжайы (BSSID)\n<xliff:g id="BSSID_1">%2$s</xliff:g>"</string>
<string name="wifi_debugging_always" msgid="2968383799517975155">"Осы желіде үнемі рұқсат ету"</string>
@@ -158,6 +160,8 @@
<string name="biometric_dialog_wrong_pattern" msgid="8954812279840889029">"Өрнек қате."</string>
<string name="biometric_dialog_wrong_password" msgid="69477929306843790">"Құпия сөз қате."</string>
<string name="biometric_dialog_credential_too_many_attempts" msgid="3083141271737748716">"Тым көп қате енгізілді.\n<xliff:g id="NUMBER">%d</xliff:g> секундта әрекетті қайталаңыз."</string>
+ <!-- no translation found for work_challenge_emergency_button_text (8946588434515599288) -->
+ <skip />
<string name="biometric_dialog_credential_attempts_before_wipe" msgid="6751859711975516999">"Әрекетті қайталаңыз. <xliff:g id="ATTEMPTS_0">%1$d</xliff:g> мүмкіндік, барлығы – <xliff:g id="MAX_ATTEMPTS">%2$d</xliff:g>."</string>
<string name="biometric_dialog_last_attempt_before_wipe_dialog_title" msgid="2874250099278693477">"Деректеріңіз жойылады"</string>
<string name="biometric_dialog_last_pattern_attempt_before_wipe_device" msgid="6562299244825817598">"Келесі әрекет кезінде қате өрнек енгізсеңіз, бұл құрылғылардың деректері жойылады."</string>
@@ -411,6 +415,8 @@
<string name="media_projection_entry_app_permission_dialog_warning_entire_screen" msgid="8736391633234144237">"Бөлісу, жазу не трансляциялау кезінде <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> экраныңызда көрінетін не құрылғыңызда ойнатылатын барлық нәрсені пайдалана алады. Сондықтан құпия сөздерді, төлем туралы мәліметті, хабарларды немесе басқа құпия ақпаратты енгізген кезде сақ болыңыз."</string>
<string name="media_projection_entry_app_permission_dialog_warning_single_app" msgid="5211695779082563959">"Қолданба экранын бөлісу, жазу не трансляциялау кезінде <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> онда көрінетін не ойнатылатын барлық нәрсені пайдалана алады. Сондықтан құпия сөздерді, төлем туралы мәліметті, хабарларды немесе басқа құпия ақпаратты енгізген кезде сақ болыңыз."</string>
<string name="media_projection_entry_app_permission_dialog_continue" msgid="295463518195075840">"Бастау"</string>
+ <!-- no translation found for media_projection_entry_app_permission_dialog_single_app_disabled (8999903044874669995) -->
+ <skip />
<string name="media_projection_entry_cast_permission_dialog_title" msgid="8860150223172993547">"Трансляциялау басталсын ба?"</string>
<string name="media_projection_entry_cast_permission_dialog_warning_entire_screen" msgid="1986212276016817231">"Трансляциялау кезінде Android жүйесі экраныңызда көрінетін не құрылғыңызда ойнатылатын барлық нәрсені пайдалана алады. Сондықтан құпия сөздерді, төлем туралы мәліметті, хабарларды немесе басқа құпия ақпаратты енгізген кезде сақ болыңыз."</string>
<string name="media_projection_entry_cast_permission_dialog_warning_single_app" msgid="9900961380294292">"Қолданба экранын трансляциялау кезінде Android жүйесі қолданбада көрінетін не ойнатылатын барлық нәрсені пайдалана алады. Сондықтан құпия сөздерді, төлем туралы мәліметті, хабарларды немесе басқа құпия ақпаратты енгізген кезде сақ болыңыз."</string>
@@ -865,8 +871,7 @@
<string name="magnification_mode_switch_state_window" msgid="8597100249594076965">"Экранның бөлігін ұлғайту"</string>
<string name="magnification_open_settings_click_label" msgid="6151849212725923363">"Ұлғайту параметрлерін ашу"</string>
<string name="magnification_close_settings_click_label" msgid="4642477260651704517">"Ұлғайту параметрлерін жабу"</string>
- <!-- no translation found for magnification_exit_edit_mode_click_label (1664818325144887117) -->
- <skip />
+ <string name="magnification_exit_edit_mode_click_label" msgid="1664818325144887117">"Өзгерту режимінен шығу"</string>
<string name="magnification_drag_corner_to_resize" msgid="1249766311052418130">"Өлшемін өзгерту үшін бұрышынан сүйреңіз."</string>
<string name="accessibility_allow_diagonal_scrolling" msgid="3258050349191496398">"Диагональ бойынша айналдыруға рұқсат беру"</string>
<string name="accessibility_resize" msgid="5733759136600611551">"Өлшемін өзгерту"</string>
diff --git a/packages/SystemUI/res/values-km/strings.xml b/packages/SystemUI/res/values-km/strings.xml
index d5ef4b5..af1e3b5 100644
--- a/packages/SystemUI/res/values-km/strings.xml
+++ b/packages/SystemUI/res/values-km/strings.xml
@@ -57,6 +57,8 @@
<string name="hdmi_cec_set_menu_language_description" msgid="8176716678074126619">"ការប្ដូរភាសាប្រព័ន្ធដែលបានស្នើសុំដោយឧបករណ៍ផ្សេងទៀត"</string>
<string name="hdmi_cec_set_menu_language_accept" msgid="2513689457281009578">"ប្ដូរភាសា"</string>
<string name="hdmi_cec_set_menu_language_decline" msgid="7650721096558646011">"រក្សាភាសាបច្ចុប្បន្ន"</string>
+ <!-- no translation found for share_wifi_button_text (1285273973812029240) -->
+ <skip />
<string name="wifi_debugging_title" msgid="7300007687492186076">"អនុញ្ញាតការជួសជុលដោយឥតខ្សែនៅលើបណ្ដាញនេះឬ?"</string>
<string name="wifi_debugging_message" msgid="5461204211731802995">"ឈ្មោះបណ្ដាញ (SSID)\n<xliff:g id="SSID_0">%1$s</xliff:g>\n\nអាសយដ្ឋាន Wi‑Fi (BSSID)\n<xliff:g id="BSSID_1">%2$s</xliff:g>"</string>
<string name="wifi_debugging_always" msgid="2968383799517975155">"អនុញ្ញាតនៅលើបណ្ដាញនេះជានិច្ច"</string>
@@ -158,6 +160,8 @@
<string name="biometric_dialog_wrong_pattern" msgid="8954812279840889029">"លំនាំមិនត្រឹមត្រូវ"</string>
<string name="biometric_dialog_wrong_password" msgid="69477929306843790">"ពាក្យសម្ងាត់មិនត្រឹមត្រូវ"</string>
<string name="biometric_dialog_credential_too_many_attempts" msgid="3083141271737748716">"ការព្យាយាមចូលខុសច្រើនដងពេក។\nសូមព្យាយាមម្តងទៀតក្នុងរយៈពេល <xliff:g id="NUMBER">%d</xliff:g> វិនាទី។"</string>
+ <!-- no translation found for work_challenge_emergency_button_text (8946588434515599288) -->
+ <skip />
<string name="biometric_dialog_credential_attempts_before_wipe" msgid="6751859711975516999">"សូមព្យាយាមម្តងទៀត។ ការព្យាយាម <xliff:g id="ATTEMPTS_0">%1$d</xliff:g> នៃ <xliff:g id="MAX_ATTEMPTS">%2$d</xliff:g> ដង។"</string>
<string name="biometric_dialog_last_attempt_before_wipe_dialog_title" msgid="2874250099278693477">"ទិន្នន័យរបស់អ្នកនឹងត្រូវបានលុប"</string>
<string name="biometric_dialog_last_pattern_attempt_before_wipe_device" msgid="6562299244825817598">"ប្រសិនបើអ្នកបញ្ចូលលំនាំមិនត្រឹមត្រូវ នៅពេលព្យាយាមបញ្ចូលលើកក្រោយ ទិន្នន័យរបស់ឧបករណ៍នេះនឹងត្រូវបានលុប។"</string>
@@ -411,6 +415,8 @@
<string name="media_projection_entry_app_permission_dialog_warning_entire_screen" msgid="8736391633234144237">"នៅពេលអ្នកកំពុងចែករំលែក ថត ឬភ្ជាប់, <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> មានសិទ្ធិចូលប្រើអ្វីៗដែលអាចមើលឃើញនៅលើអេក្រង់របស់អ្នក ឬចាក់នៅលើឧបករណ៍របស់អ្នក។ ដូច្នេះ សូមប្រុងប្រយ័ត្នចំពោះអ្វីៗដូចជា ពាក្យសម្ងាត់ ព័ត៌មានលម្អិតអំពីការទូទាត់ប្រាក់ សារ រូបថត ព្រមទាំងសំឡេង និងវីដេអូ។"</string>
<string name="media_projection_entry_app_permission_dialog_warning_single_app" msgid="5211695779082563959">"នៅពេលអ្នកកំពុងចែករំលែក ថត ឬភ្ជាប់កម្មវិធី, <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> មានសិទ្ធិចូលប្រើអ្វីៗដែលបង្ហាញ ឬចាក់នៅលើកម្មវិធីនោះ។ ដូច្នេះ សូមប្រុងប្រយ័ត្នចំពោះអ្វីៗដូចជា ពាក្យសម្ងាត់ ព័ត៌មានលម្អិតអំពីការទូទាត់ប្រាក់ សារ រូបថត ព្រមទាំងសំឡេង និងវីដេអូ។"</string>
<string name="media_projection_entry_app_permission_dialog_continue" msgid="295463518195075840">"ចាប់ផ្ដើម"</string>
+ <!-- no translation found for media_projection_entry_app_permission_dialog_single_app_disabled (8999903044874669995) -->
+ <skip />
<string name="media_projection_entry_cast_permission_dialog_title" msgid="8860150223172993547">"ចាប់ផ្តើមភ្ជាប់ឬ?"</string>
<string name="media_projection_entry_cast_permission_dialog_warning_entire_screen" msgid="1986212276016817231">"នៅពេលអ្នកកំពុងភ្ជាប់, Android មានសិទ្ធិចូលប្រើអ្វីៗដែលអាចមើលឃើញនៅលើអេក្រង់របស់អ្នក ឬចាក់នៅលើឧបករណ៍របស់អ្នក។ ដូច្នេះ សូមប្រុងប្រយ័ត្នចំពោះអ្វីៗដូចជា ពាក្យសម្ងាត់ ព័ត៌មានលម្អិតអំពីការទូទាត់ប្រាក់ សារ រូបថត ព្រមទាំងសំឡេង និងវីដេអូ។"</string>
<string name="media_projection_entry_cast_permission_dialog_warning_single_app" msgid="9900961380294292">"នៅពេលអ្នកកំពុងភ្ជាប់កម្មវិធី, Android មានសិទ្ធិចូលប្រើអ្វីៗដែលបង្ហាញ ឬចាក់នៅលើកម្មវិធីនោះ។ ដូច្នេះ សូមប្រុងប្រយ័ត្នចំពោះអ្វីៗដូចជា ពាក្យសម្ងាត់ ព័ត៌មានលម្អិតអំពីការទូទាត់ប្រាក់ សារ រូបថត ព្រមទាំងសំឡេង និងវីដេអូ។"</string>
@@ -865,8 +871,7 @@
<string name="magnification_mode_switch_state_window" msgid="8597100249594076965">"ពង្រីកផ្នែកនៃអេក្រង់"</string>
<string name="magnification_open_settings_click_label" msgid="6151849212725923363">"បើកការកំណត់ការពង្រីក"</string>
<string name="magnification_close_settings_click_label" msgid="4642477260651704517">"បិទការកំណត់ការពង្រីក"</string>
- <!-- no translation found for magnification_exit_edit_mode_click_label (1664818325144887117) -->
- <skip />
+ <string name="magnification_exit_edit_mode_click_label" msgid="1664818325144887117">"បិទមុខងារកែ"</string>
<string name="magnification_drag_corner_to_resize" msgid="1249766311052418130">"អូសជ្រុងដើម្បីប្ដូរទំហំ"</string>
<string name="accessibility_allow_diagonal_scrolling" msgid="3258050349191496398">"អនុញ្ញាតការរំកិលបញ្ឆិត"</string>
<string name="accessibility_resize" msgid="5733759136600611551">"ប្ដូរទំហំ"</string>
diff --git a/packages/SystemUI/res/values-kn/strings.xml b/packages/SystemUI/res/values-kn/strings.xml
index 0b82b55..db1b35d 100644
--- a/packages/SystemUI/res/values-kn/strings.xml
+++ b/packages/SystemUI/res/values-kn/strings.xml
@@ -57,6 +57,8 @@
<string name="hdmi_cec_set_menu_language_description" msgid="8176716678074126619">"ಬೇರೊಂದು ಸಾಧನದಿಂದ ಸಿಸ್ಟಮ್ ಭಾಷೆಯ ಬದಲಾವಣೆಯನ್ನು ವಿನಂತಿಸಲಾಗಿದೆ"</string>
<string name="hdmi_cec_set_menu_language_accept" msgid="2513689457281009578">"ಭಾಷೆಯನ್ನು ಬದಲಾಯಿಸಿ"</string>
<string name="hdmi_cec_set_menu_language_decline" msgid="7650721096558646011">"ಪ್ರಸ್ತುತ ಭಾಷೆಯನ್ನೇ ಇರಿಸಿ"</string>
+ <!-- no translation found for share_wifi_button_text (1285273973812029240) -->
+ <skip />
<string name="wifi_debugging_title" msgid="7300007687492186076">"ಈ ನೆಟ್ವರ್ಕ್ನಲ್ಲಿ ವೈರ್ಲೆಸ್ ಡೀಬಗ್ ಮಾಡುವಿಕೆಯನ್ನು ಅನುಮತಿಸಬೇಕೆ?"</string>
<string name="wifi_debugging_message" msgid="5461204211731802995">"ನೆಟ್ವರ್ಕ್ ಹೆಸರು (SSID)\n<xliff:g id="SSID_0">%1$s</xliff:g>\n\nವೈ-ಫೈ ವಿಳಾಸ (BSSID)\n<xliff:g id="BSSID_1">%2$s</xliff:g>"</string>
<string name="wifi_debugging_always" msgid="2968383799517975155">"ಈ ನೆಟ್ವರ್ಕ್ನಲ್ಲಿ ಅನುಮತಿಸಿ"</string>
@@ -158,6 +160,8 @@
<string name="biometric_dialog_wrong_pattern" msgid="8954812279840889029">"ಪ್ಯಾಟರ್ನ್ ತಪ್ಪಾಗಿದೆ"</string>
<string name="biometric_dialog_wrong_password" msgid="69477929306843790">"ಪಾಸ್ವರ್ಡ್ ತಪ್ಪಾಗಿದೆ"</string>
<string name="biometric_dialog_credential_too_many_attempts" msgid="3083141271737748716">"ಹಲವಾರು ತಪ್ಪು ಪ್ರಯತ್ನಗಳು.\nಮತ್ತೆ <xliff:g id="NUMBER">%d</xliff:g> ಸೆಕೆಂಡುಗಳಲ್ಲಿ ಪ್ರಯತ್ನಿಸಿ."</string>
+ <!-- no translation found for work_challenge_emergency_button_text (8946588434515599288) -->
+ <skip />
<string name="biometric_dialog_credential_attempts_before_wipe" msgid="6751859711975516999">"ಮತ್ತೆ ಪ್ರಯತ್ನಿಸಿ. <xliff:g id="ATTEMPTS_0">%1$d</xliff:g>/<xliff:g id="MAX_ATTEMPTS">%2$d</xliff:g> ಪ್ರಯತ್ನಗಳು."</string>
<string name="biometric_dialog_last_attempt_before_wipe_dialog_title" msgid="2874250099278693477">"ನಿಮ್ಮ ಡೇಟಾವನ್ನು ಅಳಿಸಲಾಗುತ್ತದೆ"</string>
<string name="biometric_dialog_last_pattern_attempt_before_wipe_device" msgid="6562299244825817598">"ಮುಂದಿನ ಪ್ರಯತ್ನದಲ್ಲಿ ನೀವು ತಪ್ಪಾದ ಪ್ಯಾಟರ್ನ್ ನಮೂದಿಸಿದರೆ, ಈ ಸಾಧನದ ಡೇಟಾವನ್ನು ಅಳಿಸಲಾಗುತ್ತದೆ."</string>
@@ -411,6 +415,8 @@
<string name="media_projection_entry_app_permission_dialog_warning_entire_screen" msgid="8736391633234144237">"ನೀವು ಹಂಚಿಕೊಳ್ಳುತ್ತಿರುವಾಗ, ರೆಕಾರ್ಡಿಂಗ್ ಮಾಡುತ್ತಿರುವಾಗ ಅಥವಾ ಕ್ಯಾಸ್ಟ್ ಮಾಡುತ್ತಿರುವಾಗ, ನಿಮ್ಮ ಸ್ಕ್ರೀನ್ ಮೇಲೆ ಕಾಣಿಸುವ ಅಥವಾ ನಿಮ್ಮ ಸಾಧನದಲ್ಲಿ ಪ್ಲೇ ಮಾಡುವ ಯಾವುದೇ ವಿಷಯಕ್ಕೆ <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> ಆ್ಯಕ್ಸೆಸ್ ಅನ್ನು ಹೊಂದಿರುತ್ತದೆ. ಆದ್ದರಿಂದ ಪಾಸ್ವರ್ಡ್ಗಳು, ಪಾವತಿ ವಿವರಗಳು, ಸಂದೇಶಗಳು, ಫೋಟೋಗಳು ಹಾಗೂ ಆಡಿಯೊ ಮತ್ತು ವೀಡಿಯೊದಂತಹ ವಿಷಯಗಳ ಕುರಿತು ಜಾಗರೂಕರಾಗಿರಿ."</string>
<string name="media_projection_entry_app_permission_dialog_warning_single_app" msgid="5211695779082563959">"ನೀವು ಆ್ಯಪ್ ಅನ್ನು ಹಂಚಿಕೊಳ್ಳುತ್ತಿರುವಾಗ, ರೆಕಾರ್ಡಿಂಗ್ ಮಾಡುತ್ತಿರುವಾಗ ಅಥವಾ ಕ್ಯಾಸ್ಟ್ ಮಾಡುತ್ತಿರುವಾಗ, ಆ ಆ್ಯಪ್ನಲ್ಲಿ ತೋರಿಸುವ ಅಥವಾ ಪ್ಲೇ ಮಾಡುವ ಯಾವುದೇ ವಿಷಯಕ್ಕೆ <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> ಆ್ಯಕ್ಸೆಸ್ ಅನ್ನು ಹೊಂದಿರುತ್ತದೆ. ಆದ್ದರಿಂದ ಪಾಸ್ವರ್ಡ್ಗಳು, ಪಾವತಿ ವಿವರಗಳು, ಸಂದೇಶಗಳು, ಫೋಟೋಗಳು ಹಾಗೂ ಆಡಿಯೊ ಮತ್ತು ವೀಡಿಯೊದಂತಹ ವಿಷಯಗಳ ಕುರಿತು ಜಾಗರೂಕರಾಗಿರಿ."</string>
<string name="media_projection_entry_app_permission_dialog_continue" msgid="295463518195075840">"ಪ್ರಾರಂಭಿಸಿ"</string>
+ <!-- no translation found for media_projection_entry_app_permission_dialog_single_app_disabled (8999903044874669995) -->
+ <skip />
<string name="media_projection_entry_cast_permission_dialog_title" msgid="8860150223172993547">"ಕ್ಯಾಸ್ಟ್ ಮಾಡಲು ಪ್ರಾರಂಭಿಸಬೇಕೇ?"</string>
<string name="media_projection_entry_cast_permission_dialog_warning_entire_screen" msgid="1986212276016817231">"ನೀವು ಕ್ಯಾಸ್ಟ್ ಮಾಡುವಾಗ, ನಿಮ್ಮ ಸ್ಕ್ರೀನ್ ಮೇಲೆ ಕಾಣಿಸುವ ಅಥವಾ ನಿಮ್ಮ ಸಾಧನದಲ್ಲಿ ಪ್ಲೇ ಮಾಡುವ ಯಾವುದೇ ವಿಷಯಕ್ಕೆ Android ಆ್ಯಕ್ಸೆಸ್ ಅನ್ನು ಹೊಂದಿರುತ್ತದೆ. ಆದ್ದರಿಂದ ಪಾಸ್ವರ್ಡ್ಗಳು, ಪಾವತಿ ವಿವರಗಳು, ಸಂದೇಶಗಳು, ಫೋಟೋಗಳು ಹಾಗೂ ಆಡಿಯೊ ಮತ್ತು ವೀಡಿಯೊದಂತಹ ವಿಷಯಗಳ ಕುರಿತು ಜಾಗರೂಕರಾಗಿರಿ."</string>
<string name="media_projection_entry_cast_permission_dialog_warning_single_app" msgid="9900961380294292">"ನೀವು ಆ್ಯಪ್ ಅನ್ನು ಕ್ಯಾಸ್ಟ್ ಮಾಡುತ್ತಿರುವಾಗ, ಆ ಆ್ಯಪ್ನಲ್ಲಿ ತೋರಿಸುವ ಅಥವಾ ಪ್ಲೇ ಮಾಡುವ ಯಾವುದೇ ವಿಷಯಕ್ಕೆ Android ಆ್ಯಕ್ಸೆಸ್ ಅನ್ನು ಹೊಂದಿರುತ್ತದೆ. ಆದ್ದರಿಂದ ಪಾಸ್ವರ್ಡ್ಗಳು, ಪಾವತಿ ವಿವರಗಳು, ಸಂದೇಶಗಳು, ಫೋಟೋಗಳು ಹಾಗೂ ಆಡಿಯೊ ಮತ್ತು ವೀಡಿಯೊದಂತಹ ವಿಷಯಗಳ ಕುರಿತು ಜಾಗರೂಕರಾಗಿರಿ."</string>
diff --git a/packages/SystemUI/res/values-ko/strings.xml b/packages/SystemUI/res/values-ko/strings.xml
index ea7d5c2..97271c5 100644
--- a/packages/SystemUI/res/values-ko/strings.xml
+++ b/packages/SystemUI/res/values-ko/strings.xml
@@ -57,6 +57,8 @@
<string name="hdmi_cec_set_menu_language_description" msgid="8176716678074126619">"다른 기기에서 시스템 언어 변경을 요청함"</string>
<string name="hdmi_cec_set_menu_language_accept" msgid="2513689457281009578">"언어 변경"</string>
<string name="hdmi_cec_set_menu_language_decline" msgid="7650721096558646011">"현재 언어 유지"</string>
+ <!-- no translation found for share_wifi_button_text (1285273973812029240) -->
+ <skip />
<string name="wifi_debugging_title" msgid="7300007687492186076">"이 네트워크에서 무선 디버깅을 허용하시겠습니까?"</string>
<string name="wifi_debugging_message" msgid="5461204211731802995">"네트워크 이름(SSID)\n<xliff:g id="SSID_0">%1$s</xliff:g>\n\nWi‑Fi 주소(BSSID)\n<xliff:g id="BSSID_1">%2$s</xliff:g>"</string>
<string name="wifi_debugging_always" msgid="2968383799517975155">"이 네트워크에서 항상 허용"</string>
@@ -158,6 +160,8 @@
<string name="biometric_dialog_wrong_pattern" msgid="8954812279840889029">"잘못된 패턴"</string>
<string name="biometric_dialog_wrong_password" msgid="69477929306843790">"잘못된 비밀번호"</string>
<string name="biometric_dialog_credential_too_many_attempts" msgid="3083141271737748716">"잘못된 시도 횟수가 너무 많습니다.\n<xliff:g id="NUMBER">%d</xliff:g>초 후에 다시 시도하세요."</string>
+ <!-- no translation found for work_challenge_emergency_button_text (8946588434515599288) -->
+ <skip />
<string name="biometric_dialog_credential_attempts_before_wipe" msgid="6751859711975516999">"다시 시도하세요. <xliff:g id="MAX_ATTEMPTS">%2$d</xliff:g>회 중 <xliff:g id="ATTEMPTS_0">%1$d</xliff:g>번째 시도입니다."</string>
<string name="biometric_dialog_last_attempt_before_wipe_dialog_title" msgid="2874250099278693477">"데이터가 삭제됨"</string>
<string name="biometric_dialog_last_pattern_attempt_before_wipe_device" msgid="6562299244825817598">"다음번 시도에서 잘못된 패턴을 입력하면 이 기기의 데이터가 삭제됩니다."</string>
@@ -411,6 +415,8 @@
<string name="media_projection_entry_app_permission_dialog_warning_entire_screen" msgid="8736391633234144237">"공유, 녹화 또는 전송 중에 <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> 앱이 화면에 표시되거나 기기에서 재생되는 모든 항목에 액세스할 수 있습니다. 따라서 비밀번호, 결제 세부정보, 메시지, 사진, 오디오 및 동영상 등이 노출되지 않도록 주의하세요."</string>
<string name="media_projection_entry_app_permission_dialog_warning_single_app" msgid="5211695779082563959">"앱을 공유, 녹화 또는 전송할 때는 <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> 앱이 해당 앱에 표시되거나 재생되는 모든 항목에 액세스할 수 있으므로 비밀번호, 결제 세부정보, 메시지, 사진, 오디오 및 동영상 등이 노출되지 않도록 주의하세요."</string>
<string name="media_projection_entry_app_permission_dialog_continue" msgid="295463518195075840">"시작"</string>
+ <!-- no translation found for media_projection_entry_app_permission_dialog_single_app_disabled (8999903044874669995) -->
+ <skip />
<string name="media_projection_entry_cast_permission_dialog_title" msgid="8860150223172993547">"전송을 시작하시겠습니까?"</string>
<string name="media_projection_entry_cast_permission_dialog_warning_entire_screen" msgid="1986212276016817231">"전송 중에는 Android가 화면에 표시되거나 기기에서 재생되는 모든 항목에 액세스할 수 있습니다. 따라서 비밀번호, 결제 세부정보, 메시지, 사진, 오디오 및 동영상 등이 노출되지 않도록 주의하세요."</string>
<string name="media_projection_entry_cast_permission_dialog_warning_single_app" msgid="9900961380294292">"앱을 전송할 때 Android가 해당 앱에 표시되거나 재생되는 모든 항목에 액세스할 수 있으므로 비밀번호, 결제 세부정보, 메시지, 사진, 오디오 및 동영상 등이 노출되지 않도록 주의하세요."</string>
@@ -865,8 +871,7 @@
<string name="magnification_mode_switch_state_window" msgid="8597100249594076965">"화면 일부 확대"</string>
<string name="magnification_open_settings_click_label" msgid="6151849212725923363">"확대 설정 열기"</string>
<string name="magnification_close_settings_click_label" msgid="4642477260651704517">"확대 설정 닫기"</string>
- <!-- no translation found for magnification_exit_edit_mode_click_label (1664818325144887117) -->
- <skip />
+ <string name="magnification_exit_edit_mode_click_label" msgid="1664818325144887117">"수정 모드 종료"</string>
<string name="magnification_drag_corner_to_resize" msgid="1249766311052418130">"모서리를 드래그하여 크기 조절"</string>
<string name="accessibility_allow_diagonal_scrolling" msgid="3258050349191496398">"대각선 스크롤 허용"</string>
<string name="accessibility_resize" msgid="5733759136600611551">"크기 조절"</string>
diff --git a/packages/SystemUI/res/values-ky/strings.xml b/packages/SystemUI/res/values-ky/strings.xml
index cb9e67a..db47e39 100644
--- a/packages/SystemUI/res/values-ky/strings.xml
+++ b/packages/SystemUI/res/values-ky/strings.xml
@@ -57,6 +57,8 @@
<string name="hdmi_cec_set_menu_language_description" msgid="8176716678074126619">"Система тилин өзгөртүү сурамы башка түзмөктөн жөнөтүлдү"</string>
<string name="hdmi_cec_set_menu_language_accept" msgid="2513689457281009578">"Тилди өзгөртүү"</string>
<string name="hdmi_cec_set_menu_language_decline" msgid="7650721096558646011">"Учурдагы тилди калтыруу"</string>
+ <!-- no translation found for share_wifi_button_text (1285273973812029240) -->
+ <skip />
<string name="wifi_debugging_title" msgid="7300007687492186076">"Ушул тармакта мүчүлүштүктөрдү Wi-Fi аркылуу аныктоого уруксат бересизби?"</string>
<string name="wifi_debugging_message" msgid="5461204211731802995">"Тармактын аталышы (SSID)\n<xliff:g id="SSID_0">%1$s</xliff:g>\n\nWi‑Fi дареги (BSSID)\n<xliff:g id="BSSID_1">%2$s</xliff:g>"</string>
<string name="wifi_debugging_always" msgid="2968383799517975155">"Бул тармакта ар дайым уруксат берилсин"</string>
@@ -158,6 +160,8 @@
<string name="biometric_dialog_wrong_pattern" msgid="8954812279840889029">"Графикалык ачкыч туура эмес"</string>
<string name="biometric_dialog_wrong_password" msgid="69477929306843790">"Сырсөз туура эмес"</string>
<string name="biometric_dialog_credential_too_many_attempts" msgid="3083141271737748716">"Өтө көп жолу жаңылдыңыз.\n<xliff:g id="NUMBER">%d</xliff:g> секунддан кийин кайра кайталаңыз."</string>
+ <!-- no translation found for work_challenge_emergency_button_text (8946588434515599288) -->
+ <skip />
<string name="biometric_dialog_credential_attempts_before_wipe" msgid="6751859711975516999">"Кайра кайталаңыз. <xliff:g id="MAX_ATTEMPTS">%2$d</xliff:g> аракеттен <xliff:g id="ATTEMPTS_0">%1$d</xliff:g> аракет калды."</string>
<string name="biometric_dialog_last_attempt_before_wipe_dialog_title" msgid="2874250099278693477">"Акыркы аракет калды"</string>
<string name="biometric_dialog_last_pattern_attempt_before_wipe_device" msgid="6562299244825817598">"Эгер графикалык ачкычты кийинки жолу туура эмес киргизсеңиз, бул түзмөктүн маалыматы өчүрүлөт."</string>
@@ -411,6 +415,8 @@
<string name="media_projection_entry_app_permission_dialog_warning_entire_screen" msgid="8736391633234144237">"Бөлүшүп, жаздырып же тышкы экранга чыгарып жатканда <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> колдонмосу экраныңыздагы бардык маалыматты же түзмөктө ойнотулуп жаткан нерселерди көрө алат. Андыктан сырсөздөрдү, төлөмдүн чоо-жайын, билдирүүлөрдү, сүрөттөрдү, аудио жана видеону көрсөтүп албаңыз."</string>
<string name="media_projection_entry_app_permission_dialog_warning_single_app" msgid="5211695779082563959">"Колдонмону бөлүшүп, жаздырып же тышкы экранга чыгарганда <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> колдонмосу ал колдонмодо көрсөтүлүп жана ойнотулуп жаткан нерселерди көрө алат. Андыктан сырсөздөрдү, төлөмдүн чоо-жайын, билдирүүлөрдү, сүрөттөрдү, аудио жана видеону көрсөтүп албаңыз."</string>
<string name="media_projection_entry_app_permission_dialog_continue" msgid="295463518195075840">"Баштоо"</string>
+ <!-- no translation found for media_projection_entry_app_permission_dialog_single_app_disabled (8999903044874669995) -->
+ <skip />
<string name="media_projection_entry_cast_permission_dialog_title" msgid="8860150223172993547">"Тышкы экранга чыгаруу башталсынбы?"</string>
<string name="media_projection_entry_cast_permission_dialog_warning_entire_screen" msgid="1986212276016817231">"Тышкы экранга чыгарганда Android экраныңызда көрүнүп жана түзмөктө ойнотулуп жаткан нерселерди көрө алат. Андыктан сырсөздөрдү, төлөмдүн чоо-жайын, билдирүүлөрдү, сүрөттөрдү, аудио жана видеону көрсөтүп албаңыз."</string>
<string name="media_projection_entry_cast_permission_dialog_warning_single_app" msgid="9900961380294292">"Колдонмону тышкы экранга чыгарганда Android ал колдонмодо көрсөтүлүп жана ойнотулуп жаткан нерселерди көрө алат. Андыктан сырсөздөрдү, төлөмдүн чоо-жайын, билдирүүлөрдү, сүрөттөрдү, аудио жана видеону көрсөтүп албаңыз."</string>
@@ -865,8 +871,7 @@
<string name="magnification_mode_switch_state_window" msgid="8597100249594076965">"Экрандын бир бөлүгүн чоңойтуу"</string>
<string name="magnification_open_settings_click_label" msgid="6151849212725923363">"Чоңойтуу параметрлерин ачуу"</string>
<string name="magnification_close_settings_click_label" msgid="4642477260651704517">"Чоңойтуу параметрлерин жабуу"</string>
- <!-- no translation found for magnification_exit_edit_mode_click_label (1664818325144887117) -->
- <skip />
+ <string name="magnification_exit_edit_mode_click_label" msgid="1664818325144887117">"Түзөтүү режиминен чыгуу"</string>
<string name="magnification_drag_corner_to_resize" msgid="1249766311052418130">"Өлчөмүн өзгөртүү үчүн бурчун сүйрөңүз"</string>
<string name="accessibility_allow_diagonal_scrolling" msgid="3258050349191496398">"Диагональ боюнча сыдырууга уруксат берүү"</string>
<string name="accessibility_resize" msgid="5733759136600611551">"Өлчөмүн өзгөртүү"</string>
diff --git a/packages/SystemUI/res/values-lo/strings.xml b/packages/SystemUI/res/values-lo/strings.xml
index 3e26137..ff08be1 100644
--- a/packages/SystemUI/res/values-lo/strings.xml
+++ b/packages/SystemUI/res/values-lo/strings.xml
@@ -57,6 +57,8 @@
<string name="hdmi_cec_set_menu_language_description" msgid="8176716678074126619">"ມີການຮ້ອງຂໍໃຫ້ປ່ຽນພາສາລະບົບໂດຍອຸປະກອນອື່ນ"</string>
<string name="hdmi_cec_set_menu_language_accept" msgid="2513689457281009578">"ປ່ຽນພາສາ"</string>
<string name="hdmi_cec_set_menu_language_decline" msgid="7650721096558646011">"ໃຊ້ພາສາປັດຈຸບັນ"</string>
+ <!-- no translation found for share_wifi_button_text (1285273973812029240) -->
+ <skip />
<string name="wifi_debugging_title" msgid="7300007687492186076">"ອະນຸຍາດການດີບັກໄຮ້ສາຍຢູ່ເຄືອຂ່າຍນີ້ບໍ?"</string>
<string name="wifi_debugging_message" msgid="5461204211731802995">"ຊື່ເຄືອຂ່າຍ (SSID)\n<xliff:g id="SSID_0">%1$s</xliff:g>\n\nທີ່ຢູ່ Wi‑Fi (BSSID)\n<xliff:g id="BSSID_1">%2$s</xliff:g>"</string>
<string name="wifi_debugging_always" msgid="2968383799517975155">"ອະນຸຍາດຕະຫຼອດຢູ່ເຄືອຂ່າຍນີ້"</string>
@@ -158,6 +160,8 @@
<string name="biometric_dialog_wrong_pattern" msgid="8954812279840889029">"ຮູບແບບບໍ່ຖືກຕ້ອງ"</string>
<string name="biometric_dialog_wrong_password" msgid="69477929306843790">"ລະຫັດຜ່ານບໍ່ຖືກຕ້ອງ"</string>
<string name="biometric_dialog_credential_too_many_attempts" msgid="3083141271737748716">"ມີຄວາມພະຍາຍາມບໍ່ຖືກຕ້ອງຫຼາຍເທື່ອເກີນໄປ.\nກະລຸນາລອງໃໝ່ອີກໃນ <xliff:g id="NUMBER">%d</xliff:g> ວິນາທີ."</string>
+ <!-- no translation found for work_challenge_emergency_button_text (8946588434515599288) -->
+ <skip />
<string name="biometric_dialog_credential_attempts_before_wipe" msgid="6751859711975516999">"ກະລຸນາລອງໃໝ່. ຄວາມພະຍາຍາມເທື່ອທີ <xliff:g id="ATTEMPTS_0">%1$d</xliff:g> ຈາກທັງໝົດ <xliff:g id="MAX_ATTEMPTS">%2$d</xliff:g>."</string>
<string name="biometric_dialog_last_attempt_before_wipe_dialog_title" msgid="2874250099278693477">"ຂໍ້ມູນຂອງທ່ານຈະຖືກລຶບອອກ"</string>
<string name="biometric_dialog_last_pattern_attempt_before_wipe_device" msgid="6562299244825817598">"ຫາກທ່ານໃສ່ຣູບແບບຜິດໃນຄວາມພະຍາຍາມເທື່ອຕໍ່ໄປ, ອຸປະກອນນີ້ຈະຖືກລຶບຂໍ້ມູນອອກ."</string>
@@ -411,6 +415,8 @@
<string name="media_projection_entry_app_permission_dialog_warning_entire_screen" msgid="8736391633234144237">"ເມື່ອທ່ານກຳລັງແບ່ງປັນ, ບັນທຶກ ຫຼື ສົ່ງສັນຍານ, <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> ຈະມີສິດເຂົ້າເຖິງທຸກສິ່ງທີ່ປາກົດຢູ່ໜ້າຈໍຂອງທ່ານ ຫຼື ຫຼິ້ນຢູ່ອຸປະກອນຂອງທ່ານ. ດັ່ງນັ້ນ, ໃຫ້ລະມັດລະວັງສິ່ງຕ່າງໆ ເຊັ່ນ: ລະຫັດຜ່ານ, ລາຍລະອຽດການຈ່າຍເງິນ, ຂໍ້ຄວາມ, ຮູບພາບ ພ້ອມທັງສຽງ ແລະ ວິດີໂອ."</string>
<string name="media_projection_entry_app_permission_dialog_warning_single_app" msgid="5211695779082563959">"ເມື່ອທ່ານກຳລັງແບ່ງປັນ, ບັນທຶກ ຫຼື ສົ່ງສັນຍານແອັບ, <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> ຈະມີສິດເຂົ້າເຖິງທຸກສິ່ງທີ່ສະແດງ ຫຼື ຫຼິ້ນຢູ່ແອັບດັ່ງກ່າວ. ດັ່ງນັ້ນ, ໃຫ້ລະມັດລະວັງສິ່ງຕ່າງໆ ເຊັ່ນ: ລະຫັດຜ່ານ, ລາຍລະອຽດການຈ່າຍເງິນ, ຂໍ້ຄວາມ, ຮູບພາບ ພ້ອມທັງສຽງ ແລະ ວິດີໂອ."</string>
<string name="media_projection_entry_app_permission_dialog_continue" msgid="295463518195075840">"ເລີ່ມ"</string>
+ <!-- no translation found for media_projection_entry_app_permission_dialog_single_app_disabled (8999903044874669995) -->
+ <skip />
<string name="media_projection_entry_cast_permission_dialog_title" msgid="8860150223172993547">"ເລີ່ມການສົ່ງສັນຍານບໍ?"</string>
<string name="media_projection_entry_cast_permission_dialog_warning_entire_screen" msgid="1986212276016817231">"ເມື່ອທ່ານກຳລັງສົ່ງສັນຍານ, Android ຈະມີສິດເຂົ້າເຖິງທຸກສິ່ງທີ່ປາກົດຢູ່ໜ້າຈໍຂອງທ່ານ ຫຼື ຫຼິ້ນຢູ່ອຸປະກອນຂອງທ່ານ. ດັ່ງນັ້ນ, ໃຫ້ລະມັດລະວັງສິ່ງຕ່າງໆ ເຊັ່ນ: ລະຫັດຜ່ານ, ລາຍລະອຽດການຈ່າຍເງິນ, ຂໍ້ຄວາມ, ຮູບພາບ ພ້ອມທັງສຽງ ແລະ ວິດີໂອ."</string>
<string name="media_projection_entry_cast_permission_dialog_warning_single_app" msgid="9900961380294292">"ເມື່ອທ່ານກຳລັງສົ່ງສັນຍານແອັບ, Android ຈະມີສິດເຂົ້າເຖິງທຸກສິ່ງທີ່ສະແດງ ຫຼື ຫຼິ້ນຢູ່ແອັບດັ່ງກ່າວ. ດັ່ງນັ້ນ, ໃຫ້ລະມັດລະວັງສິ່ງຕ່າງໆ ເຊັ່ນ: ລະຫັດຜ່ານ, ລາຍລະອຽດການຈ່າຍເງິນ, ຂໍ້ຄວາມ, ຮູບພາບ ພ້ອມທັງສຽງ ແລະ ວິດີໂອ."</string>
@@ -865,8 +871,7 @@
<string name="magnification_mode_switch_state_window" msgid="8597100249594076965">"ຂະຫຍາຍບາງສ່ວນຂອງໜ້າຈໍ"</string>
<string name="magnification_open_settings_click_label" msgid="6151849212725923363">"ເປີດການຕັ້ງຄ່າການຂະຫຍາຍ"</string>
<string name="magnification_close_settings_click_label" msgid="4642477260651704517">"ປິດການຕັ້ງຄ່າການຂະຫຍາຍ"</string>
- <!-- no translation found for magnification_exit_edit_mode_click_label (1664818325144887117) -->
- <skip />
+ <string name="magnification_exit_edit_mode_click_label" msgid="1664818325144887117">"ອອກຈາກໂໝດແກ້ໄຂ"</string>
<string name="magnification_drag_corner_to_resize" msgid="1249766311052418130">"ລາກຢູ່ມຸມເພື່ອປັບຂະໜາດ"</string>
<string name="accessibility_allow_diagonal_scrolling" msgid="3258050349191496398">"ອະນຸຍາດໃຫ້ເລື່ອນທາງຂວາງ"</string>
<string name="accessibility_resize" msgid="5733759136600611551">"ປ່ຽນຂະໜາດ"</string>
diff --git a/packages/SystemUI/res/values-lt/strings.xml b/packages/SystemUI/res/values-lt/strings.xml
index a7d874c..d3fc063 100644
--- a/packages/SystemUI/res/values-lt/strings.xml
+++ b/packages/SystemUI/res/values-lt/strings.xml
@@ -57,6 +57,8 @@
<string name="hdmi_cec_set_menu_language_description" msgid="8176716678074126619">"Užklausą dėl sistemos kalbos pakeitimo pateikė kitas įrenginys"</string>
<string name="hdmi_cec_set_menu_language_accept" msgid="2513689457281009578">"Keisti kalbą"</string>
<string name="hdmi_cec_set_menu_language_decline" msgid="7650721096558646011">"Palikti dabartinę kalbą"</string>
+ <!-- no translation found for share_wifi_button_text (1285273973812029240) -->
+ <skip />
<string name="wifi_debugging_title" msgid="7300007687492186076">"Leisti belaidžio ryšio derinimą prisijungus prie šio tinklo?"</string>
<string name="wifi_debugging_message" msgid="5461204211731802995">"Tinklo pavadinimas (SSID)\n<xliff:g id="SSID_0">%1$s</xliff:g>\n\n„Wi‑Fi“ adresas (BSSID)\n<xliff:g id="BSSID_1">%2$s</xliff:g>"</string>
<string name="wifi_debugging_always" msgid="2968383799517975155">"Visada leisti naudojant šį tinklą"</string>
@@ -158,6 +160,8 @@
<string name="biometric_dialog_wrong_pattern" msgid="8954812279840889029">"Netinkamas atrakinimo piešinys"</string>
<string name="biometric_dialog_wrong_password" msgid="69477929306843790">"Netinkamas slaptažodis"</string>
<string name="biometric_dialog_credential_too_many_attempts" msgid="3083141271737748716">"Per daug klaidingų bandymų.\nBandykite dar kartą po <xliff:g id="NUMBER">%d</xliff:g> sek."</string>
+ <!-- no translation found for work_challenge_emergency_button_text (8946588434515599288) -->
+ <skip />
<string name="biometric_dialog_credential_attempts_before_wipe" msgid="6751859711975516999">"Bandykite dar kartą. <xliff:g id="ATTEMPTS_0">%1$d</xliff:g> bandymas iš <xliff:g id="MAX_ATTEMPTS">%2$d</xliff:g>."</string>
<string name="biometric_dialog_last_attempt_before_wipe_dialog_title" msgid="2874250099278693477">"Duomenys bus ištrinti"</string>
<string name="biometric_dialog_last_pattern_attempt_before_wipe_device" msgid="6562299244825817598">"Jei kitu bandymu nupiešite netinkamą atrakinimo piešinį, šio įrenginio duomenys bus ištrinti."</string>
@@ -411,6 +415,8 @@
<string name="media_projection_entry_app_permission_dialog_warning_entire_screen" msgid="8736391633234144237">"Kai bendrinate, įrašote ar perduodate turinį, „<xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g>“ gali pasiekti viską, kas rodoma ekrane ar leidžiama įrenginyje. Todėl būkite atsargūs naudodami slaptažodžius, išsamią mokėjimo metodo informaciją, pranešimus, nuotraukas ir garso bei vaizdo įrašus."</string>
<string name="media_projection_entry_app_permission_dialog_warning_single_app" msgid="5211695779082563959">"Kai bendrinate, įrašote ar perduodate programą, „<xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g>“ gali pasiekti viską, kas rodoma ar leidžiama programoje. Todėl būkite atsargūs naudodami slaptažodžius, išsamią mokėjimo metodo informaciją, pranešimus, nuotraukas ir garso bei vaizdo įrašus."</string>
<string name="media_projection_entry_app_permission_dialog_continue" msgid="295463518195075840">"Pradėti"</string>
+ <!-- no translation found for media_projection_entry_app_permission_dialog_single_app_disabled (8999903044874669995) -->
+ <skip />
<string name="media_projection_entry_cast_permission_dialog_title" msgid="8860150223172993547">"Pradėti perdavimą?"</string>
<string name="media_projection_entry_cast_permission_dialog_warning_entire_screen" msgid="1986212276016817231">"Kai perduodate turinį, „Android“ gali pasiekti viską, kas rodoma ekrane ar leidžiama įrenginyje. Todėl būkite atsargūs naudodami slaptažodžius, išsamią mokėjimo metodo informaciją, pranešimus, nuotraukas ir garso bei vaizdo įrašus."</string>
<string name="media_projection_entry_cast_permission_dialog_warning_single_app" msgid="9900961380294292">"Kai perduodate programą, „Android“ gali pasiekti viską, kas rodoma ar leidžiama toje programoje. Todėl būkite atsargūs naudodami slaptažodžius, išsamią mokėjimo metodo informaciją, pranešimus, nuotraukas ir garso bei vaizdo įrašus."</string>
@@ -865,8 +871,7 @@
<string name="magnification_mode_switch_state_window" msgid="8597100249594076965">"Didinti ekrano dalį"</string>
<string name="magnification_open_settings_click_label" msgid="6151849212725923363">"Atidaryti didinimo nustatymus"</string>
<string name="magnification_close_settings_click_label" msgid="4642477260651704517">"Uždaryti didinimo nustatymus"</string>
- <!-- no translation found for magnification_exit_edit_mode_click_label (1664818325144887117) -->
- <skip />
+ <string name="magnification_exit_edit_mode_click_label" msgid="1664818325144887117">"Išeiti iš redagavimo režimo"</string>
<string name="magnification_drag_corner_to_resize" msgid="1249766311052418130">"Norėdami keisti dydį, vilkite kampą"</string>
<string name="accessibility_allow_diagonal_scrolling" msgid="3258050349191496398">"Slinkimo įstrižai leidimas"</string>
<string name="accessibility_resize" msgid="5733759136600611551">"Pakeisti dydį"</string>
diff --git a/packages/SystemUI/res/values-lv/strings.xml b/packages/SystemUI/res/values-lv/strings.xml
index 972380c..c577473 100644
--- a/packages/SystemUI/res/values-lv/strings.xml
+++ b/packages/SystemUI/res/values-lv/strings.xml
@@ -57,6 +57,8 @@
<string name="hdmi_cec_set_menu_language_description" msgid="8176716678074126619">"Sistēmas valodas maiņu pieprasīja cita ierīce."</string>
<string name="hdmi_cec_set_menu_language_accept" msgid="2513689457281009578">"Mainīt valodu"</string>
<string name="hdmi_cec_set_menu_language_decline" msgid="7650721096558646011">"Paturēt pašreizējo valodu"</string>
+ <!-- no translation found for share_wifi_button_text (1285273973812029240) -->
+ <skip />
<string name="wifi_debugging_title" msgid="7300007687492186076">"Vai atļaut bezvadu atkļūdošanu šajā tīklā?"</string>
<string name="wifi_debugging_message" msgid="5461204211731802995">"Tīkla nosaukums (SSID)\n<xliff:g id="SSID_0">%1$s</xliff:g>\n\nWi‑Fi adrese (BSSID)\n<xliff:g id="BSSID_1">%2$s</xliff:g>"</string>
<string name="wifi_debugging_always" msgid="2968383799517975155">"Vienmēr atļaut šajā tīklā"</string>
@@ -158,6 +160,8 @@
<string name="biometric_dialog_wrong_pattern" msgid="8954812279840889029">"Nepareiza kombinācija"</string>
<string name="biometric_dialog_wrong_password" msgid="69477929306843790">"Nepareiza parole"</string>
<string name="biometric_dialog_credential_too_many_attempts" msgid="3083141271737748716">"Pārāk daudz neveiksmīgu mēģinājumu.\nMēģiniet vēlreiz pēc <xliff:g id="NUMBER">%d</xliff:g> sekundēm."</string>
+ <!-- no translation found for work_challenge_emergency_button_text (8946588434515599288) -->
+ <skip />
<string name="biometric_dialog_credential_attempts_before_wipe" msgid="6751859711975516999">"Mēģiniet vēlreiz (<xliff:g id="ATTEMPTS_0">%1$d</xliff:g>. mēģinājums no <xliff:g id="MAX_ATTEMPTS">%2$d</xliff:g>)."</string>
<string name="biometric_dialog_last_attempt_before_wipe_dialog_title" msgid="2874250099278693477">"Jūsu dati tiks dzēsti"</string>
<string name="biometric_dialog_last_pattern_attempt_before_wipe_device" msgid="6562299244825817598">"Ja nākamajā mēģinājumā ievadīsiet nepareizu kombināciju, dati šajā ierīcē tiks dzēsti."</string>
@@ -411,6 +415,8 @@
<string name="media_projection_entry_app_permission_dialog_warning_entire_screen" msgid="8736391633234144237">"Kopīgošanas, ierakstīšanas vai apraides laikā <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> var piekļūt visam, kas tiek rādīts jūsu ekrānā vai atskaņots jūsu ierīcē. Tāpēc piesardzīgi apejieties ar parolēm, maksājumu informāciju, ziņojumiem, fotoattēliem un audio un video saturu."</string>
<string name="media_projection_entry_app_permission_dialog_warning_single_app" msgid="5211695779082563959">"Lietotnes kopīgošanas, ierakstīšanas vai apraides laikā <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> var piekļūt visam, kas tiek rādīts vai atskaņots attiecīgajā lietotnē. Tāpēc piesardzīgi apejieties ar parolēm, maksājumu informāciju, ziņojumiem, fotoattēliem un audio un video saturu."</string>
<string name="media_projection_entry_app_permission_dialog_continue" msgid="295463518195075840">"Sākt"</string>
+ <!-- no translation found for media_projection_entry_app_permission_dialog_single_app_disabled (8999903044874669995) -->
+ <skip />
<string name="media_projection_entry_cast_permission_dialog_title" msgid="8860150223172993547">"Vai sākt apraidi?"</string>
<string name="media_projection_entry_cast_permission_dialog_warning_entire_screen" msgid="1986212276016817231">"Apraides laikā Android var piekļūt visam, kas tiek rādīts jūsu ekrānā vai atskaņots jūsu ierīcē. Tāpēc piesardzīgi apejieties ar parolēm, maksājumu informāciju, ziņojumiem, fotoattēliem un audio un video saturu."</string>
<string name="media_projection_entry_cast_permission_dialog_warning_single_app" msgid="9900961380294292">"Lietotnes apraides laikā Android var piekļūt visam, kas tiek rādīts vai atskaņots attiecīgajā lietotnē. Tāpēc piesardzīgi apejieties ar parolēm, maksājumu informāciju, ziņojumiem, fotoattēliem un audio un video saturu."</string>
@@ -865,8 +871,7 @@
<string name="magnification_mode_switch_state_window" msgid="8597100249594076965">"Palielināt ekrāna daļu"</string>
<string name="magnification_open_settings_click_label" msgid="6151849212725923363">"Atvērt palielinājuma iestatījumus"</string>
<string name="magnification_close_settings_click_label" msgid="4642477260651704517">"Aizvērt palielinājuma iestatījumus"</string>
- <!-- no translation found for magnification_exit_edit_mode_click_label (1664818325144887117) -->
- <skip />
+ <string name="magnification_exit_edit_mode_click_label" msgid="1664818325144887117">"Iziet no rediģēšanas režīma"</string>
<string name="magnification_drag_corner_to_resize" msgid="1249766311052418130">"Velciet stūri, lai mainītu izmērus"</string>
<string name="accessibility_allow_diagonal_scrolling" msgid="3258050349191496398">"Atļaut ritināšanu pa diagonāli"</string>
<string name="accessibility_resize" msgid="5733759136600611551">"Mainīt lielumu"</string>
diff --git a/packages/SystemUI/res/values-mk/strings.xml b/packages/SystemUI/res/values-mk/strings.xml
index 4b1e5a8..9d92b5f 100644
--- a/packages/SystemUI/res/values-mk/strings.xml
+++ b/packages/SystemUI/res/values-mk/strings.xml
@@ -57,6 +57,8 @@
<string name="hdmi_cec_set_menu_language_description" msgid="8176716678074126619">"Побарана е промена на системскиот јазик од друг уред"</string>
<string name="hdmi_cec_set_menu_language_accept" msgid="2513689457281009578">"Промени го јазикот"</string>
<string name="hdmi_cec_set_menu_language_decline" msgid="7650721096558646011">"Зачувај го тековниот јазик"</string>
+ <!-- no translation found for share_wifi_button_text (1285273973812029240) -->
+ <skip />
<string name="wifi_debugging_title" msgid="7300007687492186076">"Да се дозволи безжично отстранување грешки на мрежава?"</string>
<string name="wifi_debugging_message" msgid="5461204211731802995">"Име на мрежата (SSID)\n<xliff:g id="SSID_0">%1$s</xliff:g>\n\nWi‑Fi адреса (BSSID)\n<xliff:g id="BSSID_1">%2$s</xliff:g>"</string>
<string name="wifi_debugging_always" msgid="2968383799517975155">"Секогаш дозволувај на оваа мрежа"</string>
@@ -158,6 +160,8 @@
<string name="biometric_dialog_wrong_pattern" msgid="8954812279840889029">"Погрешна шема"</string>
<string name="biometric_dialog_wrong_password" msgid="69477929306843790">"Погрешна лозинка"</string>
<string name="biometric_dialog_credential_too_many_attempts" msgid="3083141271737748716">"Премногу погрешни обиди.\nОбидете се повторно за <xliff:g id="NUMBER">%d</xliff:g>секунди."</string>
+ <!-- no translation found for work_challenge_emergency_button_text (8946588434515599288) -->
+ <skip />
<string name="biometric_dialog_credential_attempts_before_wipe" msgid="6751859711975516999">"Обидете се повторно. Обид <xliff:g id="ATTEMPTS_0">%1$d</xliff:g> од <xliff:g id="MAX_ATTEMPTS">%2$d</xliff:g>."</string>
<string name="biometric_dialog_last_attempt_before_wipe_dialog_title" msgid="2874250099278693477">"Податоците ќе се избришат"</string>
<string name="biometric_dialog_last_pattern_attempt_before_wipe_device" msgid="6562299244825817598">"Ако внесете погрешна шема при следниот обид, податоците на уредов ќе се избришат."</string>
@@ -411,6 +415,8 @@
<string name="media_projection_entry_app_permission_dialog_warning_entire_screen" msgid="8736391633234144237">"Кога споделувате, снимате или емитувате, <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> има пристап до сѐ што е видливо на вашиот екран или пуштено на вашиот уред. Затоа, бидете внимателни со работи како лозинки, детали за плаќање, пораки, фотографии и аудио и видео."</string>
<string name="media_projection_entry_app_permission_dialog_warning_single_app" msgid="5211695779082563959">"Кога споделувате, снимате или емитувате апликација, <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> има пристап до сѐ што се прикажува или пушта на таа апликација. Затоа, бидете внимателни со работи како лозинки, детали за плаќање, пораки, фотографии и аудио и видео."</string>
<string name="media_projection_entry_app_permission_dialog_continue" msgid="295463518195075840">"Започни"</string>
+ <!-- no translation found for media_projection_entry_app_permission_dialog_single_app_disabled (8999903044874669995) -->
+ <skip />
<string name="media_projection_entry_cast_permission_dialog_title" msgid="8860150223172993547">"Да се започне со емитување?"</string>
<string name="media_projection_entry_cast_permission_dialog_warning_entire_screen" msgid="1986212276016817231">"Кога емитувате, Android има пристап до сѐ што е видливо на вашиот екран или пуштено на вашиот уред. Затоа, бидете внимателни со работи како лозинки, детали за плаќање, пораки, фотографии и аудио и видео."</string>
<string name="media_projection_entry_cast_permission_dialog_warning_single_app" msgid="9900961380294292">"Додека емитувате апликација, Android има пристап до сѐ што се прикажува или пушта на таа апликација. Затоа, бидете внимателни со лозинки, детали за плаќање, пораки фотографии и аудио и видео."</string>
@@ -865,8 +871,7 @@
<string name="magnification_mode_switch_state_window" msgid="8597100249594076965">"Зголемувајте дел од екранот"</string>
<string name="magnification_open_settings_click_label" msgid="6151849212725923363">"Отвори поставки за зголемување"</string>
<string name="magnification_close_settings_click_label" msgid="4642477260651704517">"Затворете ги поставките за зголемување"</string>
- <!-- no translation found for magnification_exit_edit_mode_click_label (1664818325144887117) -->
- <skip />
+ <string name="magnification_exit_edit_mode_click_label" msgid="1664818325144887117">"Излегување од „Режим на изменување“"</string>
<string name="magnification_drag_corner_to_resize" msgid="1249766311052418130">"Повлечете на аголот за да ја промените големината"</string>
<string name="accessibility_allow_diagonal_scrolling" msgid="3258050349191496398">"Дозволете дијагонално лизгање"</string>
<string name="accessibility_resize" msgid="5733759136600611551">"Промени големина"</string>
diff --git a/packages/SystemUI/res/values-ml/strings.xml b/packages/SystemUI/res/values-ml/strings.xml
index 7202f5e..a33eb4b 100644
--- a/packages/SystemUI/res/values-ml/strings.xml
+++ b/packages/SystemUI/res/values-ml/strings.xml
@@ -57,6 +57,8 @@
<string name="hdmi_cec_set_menu_language_description" msgid="8176716678074126619">"സിസ്റ്റത്തിന്റെ ഭാഷ മാറ്റാൻ മറ്റൊരു ഉപകരണം അഭ്യർത്ഥിച്ചു"</string>
<string name="hdmi_cec_set_menu_language_accept" msgid="2513689457281009578">"ഭാഷ മാറ്റുക"</string>
<string name="hdmi_cec_set_menu_language_decline" msgid="7650721096558646011">"നിലവിലെ ഭാഷ നിലനിർത്തുക"</string>
+ <!-- no translation found for share_wifi_button_text (1285273973812029240) -->
+ <skip />
<string name="wifi_debugging_title" msgid="7300007687492186076">"ഈ നെറ്റ്വർക്കിൽ വയർലെസ് ഡീബഗ്ഗിംഗ് അനുവദിക്കണോ?"</string>
<string name="wifi_debugging_message" msgid="5461204211731802995">"നെറ്റ്വർക്കിന്റെ പേര് (SSID)\n<xliff:g id="SSID_0">%1$s</xliff:g>\n\nവൈഫൈ വിലാസം (BSSID)\n<xliff:g id="BSSID_1">%2$s</xliff:g>"</string>
<string name="wifi_debugging_always" msgid="2968383799517975155">"ഈ നെറ്റ്വർക്കിൽ എപ്പോഴും അനുവദിക്കുക"</string>
@@ -158,6 +160,8 @@
<string name="biometric_dialog_wrong_pattern" msgid="8954812279840889029">"പാറ്റേൺ തെറ്റാണ്"</string>
<string name="biometric_dialog_wrong_password" msgid="69477929306843790">"പാസ്വേഡ് തെറ്റാണ്"</string>
<string name="biometric_dialog_credential_too_many_attempts" msgid="3083141271737748716">"നിരവധി തെറ്റായ ശ്രമങ്ങൾ. \n<xliff:g id="NUMBER">%d</xliff:g> സെക്കൻഡിൽ വീണ്ടും ശ്രമിക്കുക."</string>
+ <!-- no translation found for work_challenge_emergency_button_text (8946588434515599288) -->
+ <skip />
<string name="biometric_dialog_credential_attempts_before_wipe" msgid="6751859711975516999">"വീണ്ടും ശ്രമിക്കുക. <xliff:g id="MAX_ATTEMPTS">%2$d</xliff:g> ശ്രമങ്ങളിൽ <xliff:g id="ATTEMPTS_0">%1$d</xliff:g> ശ്രമം."</string>
<string name="biometric_dialog_last_attempt_before_wipe_dialog_title" msgid="2874250099278693477">"നിങ്ങളുടെ ഡാറ്റ ഇല്ലാതാക്കപ്പെടും"</string>
<string name="biometric_dialog_last_pattern_attempt_before_wipe_device" msgid="6562299244825817598">"അടുത്ത തവണയും നിങ്ങൾ തെറ്റായ പാറ്റേൺ നൽകിയാൽ, ഈ ഉപകരണത്തിലെ ഡാറ്റ ഇല്ലാതാക്കപ്പെടും."</string>
@@ -411,6 +415,8 @@
<string name="media_projection_entry_app_permission_dialog_warning_entire_screen" msgid="8736391633234144237">"പങ്കിടുമ്പോൾ, റെക്കോർഡ് ചെയ്യുമ്പോൾ അല്ലെങ്കിൽ കാസ്റ്റ് ചെയ്യുമ്പോൾ, <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> എന്നതിന് നിങ്ങളുടെ സ്ക്രീനിൽ ദൃശ്യമാകുന്നതോ ഉപകരണത്തിൽ പ്ലേ ചെയ്യുന്നതോ ആയ ഏത് കാര്യത്തിലേക്കും ആക്സസ് ഉണ്ട്. അതിനാൽ പാസ്വേഡുകൾ, പേയ്മെന്റ് വിശദാംശങ്ങൾ, സന്ദേശങ്ങൾ, ഫോട്ടോകൾ, ഓഡിയോ, വീഡിയോ എന്നിവ പോലുള്ള കാര്യങ്ങൾ നൽകുമ്പോൾ സൂക്ഷിക്കുക."</string>
<string name="media_projection_entry_app_permission_dialog_warning_single_app" msgid="5211695779082563959">"ഒരു ആപ്പ് പങ്കിടുമ്പോൾ, റെക്കോർഡ് ചെയ്യുമ്പോൾ അല്ലെങ്കിൽ കാസ്റ്റ് ചെയ്യുമ്പോൾ, <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> എന്നതിന് ആ ആപ്പിൽ കാണിക്കുന്ന അല്ലെങ്കിൽ പ്ലേ ചെയ്യുന്ന എല്ലാത്തിലേക്കും ആക്സസ് ഉണ്ട്. അതിനാൽ പാസ്വേഡുകൾ, പേയ്മെന്റ് വിശദാംശങ്ങൾ, സന്ദേശങ്ങൾ, ഫോട്ടോകൾ, ഓഡിയോ, വീഡിയോ എന്നിവ പോലുള്ള കാര്യങ്ങൾ നൽകുമ്പോൾ സൂക്ഷിക്കുക."</string>
<string name="media_projection_entry_app_permission_dialog_continue" msgid="295463518195075840">"ആരംഭിക്കുക"</string>
+ <!-- no translation found for media_projection_entry_app_permission_dialog_single_app_disabled (8999903044874669995) -->
+ <skip />
<string name="media_projection_entry_cast_permission_dialog_title" msgid="8860150223172993547">"കാസ്റ്റ് ചെയ്യാൻ ആരംഭിക്കണോ?"</string>
<string name="media_projection_entry_cast_permission_dialog_warning_entire_screen" msgid="1986212276016817231">"നിങ്ങൾ കാസ്റ്റ് ചെയ്യുമ്പോൾ, Android-ന് സ്ക്രീനിൽ ദൃശ്യമാകുന്നതോ ഉപകരണത്തിൽ പ്ലേ ചെയ്യുന്നതോ ആയ ഏത് കാര്യത്തിലേക്കും ആക്സസ് ഉണ്ട്. അതിനാൽ പാസ്വേഡുകൾ, പേയ്മെന്റ് വിശദാംശങ്ങൾ, സന്ദേശങ്ങൾ, ഫോട്ടോകൾ, ഓഡിയോ, വീഡിയോ എന്നിവ പോലുള്ള കാര്യങ്ങൾ നൽകുമ്പോൾ സൂക്ഷിക്കുക."</string>
<string name="media_projection_entry_cast_permission_dialog_warning_single_app" msgid="9900961380294292">"നിങ്ങൾ ഒരു ആപ്പ് കാസ്റ്റ് ചെയ്യുമ്പോൾ, Android-ന് ആ ആപ്പിൽ കാണിക്കുന്നതോ പ്ലേ ചെയ്യുന്നതോ ആയ എല്ലാത്തിലേക്കും ആക്സസ് ഉണ്ട്. അതിനാൽ പാസ്വേഡുകൾ, പേയ്മെന്റ് വിശദാംശങ്ങൾ, സന്ദേശങ്ങൾ, ഫോട്ടോകൾ, ഓഡിയോ, വീഡിയോ എന്നിവ പോലുള്ള കാര്യങ്ങൾ നൽകുമ്പോൾ സൂക്ഷിക്കുക."</string>
diff --git a/packages/SystemUI/res/values-mn/strings.xml b/packages/SystemUI/res/values-mn/strings.xml
index 470e0fd..da9d794 100644
--- a/packages/SystemUI/res/values-mn/strings.xml
+++ b/packages/SystemUI/res/values-mn/strings.xml
@@ -57,6 +57,8 @@
<string name="hdmi_cec_set_menu_language_description" msgid="8176716678074126619">"Өөр төхөөрөмжөөс системийн хэлийг өөрчлөх хүсэлт тавьсан"</string>
<string name="hdmi_cec_set_menu_language_accept" msgid="2513689457281009578">"Хэл солих"</string>
<string name="hdmi_cec_set_menu_language_decline" msgid="7650721096558646011">"Одоогийн хэлээр байлгах"</string>
+ <!-- no translation found for share_wifi_button_text (1285273973812029240) -->
+ <skip />
<string name="wifi_debugging_title" msgid="7300007687492186076">"Энэ сүлжээн дээр wireless debugging-г зөвшөөрөх үү?"</string>
<string name="wifi_debugging_message" msgid="5461204211731802995">"Сүлжээний нэр (SSID)\n<xliff:g id="SSID_0">%1$s</xliff:g>\n\nWi‑Fi хаяг (BSSID)\n<xliff:g id="BSSID_1">%2$s</xliff:g>"</string>
<string name="wifi_debugging_always" msgid="2968383799517975155">"Энэ сүлжээн дээр үргэлж зөвшөөрөх"</string>
@@ -158,6 +160,8 @@
<string name="biometric_dialog_wrong_pattern" msgid="8954812279840889029">"Хээ буруу байна"</string>
<string name="biometric_dialog_wrong_password" msgid="69477929306843790">"Нууц үг буруу байна"</string>
<string name="biometric_dialog_credential_too_many_attempts" msgid="3083141271737748716">"Хэт олон удаа буруу оруулсан байна.\n<xliff:g id="NUMBER">%d</xliff:g> секундийн дараа дахин оролдоно уу."</string>
+ <!-- no translation found for work_challenge_emergency_button_text (8946588434515599288) -->
+ <skip />
<string name="biometric_dialog_credential_attempts_before_wipe" msgid="6751859711975516999">"Дахин оролдоно уу. <xliff:g id="MAX_ATTEMPTS">%2$d</xliff:g>-с <xliff:g id="ATTEMPTS_0">%1$d</xliff:g> оролдлого үлдлээ."</string>
<string name="biometric_dialog_last_attempt_before_wipe_dialog_title" msgid="2874250099278693477">"Таны өгөгдлийг устгах болно"</string>
<string name="biometric_dialog_last_pattern_attempt_before_wipe_device" msgid="6562299244825817598">"Та дараагийн оролдлогоор буруу хээ оруулбал энэ төхөөрөмжийн өгөгдлийг устгах болно."</string>
@@ -411,6 +415,8 @@
<string name="media_projection_entry_app_permission_dialog_warning_entire_screen" msgid="8736391633234144237">"Таныг хуваалцаж, бичиж эсвэл дамжуулж байх үед <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> нь таны дэлгэцэд харагдаж буй зүйл эсвэл төхөөрөмжид тань тоглуулсан аливаа зүйлд хандах эрхтэй. Тиймээс нууц үг, төлбөрийн мэдээлэл, мессеж, зураг, аудио болон видео зэрэг зүйлд болгоомжтой хандаарай."</string>
<string name="media_projection_entry_app_permission_dialog_warning_single_app" msgid="5211695779082563959">"Таныг хуваалцаж, бичлэг хийж эсвэл апп дамжуулж байх үед <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> нь тухайн аппад харуулсан эсвэл тоглуулсан аливаа зүйлд хандах эрхтэй. Тиймээс нууц үг, төлбөрийн дэлгэрэнгүй, мессеж, зураг, аудио болон видео зэрэг бусад зүйлд болгоомжтой хандаарай."</string>
<string name="media_projection_entry_app_permission_dialog_continue" msgid="295463518195075840">"Эхлүүлэх"</string>
+ <!-- no translation found for media_projection_entry_app_permission_dialog_single_app_disabled (8999903044874669995) -->
+ <skip />
<string name="media_projection_entry_cast_permission_dialog_title" msgid="8860150223172993547">"Дамжуулж эхлэх үү?"</string>
<string name="media_projection_entry_cast_permission_dialog_warning_entire_screen" msgid="1986212276016817231">"Таныг дамжуулж байх үед Android таны дэлгэцэд харагдаж буй эсвэл төхөөрөмжид тань тоглуулсан аливаа зүйлд хандах эрхтэй. Тиймээс нууц үг, төлбөрийн мэдээлэл, мессеж, зураг, аудио болон видео зэрэг зүйлд болгоомжтой хандаарай."</string>
<string name="media_projection_entry_cast_permission_dialog_warning_single_app" msgid="9900961380294292">"Таныг апп дамжуулж байх үед Android тухайн аппад харуулсан эсвэл тоглуулсан аливаа зүйлд хандах эрхтэй. Тиймээс нууц үг, төлбөрийн мэдээлэл, мессеж, зураг, аудио болон видео зэрэг зүйлд болгоомжтой хандаарай."</string>
@@ -865,8 +871,7 @@
<string name="magnification_mode_switch_state_window" msgid="8597100249594076965">"Дэлгэцийн нэг хэсгийг томруулах"</string>
<string name="magnification_open_settings_click_label" msgid="6151849212725923363">"Томруулах тохиргоог нээх"</string>
<string name="magnification_close_settings_click_label" msgid="4642477260651704517">"Томруулах тохиргоог хаах"</string>
- <!-- no translation found for magnification_exit_edit_mode_click_label (1664818325144887117) -->
- <skip />
+ <string name="magnification_exit_edit_mode_click_label" msgid="1664818325144887117">"Засах горимоос гарах"</string>
<string name="magnification_drag_corner_to_resize" msgid="1249766311052418130">"Хэмжээг өөрчлөхийн тулд булангаас чирнэ үү"</string>
<string name="accessibility_allow_diagonal_scrolling" msgid="3258050349191496398">"Хөндлөн гүйлгэхийг зөвшөөрөх"</string>
<string name="accessibility_resize" msgid="5733759136600611551">"Хэмжээг өөрчлөх"</string>
diff --git a/packages/SystemUI/res/values-mr/strings.xml b/packages/SystemUI/res/values-mr/strings.xml
index f5abe78..5fcf947 100644
--- a/packages/SystemUI/res/values-mr/strings.xml
+++ b/packages/SystemUI/res/values-mr/strings.xml
@@ -57,6 +57,8 @@
<string name="hdmi_cec_set_menu_language_description" msgid="8176716678074126619">"दुसऱ्या डिव्हाइसद्वारे सिस्टीमची भाषा बदलण्याची विनंती केली गेली"</string>
<string name="hdmi_cec_set_menu_language_accept" msgid="2513689457281009578">"भाषा बदला"</string>
<string name="hdmi_cec_set_menu_language_decline" msgid="7650721096558646011">"सध्याची भाषा ठेवा"</string>
+ <!-- no translation found for share_wifi_button_text (1285273973812029240) -->
+ <skip />
<string name="wifi_debugging_title" msgid="7300007687492186076">"या नेटवर्कवर वायरलेस डीबगिंग करण्यासाठी अनुमती द्यायची का?"</string>
<string name="wifi_debugging_message" msgid="5461204211731802995">"नेटवर्कचे नाव (SSID)\n<xliff:g id="SSID_0">%1$s</xliff:g>\n\nवाय-फाय ॲड्रेस (BSSID)\n<xliff:g id="BSSID_1">%2$s</xliff:g>"</string>
<string name="wifi_debugging_always" msgid="2968383799517975155">"या नेटवर्कवर नेहमी अनुमती द्या"</string>
@@ -158,6 +160,8 @@
<string name="biometric_dialog_wrong_pattern" msgid="8954812279840889029">"चुकीचा पॅटर्न"</string>
<string name="biometric_dialog_wrong_password" msgid="69477929306843790">"चुकीचा पासवर्ड"</string>
<string name="biometric_dialog_credential_too_many_attempts" msgid="3083141271737748716">"बरेच चुकीचे प्रयत्न. \n <xliff:g id="NUMBER">%d</xliff:g> सेकंदांमध्ये पुन्हा प्रयत्न करा."</string>
+ <!-- no translation found for work_challenge_emergency_button_text (8946588434515599288) -->
+ <skip />
<string name="biometric_dialog_credential_attempts_before_wipe" msgid="6751859711975516999">"पुन्हा प्रयत्न करा. <xliff:g id="MAX_ATTEMPTS">%2$d</xliff:g> पैकी <xliff:g id="ATTEMPTS_0">%1$d</xliff:g> प्रयत्न."</string>
<string name="biometric_dialog_last_attempt_before_wipe_dialog_title" msgid="2874250099278693477">"तुमचा डेटा हटवला जाईल"</string>
<string name="biometric_dialog_last_pattern_attempt_before_wipe_device" msgid="6562299244825817598">"तुम्ही पुढील प्रयत्नात चुकीचा पॅटर्न एंटर केल्यास, या डिव्हाइसचा डेटा हटवला जाईल."</string>
@@ -411,6 +415,8 @@
<string name="media_projection_entry_app_permission_dialog_warning_entire_screen" msgid="8736391633234144237">"तुम्ही शेअर, रेकॉर्ड किंवा कास्ट करत असताना, <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> ला तुमच्या स्क्रीनवर दाखवलेल्या किंवा डिव्हाइसवर प्ले केलेल्या कोणत्याही गोष्टीचा अॅक्सेस असतो. त्यामुळे पासवर्ड, पेमेंट तपशील, मेसेज, फोटो आणि ऑडिओ व व्हिडिओ यांसारख्या गोष्टींबाबत सावधगिरी बाळगा."</string>
<string name="media_projection_entry_app_permission_dialog_warning_single_app" msgid="5211695779082563959">"तुम्ही एखादे अॅप शेअर, रेकॉर्ड किंवा कास्ट करत असताना, <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> ला त्या अॅपवर दाखवलेल्या किंवा प्ले केलेल्या कोणत्याही गोष्टीचा अॅक्सेस असतो. त्यामुळे पासवर्ड, पेमेंट तपशील, मेसेज, फोटो आणि ऑडिओ व व्हिडिओ यांसारख्या गोष्टींबाबत सावधगिरी बाळगा."</string>
<string name="media_projection_entry_app_permission_dialog_continue" msgid="295463518195075840">"सुरुवात करा"</string>
+ <!-- no translation found for media_projection_entry_app_permission_dialog_single_app_disabled (8999903044874669995) -->
+ <skip />
<string name="media_projection_entry_cast_permission_dialog_title" msgid="8860150223172993547">"कास्ट करणे सुरू करायचे आहे का?"</string>
<string name="media_projection_entry_cast_permission_dialog_warning_entire_screen" msgid="1986212276016817231">"तुम्ही कास्ट करत असताना, Android ला तुमच्या स्क्रीनवर दाखवलेल्या किंवा डिव्हाइसवर प्ले केलेल्या कोणत्याही गोष्टीचा अॅक्सेस असतो. त्यामुळे पासवर्ड, पेमेंट तपशील, मेसेज, फोटो आणि ऑडिओ व व्हिडिओ यांसारख्या गोष्टींबाबत सावधगिरी बाळगा."</string>
<string name="media_projection_entry_cast_permission_dialog_warning_single_app" msgid="9900961380294292">"तुम्ही एखादे अॅप कास्ट करत असताना, Android ला त्या अॅपवर दाखवलेल्या किंवा प्ले केलेल्या कोणत्याही गोष्टीचा अॅक्सेस असतो. त्यामुळे पासवर्ड, पेमेंट तपशील, मेसेज, फोटो आणि ऑडिओ व व्हिडिओ यांसारख्या गोष्टींबाबत सावधगिरी बाळगा."</string>
@@ -865,8 +871,7 @@
<string name="magnification_mode_switch_state_window" msgid="8597100249594076965">"स्क्रीनचा काही भाग मॅग्निफाय करा"</string>
<string name="magnification_open_settings_click_label" msgid="6151849212725923363">"मॅग्निफिकेशन सेटिंग्ज उघडा"</string>
<string name="magnification_close_settings_click_label" msgid="4642477260651704517">"मॅग्निफिकेशन सेटिंग्ज बंद करा"</string>
- <!-- no translation found for magnification_exit_edit_mode_click_label (1664818325144887117) -->
- <skip />
+ <string name="magnification_exit_edit_mode_click_label" msgid="1664818325144887117">"संपादन मोडमधून बाहेर पडा"</string>
<string name="magnification_drag_corner_to_resize" msgid="1249766311052418130">"आकार बदलण्यासाठी कोपरा ड्रॅग करा"</string>
<string name="accessibility_allow_diagonal_scrolling" msgid="3258050349191496398">"तिरपे स्क्रोल करण्याची अनुमती द्या"</string>
<string name="accessibility_resize" msgid="5733759136600611551">"आकार बदला"</string>
diff --git a/packages/SystemUI/res/values-ms/strings.xml b/packages/SystemUI/res/values-ms/strings.xml
index 6b74c43..de88987 100644
--- a/packages/SystemUI/res/values-ms/strings.xml
+++ b/packages/SystemUI/res/values-ms/strings.xml
@@ -57,6 +57,8 @@
<string name="hdmi_cec_set_menu_language_description" msgid="8176716678074126619">"Permintaan pertukaran bahasa sistem oleh peranti lain"</string>
<string name="hdmi_cec_set_menu_language_accept" msgid="2513689457281009578">"Tukar bahasa"</string>
<string name="hdmi_cec_set_menu_language_decline" msgid="7650721096558646011">"Kekalkan bahasa semasa"</string>
+ <!-- no translation found for share_wifi_button_text (1285273973812029240) -->
+ <skip />
<string name="wifi_debugging_title" msgid="7300007687492186076">"Benarkan penyahpepijatan wayarles pada rangkaian ini?"</string>
<string name="wifi_debugging_message" msgid="5461204211731802995">"Nama Rangkaian (SSID)\n<xliff:g id="SSID_0">%1$s</xliff:g>\n\nAlamat Wi‑Fi (BSSID)\n<xliff:g id="BSSID_1">%2$s</xliff:g>"</string>
<string name="wifi_debugging_always" msgid="2968383799517975155">"Sentiasa benarkan pada rangkaian ini"</string>
@@ -158,6 +160,8 @@
<string name="biometric_dialog_wrong_pattern" msgid="8954812279840889029">"Corak salah"</string>
<string name="biometric_dialog_wrong_password" msgid="69477929306843790">"Kata laluan salah"</string>
<string name="biometric_dialog_credential_too_many_attempts" msgid="3083141271737748716">"Terlalu banyak percubaan yang salah.\nCuba lagi dalam masa <xliff:g id="NUMBER">%d</xliff:g> saat."</string>
+ <!-- no translation found for work_challenge_emergency_button_text (8946588434515599288) -->
+ <skip />
<string name="biometric_dialog_credential_attempts_before_wipe" msgid="6751859711975516999">"Cuba lagi. Percubaan <xliff:g id="ATTEMPTS_0">%1$d</xliff:g> daripada <xliff:g id="MAX_ATTEMPTS">%2$d</xliff:g>."</string>
<string name="biometric_dialog_last_attempt_before_wipe_dialog_title" msgid="2874250099278693477">"Data anda akan dipadamkan"</string>
<string name="biometric_dialog_last_pattern_attempt_before_wipe_device" msgid="6562299244825817598">"Jika anda memasukkan corak yang salah pada percubaan seterusnya, data peranti ini akan dipadamkan."</string>
@@ -411,6 +415,8 @@
<string name="media_projection_entry_app_permission_dialog_warning_entire_screen" msgid="8736391633234144237">"Apabila anda membuat perkongsian, rakaman atau penghantaran, <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> boleh mengakses apa-apa sahaja yang boleh dilihat pada skrin anda atau dimainkan pada peranti anda. Oleh hal yang demikian, berhati-hati dengan perkara seperti kata laluan, butiran pembayaran, mesej, foto dan audio serta video."</string>
<string name="media_projection_entry_app_permission_dialog_warning_single_app" msgid="5211695779082563959">"Apabila anda berkongsi, merakam atau menghantar apl, <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> boleh mengakses apa-apa sahaja yang ditunjukan atau dimainkan pada apl tersebut. Oleh hal yang demikian, berhati-hati dengan perkara seperti kata laluan, butiran pembayaran, mesej, foto dan audio serta video."</string>
<string name="media_projection_entry_app_permission_dialog_continue" msgid="295463518195075840">"Mula"</string>
+ <!-- no translation found for media_projection_entry_app_permission_dialog_single_app_disabled (8999903044874669995) -->
+ <skip />
<string name="media_projection_entry_cast_permission_dialog_title" msgid="8860150223172993547">"Mulakan penghantaran?"</string>
<string name="media_projection_entry_cast_permission_dialog_warning_entire_screen" msgid="1986212276016817231">"Apabila anda membuat penghantaran, Android boleh mengakses apa-apa sahaja yang boleh dilihat pada skrin anda atau dimainkan pada peranti anda. Oleh hal yang demikian, berhati-hati dengan perkara seperti kata laluan, butiran pembayaran, mesej, foto dan audio serta video."</string>
<string name="media_projection_entry_cast_permission_dialog_warning_single_app" msgid="9900961380294292">"Apabila anda menghantar apl, Android boleh mengakses apa-apa sahaja yang ditunjukan atau dimainkan pada apl itu. Oleh hal yang demikian, berhati-hati dengan perkara seperti kata laluan, butiran pembayaran, mesej, foto dan audio serta video."</string>
diff --git a/packages/SystemUI/res/values-my/strings.xml b/packages/SystemUI/res/values-my/strings.xml
index 2e2567d..1f54ef6 100644
--- a/packages/SystemUI/res/values-my/strings.xml
+++ b/packages/SystemUI/res/values-my/strings.xml
@@ -57,6 +57,8 @@
<string name="hdmi_cec_set_menu_language_description" msgid="8176716678074126619">"စက်နောက်တစ်ခုက စနစ်၏ ဘာသာစကားပြောင်းရန် တောင်းဆိုထားသည်"</string>
<string name="hdmi_cec_set_menu_language_accept" msgid="2513689457281009578">"ဘာသာစကားပြောင်းရန်"</string>
<string name="hdmi_cec_set_menu_language_decline" msgid="7650721096558646011">"လက်ရှိဘာသာစကားဆက်သုံးရန်"</string>
+ <!-- no translation found for share_wifi_button_text (1285273973812029240) -->
+ <skip />
<string name="wifi_debugging_title" msgid="7300007687492186076">"ဤကွန်ရက်တွင် ကြိုးမဲ့ အမှားရှာပြင်ခြင်းကို ခွင့်ပြုမလား။"</string>
<string name="wifi_debugging_message" msgid="5461204211731802995">"ကွန်ရက်အမည် (SSID)\n<xliff:g id="SSID_0">%1$s</xliff:g>\n\nWi‑Fi လိပ်စာ (BSSID)\n<xliff:g id="BSSID_1">%2$s</xliff:g>"</string>
<string name="wifi_debugging_always" msgid="2968383799517975155">"ဤကွန်ရက်ကို အမြဲခွင့်ပြုပါ"</string>
@@ -158,6 +160,8 @@
<string name="biometric_dialog_wrong_pattern" msgid="8954812279840889029">"ပုံစံ မှားနေသည်"</string>
<string name="biometric_dialog_wrong_password" msgid="69477929306843790">"စကားဝှက် မှားနေသည်"</string>
<string name="biometric_dialog_credential_too_many_attempts" msgid="3083141271737748716">"မှားသည့် အကြိမ် အရေအတွက် အလွန်များသည်။\n<xliff:g id="NUMBER">%d</xliff:g>စက္ကန့်အကြာတွင် ထပ်စမ်းကြည့်ပါ။"</string>
+ <!-- no translation found for work_challenge_emergency_button_text (8946588434515599288) -->
+ <skip />
<string name="biometric_dialog_credential_attempts_before_wipe" msgid="6751859711975516999">"ထပ်စမ်းကြည့်ပါ။ <xliff:g id="MAX_ATTEMPTS">%2$d</xliff:g> ကြိမ်အနက်မှ <xliff:g id="ATTEMPTS_0">%1$d</xliff:g> ကြိမ် ဖြစ်သည်။"</string>
<string name="biometric_dialog_last_attempt_before_wipe_dialog_title" msgid="2874250099278693477">"သင်၏ဒေတာများ ပျက်သွားပါလိမ့်မည်"</string>
<string name="biometric_dialog_last_pattern_attempt_before_wipe_device" msgid="6562299244825817598">"မှားယွင်းသည့် ပုံစံကို နောက်တစ်ကြိမ်ထည့်သွင်းပါက ဤစက်ပစ္စည်းပေါ်ရှိ ဒေတာများကို ဖျက်လိုက်ပါမည်။"</string>
@@ -411,6 +415,8 @@
<string name="media_projection_entry_app_permission_dialog_warning_entire_screen" msgid="8736391633234144237">"မျှဝေ၊ ရုပ်သံဖမ်း (သို့) ကာစ်လုပ်သည့်အခါ <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> သည် သင့်ဖန်သားပြင်ရှိ မြင်နိုင်သည့် (သို့) သင့်စက်တွင် ဖွင့်ထားသည့် အရာအားလုံးကို တွေ့နိုင်သည်။ စကားဝှက်၊ ငွေပေးချေမှု အချက်အလက်၊ မက်ဆေ့ဂျ်၊ ဓာတ်ပုံ၊ အသံနှင့် ဗီဒီယိုကဲ့သို့ အရာများကို ဂရုစိုက်ပါ။"</string>
<string name="media_projection_entry_app_permission_dialog_warning_single_app" msgid="5211695779082563959">"အက်ပ်တစ်ခုဖြင့် မျှဝေ၊ ရုပ်သံဖမ်း (သို့) ကာစ်လုပ်သည့်အခါ <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> သည် ယင်းအက်ပ်တွင် ပြထားသည့် (သို့) ဖွင့်ထားသည့် အရာအားလုံးကို တွေ့နိုင်သည်။ ထို့ကြောင့် စကားဝှက်၊ ငွေပေးချေမှု အချက်အလက်၊ မက်ဆေ့ဂျ်၊ ဓာတ်ပုံ၊ အသံနှင့် ဗီဒီယိုကဲ့သို့အရာများကို ဂရုစိုက်ပါ။"</string>
<string name="media_projection_entry_app_permission_dialog_continue" msgid="295463518195075840">"စတင်ရန်"</string>
+ <!-- no translation found for media_projection_entry_app_permission_dialog_single_app_disabled (8999903044874669995) -->
+ <skip />
<string name="media_projection_entry_cast_permission_dialog_title" msgid="8860150223172993547">"ကာစ်လုပ်ခြင်း စမလား။"</string>
<string name="media_projection_entry_cast_permission_dialog_warning_entire_screen" msgid="1986212276016817231">"ကာစ်လုပ်သည့်အခါ Android သည် သင့်ဖန်သားပြင်ရှိ မြင်နိုင်သည့် (သို့) သင့်စက်တွင် ဖွင့်ထားသည့် အရာအားလုံးကို တွေ့နိုင်သည်။ စကားဝှက်၊ ငွေပေးချေမှု အချက်အလက်၊ မက်ဆေ့ဂျ်၊ ဓာတ်ပုံ၊ အသံနှင့် ဗီဒီယိုကဲ့သို့ အရာများကို ဂရုစိုက်ပါ။"</string>
<string name="media_projection_entry_cast_permission_dialog_warning_single_app" msgid="9900961380294292">"အက်ပ်တစ်ခုကို ကာစ်လုပ်သည့်အခါ Android သည် ယင်းအက်ပ်တွင် ပြထားသည့် (သို့) ဖွင့်ထားသည့် အရာအားလုံးကို တွေ့နိုင်သည်။ ထို့ကြောင့် စကားဝှက်၊ ငွေပေးချေမှု အချက်အလက်၊ မက်ဆေ့ဂျ်၊ ဓာတ်ပုံ၊ အသံနှင့် ဗီဒီယိုကဲ့သို့ အရာများကို ဂရုစိုက်ပါ။"</string>
diff --git a/packages/SystemUI/res/values-nb/strings.xml b/packages/SystemUI/res/values-nb/strings.xml
index 8ef7794..54a5341 100644
--- a/packages/SystemUI/res/values-nb/strings.xml
+++ b/packages/SystemUI/res/values-nb/strings.xml
@@ -57,6 +57,8 @@
<string name="hdmi_cec_set_menu_language_description" msgid="8176716678074126619">"Bytte av systemspråk er forespurt av en annen enhet"</string>
<string name="hdmi_cec_set_menu_language_accept" msgid="2513689457281009578">"Bytt språk"</string>
<string name="hdmi_cec_set_menu_language_decline" msgid="7650721096558646011">"Behold gjeldende språk"</string>
+ <!-- no translation found for share_wifi_button_text (1285273973812029240) -->
+ <skip />
<string name="wifi_debugging_title" msgid="7300007687492186076">"Vil du tillate trådløs feilsøking på dette nettverket?"</string>
<string name="wifi_debugging_message" msgid="5461204211731802995">"Nettverksnavn (SSID)\n<xliff:g id="SSID_0">%1$s</xliff:g>\n\nWi‑Fi-adresse (BSSID)\n<xliff:g id="BSSID_1">%2$s</xliff:g>"</string>
<string name="wifi_debugging_always" msgid="2968383799517975155">"Tillat alltid på dette nettverket"</string>
@@ -158,6 +160,8 @@
<string name="biometric_dialog_wrong_pattern" msgid="8954812279840889029">"Feil mønster"</string>
<string name="biometric_dialog_wrong_password" msgid="69477929306843790">"Feil passord"</string>
<string name="biometric_dialog_credential_too_many_attempts" msgid="3083141271737748716">"For mange ugyldige forsøk.\nPrøv på nytt om <xliff:g id="NUMBER">%d</xliff:g> sekunder."</string>
+ <!-- no translation found for work_challenge_emergency_button_text (8946588434515599288) -->
+ <skip />
<string name="biometric_dialog_credential_attempts_before_wipe" msgid="6751859711975516999">"Prøv på nytt. Forsøk <xliff:g id="ATTEMPTS_0">%1$d</xliff:g> av <xliff:g id="MAX_ATTEMPTS">%2$d</xliff:g>."</string>
<string name="biometric_dialog_last_attempt_before_wipe_dialog_title" msgid="2874250099278693477">"Dataene dine blir slettet"</string>
<string name="biometric_dialog_last_pattern_attempt_before_wipe_device" msgid="6562299244825817598">"Hvis du oppgir feil mønster på neste forsøk, slettes dataene på denne enheten."</string>
@@ -411,6 +415,8 @@
<string name="media_projection_entry_app_permission_dialog_warning_entire_screen" msgid="8736391633234144237">"Når du deler, tar opp eller caster noe, har <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> tilgang til alt som vises på skjermen eller spilles av på enheten. Derfor bør du være forsiktig med for eksempel passord, betalingsopplysninger, meldinger, bilder, lyd og video."</string>
<string name="media_projection_entry_app_permission_dialog_warning_single_app" msgid="5211695779082563959">"Når du deler, tar opp eller caster en app, har <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> tilgang til alt som vises eller spilles av i den aktuelle appen. Derfor bør du være forsiktig med for eksempel passord, betalingsopplysninger, meldinger, bilder, lyd og video."</string>
<string name="media_projection_entry_app_permission_dialog_continue" msgid="295463518195075840">"Begynn"</string>
+ <!-- no translation found for media_projection_entry_app_permission_dialog_single_app_disabled (8999903044874669995) -->
+ <skip />
<string name="media_projection_entry_cast_permission_dialog_title" msgid="8860150223172993547">"Vil du begynne å caste?"</string>
<string name="media_projection_entry_cast_permission_dialog_warning_entire_screen" msgid="1986212276016817231">"Når du caster, har Android tilgang til alt som vises på skjermen eller spilles av på enheten. Derfor bør du være forsiktig med for eksempel passord, betalingsopplysninger, meldinger, bilder, lyd og video."</string>
<string name="media_projection_entry_cast_permission_dialog_warning_single_app" msgid="9900961380294292">"Når du caster en app, har Android tilgang til alt som vises eller spilles av i den aktuelle appen. Derfor bør du være forsiktig med for eksempel passord, betalingsopplysninger, meldinger, bilder, lyd og video."</string>
@@ -865,8 +871,7 @@
<string name="magnification_mode_switch_state_window" msgid="8597100249594076965">"Forstørr en del av skjermen"</string>
<string name="magnification_open_settings_click_label" msgid="6151849212725923363">"Åpne innstillinger for forstørring"</string>
<string name="magnification_close_settings_click_label" msgid="4642477260651704517">"Lukk forstørringsinnstillingene"</string>
- <!-- no translation found for magnification_exit_edit_mode_click_label (1664818325144887117) -->
- <skip />
+ <string name="magnification_exit_edit_mode_click_label" msgid="1664818325144887117">"Avslutt redigeringsmodus"</string>
<string name="magnification_drag_corner_to_resize" msgid="1249766311052418130">"Dra hjørnet for å endre størrelse"</string>
<string name="accessibility_allow_diagonal_scrolling" msgid="3258050349191496398">"Tillat diagonal rulling"</string>
<string name="accessibility_resize" msgid="5733759136600611551">"Endre størrelse"</string>
diff --git a/packages/SystemUI/res/values-ne/strings.xml b/packages/SystemUI/res/values-ne/strings.xml
index c56ebb6..29b5098 100644
--- a/packages/SystemUI/res/values-ne/strings.xml
+++ b/packages/SystemUI/res/values-ne/strings.xml
@@ -57,6 +57,8 @@
<string name="hdmi_cec_set_menu_language_description" msgid="8176716678074126619">"अर्को डिभाइसले सिस्टमको भाषा परिवर्तन गर्न अनुरोध गरेको छ"</string>
<string name="hdmi_cec_set_menu_language_accept" msgid="2513689457281009578">"भाषा परिवर्तन गर्नुहोस्"</string>
<string name="hdmi_cec_set_menu_language_decline" msgid="7650721096558646011">"अहिलेको भाषा राख्नुहोस्"</string>
+ <!-- no translation found for share_wifi_button_text (1285273973812029240) -->
+ <skip />
<string name="wifi_debugging_title" msgid="7300007687492186076">"यस नेटवर्कमा वायरलेस डिबगिङ सेवा प्रयोग गर्न दिने हो?"</string>
<string name="wifi_debugging_message" msgid="5461204211731802995">"नेटवर्कको नाम (SSID)\n<xliff:g id="SSID_0">%1$s</xliff:g>\n\nWi‑Fi ठेगाना (BSSID)\n<xliff:g id="BSSID_1">%2$s</xliff:g>"</string>
<string name="wifi_debugging_always" msgid="2968383799517975155">"यस नेटवर्कमा सधैँ अनुमति दिइयोस्"</string>
@@ -158,6 +160,8 @@
<string name="biometric_dialog_wrong_pattern" msgid="8954812279840889029">"प्याटर्न मिलेन"</string>
<string name="biometric_dialog_wrong_password" msgid="69477929306843790">"पासवर्ड मिलेन"</string>
<string name="biometric_dialog_credential_too_many_attempts" msgid="3083141271737748716">"अत्यन्तै धेरै पटक गलत प्रयास गरिए। \n <xliff:g id="NUMBER">%d</xliff:g>सेकेन्ड पछि पुनः प्रयास गर्नुहोस्।"</string>
+ <!-- no translation found for work_challenge_emergency_button_text (8946588434515599288) -->
+ <skip />
<string name="biometric_dialog_credential_attempts_before_wipe" msgid="6751859711975516999">"फेरि प्रयास गर्नुहोस्। <xliff:g id="MAX_ATTEMPTS">%2$d</xliff:g> मध्ये <xliff:g id="ATTEMPTS_0">%1$d</xliff:g> प्रयास।"</string>
<string name="biometric_dialog_last_attempt_before_wipe_dialog_title" msgid="2874250099278693477">"तपाईंको डेटा मेटाइने छ"</string>
<string name="biometric_dialog_last_pattern_attempt_before_wipe_device" msgid="6562299244825817598">"तपाईंले अर्को पटक पनि गलत ढाँचा प्रविष्टि गर्नुभयो भने यो डिभाइसको डेटा मेटाइने छ।"</string>
@@ -411,6 +415,8 @@
<string name="media_projection_entry_app_permission_dialog_warning_entire_screen" msgid="8736391633234144237">"तपाईंले सेयर गर्दा, रेकर्ड गर्दा वा कास्ट गर्दा <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> ले तपाईंको स्क्रिनमा देखिने वा डिभाइसमा प्ले गरिने सबै कुरा हेर्न तथा प्रयोग गर्न सक्छ। त्यसैले पासवर्ड, भुक्तानीसम्बन्धी विवरण, म्यासेज, फोटो र अडियो तथा भिडियो जस्ता कुरा हेर्दा वा प्ले गर्दा सावधानी अपनाउनुहोला।"</string>
<string name="media_projection_entry_app_permission_dialog_warning_single_app" msgid="5211695779082563959">"तपाईंले कुनै एप सेयर गर्दा, रेकर्ड गर्दा वा कास्ट गर्दा <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> ले उक्त एपमा देखाइने वा प्ले गरिने सबै कुरा हेर्न तथा प्रयोग गर्न सक्छ। त्यसैले पासवर्ड, भुक्तानीसम्बन्धी विवरण, म्यासेज, फोटो र अडियो तथा भिडियो जस्ता कुरा हेर्दा वा प्ले गर्दा सावधानी अपनाउनुहोला।"</string>
<string name="media_projection_entry_app_permission_dialog_continue" msgid="295463518195075840">"सुरु गर्नुहोस्"</string>
+ <!-- no translation found for media_projection_entry_app_permission_dialog_single_app_disabled (8999903044874669995) -->
+ <skip />
<string name="media_projection_entry_cast_permission_dialog_title" msgid="8860150223172993547">"कास्ट गर्न थाल्ने हो?"</string>
<string name="media_projection_entry_cast_permission_dialog_warning_entire_screen" msgid="1986212276016817231">"तपाईंले कास्ट गर्दा Android ले तपाईंको स्क्रिनमा देखिने वा डिभाइसमा प्ले गरिने सबै कुरा हेर्न तथा प्रयोग गर्न सक्छ। त्यसैले पासवर्ड, भुक्तानीसम्बन्धी विवरण, म्यासेज, फोटो र अडियो तथा भिडियो जस्ता कुरा हेर्दा वा प्ले गर्दा सावधानी अपनाउनुहोला।"</string>
<string name="media_projection_entry_cast_permission_dialog_warning_single_app" msgid="9900961380294292">"तपाईंले कुनै एप कास्ट गर्दा Android ले उक्त एपमा देखाइने वा प्ले गरिने सबै कुरा हेर्न तथा प्रयोग गर्न सक्छ। त्यसैले पासवर्ड, भुक्तानीसम्बन्धी विवरण, म्यासेज, फोटो र अडियो तथा भिडियो जस्ता कुरा हेर्दा वा प्ले गर्दा सावधानी अपनाउनुहोला।"</string>
@@ -865,8 +871,7 @@
<string name="magnification_mode_switch_state_window" msgid="8597100249594076965">"स्क्रिनको केही भाग म्याग्निफाइ गर्नुहोस्"</string>
<string name="magnification_open_settings_click_label" msgid="6151849212725923363">"जुम इनसम्बन्धी सेटिङ खोल्नुहोस्"</string>
<string name="magnification_close_settings_click_label" msgid="4642477260651704517">"जुम इन गर्ने सुविधाको सेटिङ बन्द गर्नुहोस्"</string>
- <!-- no translation found for magnification_exit_edit_mode_click_label (1664818325144887117) -->
- <skip />
+ <string name="magnification_exit_edit_mode_click_label" msgid="1664818325144887117">"सम्पादन गर्ने मोडबाट बाहिरिनुहोस्"</string>
<string name="magnification_drag_corner_to_resize" msgid="1249766311052418130">"आकार बदल्न कुनाबाट ड्र्याग गर्नुहोस्"</string>
<string name="accessibility_allow_diagonal_scrolling" msgid="3258050349191496398">"डायगोनल तरिकाले स्क्रोल गर्ने अनुमति दिनुहोस्"</string>
<string name="accessibility_resize" msgid="5733759136600611551">"आकार बदल्नुहोस्"</string>
diff --git a/packages/SystemUI/res/values-nl/strings.xml b/packages/SystemUI/res/values-nl/strings.xml
index 088bcbd..5799c35 100644
--- a/packages/SystemUI/res/values-nl/strings.xml
+++ b/packages/SystemUI/res/values-nl/strings.xml
@@ -57,6 +57,8 @@
<string name="hdmi_cec_set_menu_language_description" msgid="8176716678074126619">"Wijziging van systeemtaal aangevraagd door een ander apparaat"</string>
<string name="hdmi_cec_set_menu_language_accept" msgid="2513689457281009578">"Taal wijzigen"</string>
<string name="hdmi_cec_set_menu_language_decline" msgid="7650721096558646011">"Huidige taal houden"</string>
+ <!-- no translation found for share_wifi_button_text (1285273973812029240) -->
+ <skip />
<string name="wifi_debugging_title" msgid="7300007687492186076">"Draadloze foutopsporing toestaan in dit netwerk?"</string>
<string name="wifi_debugging_message" msgid="5461204211731802995">"Netwerknaam (SSID)\n<xliff:g id="SSID_0">%1$s</xliff:g>\n\nWifi-adres (BSSID)\n<xliff:g id="BSSID_1">%2$s</xliff:g>"</string>
<string name="wifi_debugging_always" msgid="2968383799517975155">"Altijd toestaan in dit netwerk"</string>
@@ -158,6 +160,8 @@
<string name="biometric_dialog_wrong_pattern" msgid="8954812279840889029">"Onjuist patroon"</string>
<string name="biometric_dialog_wrong_password" msgid="69477929306843790">"Onjuist wachtwoord"</string>
<string name="biometric_dialog_credential_too_many_attempts" msgid="3083141271737748716">"Te veel onjuiste pogingen.\nProbeer het over <xliff:g id="NUMBER">%d</xliff:g> seconden opnieuw."</string>
+ <!-- no translation found for work_challenge_emergency_button_text (8946588434515599288) -->
+ <skip />
<string name="biometric_dialog_credential_attempts_before_wipe" msgid="6751859711975516999">"Probeer het opnieuw. Poging <xliff:g id="ATTEMPTS_0">%1$d</xliff:g> van <xliff:g id="MAX_ATTEMPTS">%2$d</xliff:g>."</string>
<string name="biometric_dialog_last_attempt_before_wipe_dialog_title" msgid="2874250099278693477">"Je gegevens worden verwijderd"</string>
<string name="biometric_dialog_last_pattern_attempt_before_wipe_device" msgid="6562299244825817598">"Als je bij de volgende poging een onjuist patroon opgeeft, worden de gegevens van dit apparaat verwijderd."</string>
@@ -411,6 +415,8 @@
<string name="media_projection_entry_app_permission_dialog_warning_entire_screen" msgid="8736391633234144237">"Als je deelt, opneemt of cast, heeft <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> toegang tot alles dat zichtbaar is op je scherm of wordt afgespeeld op je apparaat. Wees daarom voorzichtig met bijvoorbeeld wachtwoorden, betalingsgegevens, berichten, foto\'s, en audio en video."</string>
<string name="media_projection_entry_app_permission_dialog_warning_single_app" msgid="5211695779082563959">"Als je deelt, opneemt of cast, heeft <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> toegang tot alles dat wordt getoond of afgespeeld in die app. Wees daarom voorzichtig met bijvoorbeeld wachtwoorden, betalingsgegevens, berichten, foto\'s, en audio en video."</string>
<string name="media_projection_entry_app_permission_dialog_continue" msgid="295463518195075840">"Starten"</string>
+ <!-- no translation found for media_projection_entry_app_permission_dialog_single_app_disabled (8999903044874669995) -->
+ <skip />
<string name="media_projection_entry_cast_permission_dialog_title" msgid="8860150223172993547">"Casten starten?"</string>
<string name="media_projection_entry_cast_permission_dialog_warning_entire_screen" msgid="1986212276016817231">"Als je cast, heeft Android toegang tot alles dat zichtbaar is op je scherm of wordt afgespeeld op je apparaat. Wees daarom voorzichtig met bijvoorbeeld wachtwoorden, betalingsgegevens, berichten, foto\'s, en audio en video."</string>
<string name="media_projection_entry_cast_permission_dialog_warning_single_app" msgid="9900961380294292">"Als je een app cast, heeft Android toegang tot alles dat zichtbaar is op je scherm of wordt afgespeeld op je apparaat. Wees daarom voorzichtig met bijvoorbeeld wachtwoorden, betalingsgegevens, berichten, foto\'s, en audio en video."</string>
diff --git a/packages/SystemUI/res/values-or/strings.xml b/packages/SystemUI/res/values-or/strings.xml
index 64955e4..68acfc0 100644
--- a/packages/SystemUI/res/values-or/strings.xml
+++ b/packages/SystemUI/res/values-or/strings.xml
@@ -57,6 +57,8 @@
<string name="hdmi_cec_set_menu_language_description" msgid="8176716678074126619">"ଅନ୍ୟ ଏକ ଡିଭାଇସ ଦ୍ୱାରା ସିଷ୍ଟମ ଭାଷା ପରିବର୍ତ୍ତନ ପାଇଁ ଅନୁରୋଧ କରାଯାଇଛି"</string>
<string name="hdmi_cec_set_menu_language_accept" msgid="2513689457281009578">"ଭାଷା ପରିବର୍ତ୍ତନ କରନ୍ତୁ"</string>
<string name="hdmi_cec_set_menu_language_decline" msgid="7650721096558646011">"ବର୍ତ୍ତମାନର ଭାଷା ରଖନ୍ତୁ"</string>
+ <!-- no translation found for share_wifi_button_text (1285273973812029240) -->
+ <skip />
<string name="wifi_debugging_title" msgid="7300007687492186076">"ଏହି ନେଟୱାର୍କରେ ୱାୟାରଲେସ୍ ଡିବଗିଂ ପାଇଁ ଅନୁମତି ଦେବେ?"</string>
<string name="wifi_debugging_message" msgid="5461204211731802995">"ନେଟୱାର୍କ ନାମ (SSID)\n<xliff:g id="SSID_0">%1$s</xliff:g>\n\nୱାଇଫାଇ ଠିକଣା (BSSID)\n<xliff:g id="BSSID_1">%2$s</xliff:g>"</string>
<string name="wifi_debugging_always" msgid="2968383799517975155">"ସର୍ବଦା ଏହି ନେଟୱାର୍କରେ ଅନୁମତି ଦିଅନ୍ତୁ"</string>
@@ -158,6 +160,8 @@
<string name="biometric_dialog_wrong_pattern" msgid="8954812279840889029">"ଭୁଲ ପାଟର୍ନ"</string>
<string name="biometric_dialog_wrong_password" msgid="69477929306843790">"ଭୁଲ ପାସ୍ୱାର୍ଡ"</string>
<string name="biometric_dialog_credential_too_many_attempts" msgid="3083141271737748716">"ଅନେକ ଥର ଭୁଲ ଚେଷ୍ଟା। \n <xliff:g id="NUMBER">%d</xliff:g>ସେକେଣ୍ଡରେ ପୁଣି ଚେଷ୍ଟା କରନ୍ତୁ।"</string>
+ <!-- no translation found for work_challenge_emergency_button_text (8946588434515599288) -->
+ <skip />
<string name="biometric_dialog_credential_attempts_before_wipe" msgid="6751859711975516999">"ପୁଣି ଚେଷ୍ଟା କରନ୍ତୁ। <xliff:g id="MAX_ATTEMPTS">%2$d</xliff:g>ଟିରୁ <xliff:g id="ATTEMPTS_0">%1$d</xliff:g>ଟି ପ୍ରଚେଷ୍ଟା।"</string>
<string name="biometric_dialog_last_attempt_before_wipe_dialog_title" msgid="2874250099278693477">"ଆପଣଙ୍କ ଡାଟାକୁ ଡିଲିଟ୍ କରିଦିଆଯିବ"</string>
<string name="biometric_dialog_last_pattern_attempt_before_wipe_device" msgid="6562299244825817598">"ଆପଣ ପରବର୍ତ୍ତୀ ପ୍ରଚେଷ୍ଟାରେ ଏକ ଭୁଲ ପାଟର୍ନ ପ୍ରବେଶ କଲେ, ଏହି ଡିଭାଇସର ଡାଟାକୁ ଡିଲିଟ୍ କରିଦିଆଯିବ।"</string>
@@ -411,6 +415,8 @@
<string name="media_projection_entry_app_permission_dialog_warning_entire_screen" msgid="8736391633234144237">"ଆପଣ ସେୟାର, ରେକର୍ଡ କିମ୍ବା କାଷ୍ଟ କରିବା ସମୟରେ, ଆପଣଙ୍କ ସ୍କ୍ରିନରେ ଦେଖାଯାଉଥିବା କିମ୍ବା ଆପଣଙ୍କ ଡିଭାଇସରେ ପ୍ଲେ ହେଉଥିବା ସବୁକିଛିକୁ <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g>ର ଆକ୍ସେସ ଅଛି। ତେଣୁ ପାସୱାର୍ଡ, ପେମେଣ୍ଟ ବିବରଣୀ, ମେସେଜ, ଫଟୋ ଏବଂ ଅଡିଓ ଓ ଭିଡିଓ ପରି ବିଷୟଗୁଡ଼ିକ ପ୍ରତି ସତର୍କ ରୁହନ୍ତୁ।"</string>
<string name="media_projection_entry_app_permission_dialog_warning_single_app" msgid="5211695779082563959">"ଆପଣ ଏକ ଆପ ସେୟାର, ରେକର୍ଡ କିମ୍ବା କାଷ୍ଟ କରିବା ସମୟରେ, ସେହି ଆପରେ ଦେଖାଯାଉଥିବା କିମ୍ବା ପ୍ଲେ ହେଉଥିବା ସବୁକିଛିକୁ <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g>ର ଆକ୍ସେସ ଅଛି। ତେଣୁ ପାସୱାର୍ଡ, ପେମେଣ୍ଟ ବିବରଣୀ, ମେସେଜ, ଫଟୋ ଏବଂ ଅଡିଓ ଓ ଭିଡିଓ ପରି ବିଷୟଗୁଡ଼ିକ ପ୍ରତି ସତର୍କ ରୁହନ୍ତୁ।"</string>
<string name="media_projection_entry_app_permission_dialog_continue" msgid="295463518195075840">"ଆରମ୍ଭ କରନ୍ତୁ"</string>
+ <!-- no translation found for media_projection_entry_app_permission_dialog_single_app_disabled (8999903044874669995) -->
+ <skip />
<string name="media_projection_entry_cast_permission_dialog_title" msgid="8860150223172993547">"କାଷ୍ଟିଂ ଆରମ୍ଭ କରିବେ?"</string>
<string name="media_projection_entry_cast_permission_dialog_warning_entire_screen" msgid="1986212276016817231">"ଆପଣ କାଷ୍ଟ କରିବା ସମୟରେ, ଆପଣଙ୍କ ସ୍କ୍ରିନରେ ଦେଖାଯାଉଥିବା କିମ୍ବା ଆପଣଙ୍କ ଡିଭାଇସରେ ପ୍ଲେ ହେଉଥିବା ସବୁକିଛିକୁ Androidର ଆକ୍ସେସ ଅଛି। ତେଣୁ ପାସୱାର୍ଡ, ପେମେଣ୍ଟ ବିବରଣୀ, ମେସେଜ, ଫଟୋ ଏବଂ ଅଡିଓ ଓ ଭିଡିଓ ପରି ବିଷୟଗୁଡ଼ିକ ପ୍ରତି ସତର୍କ ରୁହନ୍ତୁ।"</string>
<string name="media_projection_entry_cast_permission_dialog_warning_single_app" msgid="9900961380294292">"ଆପଣ ଏକ ଆପ କାଷ୍ଟ କରିବା ସମୟରେ, ସେହି ଆପରେ ଦେଖାଯାଉଥିବା କିମ୍ବା ପ୍ଲେ ହେଉଥିବା ସବୁକିଛିକୁ Androidର ଆକ୍ସେସ ଅଛି। ତେଣୁ ପାସୱାର୍ଡ, ପେମେଣ୍ଟ ବିବରଣୀ, ମେସେଜ, ଫଟୋ ଏବଂ ଅଡିଓ ଓ ଭିଡିଓ ପରି ବିଷୟଗୁଡ଼ିକ ପ୍ରତି ସତର୍କ ରୁହନ୍ତୁ।"</string>
@@ -865,8 +871,7 @@
<string name="magnification_mode_switch_state_window" msgid="8597100249594076965">"ସ୍କ୍ରିନର ଅଂଶ ମାଗ୍ନିଫାଏ କରନ୍ତୁ"</string>
<string name="magnification_open_settings_click_label" msgid="6151849212725923363">"ମାଗ୍ନିଫିକେସନ ସେଟିଂସ ଖୋଲନ୍ତୁ"</string>
<string name="magnification_close_settings_click_label" msgid="4642477260651704517">"ମେଗ୍ନିଫିକେସନ ସେଟିଂସକୁ ବନ୍ଦ କରନ୍ତୁ"</string>
- <!-- no translation found for magnification_exit_edit_mode_click_label (1664818325144887117) -->
- <skip />
+ <string name="magnification_exit_edit_mode_click_label" msgid="1664818325144887117">"ଏଡିଟ ମୋଡରୁ ବାହାରି ଯାଆନ୍ତୁ"</string>
<string name="magnification_drag_corner_to_resize" msgid="1249766311052418130">"ରିସାଇଜ କରିବା ପାଇଁ କୋଣକୁ ଡ୍ରାଗ କରନ୍ତୁ"</string>
<string name="accessibility_allow_diagonal_scrolling" msgid="3258050349191496398">"ଡାଏଗୋନାଲ ସ୍କ୍ରୋଲିଂକୁ ଅନୁମତି ଦିଅନ୍ତୁ"</string>
<string name="accessibility_resize" msgid="5733759136600611551">"ରିସାଇଜ କରନ୍ତୁ"</string>
@@ -1181,7 +1186,7 @@
<string name="privacy_dialog_active_call_usage" msgid="7858746847946397562">"ଫୋନ କଲରେ ବ୍ୟବହାର କରାଯାଉଛି"</string>
<string name="privacy_dialog_recent_call_usage" msgid="1214810644978167344">"ଏବେ ଫୋନ କଲରେ ବ୍ୟବହାର କରାଯାଇଛି"</string>
<string name="privacy_dialog_active_app_usage" msgid="631997836335929880">"<xliff:g id="APP_NAME">%1$s</xliff:g> ଦ୍ୱାରା ବ୍ୟବହାର କରାଯାଉଛି"</string>
- <string name="privacy_dialog_recent_app_usage" msgid="4883417856848222450">"ଏବେ <xliff:g id="APP_NAME">%1$s</xliff:g> ଦ୍ୱାରା ବ୍ୟବହାର କରାଯାଉଛି"</string>
+ <string name="privacy_dialog_recent_app_usage" msgid="4883417856848222450">"ଏବେ <xliff:g id="APP_NAME">%1$s</xliff:g> ଦ୍ୱାରା ବ୍ୟବହାର କରାଯାଇଛି"</string>
<string name="privacy_dialog_active_app_usage_1" msgid="9047570143069220911">"<xliff:g id="APP_NAME">%1$s</xliff:g> ଦ୍ୱାରା ବ୍ୟବହାର କରାଯାଉଛି (<xliff:g id="ATTRIBUTION_LABEL">%2$s</xliff:g>)"</string>
<string name="privacy_dialog_recent_app_usage_1" msgid="2551340497722370109">"ଏବେ <xliff:g id="APP_NAME">%1$s</xliff:g> (<xliff:g id="ATTRIBUTION_LABEL">%2$s</xliff:g>) ଦ୍ୱାରା ବ୍ୟବହାର କରାଯାଉଛି"</string>
<string name="privacy_dialog_active_app_usage_2" msgid="2770926061339921767">"<xliff:g id="APP_NAME">%1$s</xliff:g> ଦ୍ୱାରା ବ୍ୟବହାର କରାଯାଉଛି (<xliff:g id="ATTRIBUTION_LABEL">%2$s</xliff:g> • <xliff:g id="PROXY_LABEL">%3$s</xliff:g>)"</string>
diff --git a/packages/SystemUI/res/values-pa/strings.xml b/packages/SystemUI/res/values-pa/strings.xml
index b3d4482..f8aac75 100644
--- a/packages/SystemUI/res/values-pa/strings.xml
+++ b/packages/SystemUI/res/values-pa/strings.xml
@@ -57,6 +57,8 @@
<string name="hdmi_cec_set_menu_language_description" msgid="8176716678074126619">"ਕਿਸੇ ਹੋਰ ਡੀਵਾਈਸ ਵੱਲੋਂ ਸਿਸਟਮ ਦੀ ਭਾਸ਼ਾ ਬਦਲਣ ਦੀ ਬੇਨਤੀ ਕੀਤੀ ਗਈ"</string>
<string name="hdmi_cec_set_menu_language_accept" msgid="2513689457281009578">"ਭਾਸ਼ਾ ਬਦਲੋ"</string>
<string name="hdmi_cec_set_menu_language_decline" msgid="7650721096558646011">"ਮੌਜੂਦਾ ਭਾਸ਼ਾ ਰੱਖੋ"</string>
+ <!-- no translation found for share_wifi_button_text (1285273973812029240) -->
+ <skip />
<string name="wifi_debugging_title" msgid="7300007687492186076">"ਕੀ ਇਸ ਨੈੱਟਵਰਕ \'ਤੇ ਵਾਇਰਲੈੱਸ ਡੀਬੱਗਿੰਗ ਦੀ ਆਗਿਆ ਦੇਣੀ ਹੈ?"</string>
<string name="wifi_debugging_message" msgid="5461204211731802995">"ਨੈੱਟਵਰਕ ਨਾਮ (SSID)\n<xliff:g id="SSID_0">%1$s</xliff:g>\n\nਵਾਈ-ਫਾਈ ਪਤਾ (BSSID)\n<xliff:g id="BSSID_1">%2$s</xliff:g>"</string>
<string name="wifi_debugging_always" msgid="2968383799517975155">"ਇਸ ਨੈੱਟਵਰਕ \'ਤੇ ਹਮੇਸ਼ਾਂ ਆਗਿਆ ਦਿਓ"</string>
@@ -158,6 +160,8 @@
<string name="biometric_dialog_wrong_pattern" msgid="8954812279840889029">"ਗਲਤ ਪੈਟਰਨ"</string>
<string name="biometric_dialog_wrong_password" msgid="69477929306843790">"ਗਲਤ ਪਾਸਵਰਡ"</string>
<string name="biometric_dialog_credential_too_many_attempts" msgid="3083141271737748716">"ਬਹੁਤ ਸਾਰੀਆਂ ਗ਼ਲਤ ਕੋਸ਼ਿਸ਼ਾਂ।\n<xliff:g id="NUMBER">%d</xliff:g> ਸਕਿੰਟਾਂ ਵਿੱਚ ਦੁਬਾਰਾ ਕੋਸ਼ਿਸ਼ ਕਰੋ।"</string>
+ <!-- no translation found for work_challenge_emergency_button_text (8946588434515599288) -->
+ <skip />
<string name="biometric_dialog_credential_attempts_before_wipe" msgid="6751859711975516999">"ਦੁਬਾਰਾ ਕੋਸ਼ਿਸ਼ ਕਰੋ। <xliff:g id="MAX_ATTEMPTS">%2$d</xliff:g> ਵਿੱਚੋਂ <xliff:g id="ATTEMPTS_0">%1$d</xliff:g> ਕੋਸ਼ਿਸ਼।"</string>
<string name="biometric_dialog_last_attempt_before_wipe_dialog_title" msgid="2874250099278693477">"ਤੁਹਾਡਾ ਡਾਟਾ ਮਿਟਾ ਦਿੱਤਾ ਜਾਵੇਗਾ"</string>
<string name="biometric_dialog_last_pattern_attempt_before_wipe_device" msgid="6562299244825817598">"ਜੇ ਤੁਸੀਂ ਅਗਲੀ ਕੋਸ਼ਿਸ਼ ਵਿੱਚ ਕੋਈ ਗਲਤ ਪੈਟਰਨ ਦਾਖਲ ਕਰਦੇ ਹੋ, ਤਾਂ ਇਸ ਡੀਵਾਈਸ ਦਾ ਡਾਟਾ ਮਿਟਾ ਦਿੱਤਾ ਜਾਵੇਗਾ।"</string>
@@ -411,6 +415,8 @@
<string name="media_projection_entry_app_permission_dialog_warning_entire_screen" msgid="8736391633234144237">"ਤੁਹਾਡੇ ਵੱਲੋਂ ਸਾਂਝਾ ਕਰਨ, ਰਿਕਾਰਡਿੰਗ ਜਾਂ ਕਾਸਟ ਕਰਨ \'ਤੇ, <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> ਕੋਲ ਤੁਹਾਡੀ ਸਕ੍ਰੀਨ \'ਤੇ ਦਿਖਣ ਵਾਲੀ ਜਾਂ ਤੁਹਾਡੇ ਡੀਵਾਈਸ \'ਤੇ ਚਲਾਈ ਜਾਣ ਵਾਲੀ ਹਰੇਕ ਚੀਜ਼ ਤੱਕ ਪਹੁੰਚ ਹੁੰਦੀ ਹੈ। ਇਸ ਲਈ ਪਾਸਵਰਡਾਂ, ਭੁਗਤਾਨ ਵੇਰਵਿਆਂ, ਸੁਨੇਹਿਆਂ, ਫ਼ੋਟੋਆਂ ਅਤੇ ਆਡੀਓ ਅਤੇ ਵੀਡੀਓ ਵਰਗੀਆਂ ਚੀਜ਼ਾਂ ਵਾਸਤੇ ਸਾਵਧਾਨ ਰਹੋ।"</string>
<string name="media_projection_entry_app_permission_dialog_warning_single_app" msgid="5211695779082563959">"ਤੁਹਾਡੇ ਵੱਲੋਂ ਸਾਂਝਾ ਕਰਨ, ਰਿਕਾਰਡ ਕਰਨ, ਜਾਂ ਕਾਸਟ ਕਰਨ \'ਤੇ, <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> ਕੋਲ ਉਸ ਐਪ \'ਤੇ ਦਿਖਾਈ ਗਈ ਜਾਂ ਚਲਾਈ ਗਈ ਹਰੇਕ ਚੀਜ਼ ਤੱਕ ਪਹੁੰਚ ਹੁੰਦੀ ਹੈ। ਇਸ ਲਈ ਪਾਸਵਰਡਾਂ, ਭੁਗਤਾਨ ਵੇਰਵਿਆਂ, ਸੁਨੇਹਿਆਂ, ਫ਼ੋਟੋਆਂ ਅਤੇ ਆਡੀਓ ਅਤੇ ਵੀਡੀਓ ਵਰਗੀਆਂ ਚੀਜ਼ਾਂ ਸੰਬੰਧੀ ਸਾਵਧਾਨ ਰਹੋ।"</string>
<string name="media_projection_entry_app_permission_dialog_continue" msgid="295463518195075840">"ਸ਼ੁਰੂ ਕਰੋ"</string>
+ <!-- no translation found for media_projection_entry_app_permission_dialog_single_app_disabled (8999903044874669995) -->
+ <skip />
<string name="media_projection_entry_cast_permission_dialog_title" msgid="8860150223172993547">"ਕੀ ਕਾਸਟ ਕਰਨਾ ਸ਼ੁਰੂ ਕਰਨਾ ਹੈ?"</string>
<string name="media_projection_entry_cast_permission_dialog_warning_entire_screen" msgid="1986212276016817231">"ਤੁਹਾਡੇ ਵੱਲੋਂ ਕਾਸਟ ਕਰਨ \'ਤੇ, Android ਕੋਲ ਤੁਹਾਡੀ ਸਕ੍ਰੀਨ \'ਤੇ ਦਿਸਣ ਵਾਲੀ ਜਾਂ ਤੁਹਾਡੇ ਡੀਵਾਈਸ \'ਤੇ ਚਲਾਈ ਜਾਣ ਵਾਲੀ ਹਰੇਕ ਚੀਜ਼ ਤੱਕ ਪਹੁੰਚ ਹੁੰਦੀ ਹੈ। ਇਸ ਲਈ ਪਾਸਵਰਡਾਂ, ਭੁਗਤਾਨ ਵੇਰਵਿਆਂ, ਸੁਨੇਹਿਆਂ, ਫ਼ੋਟੋਆਂ ਅਤੇ ਆਡੀਓ ਅਤੇ ਵੀਡੀਓ ਵਰਗੀਆਂ ਚੀਜ਼ਾਂ ਵਾਸਤੇ ਸਾਵਧਾਨ ਰਹੋ।"</string>
<string name="media_projection_entry_cast_permission_dialog_warning_single_app" msgid="9900961380294292">"ਤੁਹਾਡੇ ਵੱਲੋਂ ਕਾਸਟ ਕਰਨ \'ਤੇ, Android ਕੋਲ ਉਸ ਐਪ \'ਤੇ ਦਿਖਾਈ ਗਈ ਜਾਂ ਚਲਾਈ ਗਈ ਹਰੇਕ ਚੀਜ਼ ਤੱਕ ਪਹੁੰਚ ਹੁੰਦੀ ਹੈ। ਇਸ ਲਈ ਪਾਸਵਰਡਾਂ, ਭੁਗਤਾਨ ਵੇਰਵਿਆਂ, ਸੁਨੇਹਿਆਂ, ਫ਼ੋਟੋਆਂ ਅਤੇ ਆਡੀਓ ਅਤੇ ਵੀਡੀਓ ਵਰਗੀਆਂ ਚੀਜ਼ਾਂ ਸੰਬੰਧੀ ਸਾਵਧਾਨ ਰਹੋ।"</string>
@@ -865,8 +871,7 @@
<string name="magnification_mode_switch_state_window" msgid="8597100249594076965">"ਸਕ੍ਰੀਨ ਦੇ ਹਿੱਸੇ ਨੂੰ ਵੱਡਾ ਕਰੋ"</string>
<string name="magnification_open_settings_click_label" msgid="6151849212725923363">"ਵੱਡਦਰਸ਼ੀਕਰਨ ਸੈਟਿੰਗਾਂ ਖੋਲ੍ਹੋ"</string>
<string name="magnification_close_settings_click_label" msgid="4642477260651704517">"ਵੱਡਦਰਸ਼ੀਕਰਨ ਸੈਟਿੰਗਾਂ ਬੰਦ ਕਰੋ"</string>
- <!-- no translation found for magnification_exit_edit_mode_click_label (1664818325144887117) -->
- <skip />
+ <string name="magnification_exit_edit_mode_click_label" msgid="1664818325144887117">"ਸੰਪਾਦਨ ਮੋਡ ਤੋਂ ਬਾਹਰ ਜਾਓ"</string>
<string name="magnification_drag_corner_to_resize" msgid="1249766311052418130">"ਆਕਾਰ ਬਦਲਣ ਲਈ ਕੋਨਾ ਘਸੀਟੋ"</string>
<string name="accessibility_allow_diagonal_scrolling" msgid="3258050349191496398">"ਟੇਡੀ ਦਿਸ਼ਾ ਵਿੱਚ ਸਕ੍ਰੋਲ ਕਰਨ ਦਿਓ"</string>
<string name="accessibility_resize" msgid="5733759136600611551">"ਆਕਾਰ ਬਦਲੋ"</string>
diff --git a/packages/SystemUI/res/values-pl/strings.xml b/packages/SystemUI/res/values-pl/strings.xml
index e3beef0..ad63d1e 100644
--- a/packages/SystemUI/res/values-pl/strings.xml
+++ b/packages/SystemUI/res/values-pl/strings.xml
@@ -57,6 +57,8 @@
<string name="hdmi_cec_set_menu_language_description" msgid="8176716678074126619">"Inny użytkownik poprosił o zmianę języka systemu"</string>
<string name="hdmi_cec_set_menu_language_accept" msgid="2513689457281009578">"Zmień język"</string>
<string name="hdmi_cec_set_menu_language_decline" msgid="7650721096558646011">"Zachowaj bieżący język"</string>
+ <!-- no translation found for share_wifi_button_text (1285273973812029240) -->
+ <skip />
<string name="wifi_debugging_title" msgid="7300007687492186076">"Zezwolić na debugowanie bezprzewodowe w tej sieci?"</string>
<string name="wifi_debugging_message" msgid="5461204211731802995">"Nazwa sieci (SSID)\n<xliff:g id="SSID_0">%1$s</xliff:g>\n\nAdres Wi‑Fi (BSSID)\n<xliff:g id="BSSID_1">%2$s</xliff:g>"</string>
<string name="wifi_debugging_always" msgid="2968383799517975155">"Zawsze zezwalaj w tej sieci"</string>
@@ -158,6 +160,8 @@
<string name="biometric_dialog_wrong_pattern" msgid="8954812279840889029">"Nieprawidłowy wzór"</string>
<string name="biometric_dialog_wrong_password" msgid="69477929306843790">"Nieprawidłowe hasło"</string>
<string name="biometric_dialog_credential_too_many_attempts" msgid="3083141271737748716">"Zbyt wiele nieudanych prób.\n Spróbuj ponownie za <xliff:g id="NUMBER">%d</xliff:g> s."</string>
+ <!-- no translation found for work_challenge_emergency_button_text (8946588434515599288) -->
+ <skip />
<string name="biometric_dialog_credential_attempts_before_wipe" msgid="6751859711975516999">"Spróbuj ponownie. Próba <xliff:g id="ATTEMPTS_0">%1$d</xliff:g> z <xliff:g id="MAX_ATTEMPTS">%2$d</xliff:g>."</string>
<string name="biometric_dialog_last_attempt_before_wipe_dialog_title" msgid="2874250099278693477">"Dane zostaną usunięte"</string>
<string name="biometric_dialog_last_pattern_attempt_before_wipe_device" msgid="6562299244825817598">"Jeśli następnym razem podasz nieprawidłowy wzór, dane na urządzeniu zostaną usunięte."</string>
@@ -411,6 +415,8 @@
<string name="media_projection_entry_app_permission_dialog_warning_entire_screen" msgid="8736391633234144237">"Podczas udostępniania, nagrywania lub przesyłania treści aplikacja <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> ma dostęp do wszystkiego, co jest widoczne na ekranie lub odtwarzane na urządzeniu. Dlatego zachowaj ostrożność w zakresie haseł, danych do płatności, wiadomości, zdjęć, audio i filmów."</string>
<string name="media_projection_entry_app_permission_dialog_warning_single_app" msgid="5211695779082563959">"Podczas udostępniania, nagrywania lub przesyłania treści aplikacja <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> ma dostęp do wszystkiego, co jest w niej wyświetlane lub odtwarzane. Dlatego zachowaj ostrożność w zakresie haseł, danych do płatności, wiadomości, zdjęć, audio i filmów."</string>
<string name="media_projection_entry_app_permission_dialog_continue" msgid="295463518195075840">"Rozpocznij"</string>
+ <!-- no translation found for media_projection_entry_app_permission_dialog_single_app_disabled (8999903044874669995) -->
+ <skip />
<string name="media_projection_entry_cast_permission_dialog_title" msgid="8860150223172993547">"Rozpocząć przesyłanie?"</string>
<string name="media_projection_entry_cast_permission_dialog_warning_entire_screen" msgid="1986212276016817231">"Podczas przesyłania, Android ma dostęp do wszystkiego, co jest widoczne na ekranie lub odtwarzane na urządzeniu. Dlatego zachowaj ostrożność w zakresie haseł, danych do płatności, wiadomości, zdjęć, audio i filmów."</string>
<string name="media_projection_entry_cast_permission_dialog_warning_single_app" msgid="9900961380294292">"Podczas przesyłania treści z aplikacji Android ma dostęp do wszystkiego, co jest w niej wyświetlane lub odtwarzane. Dlatego zachowaj ostrożność w zakresie haseł, danych do płatności, wiadomości, zdjęć, audio i filmów."</string>
@@ -865,8 +871,7 @@
<string name="magnification_mode_switch_state_window" msgid="8597100249594076965">"Powiększ część ekranu"</string>
<string name="magnification_open_settings_click_label" msgid="6151849212725923363">"Otwórz ustawienia powiększenia"</string>
<string name="magnification_close_settings_click_label" msgid="4642477260651704517">"Zamknij ustawienia powiększenia"</string>
- <!-- no translation found for magnification_exit_edit_mode_click_label (1664818325144887117) -->
- <skip />
+ <string name="magnification_exit_edit_mode_click_label" msgid="1664818325144887117">"Wyjdź z trybu edycji"</string>
<string name="magnification_drag_corner_to_resize" msgid="1249766311052418130">"Przeciągnij róg, aby zmienić rozmiar"</string>
<string name="accessibility_allow_diagonal_scrolling" msgid="3258050349191496398">"Zezwalaj na przewijanie poprzeczne"</string>
<string name="accessibility_resize" msgid="5733759136600611551">"Zmień rozmiar"</string>
@@ -1168,7 +1173,7 @@
<string name="assistant_attention_content_description" msgid="6830215897604642875">"Asystent jest aktywny"</string>
<string name="set_default_notes_app_toast_content" msgid="2812374329662610753">"Ustaw domyślną aplikację do obsługi notatek w Ustawieniach"</string>
<string name="install_app" msgid="5066668100199613936">"Zainstaluj aplikację"</string>
- <string name="privacy_dialog_title" msgid="7839968133469098311">"Mikrofon i Aparat"</string>
+ <string name="privacy_dialog_title" msgid="7839968133469098311">"Mikrofon i aparat"</string>
<string name="privacy_dialog_summary" msgid="2458769652125995409">"Aplikacje korzystające w ostatnim czasie"</string>
<string name="privacy_dialog_more_button" msgid="7610604080293562345">"Zobacz ostatni dostęp"</string>
<string name="privacy_dialog_done_button" msgid="4504330708531434263">"Gotowe"</string>
diff --git a/packages/SystemUI/res/values-pt-rBR/strings.xml b/packages/SystemUI/res/values-pt-rBR/strings.xml
index 8f5d7fa..7f917c8 100644
--- a/packages/SystemUI/res/values-pt-rBR/strings.xml
+++ b/packages/SystemUI/res/values-pt-rBR/strings.xml
@@ -57,6 +57,8 @@
<string name="hdmi_cec_set_menu_language_description" msgid="8176716678074126619">"Mudança do idioma do sistema solicitada por outro dispositivo"</string>
<string name="hdmi_cec_set_menu_language_accept" msgid="2513689457281009578">"Alterar idioma"</string>
<string name="hdmi_cec_set_menu_language_decline" msgid="7650721096558646011">"Manter idioma atual"</string>
+ <!-- no translation found for share_wifi_button_text (1285273973812029240) -->
+ <skip />
<string name="wifi_debugging_title" msgid="7300007687492186076">"Permitir a depuração por Wi-Fi nesta rede?"</string>
<string name="wifi_debugging_message" msgid="5461204211731802995">"Nome da rede (SSID)\n<xliff:g id="SSID_0">%1$s</xliff:g>\n\nEndereço do Wi-Fi (BSSID)\n<xliff:g id="BSSID_1">%2$s</xliff:g>"</string>
<string name="wifi_debugging_always" msgid="2968383799517975155">"Sempre permitir nesta rede"</string>
@@ -158,6 +160,8 @@
<string name="biometric_dialog_wrong_pattern" msgid="8954812279840889029">"Padrão incorreto"</string>
<string name="biometric_dialog_wrong_password" msgid="69477929306843790">"Senha incorreta"</string>
<string name="biometric_dialog_credential_too_many_attempts" msgid="3083141271737748716">"Excesso de tentativas incorretas.\nTente novamente em <xliff:g id="NUMBER">%d</xliff:g> segundos."</string>
+ <!-- no translation found for work_challenge_emergency_button_text (8946588434515599288) -->
+ <skip />
<string name="biometric_dialog_credential_attempts_before_wipe" msgid="6751859711975516999">"Tente novamente. Tentativa <xliff:g id="ATTEMPTS_0">%1$d</xliff:g> de <xliff:g id="MAX_ATTEMPTS">%2$d</xliff:g>."</string>
<string name="biometric_dialog_last_attempt_before_wipe_dialog_title" msgid="2874250099278693477">"Seus dados serão excluídos"</string>
<string name="biometric_dialog_last_pattern_attempt_before_wipe_device" msgid="6562299244825817598">"Se você informar um padrão incorreto na próxima tentativa, os dados deste dispositivo serão excluídos."</string>
@@ -411,6 +415,8 @@
<string name="media_projection_entry_app_permission_dialog_warning_entire_screen" msgid="8736391633234144237">"Quando você compartilha, grava ou transmite a tela, o <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> tem acesso a todas as informações visíveis nela ou reproduzidas no dispositivo. Portanto, tenha cuidado com senhas, detalhes de pagamento, mensagens, fotos, áudios e vídeos."</string>
<string name="media_projection_entry_app_permission_dialog_warning_single_app" msgid="5211695779082563959">"Quando você compartilha, grava ou transmite um app, o <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> tem acesso a todas as informações visíveis ou reproduzidas no dispositivo. Tenha cuidado com senhas, detalhes de pagamento, mensagens, fotos, áudios e vídeos."</string>
<string name="media_projection_entry_app_permission_dialog_continue" msgid="295463518195075840">"Início"</string>
+ <!-- no translation found for media_projection_entry_app_permission_dialog_single_app_disabled (8999903044874669995) -->
+ <skip />
<string name="media_projection_entry_cast_permission_dialog_title" msgid="8860150223172993547">"Começar a transmissão?"</string>
<string name="media_projection_entry_cast_permission_dialog_warning_entire_screen" msgid="1986212276016817231">"Quando você transmite a tela, o Android tem acesso a todas as informações visíveis nela ou reproduzidas no dispositivo. Portanto, tenha cuidado com senhas, detalhes de pagamento, mensagens, fotos, áudios e vídeos."</string>
<string name="media_projection_entry_cast_permission_dialog_warning_single_app" msgid="9900961380294292">"Quando você transmite um app, o Android tem acesso a todas as informações visíveis ou reproduzidas nele. Tenha cuidado com senhas, detalhes de pagamento, mensagens fotos, áudios e vídeos."</string>
diff --git a/packages/SystemUI/res/values-pt-rPT/strings.xml b/packages/SystemUI/res/values-pt-rPT/strings.xml
index a4f17b6..e455f01 100644
--- a/packages/SystemUI/res/values-pt-rPT/strings.xml
+++ b/packages/SystemUI/res/values-pt-rPT/strings.xml
@@ -57,6 +57,8 @@
<string name="hdmi_cec_set_menu_language_description" msgid="8176716678074126619">"Alteração do idioma do sistema solicitada por outro dispositivo"</string>
<string name="hdmi_cec_set_menu_language_accept" msgid="2513689457281009578">"Alterar idioma"</string>
<string name="hdmi_cec_set_menu_language_decline" msgid="7650721096558646011">"Manter idioma atual"</string>
+ <!-- no translation found for share_wifi_button_text (1285273973812029240) -->
+ <skip />
<string name="wifi_debugging_title" msgid="7300007687492186076">"Permitir a depuração sem fios nesta rede?"</string>
<string name="wifi_debugging_message" msgid="5461204211731802995">"Nome da rede (SSID)\n<xliff:g id="SSID_0">%1$s</xliff:g>\n\nEndereço Wi-Fi (BSSID)\n<xliff:g id="BSSID_1">%2$s</xliff:g>"</string>
<string name="wifi_debugging_always" msgid="2968383799517975155">"Permitir sempre nesta rede"</string>
@@ -158,6 +160,8 @@
<string name="biometric_dialog_wrong_pattern" msgid="8954812279840889029">"Padrão incorreto."</string>
<string name="biometric_dialog_wrong_password" msgid="69477929306843790">"Palavra-passe incorreta."</string>
<string name="biometric_dialog_credential_too_many_attempts" msgid="3083141271737748716">"Demasiadas tentativas incorretas.\nTente novamente dentro de <xliff:g id="NUMBER">%d</xliff:g> segundos."</string>
+ <!-- no translation found for work_challenge_emergency_button_text (8946588434515599288) -->
+ <skip />
<string name="biometric_dialog_credential_attempts_before_wipe" msgid="6751859711975516999">"Tente novamente. Tentativa <xliff:g id="ATTEMPTS_0">%1$d</xliff:g> de <xliff:g id="MAX_ATTEMPTS">%2$d</xliff:g>."</string>
<string name="biometric_dialog_last_attempt_before_wipe_dialog_title" msgid="2874250099278693477">"Os seus dados serão eliminados"</string>
<string name="biometric_dialog_last_pattern_attempt_before_wipe_device" msgid="6562299244825817598">"Se introduzir um padrão incorreto na tentativa seguinte, os dados deste dispositivo serão eliminados."</string>
@@ -411,6 +415,8 @@
<string name="media_projection_entry_app_permission_dialog_warning_entire_screen" msgid="8736391633234144237">"Quando está a partilhar, gravar ou transmitir, a app <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> tem acesso a tudo o que está visível no seu ecrã ou é reproduzido no seu dispositivo. Por isso, tenha cuidado com, por exemplo, palavras-passe, detalhes de pagamento, mensagens, fotos, áudio e vídeo."</string>
<string name="media_projection_entry_app_permission_dialog_warning_single_app" msgid="5211695779082563959">"Quando está a partilhar, gravar ou transmitir uma app, a app <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> tem acesso a tudo o que é apresentado ou reproduzido nessa app. Por isso, tenha cuidado com, por exemplo, palavras-passe, detalhes de pagamento, mensagens, fotos, áudio e vídeo."</string>
<string name="media_projection_entry_app_permission_dialog_continue" msgid="295463518195075840">"Iniciar"</string>
+ <!-- no translation found for media_projection_entry_app_permission_dialog_single_app_disabled (8999903044874669995) -->
+ <skip />
<string name="media_projection_entry_cast_permission_dialog_title" msgid="8860150223172993547">"Começar a transmitir?"</string>
<string name="media_projection_entry_cast_permission_dialog_warning_entire_screen" msgid="1986212276016817231">"Quando está a transmitir conteúdo, o Android tem acesso a tudo o que está visível no seu ecrã ou é reproduzido no seu dispositivo. Por isso, tenha cuidado com, por exemplo, palavras-passe, detalhes de pagamento, mensagens, fotos, áudio e vídeo."</string>
<string name="media_projection_entry_cast_permission_dialog_warning_single_app" msgid="9900961380294292">"Quando está a transmitir uma app, o Android tem acesso a tudo o que é apresentado ou reproduzido nessa app. Por isso, tenha cuidado com, por exemplo, palavras-passe, detalhes de pagamento, mensagens, fotos, áudio e vídeo."</string>
@@ -1180,7 +1186,7 @@
<string name="privacy_dialog_active_call_usage" msgid="7858746847946397562">"Em utilização por uma chamada telefónica"</string>
<string name="privacy_dialog_recent_call_usage" msgid="1214810644978167344">"Usado recentemente numa chamada telefónica"</string>
<string name="privacy_dialog_active_app_usage" msgid="631997836335929880">"Em utilização pela app <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
- <string name="privacy_dialog_recent_app_usage" msgid="4883417856848222450">"Usado recentemente pela app <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
+ <string name="privacy_dialog_recent_app_usage" msgid="4883417856848222450">"Utilização recente pela app <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
<string name="privacy_dialog_active_app_usage_1" msgid="9047570143069220911">"Em utilização pela app <xliff:g id="APP_NAME">%1$s</xliff:g> (<xliff:g id="ATTRIBUTION_LABEL">%2$s</xliff:g>)"</string>
<string name="privacy_dialog_recent_app_usage_1" msgid="2551340497722370109">"Usado recentemente pela app <xliff:g id="APP_NAME">%1$s</xliff:g> (<xliff:g id="ATTRIBUTION_LABEL">%2$s</xliff:g>)"</string>
<string name="privacy_dialog_active_app_usage_2" msgid="2770926061339921767">"Em utilização pela app <xliff:g id="APP_NAME">%1$s</xliff:g> (<xliff:g id="ATTRIBUTION_LABEL">%2$s</xliff:g> • <xliff:g id="PROXY_LABEL">%3$s</xliff:g>)"</string>
diff --git a/packages/SystemUI/res/values-pt/strings.xml b/packages/SystemUI/res/values-pt/strings.xml
index 8f5d7fa..7f917c8 100644
--- a/packages/SystemUI/res/values-pt/strings.xml
+++ b/packages/SystemUI/res/values-pt/strings.xml
@@ -57,6 +57,8 @@
<string name="hdmi_cec_set_menu_language_description" msgid="8176716678074126619">"Mudança do idioma do sistema solicitada por outro dispositivo"</string>
<string name="hdmi_cec_set_menu_language_accept" msgid="2513689457281009578">"Alterar idioma"</string>
<string name="hdmi_cec_set_menu_language_decline" msgid="7650721096558646011">"Manter idioma atual"</string>
+ <!-- no translation found for share_wifi_button_text (1285273973812029240) -->
+ <skip />
<string name="wifi_debugging_title" msgid="7300007687492186076">"Permitir a depuração por Wi-Fi nesta rede?"</string>
<string name="wifi_debugging_message" msgid="5461204211731802995">"Nome da rede (SSID)\n<xliff:g id="SSID_0">%1$s</xliff:g>\n\nEndereço do Wi-Fi (BSSID)\n<xliff:g id="BSSID_1">%2$s</xliff:g>"</string>
<string name="wifi_debugging_always" msgid="2968383799517975155">"Sempre permitir nesta rede"</string>
@@ -158,6 +160,8 @@
<string name="biometric_dialog_wrong_pattern" msgid="8954812279840889029">"Padrão incorreto"</string>
<string name="biometric_dialog_wrong_password" msgid="69477929306843790">"Senha incorreta"</string>
<string name="biometric_dialog_credential_too_many_attempts" msgid="3083141271737748716">"Excesso de tentativas incorretas.\nTente novamente em <xliff:g id="NUMBER">%d</xliff:g> segundos."</string>
+ <!-- no translation found for work_challenge_emergency_button_text (8946588434515599288) -->
+ <skip />
<string name="biometric_dialog_credential_attempts_before_wipe" msgid="6751859711975516999">"Tente novamente. Tentativa <xliff:g id="ATTEMPTS_0">%1$d</xliff:g> de <xliff:g id="MAX_ATTEMPTS">%2$d</xliff:g>."</string>
<string name="biometric_dialog_last_attempt_before_wipe_dialog_title" msgid="2874250099278693477">"Seus dados serão excluídos"</string>
<string name="biometric_dialog_last_pattern_attempt_before_wipe_device" msgid="6562299244825817598">"Se você informar um padrão incorreto na próxima tentativa, os dados deste dispositivo serão excluídos."</string>
@@ -411,6 +415,8 @@
<string name="media_projection_entry_app_permission_dialog_warning_entire_screen" msgid="8736391633234144237">"Quando você compartilha, grava ou transmite a tela, o <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> tem acesso a todas as informações visíveis nela ou reproduzidas no dispositivo. Portanto, tenha cuidado com senhas, detalhes de pagamento, mensagens, fotos, áudios e vídeos."</string>
<string name="media_projection_entry_app_permission_dialog_warning_single_app" msgid="5211695779082563959">"Quando você compartilha, grava ou transmite um app, o <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> tem acesso a todas as informações visíveis ou reproduzidas no dispositivo. Tenha cuidado com senhas, detalhes de pagamento, mensagens, fotos, áudios e vídeos."</string>
<string name="media_projection_entry_app_permission_dialog_continue" msgid="295463518195075840">"Início"</string>
+ <!-- no translation found for media_projection_entry_app_permission_dialog_single_app_disabled (8999903044874669995) -->
+ <skip />
<string name="media_projection_entry_cast_permission_dialog_title" msgid="8860150223172993547">"Começar a transmissão?"</string>
<string name="media_projection_entry_cast_permission_dialog_warning_entire_screen" msgid="1986212276016817231">"Quando você transmite a tela, o Android tem acesso a todas as informações visíveis nela ou reproduzidas no dispositivo. Portanto, tenha cuidado com senhas, detalhes de pagamento, mensagens, fotos, áudios e vídeos."</string>
<string name="media_projection_entry_cast_permission_dialog_warning_single_app" msgid="9900961380294292">"Quando você transmite um app, o Android tem acesso a todas as informações visíveis ou reproduzidas nele. Tenha cuidado com senhas, detalhes de pagamento, mensagens fotos, áudios e vídeos."</string>
diff --git a/packages/SystemUI/res/values-ro/strings.xml b/packages/SystemUI/res/values-ro/strings.xml
index 749106b..844027a 100644
--- a/packages/SystemUI/res/values-ro/strings.xml
+++ b/packages/SystemUI/res/values-ro/strings.xml
@@ -57,6 +57,8 @@
<string name="hdmi_cec_set_menu_language_description" msgid="8176716678074126619">"Alt dispozitiv solicită schimbarea limbii de sistem"</string>
<string name="hdmi_cec_set_menu_language_accept" msgid="2513689457281009578">"Schimbă limba"</string>
<string name="hdmi_cec_set_menu_language_decline" msgid="7650721096558646011">"Păstrează limba actuală"</string>
+ <!-- no translation found for share_wifi_button_text (1285273973812029240) -->
+ <skip />
<string name="wifi_debugging_title" msgid="7300007687492186076">"Permiți remedierea erorilor wireless în această rețea?"</string>
<string name="wifi_debugging_message" msgid="5461204211731802995">"Numele rețelei (SSID)\n<xliff:g id="SSID_0">%1$s</xliff:g>\n\nAdresa Wi‑Fi (BSSID)\n<xliff:g id="BSSID_1">%2$s</xliff:g>"</string>
<string name="wifi_debugging_always" msgid="2968383799517975155">"Permite întotdeauna în această rețea"</string>
@@ -158,6 +160,8 @@
<string name="biometric_dialog_wrong_pattern" msgid="8954812279840889029">"Model greșit"</string>
<string name="biometric_dialog_wrong_password" msgid="69477929306843790">"Parolă greșită"</string>
<string name="biometric_dialog_credential_too_many_attempts" msgid="3083141271737748716">"Prea multe încercări incorecte.\nÎncearcă din nou peste <xliff:g id="NUMBER">%d</xliff:g> secunde."</string>
+ <!-- no translation found for work_challenge_emergency_button_text (8946588434515599288) -->
+ <skip />
<string name="biometric_dialog_credential_attempts_before_wipe" msgid="6751859711975516999">"Încearcă din nou. Încercarea <xliff:g id="ATTEMPTS_0">%1$d</xliff:g> din <xliff:g id="MAX_ATTEMPTS">%2$d</xliff:g>."</string>
<string name="biometric_dialog_last_attempt_before_wipe_dialog_title" msgid="2874250099278693477">"Datele tale vor fi șterse"</string>
<string name="biometric_dialog_last_pattern_attempt_before_wipe_device" msgid="6562299244825817598">"Dacă la următoarea încercare introduci un model incorect, datele de pe acest dispozitiv vor fi șterse."</string>
@@ -411,6 +415,8 @@
<string name="media_projection_entry_app_permission_dialog_warning_entire_screen" msgid="8736391633234144237">"Când permiți accesul, înregistrezi sau proiectezi, <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> are acces la orice este vizibil pe ecran sau se redă pe dispozitiv. Prin urmare, ai grijă cu parolele, detaliile de plată, mesajele, fotografiile și conținutul audio și video."</string>
<string name="media_projection_entry_app_permission_dialog_warning_single_app" msgid="5211695779082563959">"Când permiți accesul, înregistrezi sau proiectezi o aplicație, <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> are acces la orice se afișează pe ecran sau se redă în aplicație. Prin urmare, ai grijă cu informații cum ar fi parolele, detaliile de plată, mesajele, fotografiile și conținutul audio și video."</string>
<string name="media_projection_entry_app_permission_dialog_continue" msgid="295463518195075840">"Începe"</string>
+ <!-- no translation found for media_projection_entry_app_permission_dialog_single_app_disabled (8999903044874669995) -->
+ <skip />
<string name="media_projection_entry_cast_permission_dialog_title" msgid="8860150223172993547">"Începi să proiectezi?"</string>
<string name="media_projection_entry_cast_permission_dialog_warning_entire_screen" msgid="1986212276016817231">"Când proiectezi, Android are acces la orice este vizibil pe ecran sau se redă pe dispozitiv. Prin urmare, ai grijă cu parolele, detaliile de plată, mesajele, fotografiile și conținutul audio și video."</string>
<string name="media_projection_entry_cast_permission_dialog_warning_single_app" msgid="9900961380294292">"Când proiectezi o aplicație, Android are acces la orice se afișează sau se redă în aplicație. Prin urmare, ai grijă cu parolele, detaliile de plată, mesajele, fotografiile și conținutul audio și video."</string>
@@ -865,8 +871,7 @@
<string name="magnification_mode_switch_state_window" msgid="8597100249594076965">"Mărește o parte a ecranului"</string>
<string name="magnification_open_settings_click_label" msgid="6151849212725923363">"Deschide setările pentru mărire"</string>
<string name="magnification_close_settings_click_label" msgid="4642477260651704517">"Închide setările de mărire"</string>
- <!-- no translation found for magnification_exit_edit_mode_click_label (1664818325144887117) -->
- <skip />
+ <string name="magnification_exit_edit_mode_click_label" msgid="1664818325144887117">"Ieși din modul de editare"</string>
<string name="magnification_drag_corner_to_resize" msgid="1249766311052418130">"Trage de colț pentru a redimensiona"</string>
<string name="accessibility_allow_diagonal_scrolling" msgid="3258050349191496398">"Permite derularea pe diagonală"</string>
<string name="accessibility_resize" msgid="5733759136600611551">"Redimensionează"</string>
diff --git a/packages/SystemUI/res/values-ru/strings.xml b/packages/SystemUI/res/values-ru/strings.xml
index 5f7445a..9728103 100644
--- a/packages/SystemUI/res/values-ru/strings.xml
+++ b/packages/SystemUI/res/values-ru/strings.xml
@@ -57,6 +57,8 @@
<string name="hdmi_cec_set_menu_language_description" msgid="8176716678074126619">"Получен запрос на изменение системного языка от другого устройства."</string>
<string name="hdmi_cec_set_menu_language_accept" msgid="2513689457281009578">"Изменить язык"</string>
<string name="hdmi_cec_set_menu_language_decline" msgid="7650721096558646011">"Оставить текущий язык"</string>
+ <!-- no translation found for share_wifi_button_text (1285273973812029240) -->
+ <skip />
<string name="wifi_debugging_title" msgid="7300007687492186076">"Разрешить отладку по Wi-Fi в этой сети?"</string>
<string name="wifi_debugging_message" msgid="5461204211731802995">"Название сети (SSID):\n<xliff:g id="SSID_0">%1$s</xliff:g>\n\nMAC-адрес точки доступа (BSSID):\n<xliff:g id="BSSID_1">%2$s</xliff:g>"</string>
<string name="wifi_debugging_always" msgid="2968383799517975155">"Всегда разрешать отладку в этой сети"</string>
@@ -158,6 +160,8 @@
<string name="biometric_dialog_wrong_pattern" msgid="8954812279840889029">"Неверный графический ключ."</string>
<string name="biometric_dialog_wrong_password" msgid="69477929306843790">"Неверный пароль."</string>
<string name="biometric_dialog_credential_too_many_attempts" msgid="3083141271737748716">"Слишком много неудачных попыток.\nПовторите через <xliff:g id="NUMBER">%d</xliff:g> сек."</string>
+ <!-- no translation found for work_challenge_emergency_button_text (8946588434515599288) -->
+ <skip />
<string name="biometric_dialog_credential_attempts_before_wipe" msgid="6751859711975516999">"Попробуйте ещё раз. Попытка <xliff:g id="ATTEMPTS_0">%1$d</xliff:g> из <xliff:g id="MAX_ATTEMPTS">%2$d</xliff:g>."</string>
<string name="biometric_dialog_last_attempt_before_wipe_dialog_title" msgid="2874250099278693477">"Осталась одна попытка"</string>
<string name="biometric_dialog_last_pattern_attempt_before_wipe_device" msgid="6562299244825817598">"Если вы неправильно введете графический ключ ещё раз, с устройства будут удалены все данные."</string>
@@ -411,6 +415,8 @@
<string name="media_projection_entry_app_permission_dialog_warning_entire_screen" msgid="8736391633234144237">"Когда вы демонстрируете, транслируете экран или записываете видео с него, приложение \"<xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g>\" получает доступ ко всему, что видно или воспроизводится на устройстве. Поэтому будьте осторожны с паролями, сведениями о способах оплаты, сообщениями, фотографиями, аудио- и видеозаписями."</string>
<string name="media_projection_entry_app_permission_dialog_warning_single_app" msgid="5211695779082563959">"Когда вы демонстрируете, записываете или транслируете экран приложения, <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> получает доступ ко всему, что видно или воспроизводится в этом приложении. Будьте осторожны с паролями, сведениями о способах оплаты, сообщениями, фотографиями, аудио- и видеозаписями."</string>
<string name="media_projection_entry_app_permission_dialog_continue" msgid="295463518195075840">"Начать"</string>
+ <!-- no translation found for media_projection_entry_app_permission_dialog_single_app_disabled (8999903044874669995) -->
+ <skip />
<string name="media_projection_entry_cast_permission_dialog_title" msgid="8860150223172993547">"Начать трансляцию?"</string>
<string name="media_projection_entry_cast_permission_dialog_warning_entire_screen" msgid="1986212276016817231">"Во время трансляции система Android получает доступ ко всему, что видно или воспроизводится на устройстве. Поэтому будьте осторожны с паролями, сведениями о способах оплаты, сообщениями, фотографиями, аудио- и видеозаписями."</string>
<string name="media_projection_entry_cast_permission_dialog_warning_single_app" msgid="9900961380294292">"Когда вы транслируете экран приложения, система Android получает доступ ко всему, что видно или воспроизводится в нем. Поэтому будьте осторожны с паролями, сведениями о способах оплаты, сообщениями, фотографиями, аудио- и видеозаписями."</string>
@@ -865,8 +871,7 @@
<string name="magnification_mode_switch_state_window" msgid="8597100249594076965">"Увеличить часть экрана"</string>
<string name="magnification_open_settings_click_label" msgid="6151849212725923363">"Открыть настройки увеличения"</string>
<string name="magnification_close_settings_click_label" msgid="4642477260651704517">"Закрыть настройки увеличения"</string>
- <!-- no translation found for magnification_exit_edit_mode_click_label (1664818325144887117) -->
- <skip />
+ <string name="magnification_exit_edit_mode_click_label" msgid="1664818325144887117">"Выйти из режима редактирования"</string>
<string name="magnification_drag_corner_to_resize" msgid="1249766311052418130">"Потяните за угол, чтобы изменить размер"</string>
<string name="accessibility_allow_diagonal_scrolling" msgid="3258050349191496398">"Разрешить прокручивать по диагонали"</string>
<string name="accessibility_resize" msgid="5733759136600611551">"Изменить размер"</string>
@@ -1169,7 +1174,7 @@
<string name="set_default_notes_app_toast_content" msgid="2812374329662610753">"Задайте стандартное приложение для заметок в настройках."</string>
<string name="install_app" msgid="5066668100199613936">"Установить приложение"</string>
<string name="privacy_dialog_title" msgid="7839968133469098311">"Микрофон и камера"</string>
- <string name="privacy_dialog_summary" msgid="2458769652125995409">"Недавно использовались приложениями"</string>
+ <string name="privacy_dialog_summary" msgid="2458769652125995409">"Недавнее использование приложениями"</string>
<string name="privacy_dialog_more_button" msgid="7610604080293562345">"Посмотреть недавний доступ"</string>
<string name="privacy_dialog_done_button" msgid="4504330708531434263">"Готово"</string>
<string name="privacy_dialog_expand_action" msgid="9129262348628331377">"Развернуть и показать параметры"</string>
diff --git a/packages/SystemUI/res/values-si/strings.xml b/packages/SystemUI/res/values-si/strings.xml
index 5cc1fa9..2424fe1 100644
--- a/packages/SystemUI/res/values-si/strings.xml
+++ b/packages/SystemUI/res/values-si/strings.xml
@@ -57,6 +57,8 @@
<string name="hdmi_cec_set_menu_language_description" msgid="8176716678074126619">"වෙනත් උපාංගයකින් පද්ධති භාෂාව වෙනස් කිරීම ඉල්ලා ඇත"</string>
<string name="hdmi_cec_set_menu_language_accept" msgid="2513689457281009578">"භාෂාව වෙනස් කරන්න"</string>
<string name="hdmi_cec_set_menu_language_decline" msgid="7650721096558646011">"වත්මන් භාෂාව තබා ගන්න"</string>
+ <!-- no translation found for share_wifi_button_text (1285273973812029240) -->
+ <skip />
<string name="wifi_debugging_title" msgid="7300007687492186076">"මෙම ජාලයේ නොරැහැන් නිදොස්කරණය ඉඩ දෙන්නද?"</string>
<string name="wifi_debugging_message" msgid="5461204211731802995">"ජාල නම (SSID)\n<xliff:g id="SSID_0">%1$s</xliff:g>\n\nWi‑Fi ලිපිනය (BSSID)\n<xliff:g id="BSSID_1">%2$s</xliff:g>"</string>
<string name="wifi_debugging_always" msgid="2968383799517975155">"මෙම ජාලයේ සැමවිට ඉඩ දෙන්න"</string>
@@ -158,6 +160,8 @@
<string name="biometric_dialog_wrong_pattern" msgid="8954812279840889029">"වැරදි රටාවකි"</string>
<string name="biometric_dialog_wrong_password" msgid="69477929306843790">"වැරදි මුරපදයකි"</string>
<string name="biometric_dialog_credential_too_many_attempts" msgid="3083141271737748716">"වැරදි උත්සාහයන් ගණන වැඩියි. තත්පර \n තත්පර <xliff:g id="NUMBER">%d</xliff:g>කින් නැවත උත්සාහ කරන්න."</string>
+ <!-- no translation found for work_challenge_emergency_button_text (8946588434515599288) -->
+ <skip />
<string name="biometric_dialog_credential_attempts_before_wipe" msgid="6751859711975516999">"නැවත උත්සාහ කරන්න. <xliff:g id="MAX_ATTEMPTS">%2$d</xliff:g>කින් <xliff:g id="ATTEMPTS_0">%1$d</xliff:g> උත්සාහය."</string>
<string name="biometric_dialog_last_attempt_before_wipe_dialog_title" msgid="2874250099278693477">"ඔබේ දත්ත මකනු ඇත"</string>
<string name="biometric_dialog_last_pattern_attempt_before_wipe_device" msgid="6562299244825817598">"ඔබ ඊළඟ උත්සාහයේදී වැරදි රටාවක් ඇතුළු කළහොත්, මෙම උපාංගයෙහි දත්ත මකනු ඇත."</string>
@@ -411,6 +415,8 @@
<string name="media_projection_entry_app_permission_dialog_warning_entire_screen" msgid="8736391633234144237">"ඔබ බෙදා ගන්නා විට, පටිගත කරන විට, හෝ විකාශනය කරන විට, <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> හට ඔබේ තිරයේ පෙනෙන හෝ ඔබේ උපාංගයේ වාදනය වන ඕනෑම දෙයකට ප්රවේශය ඇත. ඒ නිසා මුරපද, ගෙවීම් විස්තර, පණිවුඩ, ඡායාරූප, සහ ශ්රව්ය සහ දෘශ්ය වැනි දේවල් පිළිබඳ ප්රවේශම් වන්න."</string>
<string name="media_projection_entry_app_permission_dialog_warning_single_app" msgid="5211695779082563959">"ඔබ යෙදුමක් බෙදා ගන්නා විට, පටිගත කරන විට හෝ විකාශය කරන විට, <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> හට එම යෙදුමේ පෙන්වන හෝ වාදනය කරන ඕනෑම දෙයකට ප්රවේශය ඇත. ඒ නිසා මුරපද, ගෙවීම් විස්තර, පණිවුඩ, ඡායාරූප, සහ ශ්රව්ය සහ දෘශ්ය වැනි දේවල් පිළිබඳ ප්රවේශම් වන්න."</string>
<string name="media_projection_entry_app_permission_dialog_continue" msgid="295463518195075840">"අරඹන්න"</string>
+ <!-- no translation found for media_projection_entry_app_permission_dialog_single_app_disabled (8999903044874669995) -->
+ <skip />
<string name="media_projection_entry_cast_permission_dialog_title" msgid="8860150223172993547">"විකාශය ආරම්භ කරන්න ද?"</string>
<string name="media_projection_entry_cast_permission_dialog_warning_entire_screen" msgid="1986212276016817231">"ඔබ විකාශය කරන විට, Android හට ඔබේ තිරයේ පෙනෙන හෝ ඔබේ උපාංගයේ වාදනය වන ඕනෑම දෙයකට ප්රවේශය ඇත. ඒ නිසා මුරපද, ගෙවීම් විස්තර, පණිවුඩ, ඡායාරූප, සහ ශ්රව්ය සහ දෘශ්ය වැනි දේවල් පිළිබඳ ප්රවේශම් වන්න."</string>
<string name="media_projection_entry_cast_permission_dialog_warning_single_app" msgid="9900961380294292">"ඔබ යෙදුමක් විකාශය කරන විට, Android හට එම යෙදුමේ පෙන්වන හෝ වාදනය කරන ඕනෑම දෙයකට ප්රවේශය ඇත. ඒ නිසා මුරපද, ගෙවීම් විස්තර, පණිවුඩ, ඡායාරූප, සහ ශ්රව්ය සහ දෘශ්ය වැනි දේවල් පිළිබඳ ප්රවේශම් වන්න."</string>
@@ -865,8 +871,7 @@
<string name="magnification_mode_switch_state_window" msgid="8597100249594076965">"තිරයේ කොටසක් විශාලනය කරන්න"</string>
<string name="magnification_open_settings_click_label" msgid="6151849212725923363">"විශාලන සැකසීම් විවෘත කරන්න"</string>
<string name="magnification_close_settings_click_label" msgid="4642477260651704517">"විශාලන සැකසීම් වසන්න"</string>
- <!-- no translation found for magnification_exit_edit_mode_click_label (1664818325144887117) -->
- <skip />
+ <string name="magnification_exit_edit_mode_click_label" msgid="1664818325144887117">"සංස්කරණ ප්රකාරයෙන් පිටවන්න"</string>
<string name="magnification_drag_corner_to_resize" msgid="1249766311052418130">"ප්රමාණය වෙනස් කිරීමට කොන අදින්න"</string>
<string name="accessibility_allow_diagonal_scrolling" msgid="3258050349191496398">"විකර්ණ අනුචලනයට ඉඩ දෙන්න"</string>
<string name="accessibility_resize" msgid="5733759136600611551">"ප්රතිප්රමාණය කරන්න"</string>
diff --git a/packages/SystemUI/res/values-sk/strings.xml b/packages/SystemUI/res/values-sk/strings.xml
index 1da7aca..4e1b46b 100644
--- a/packages/SystemUI/res/values-sk/strings.xml
+++ b/packages/SystemUI/res/values-sk/strings.xml
@@ -57,6 +57,8 @@
<string name="hdmi_cec_set_menu_language_description" msgid="8176716678074126619">"Zmenu jazyka systému vyžiadalo iné zariadenie"</string>
<string name="hdmi_cec_set_menu_language_accept" msgid="2513689457281009578">"Zmeniť jazyk"</string>
<string name="hdmi_cec_set_menu_language_decline" msgid="7650721096558646011">"Ponechať aktuálny jazyk"</string>
+ <!-- no translation found for share_wifi_button_text (1285273973812029240) -->
+ <skip />
<string name="wifi_debugging_title" msgid="7300007687492186076">"Chcete povoliť bezdrôtové ladenie v tejto sieti?"</string>
<string name="wifi_debugging_message" msgid="5461204211731802995">"Názov siete (SSID)\n<xliff:g id="SSID_0">%1$s</xliff:g>\n\nAdresa Wi‑Fi (BSSID)\n<xliff:g id="BSSID_1">%2$s</xliff:g>"</string>
<string name="wifi_debugging_always" msgid="2968383799517975155">"Vždy povoliť v tejto sieti"</string>
@@ -158,6 +160,8 @@
<string name="biometric_dialog_wrong_pattern" msgid="8954812279840889029">"Nesprávny vzor"</string>
<string name="biometric_dialog_wrong_password" msgid="69477929306843790">"Nesprávne heslo"</string>
<string name="biometric_dialog_credential_too_many_attempts" msgid="3083141271737748716">"Príliš veľa nesprávnych pokusov. \nSkúste to znova o <xliff:g id="NUMBER">%d</xliff:g> s."</string>
+ <!-- no translation found for work_challenge_emergency_button_text (8946588434515599288) -->
+ <skip />
<string name="biometric_dialog_credential_attempts_before_wipe" msgid="6751859711975516999">"Skúste to znova. <xliff:g id="ATTEMPTS_0">%1$d</xliff:g>. z <xliff:g id="MAX_ATTEMPTS">%2$d</xliff:g> pokusov."</string>
<string name="biometric_dialog_last_attempt_before_wipe_dialog_title" msgid="2874250099278693477">"Vaše dáta budú odstránené"</string>
<string name="biometric_dialog_last_pattern_attempt_before_wipe_device" msgid="6562299244825817598">"Ak pri ďalšom pokuse zadáte nesprávny vzor, dáta tohto zariadenia budú odstránené."</string>
@@ -411,6 +415,8 @@
<string name="media_projection_entry_app_permission_dialog_warning_entire_screen" msgid="8736391633234144237">"Počas zdieľania, nahrávania alebo prenosu bude mať <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> prístup k všetkému, čo sa zobrazuje na obrazovke alebo prehráva v zariadení. Preto zvýšte pozornosť v prípade položiek, ako sú heslá, platobné údaje, správy, fotky a zvuk či video."</string>
<string name="media_projection_entry_app_permission_dialog_warning_single_app" msgid="5211695779082563959">"Počas zdieľania, nahrávania alebo prenosu v aplikácii bude mať <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> prístup k všetkému, čo sa v danej aplikácii zobrazuje alebo prehráva. Preto zvýšte pozornosť v prípade položiek, ako sú heslá, platobné údaje, správy, fotky a zvuk či video."</string>
<string name="media_projection_entry_app_permission_dialog_continue" msgid="295463518195075840">"Začať"</string>
+ <!-- no translation found for media_projection_entry_app_permission_dialog_single_app_disabled (8999903044874669995) -->
+ <skip />
<string name="media_projection_entry_cast_permission_dialog_title" msgid="8860150223172993547">"Chcete spustiť prenos?"</string>
<string name="media_projection_entry_cast_permission_dialog_warning_entire_screen" msgid="1986212276016817231">"Počas prenosu bude mať Android prístup k všetkému, čo sa zobrazuje na obrazovke alebo prehráva v zariadení. Preto zvýšte pozornosť v prípade položiek, ako sú heslá, platobné údaje, správy, fotky a zvuk či video."</string>
<string name="media_projection_entry_cast_permission_dialog_warning_single_app" msgid="9900961380294292">"Počas prenosu v aplikácii bude mať Android prístup k všetkému, čo sa v danej aplikácii zobrazuje alebo prehráva. Preto zvýšte pozornosť v prípade položiek, ako sú heslá, platobné údaje, správy, fotky a zvuk či video."</string>
@@ -865,8 +871,7 @@
<string name="magnification_mode_switch_state_window" msgid="8597100249594076965">"Zväčšiť časť obrazovky"</string>
<string name="magnification_open_settings_click_label" msgid="6151849212725923363">"Otvoriť nastavenia zväčšenia"</string>
<string name="magnification_close_settings_click_label" msgid="4642477260651704517">"Zavrieť nastavenia zväčšenia"</string>
- <!-- no translation found for magnification_exit_edit_mode_click_label (1664818325144887117) -->
- <skip />
+ <string name="magnification_exit_edit_mode_click_label" msgid="1664818325144887117">"Ukončiť režim úprav"</string>
<string name="magnification_drag_corner_to_resize" msgid="1249766311052418130">"Veľkosť zmeníte presunutím rohu"</string>
<string name="accessibility_allow_diagonal_scrolling" msgid="3258050349191496398">"Povoliť diagonálne posúvanie"</string>
<string name="accessibility_resize" msgid="5733759136600611551">"Zmeniť veľkosť"</string>
@@ -1168,7 +1173,7 @@
<string name="assistant_attention_content_description" msgid="6830215897604642875">"Pozornosť Asistenta je zapnutá"</string>
<string name="set_default_notes_app_toast_content" msgid="2812374329662610753">"Nastavte predvolenú aplikáciu na poznámky v Nastaveniach"</string>
<string name="install_app" msgid="5066668100199613936">"Inštalovať aplikáciu"</string>
- <string name="privacy_dialog_title" msgid="7839968133469098311">"Mikrofón a kamera"</string>
+ <string name="privacy_dialog_title" msgid="7839968133469098311">"Mikrofón a fotoaparát"</string>
<string name="privacy_dialog_summary" msgid="2458769652125995409">"Nedávne využitie aplikácie"</string>
<string name="privacy_dialog_more_button" msgid="7610604080293562345">"Zobraziť nedávny prístup"</string>
<string name="privacy_dialog_done_button" msgid="4504330708531434263">"Hotovo"</string>
diff --git a/packages/SystemUI/res/values-sl/strings.xml b/packages/SystemUI/res/values-sl/strings.xml
index 79e5437..89e9276 100644
--- a/packages/SystemUI/res/values-sl/strings.xml
+++ b/packages/SystemUI/res/values-sl/strings.xml
@@ -57,6 +57,8 @@
<string name="hdmi_cec_set_menu_language_description" msgid="8176716678074126619">"Spremembo jezika sistema je zahtevala druga naprava."</string>
<string name="hdmi_cec_set_menu_language_accept" msgid="2513689457281009578">"Spremeni jezik"</string>
<string name="hdmi_cec_set_menu_language_decline" msgid="7650721096558646011">"Obdrži trenutni jezik"</string>
+ <!-- no translation found for share_wifi_button_text (1285273973812029240) -->
+ <skip />
<string name="wifi_debugging_title" msgid="7300007687492186076">"Ali dovolite brezžično odpravljanje napak v tem omrežju?"</string>
<string name="wifi_debugging_message" msgid="5461204211731802995">"Ime omrežja (SSID)\n<xliff:g id="SSID_0">%1$s</xliff:g>\n\nNaslov omrežja Wi‑Fi (BSSID)\n<xliff:g id="BSSID_1">%2$s</xliff:g>"</string>
<string name="wifi_debugging_always" msgid="2968383799517975155">"Vedno dovoli v tem omrežju"</string>
@@ -158,6 +160,8 @@
<string name="biometric_dialog_wrong_pattern" msgid="8954812279840889029">"Napačen vzorec"</string>
<string name="biometric_dialog_wrong_password" msgid="69477929306843790">"Napačno geslo"</string>
<string name="biometric_dialog_credential_too_many_attempts" msgid="3083141271737748716">"Preveč napačnih poskusov.\nPoskusite znova čez <xliff:g id="NUMBER">%d</xliff:g> s."</string>
+ <!-- no translation found for work_challenge_emergency_button_text (8946588434515599288) -->
+ <skip />
<string name="biometric_dialog_credential_attempts_before_wipe" msgid="6751859711975516999">"Poskusite znova. Poskus <xliff:g id="ATTEMPTS_0">%1$d</xliff:g> od <xliff:g id="MAX_ATTEMPTS">%2$d</xliff:g>."</string>
<string name="biometric_dialog_last_attempt_before_wipe_dialog_title" msgid="2874250099278693477">"Vaši podatki bodo izbrisani"</string>
<string name="biometric_dialog_last_pattern_attempt_before_wipe_device" msgid="6562299244825817598">"Če pri naslednjem poskusu vnesete napačen vzorec, bodo podatki v tej napravi izbrisani."</string>
@@ -411,6 +415,8 @@
<string name="media_projection_entry_app_permission_dialog_warning_entire_screen" msgid="8736391633234144237">"Pri deljenju, snemanju ali predvajanju ima aplikacija <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> dostop do vsega, kar je prikazano na zaslonu ali se predvaja v napravi. Zato bodite previdni z gesli, podatki za plačilo, sporočili, fotografijami ter z zvokom in videom."</string>
<string name="media_projection_entry_app_permission_dialog_warning_single_app" msgid="5211695779082563959">"Pri deljenju, snemanju ali predvajanju aplikacije ima aplikacija <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> dostop do vsega, kar je prikazano ali predvajano v tej aplikaciji, zato bodite previdni z gesli, podatki za plačilo, sporočili, fotografijami ter z zvokom in videom."</string>
<string name="media_projection_entry_app_permission_dialog_continue" msgid="295463518195075840">"Začni"</string>
+ <!-- no translation found for media_projection_entry_app_permission_dialog_single_app_disabled (8999903044874669995) -->
+ <skip />
<string name="media_projection_entry_cast_permission_dialog_title" msgid="8860150223172993547">"Želite začeti predvajati?"</string>
<string name="media_projection_entry_cast_permission_dialog_warning_entire_screen" msgid="1986212276016817231">"Pri predvajanju ima Android dostop do vsega, kar je prikazano na zaslonu ali se predvaja v napravi. Zato bodite previdni z gesli, podatki za plačilo, sporočili, fotografijami ter z zvokom in videom."</string>
<string name="media_projection_entry_cast_permission_dialog_warning_single_app" msgid="9900961380294292">"Pri predvajanju aplikacije ima Android dostop do vsega, kar je prikazano ali predvajano v tej aplikaciji, zato bodite previdni z gesli, podatki za plačilo, sporočili, fotografijami ter z zvokom in videom."</string>
diff --git a/packages/SystemUI/res/values-sq/strings.xml b/packages/SystemUI/res/values-sq/strings.xml
index 5b58fc7..2c61054 100644
--- a/packages/SystemUI/res/values-sq/strings.xml
+++ b/packages/SystemUI/res/values-sq/strings.xml
@@ -57,6 +57,8 @@
<string name="hdmi_cec_set_menu_language_description" msgid="8176716678074126619">"Një pajisje tjetër kërkoi ndryshimin e gjuhës së sistemit"</string>
<string name="hdmi_cec_set_menu_language_accept" msgid="2513689457281009578">"Ndrysho gjuhën"</string>
<string name="hdmi_cec_set_menu_language_decline" msgid="7650721096558646011">"Mbaj gjuhën aktuale"</string>
+ <!-- no translation found for share_wifi_button_text (1285273973812029240) -->
+ <skip />
<string name="wifi_debugging_title" msgid="7300007687492186076">"Të lejohet korrigjimi përmes Wi-Fi në këtë rrjet?"</string>
<string name="wifi_debugging_message" msgid="5461204211731802995">"Emri i rrjetit (SSID)\n<xliff:g id="SSID_0">%1$s</xliff:g>\n\nAdresa Wi‑Fi (BSSID)\n<xliff:g id="BSSID_1">%2$s</xliff:g>"</string>
<string name="wifi_debugging_always" msgid="2968383799517975155">"Lejo gjithmonë në këtë rrjet"</string>
@@ -158,6 +160,8 @@
<string name="biometric_dialog_wrong_pattern" msgid="8954812279840889029">"Motiv i gabuar"</string>
<string name="biometric_dialog_wrong_password" msgid="69477929306843790">"Fjalëkalim i gabuar"</string>
<string name="biometric_dialog_credential_too_many_attempts" msgid="3083141271737748716">"Shumë tentativa të pasakta.\nProvo përsëri brenda <xliff:g id="NUMBER">%d</xliff:g> sekondash."</string>
+ <!-- no translation found for work_challenge_emergency_button_text (8946588434515599288) -->
+ <skip />
<string name="biometric_dialog_credential_attempts_before_wipe" msgid="6751859711975516999">"Provo sërish. Tentativa <xliff:g id="ATTEMPTS_0">%1$d</xliff:g> nga <xliff:g id="MAX_ATTEMPTS">%2$d</xliff:g>."</string>
<string name="biometric_dialog_last_attempt_before_wipe_dialog_title" msgid="2874250099278693477">"Të dhënat e tua do të fshihen"</string>
<string name="biometric_dialog_last_pattern_attempt_before_wipe_device" msgid="6562299244825817598">"Nëse fut një motiv të pasaktë në tentativën tjetër, të dhënat e kësaj pajisjeje do të fshihen."</string>
@@ -411,6 +415,8 @@
<string name="media_projection_entry_app_permission_dialog_warning_entire_screen" msgid="8736391633234144237">"Kur ti ndan, regjistron ose transmeton, <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> ka qasje te çdo gjë e dukshme në ekranin tënd ose që po luhet në pajisjen tënde. Prandaj, ki kujdes me gjërat si fjalëkalimet, detajet e pagesave, mesazhet, fotografitë, si dhe audion dhe videon."</string>
<string name="media_projection_entry_app_permission_dialog_warning_single_app" msgid="5211695779082563959">"Kur ti ndan, regjistron ose transmeton një aplikacion, <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> ka qasje te çdo gjë e dukshme në ekranin tënd ose që po luhet në atë aplikacion. Prandaj, ki kujdes me gjërat si fjalëkalimet, detajet e pagesave, mesazhet, fotografitë, si dhe audion dhe videon."</string>
<string name="media_projection_entry_app_permission_dialog_continue" msgid="295463518195075840">"Nis"</string>
+ <!-- no translation found for media_projection_entry_app_permission_dialog_single_app_disabled (8999903044874669995) -->
+ <skip />
<string name="media_projection_entry_cast_permission_dialog_title" msgid="8860150223172993547">"Të niset transmetimi?"</string>
<string name="media_projection_entry_cast_permission_dialog_warning_entire_screen" msgid="1986212276016817231">"Kur ti transmeton, Android ka qasje te çdo gjë e dukshme në ekranin tënd ose që po luhet në pajisjen tënde. Prandaj, ki kujdes me gjërat si fjalëkalimet, detajet e pagesave, mesazhet, fotografitë, si dhe audion dhe videon."</string>
<string name="media_projection_entry_cast_permission_dialog_warning_single_app" msgid="9900961380294292">"Kur ti transmeton një aplikacion, Android ka qasje te çdo gjë e dukshme ose që po luhet në atë aplikacion. Prandaj, ki kujdes me gjërat si fjalëkalimet, detajet e pagesës, mesazhet, fotografitë, si dhe audion dhe videon."</string>
@@ -865,8 +871,7 @@
<string name="magnification_mode_switch_state_window" msgid="8597100249594076965">"Zmadho një pjesë të ekranit"</string>
<string name="magnification_open_settings_click_label" msgid="6151849212725923363">"Hap cilësimet e zmadhimit"</string>
<string name="magnification_close_settings_click_label" msgid="4642477260651704517">"Mbyll cilësimet e zmadhimit"</string>
- <!-- no translation found for magnification_exit_edit_mode_click_label (1664818325144887117) -->
- <skip />
+ <string name="magnification_exit_edit_mode_click_label" msgid="1664818325144887117">"Dil nga modaliteti i modifikimit"</string>
<string name="magnification_drag_corner_to_resize" msgid="1249766311052418130">"Zvarrit këndin për të ndryshuar përmasat"</string>
<string name="accessibility_allow_diagonal_scrolling" msgid="3258050349191496398">"Lejo lëvizjen diagonale"</string>
<string name="accessibility_resize" msgid="5733759136600611551">"Ndrysho përmasat"</string>
diff --git a/packages/SystemUI/res/values-sr/strings.xml b/packages/SystemUI/res/values-sr/strings.xml
index eee0fe6..7b208290 100644
--- a/packages/SystemUI/res/values-sr/strings.xml
+++ b/packages/SystemUI/res/values-sr/strings.xml
@@ -57,6 +57,8 @@
<string name="hdmi_cec_set_menu_language_description" msgid="8176716678074126619">"Други уређај је затражио промену језика система"</string>
<string name="hdmi_cec_set_menu_language_accept" msgid="2513689457281009578">"Промени језик"</string>
<string name="hdmi_cec_set_menu_language_decline" msgid="7650721096558646011">"Задржи актуелни језик"</string>
+ <!-- no translation found for share_wifi_button_text (1285273973812029240) -->
+ <skip />
<string name="wifi_debugging_title" msgid="7300007687492186076">"Желите да дозволите бежично отклањање грешака на овој мрежи?"</string>
<string name="wifi_debugging_message" msgid="5461204211731802995">"Назив мреже (SSID)\n<xliff:g id="SSID_0">%1$s</xliff:g>\n\nWi‑Fi адреса (BSSID)\n<xliff:g id="BSSID_1">%2$s</xliff:g>"</string>
<string name="wifi_debugging_always" msgid="2968383799517975155">"Увек дозволи на овој мрежи"</string>
@@ -158,6 +160,8 @@
<string name="biometric_dialog_wrong_pattern" msgid="8954812279840889029">"Погрешан шаблон"</string>
<string name="biometric_dialog_wrong_password" msgid="69477929306843790">"Погрешна лозинка"</string>
<string name="biometric_dialog_credential_too_many_attempts" msgid="3083141271737748716">"Превише нетачних покушаја.\n Пробајте поново за <xliff:g id="NUMBER">%d</xliff:g> сек."</string>
+ <!-- no translation found for work_challenge_emergency_button_text (8946588434515599288) -->
+ <skip />
<string name="biometric_dialog_credential_attempts_before_wipe" msgid="6751859711975516999">"Пробајте поново. <xliff:g id="ATTEMPTS_0">%1$d</xliff:g>. покушај од <xliff:g id="MAX_ATTEMPTS">%2$d</xliff:g>."</string>
<string name="biometric_dialog_last_attempt_before_wipe_dialog_title" msgid="2874250099278693477">"Подаци ће се избрисати"</string>
<string name="biometric_dialog_last_pattern_attempt_before_wipe_device" msgid="6562299244825817598">"Ако унесете нетачан шаблон при следећем покушају, избрисаћемо податке са овог уређаја."</string>
@@ -411,6 +415,8 @@
<string name="media_projection_entry_app_permission_dialog_warning_entire_screen" msgid="8736391633234144237">"Када делите, снимате или пребацујете, <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> има приступ комплетном садржају који је видљив на екрану или се пушта на уређају. Зато будите пажљиви са лозинкама, информацијама о плаћању, порукама, сликама и аудио и видео снимцима."</string>
<string name="media_projection_entry_app_permission_dialog_warning_single_app" msgid="5211695779082563959">"Када делите, снимате или пребацујете апликацију, <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> има приступ комплетном садржају који је видљив или се пушта у тој апликацији. Зато будите пажљиви са лозинкама, информацијама о плаћању, порукама, сликама и аудио и видео снимцима."</string>
<string name="media_projection_entry_app_permission_dialog_continue" msgid="295463518195075840">"Покрени"</string>
+ <!-- no translation found for media_projection_entry_app_permission_dialog_single_app_disabled (8999903044874669995) -->
+ <skip />
<string name="media_projection_entry_cast_permission_dialog_title" msgid="8860150223172993547">"Желите да започнете пребацивање?"</string>
<string name="media_projection_entry_cast_permission_dialog_warning_entire_screen" msgid="1986212276016817231">"Када пребацујете, Android има приступ комплетном садржају који је видљив на екрану или се пушта на уређају. Зато будите пажљиви са лозинкама, информацијама о плаћању, порукама, сликама и аудио и видео снимцима."</string>
<string name="media_projection_entry_cast_permission_dialog_warning_single_app" msgid="9900961380294292">"Када пребацујете апликацију, Android има приступ комплетном садржају који је видљив или се пушта у тој апликацији. Зато будите пажљиви са лозинкама, информацијама о плаћању, порукама, сликама и аудио и видео снимцима."</string>
@@ -426,7 +432,7 @@
<string name="screen_capturing_disabled_by_policy_dialog_title" msgid="2113331792064527203">"Блокира ИТ администратор"</string>
<string name="screen_capturing_disabled_by_policy_dialog_description" msgid="6015975736747696431">"Снимање екрана је онемогућено смерницама за уређај"</string>
<string name="clear_all_notifications_text" msgid="348312370303046130">"Обриши све"</string>
- <string name="manage_notifications_text" msgid="6885645344647733116">"Управљајте"</string>
+ <string name="manage_notifications_text" msgid="6885645344647733116">"Управљај"</string>
<string name="manage_notifications_history_text" msgid="57055985396576230">"Историја"</string>
<string name="notification_section_header_incoming" msgid="850925217908095197">"Ново"</string>
<string name="notification_section_header_gentle" msgid="6804099527336337197">"Нечујно"</string>
diff --git a/packages/SystemUI/res/values-sv/strings.xml b/packages/SystemUI/res/values-sv/strings.xml
index b996649..0ebb778 100644
--- a/packages/SystemUI/res/values-sv/strings.xml
+++ b/packages/SystemUI/res/values-sv/strings.xml
@@ -57,6 +57,8 @@
<string name="hdmi_cec_set_menu_language_description" msgid="8176716678074126619">"Ändring av systemspråk har begärts av en annan enhet"</string>
<string name="hdmi_cec_set_menu_language_accept" msgid="2513689457281009578">"Ändra språk"</string>
<string name="hdmi_cec_set_menu_language_decline" msgid="7650721096558646011">"Behåll nuvarande språk"</string>
+ <!-- no translation found for share_wifi_button_text (1285273973812029240) -->
+ <skip />
<string name="wifi_debugging_title" msgid="7300007687492186076">"Vill du tillåta trådlös felsökning i det här nätverket?"</string>
<string name="wifi_debugging_message" msgid="5461204211731802995">"Nätverksnamn (SSID)\n<xliff:g id="SSID_0">%1$s</xliff:g>\n\nWi‑Fi-adress (BSSID)\n<xliff:g id="BSSID_1">%2$s</xliff:g>"</string>
<string name="wifi_debugging_always" msgid="2968383799517975155">"Tillåt alltid i det här nätverket"</string>
@@ -158,6 +160,8 @@
<string name="biometric_dialog_wrong_pattern" msgid="8954812279840889029">"Fel mönster"</string>
<string name="biometric_dialog_wrong_password" msgid="69477929306843790">"Fel lösenord"</string>
<string name="biometric_dialog_credential_too_many_attempts" msgid="3083141271737748716">"För många felaktiga försök.\nFörsök igen om <xliff:g id="NUMBER">%d</xliff:g> sekunder."</string>
+ <!-- no translation found for work_challenge_emergency_button_text (8946588434515599288) -->
+ <skip />
<string name="biometric_dialog_credential_attempts_before_wipe" msgid="6751859711975516999">"Försök igen. Försök <xliff:g id="ATTEMPTS_0">%1$d</xliff:g> av <xliff:g id="MAX_ATTEMPTS">%2$d</xliff:g>."</string>
<string name="biometric_dialog_last_attempt_before_wipe_dialog_title" msgid="2874250099278693477">"Din data raderas."</string>
<string name="biometric_dialog_last_pattern_attempt_before_wipe_device" msgid="6562299244825817598">"Enhetens data raderas om du ritar fel mönster vid nästa försök."</string>
@@ -411,6 +415,8 @@
<string name="media_projection_entry_app_permission_dialog_warning_entire_screen" msgid="8736391633234144237">"När du delar, spelar in eller castar har <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> åtkomst till allt som visas på skärmen eller spelas upp på enheten. Var försiktig med sådant som lösenord, betalningsuppgifter, meddelanden, foton och ljud och video."</string>
<string name="media_projection_entry_app_permission_dialog_warning_single_app" msgid="5211695779082563959">"När du delar, spelar in eller castar en app har <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> åtkomst till allt som visas eller spelas upp i appen. Var försiktig med sådant som lösenord, betalningsuppgifter, meddelanden, foton och ljud och video."</string>
<string name="media_projection_entry_app_permission_dialog_continue" msgid="295463518195075840">"Börja"</string>
+ <!-- no translation found for media_projection_entry_app_permission_dialog_single_app_disabled (8999903044874669995) -->
+ <skip />
<string name="media_projection_entry_cast_permission_dialog_title" msgid="8860150223172993547">"Vill du börja casta?"</string>
<string name="media_projection_entry_cast_permission_dialog_warning_entire_screen" msgid="1986212276016817231">"När du castar har Android åtkomst till allt som visas på skärmen eller spelas upp på enheten. Var försiktig med sådant som lösenord, betalningsuppgifter, meddelanden, foton och ljud och video."</string>
<string name="media_projection_entry_cast_permission_dialog_warning_single_app" msgid="9900961380294292">"När du castar en app har Android åtkomst till allt som visas eller spelas upp i appen. Var försiktig med sådant som lösenord, betalningsuppgifter, meddelanden, foton och ljud och video."</string>
@@ -865,8 +871,7 @@
<string name="magnification_mode_switch_state_window" msgid="8597100249594076965">"Förstora en del av skärmen"</string>
<string name="magnification_open_settings_click_label" msgid="6151849212725923363">"Öppna inställningarna för förstoring"</string>
<string name="magnification_close_settings_click_label" msgid="4642477260651704517">"Stäng inställningarna för förstoring"</string>
- <!-- no translation found for magnification_exit_edit_mode_click_label (1664818325144887117) -->
- <skip />
+ <string name="magnification_exit_edit_mode_click_label" msgid="1664818325144887117">"Lämna redigeringsläget"</string>
<string name="magnification_drag_corner_to_resize" msgid="1249766311052418130">"Dra i hörnet för att ändra storlek"</string>
<string name="accessibility_allow_diagonal_scrolling" msgid="3258050349191496398">"Tillåt diagonal scrollning"</string>
<string name="accessibility_resize" msgid="5733759136600611551">"Ändra storlek"</string>
diff --git a/packages/SystemUI/res/values-sw/strings.xml b/packages/SystemUI/res/values-sw/strings.xml
index b00f9b6..29d6405 100644
--- a/packages/SystemUI/res/values-sw/strings.xml
+++ b/packages/SystemUI/res/values-sw/strings.xml
@@ -57,6 +57,8 @@
<string name="hdmi_cec_set_menu_language_description" msgid="8176716678074126619">"Mabadiliko ya lugha ya mfumo yameombwa na kifaa kingine"</string>
<string name="hdmi_cec_set_menu_language_accept" msgid="2513689457281009578">"Badilisha lugha"</string>
<string name="hdmi_cec_set_menu_language_decline" msgid="7650721096558646011">"Usibadilishe lugha ya sasa"</string>
+ <!-- no translation found for share_wifi_button_text (1285273973812029240) -->
+ <skip />
<string name="wifi_debugging_title" msgid="7300007687492186076">"Ungependa kuruhusu utatuzi usiotumia waya kwenye mtandao huu?"</string>
<string name="wifi_debugging_message" msgid="5461204211731802995">"Jina la Mtandao (SSID)\n<xliff:g id="SSID_0">%1$s</xliff:g>\n\nAnwani ya Wi-Fi (BSSID)\n<xliff:g id="BSSID_1">%2$s</xliff:g>"</string>
<string name="wifi_debugging_always" msgid="2968383799517975155">"Ruhusu kila wakati kwenye mtandao huu"</string>
@@ -158,6 +160,8 @@
<string name="biometric_dialog_wrong_pattern" msgid="8954812279840889029">"Mchoro si sahihi"</string>
<string name="biometric_dialog_wrong_password" msgid="69477929306843790">"Nenosiri si sahihi"</string>
<string name="biometric_dialog_credential_too_many_attempts" msgid="3083141271737748716">"Majaribio mengi mno yasiyo sahihi.\nJaribu tena baada ya sekunde <xliff:g id="NUMBER">%d</xliff:g>."</string>
+ <!-- no translation found for work_challenge_emergency_button_text (8946588434515599288) -->
+ <skip />
<string name="biometric_dialog_credential_attempts_before_wipe" msgid="6751859711975516999">"Jaribu tena. Jaribio la <xliff:g id="ATTEMPTS_0">%1$d</xliff:g> kati ya <xliff:g id="MAX_ATTEMPTS">%2$d</xliff:g>."</string>
<string name="biometric_dialog_last_attempt_before_wipe_dialog_title" msgid="2874250099278693477">"Data yako itafutwa"</string>
<string name="biometric_dialog_last_pattern_attempt_before_wipe_device" msgid="6562299244825817598">"Ukiweka mchoro usio sahihi utakapojaribu tena, data iliyo kwenye kifaa hiki itafutwa."</string>
@@ -411,6 +415,8 @@
<string name="media_projection_entry_app_permission_dialog_warning_entire_screen" msgid="8736391633234144237">"Unaposhiriki, kurekodi au kutuma, programu ya <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> inaweza kufikia kitu chochote kitakachoonekana kwenye skrini yako au kuchezwa kwenye kifaa chako. Kwa hivyo kuwa mwangalifu na vitu kama vile manenosiri, maelezo ya malipo, ujumbe, picha na sauti na video."</string>
<string name="media_projection_entry_app_permission_dialog_warning_single_app" msgid="5211695779082563959">"Unaposhiriki, kurekodi au kutuma programu, programu, programu ya <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> inaweza kufikia kitu chochote kitakachoonekana au kuchezwa kwenye programu hiyo. Kwa hivyo kuwa mwangalifu na vitu kama vile manenosiri, maelezo ya malipo, ujumbe, picha na sauti na video."</string>
<string name="media_projection_entry_app_permission_dialog_continue" msgid="295463518195075840">"Anza"</string>
+ <!-- no translation found for media_projection_entry_app_permission_dialog_single_app_disabled (8999903044874669995) -->
+ <skip />
<string name="media_projection_entry_cast_permission_dialog_title" msgid="8860150223172993547">"Ungependa kuanza kutuma?"</string>
<string name="media_projection_entry_cast_permission_dialog_warning_entire_screen" msgid="1986212276016817231">"Unapotuma, Android inaweza kufikia kitu chochote kitakachoonekana kwenye skrini yako au kuchezwa kwenye kifaa chako. Kwa hivyo kuwa mwangalifu na vitu kama vile manenosiri, maelezo ya malipo, ujumbe, picha na sauti na video."</string>
<string name="media_projection_entry_cast_permission_dialog_warning_single_app" msgid="9900961380294292">"Unapotuma programu, Android inaweza kufikia kitu chochote kitakachoonekana au kuchezwa kwenye programu hiyo. Kwa hivyo kuwa mwangalifu na vitu kama vile manenosiri, maelezo ya malipo, ujumbe, picha na sauti na video."</string>
@@ -865,8 +871,7 @@
<string name="magnification_mode_switch_state_window" msgid="8597100249594076965">"Kuza sehemu ya skrini"</string>
<string name="magnification_open_settings_click_label" msgid="6151849212725923363">"Fungua mipangilio ya ukuzaji"</string>
<string name="magnification_close_settings_click_label" msgid="4642477260651704517">"Funga mipangilio ya ukuzaji"</string>
- <!-- no translation found for magnification_exit_edit_mode_click_label (1664818325144887117) -->
- <skip />
+ <string name="magnification_exit_edit_mode_click_label" msgid="1664818325144887117">"Ondoka kwenye hali ya kubadilisha"</string>
<string name="magnification_drag_corner_to_resize" msgid="1249766311052418130">"Buruta kona ili ubadilishe ukubwa"</string>
<string name="accessibility_allow_diagonal_scrolling" msgid="3258050349191496398">"Ruhusu usogezaji wa kimshazari"</string>
<string name="accessibility_resize" msgid="5733759136600611551">"Badilisha ukubwa"</string>
diff --git a/packages/SystemUI/res/values-ta/strings.xml b/packages/SystemUI/res/values-ta/strings.xml
index 57f537f..13e92e5 100644
--- a/packages/SystemUI/res/values-ta/strings.xml
+++ b/packages/SystemUI/res/values-ta/strings.xml
@@ -57,6 +57,8 @@
<string name="hdmi_cec_set_menu_language_description" msgid="8176716678074126619">"சிஸ்டம் மொழியை மாற்றும்படி வேறொரு சாதனம் கோருகிறது"</string>
<string name="hdmi_cec_set_menu_language_accept" msgid="2513689457281009578">"மொழியை மாற்று"</string>
<string name="hdmi_cec_set_menu_language_decline" msgid="7650721096558646011">"தற்போதைய மொழியை வைத்திரு"</string>
+ <!-- no translation found for share_wifi_button_text (1285273973812029240) -->
+ <skip />
<string name="wifi_debugging_title" msgid="7300007687492186076">"இந்த நெட்வொர்க்கில் வைஃபை பிழைதிருத்தத்தை அனுமதிக்கவா?"</string>
<string name="wifi_debugging_message" msgid="5461204211731802995">"நெட்வொர்க் பெயர் (SSID)\n<xliff:g id="SSID_0">%1$s</xliff:g>\n\nவைஃபை முகவரி (BSSID)\n<xliff:g id="BSSID_1">%2$s</xliff:g>"</string>
<string name="wifi_debugging_always" msgid="2968383799517975155">"இந்த நெட்வொர்க்கில் எப்போதும் அனுமதி"</string>
@@ -158,6 +160,8 @@
<string name="biometric_dialog_wrong_pattern" msgid="8954812279840889029">"தவறான பேட்டர்ன்"</string>
<string name="biometric_dialog_wrong_password" msgid="69477929306843790">"தவறான கடவுச்சொல்"</string>
<string name="biometric_dialog_credential_too_many_attempts" msgid="3083141271737748716">"பல தவறான முயற்சிகள்.\n<xliff:g id="NUMBER">%d</xliff:g> வினாடிகளில் மீண்டும் முயலவும்."</string>
+ <!-- no translation found for work_challenge_emergency_button_text (8946588434515599288) -->
+ <skip />
<string name="biometric_dialog_credential_attempts_before_wipe" msgid="6751859711975516999">"மீண்டும் முயலவும். <xliff:g id="ATTEMPTS_0">%1$d</xliff:g>/<xliff:g id="MAX_ATTEMPTS">%2$d</xliff:g> முறை முயன்றுவிட்டீர்கள்."</string>
<string name="biometric_dialog_last_attempt_before_wipe_dialog_title" msgid="2874250099278693477">"உங்கள் தரவு நீக்கப்படும்"</string>
<string name="biometric_dialog_last_pattern_attempt_before_wipe_device" msgid="6562299244825817598">"அடுத்த முறை தவறான பேட்டர்னை வரைந்தால் இந்தச் சாதனத்தின் தரவு நீக்கப்படும்."</string>
@@ -411,6 +415,8 @@
<string name="media_projection_entry_app_permission_dialog_warning_entire_screen" msgid="8736391633234144237">"நீங்கள் பகிரும்போதோ ரெக்கார்டு செய்யும்போதோ அலைபரப்பும்போதோ உங்கள் திரையில் காட்டப்படுகின்ற அல்லது சாதனத்தில் பிளே செய்யப்படுகின்ற அனைத்தையும் <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> ஆப்ஸால் அணுக முடியும். எனவே கடவுச்சொற்கள், பேமெண்ட் விவரங்கள், மெசேஜ்கள், படங்கள், ஆடியோ, வீடியோ போன்றவை குறித்துக் கவனத்துடன் இருங்கள்."</string>
<string name="media_projection_entry_app_permission_dialog_warning_single_app" msgid="5211695779082563959">"நீங்கள் ஓர் ஆப்ஸைப் பகிரும்போதோ ரெக்கார்டு செய்யும்போதோ அலைபரப்பும்போதோ அந்த ஆப்ஸில் காட்டப்படுகின்ற அல்லது பிளே செய்யப்படுகின்ற அனைத்தையும் <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> ஆப்ஸால் அணுக முடியும். எனவே கடவுச்சொற்கள், பேமெண்ட் விவரங்கள், மெசேஜ்கள், படங்கள், ஆடியோ, வீடியோ போன்றவை குறித்துக் கவனத்துடன் இருங்கள்."</string>
<string name="media_projection_entry_app_permission_dialog_continue" msgid="295463518195075840">"தொடங்கு"</string>
+ <!-- no translation found for media_projection_entry_app_permission_dialog_single_app_disabled (8999903044874669995) -->
+ <skip />
<string name="media_projection_entry_cast_permission_dialog_title" msgid="8860150223172993547">"அலைபரப்பைத் தொடங்கவா?"</string>
<string name="media_projection_entry_cast_permission_dialog_warning_entire_screen" msgid="1986212276016817231">"நீங்கள் அலைபரப்பும்போது உங்கள் திரையில் காட்டப்படுகின்ற அல்லது சாதனத்தில் பிளே செய்யப்படுகின்ற அனைத்தையும் Android அணுக முடியும். எனவே கடவுச்சொற்கள், பேமெண்ட் விவரங்கள், மெசேஜ்கள், படங்கள், ஆடியோ, வீடியோ போன்றவை குறித்துக் கவனத்துடன் இருங்கள்."</string>
<string name="media_projection_entry_cast_permission_dialog_warning_single_app" msgid="9900961380294292">"ஓர் ஆப்ஸை நீங்கள் அலைபரப்பும்போது அந்த ஆப்ஸில் காட்டப்படுகின்ற அல்லது பிளே செய்யப்படுகின்ற அனைத்தையும் Android அணுக முடியும். எனவே கடவுச்சொற்கள், பேமெண்ட் விவரங்கள், மெசேஜ்கள், படங்கள், ஆடியோ, வீடியோ போன்றவை குறித்துக் கவனத்துடன் இருங்கள்."</string>
@@ -865,8 +871,7 @@
<string name="magnification_mode_switch_state_window" msgid="8597100249594076965">"திரையின் ஒரு பகுதியைப் பெரிதாக்கும்"</string>
<string name="magnification_open_settings_click_label" msgid="6151849212725923363">"பெரிதாக்கல் அமைப்புகளைத் திற"</string>
<string name="magnification_close_settings_click_label" msgid="4642477260651704517">"பெரிதாக்கல் அமைப்புகளை மூடுக"</string>
- <!-- no translation found for magnification_exit_edit_mode_click_label (1664818325144887117) -->
- <skip />
+ <string name="magnification_exit_edit_mode_click_label" msgid="1664818325144887117">"திருத்துதல் பயன்முறையிலிருந்து வெளியேறு"</string>
<string name="magnification_drag_corner_to_resize" msgid="1249766311052418130">"அளவை மாற்ற மூலையை இழுக்கவும்"</string>
<string name="accessibility_allow_diagonal_scrolling" msgid="3258050349191496398">"குறுக்கே ஸ்க்ரோல் செய்வதை அனுமதி"</string>
<string name="accessibility_resize" msgid="5733759136600611551">"அளவை மாற்று"</string>
diff --git a/packages/SystemUI/res/values-te/strings.xml b/packages/SystemUI/res/values-te/strings.xml
index 124f273..421fec6 100644
--- a/packages/SystemUI/res/values-te/strings.xml
+++ b/packages/SystemUI/res/values-te/strings.xml
@@ -57,6 +57,8 @@
<string name="hdmi_cec_set_menu_language_description" msgid="8176716678074126619">"మరొక పరికరం ద్వారా సిస్టమ్ భాష మార్పు రిక్వెస్ట్ చేయబడింది"</string>
<string name="hdmi_cec_set_menu_language_accept" msgid="2513689457281009578">"భాషను మార్చండి"</string>
<string name="hdmi_cec_set_menu_language_decline" msgid="7650721096558646011">"ప్రస్తుత భాషను అలా ఉంచండి"</string>
+ <!-- no translation found for share_wifi_button_text (1285273973812029240) -->
+ <skip />
<string name="wifi_debugging_title" msgid="7300007687492186076">"ఈ నెట్వర్క్ ద్వారా వైర్లెస్ డీబగ్గింగ్ను అనుమతించాలా?"</string>
<string name="wifi_debugging_message" msgid="5461204211731802995">"నెట్వర్క్ పేరు (SSID)\n<xliff:g id="SSID_0">%1$s</xliff:g>\n\nWi‑Fi అడ్రస్ (BSSID)\n<xliff:g id="BSSID_1">%2$s</xliff:g>"</string>
<string name="wifi_debugging_always" msgid="2968383799517975155">"ఈ నెట్వర్క్ నుండి ఎల్లప్పుడూ అనుమతించండి"</string>
@@ -158,6 +160,8 @@
<string name="biometric_dialog_wrong_pattern" msgid="8954812279840889029">"ఆకృతి తప్పు"</string>
<string name="biometric_dialog_wrong_password" msgid="69477929306843790">"పాస్వర్డ్ తప్పు"</string>
<string name="biometric_dialog_credential_too_many_attempts" msgid="3083141271737748716">"చాలా ఎక్కువ తప్పు ప్రయత్నాలు చేశారు.\n<xliff:g id="NUMBER">%d</xliff:g> సెకన్ల తర్వాత మళ్లీ ట్రై చేయండి."</string>
+ <!-- no translation found for work_challenge_emergency_button_text (8946588434515599288) -->
+ <skip />
<string name="biometric_dialog_credential_attempts_before_wipe" msgid="6751859711975516999">"మళ్లీ ట్రై చేయండి. <xliff:g id="MAX_ATTEMPTS">%2$d</xliff:g>లో <xliff:g id="ATTEMPTS_0">%1$d</xliff:g> ప్రయత్నం చేశారు."</string>
<string name="biometric_dialog_last_attempt_before_wipe_dialog_title" msgid="2874250099278693477">"మీ డేటా తొలగించబడుతుంది"</string>
<string name="biometric_dialog_last_pattern_attempt_before_wipe_device" msgid="6562299244825817598">"మీరు ఒకవేళ తర్వాతి ప్రయత్నంలో తప్పు ఆకృతిని ఎంటర్ చేస్తే, ఈ పరికరం యొక్క డేటా తొలగించబడుతుంది."</string>
@@ -411,6 +415,8 @@
<string name="media_projection_entry_app_permission_dialog_warning_entire_screen" msgid="8736391633234144237">"మీరు షేర్ చేస్తున్నప్పుడు, రికార్డ్ చేస్తున్నప్పుడు, లేదా ప్రసారం చేస్తున్నప్పుడు, మీ స్క్రీన్పై కనిపించే దేనికైనా లేదా మీ పరికరంలో ప్లే అయిన దేనికైనా <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g>కు యాక్సెస్ ఉంటుంది. కాబట్టి పాస్వర్డ్లు, పేమెంట్ వివరాలు, మెసేజ్లు, ఫోటోలు, ఆడియో, ఇంకా వీడియో వంటి విషయాల్లో జాగ్రత్త వహించండి."</string>
<string name="media_projection_entry_app_permission_dialog_warning_single_app" msgid="5211695779082563959">"మీరు ఏదైనా యాప్ను షేర్ చేస్తున్నప్పుడు, రికార్డ్ చేస్తున్నప్పుడు, లేదా ప్రసారం చేస్తున్నప్పుడు, ఆ యాప్లో చూపబడిన దేనికైనా లేదా ప్లే అయిన దేనికైనా <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g>కు యాక్సెస్ ఉంటుంది. కాబట్టి పాస్వర్డ్లు, పేమెంట్ వివరాలు, మెసేజ్లు, ఫోటోలు, ఆడియో, ఇంకా వీడియో వంటి విషయాల్లో జాగ్రత్త వహించండి."</string>
<string name="media_projection_entry_app_permission_dialog_continue" msgid="295463518195075840">"ప్రారంభించండి"</string>
+ <!-- no translation found for media_projection_entry_app_permission_dialog_single_app_disabled (8999903044874669995) -->
+ <skip />
<string name="media_projection_entry_cast_permission_dialog_title" msgid="8860150223172993547">"ప్రసారాన్ని ప్రారంభించాలా?"</string>
<string name="media_projection_entry_cast_permission_dialog_warning_entire_screen" msgid="1986212276016817231">"మీరు ప్రసారం చేసేటప్పుడు, మీ స్క్రీన్పై కనిపించే దేనికైనా లేదా మీ పరికరంలో ప్లే అయిన దేనికైనా Androidకు యాక్సెస్ ఉంటుంది. కాబట్టి పాస్వర్డ్లు, పేమెంట్ వివరాలు, మెసేజ్లు, ఫోటోలు, ఆడియో, ఇంకా వీడియో వంటి విషయాల్లో జాగ్రత్త వహించండి."</string>
<string name="media_projection_entry_cast_permission_dialog_warning_single_app" msgid="9900961380294292">"మీరు ఏదైనా యాప్ను ప్రసారం చేసేటప్పుడు, ఆ యాప్లో చూపబడిన దేనికైనా లేదా ప్లే అయిన దేనికైనా Androidకు యాక్సెస్ ఉంటుంది. కాబట్టి పాస్వర్డ్లు, పేమెంట్ వివరాలు, మెసేజ్లు, ఫోటోలు, ఆడియో, ఇంకా వీడియో వంటి విషయాల్లో జాగ్రత్త వహించండి."</string>
diff --git a/packages/SystemUI/res/values-th/strings.xml b/packages/SystemUI/res/values-th/strings.xml
index 46fec04..0590806 100644
--- a/packages/SystemUI/res/values-th/strings.xml
+++ b/packages/SystemUI/res/values-th/strings.xml
@@ -57,6 +57,8 @@
<string name="hdmi_cec_set_menu_language_description" msgid="8176716678074126619">"คำขอเปลี่ยนภาษาของระบบโดยอุปกรณ์อื่น"</string>
<string name="hdmi_cec_set_menu_language_accept" msgid="2513689457281009578">"เปลี่ยนภาษา"</string>
<string name="hdmi_cec_set_menu_language_decline" msgid="7650721096558646011">"ใช้ภาษาปัจจุบันต่อไป"</string>
+ <!-- no translation found for share_wifi_button_text (1285273973812029240) -->
+ <skip />
<string name="wifi_debugging_title" msgid="7300007687492186076">"อนุญาตให้แก้ไขข้อบกพร่องผ่าน Wi-Fi ในเครือข่ายนี้ใช่ไหม"</string>
<string name="wifi_debugging_message" msgid="5461204211731802995">"ชื่อเครือข่าย (SSID)\n<xliff:g id="SSID_0">%1$s</xliff:g>\n\nที่อยู่ Wi‑Fi (BSSID)\n<xliff:g id="BSSID_1">%2$s</xliff:g>"</string>
<string name="wifi_debugging_always" msgid="2968383799517975155">"อนุญาตเสมอในเครือข่ายนี้"</string>
@@ -158,6 +160,8 @@
<string name="biometric_dialog_wrong_pattern" msgid="8954812279840889029">"รูปแบบไม่ถูกต้อง"</string>
<string name="biometric_dialog_wrong_password" msgid="69477929306843790">"รหัสผ่านไม่ถูกต้อง"</string>
<string name="biometric_dialog_credential_too_many_attempts" msgid="3083141271737748716">"ดำเนินการไม่ถูกต้องหลายครั้งเกินไป\nลองอีกครั้งใน <xliff:g id="NUMBER">%d</xliff:g> วินาที"</string>
+ <!-- no translation found for work_challenge_emergency_button_text (8946588434515599288) -->
+ <skip />
<string name="biometric_dialog_credential_attempts_before_wipe" msgid="6751859711975516999">"ลองอีกครั้ง ความพยายามครั้งที่ <xliff:g id="ATTEMPTS_0">%1$d</xliff:g> จาก <xliff:g id="MAX_ATTEMPTS">%2$d</xliff:g>"</string>
<string name="biometric_dialog_last_attempt_before_wipe_dialog_title" msgid="2874250099278693477">"ระบบจะลบข้อมูลของคุณ"</string>
<string name="biometric_dialog_last_pattern_attempt_before_wipe_device" msgid="6562299244825817598">"หากคุณป้อนรูปแบบไม่ถูกต้องในความพยายามครั้งถัดไป ระบบจะลบข้อมูลในอุปกรณ์เครื่องนี้"</string>
@@ -411,6 +415,8 @@
<string name="media_projection_entry_app_permission_dialog_warning_entire_screen" msgid="8736391633234144237">"เมื่อกำลังแชร์ บันทึก หรือแคสต์ <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> จะมีสิทธิ์เข้าถึงทุกสิ่งที่ปรากฏบนหน้าจอหรือเล่นอยู่ในอุปกรณ์ ดังนั้นโปรดระวังสิ่งต่างๆ อย่างเช่นรหัสผ่าน รายละเอียดการชำระเงิน ข้อความ รูปภาพ รวมถึงเสียงและวิดีโอ"</string>
<string name="media_projection_entry_app_permission_dialog_warning_single_app" msgid="5211695779082563959">"เมื่อกำลังแชร์ บันทึก หรือแคสต์แอป <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> จะมีสิทธิ์เข้าถึงทุกสิ่งที่แสดงหรือเล่นอยู่ในแอปดังกล่าว ดังนั้นโปรดระวังสิ่งต่างๆ อย่างเช่นรหัสผ่าน รายละเอียดการชำระเงิน ข้อความ รูปภาพ รวมถึงเสียงและวิดีโอ"</string>
<string name="media_projection_entry_app_permission_dialog_continue" msgid="295463518195075840">"เริ่ม"</string>
+ <!-- no translation found for media_projection_entry_app_permission_dialog_single_app_disabled (8999903044874669995) -->
+ <skip />
<string name="media_projection_entry_cast_permission_dialog_title" msgid="8860150223172993547">"เริ่มแคสต์เลยไหม"</string>
<string name="media_projection_entry_cast_permission_dialog_warning_entire_screen" msgid="1986212276016817231">"เมื่อกำลังแคสต์ Android จะมีสิทธิ์เข้าถึงทุกสิ่งที่ปรากฏบนหน้าจอหรือเล่นอยู่ในอุปกรณ์ ดังนั้นโปรดระวังสิ่งต่างๆ อย่างเช่นรหัสผ่าน รายละเอียดการชำระเงิน ข้อความ รูปภาพ รวมถึงเสียงและวิดีโอ"</string>
<string name="media_projection_entry_cast_permission_dialog_warning_single_app" msgid="9900961380294292">"เมื่อกำลังแคสต์แอป Android จะมีสิทธิ์เข้าถึงทุกสิ่งที่แสดงหรือเล่นอยู่ในแอปดังกล่าว ดังนั้นโปรดระวังสิ่งต่างๆ อย่างเช่นรหัสผ่าน รายละเอียดการชำระเงิน ข้อความ รูปภาพ รวมถึงเสียงและวิดีโอ"</string>
diff --git a/packages/SystemUI/res/values-tl/strings.xml b/packages/SystemUI/res/values-tl/strings.xml
index 079bd9b..859191a 100644
--- a/packages/SystemUI/res/values-tl/strings.xml
+++ b/packages/SystemUI/res/values-tl/strings.xml
@@ -57,6 +57,8 @@
<string name="hdmi_cec_set_menu_language_description" msgid="8176716678074126619">"Hiniling ng ibang device na palitan ang wika ng system"</string>
<string name="hdmi_cec_set_menu_language_accept" msgid="2513689457281009578">"Palitan ang wika"</string>
<string name="hdmi_cec_set_menu_language_decline" msgid="7650721096558646011">"Huwag palitan ang wika"</string>
+ <!-- no translation found for share_wifi_button_text (1285273973812029240) -->
+ <skip />
<string name="wifi_debugging_title" msgid="7300007687492186076">"Payagan ang wireless na pag-debug sa network na ito?"</string>
<string name="wifi_debugging_message" msgid="5461204211731802995">"Pangalan ng Network (SSID)\n<xliff:g id="SSID_0">%1$s</xliff:g>\n\nAddress ng Wi‑Fi (BSSID)\n<xliff:g id="BSSID_1">%2$s</xliff:g>"</string>
<string name="wifi_debugging_always" msgid="2968383799517975155">"Palaging payagan sa network na ito"</string>
@@ -158,6 +160,8 @@
<string name="biometric_dialog_wrong_pattern" msgid="8954812279840889029">"Maling pattern"</string>
<string name="biometric_dialog_wrong_password" msgid="69477929306843790">"Maling password"</string>
<string name="biometric_dialog_credential_too_many_attempts" msgid="3083141271737748716">"Masyadong maraming maling pagsubok.\nSubukan ulit sa loob ng <xliff:g id="NUMBER">%d</xliff:g> (na) segundo."</string>
+ <!-- no translation found for work_challenge_emergency_button_text (8946588434515599288) -->
+ <skip />
<string name="biometric_dialog_credential_attempts_before_wipe" msgid="6751859711975516999">"Subukan ulit. ika-<xliff:g id="ATTEMPTS_0">%1$d</xliff:g> (na) pagsubok sa <xliff:g id="MAX_ATTEMPTS">%2$d</xliff:g>."</string>
<string name="biometric_dialog_last_attempt_before_wipe_dialog_title" msgid="2874250099278693477">"Made-delete ang iyong data"</string>
<string name="biometric_dialog_last_pattern_attempt_before_wipe_device" msgid="6562299244825817598">"Kung maling pattern ang mailalagay mo sa susunod na pagsubok, made-delete ang data ng device na ito."</string>
@@ -411,6 +415,8 @@
<string name="media_projection_entry_app_permission_dialog_warning_entire_screen" msgid="8736391633234144237">"Kapag nagbabahagi, nagre-record, o nagka-cast ka, may access ang <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> sa kahit anong nakikita sa iyong screen o pine-play sa device mo. Kaya mag-ingat sa mga bagay-bagay tulad ng mga password, detalye ng pagbabayad, mensahe, larawan, at audio at video."</string>
<string name="media_projection_entry_app_permission_dialog_warning_single_app" msgid="5211695779082563959">"Kapag nagbabahagi, nagre-record, o nagka-cast ka ng app, may access ang <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> sa kahit anong ipinapakita o pine-play sa app na iyon. Kaya mag-ingat sa mga bagay-bagay tulad ng mga password, detalye ng pagbabayad, mensahe, larawan, at audio at video."</string>
<string name="media_projection_entry_app_permission_dialog_continue" msgid="295463518195075840">"Simulan"</string>
+ <!-- no translation found for media_projection_entry_app_permission_dialog_single_app_disabled (8999903044874669995) -->
+ <skip />
<string name="media_projection_entry_cast_permission_dialog_title" msgid="8860150223172993547">"Simulan ang pag-cast?"</string>
<string name="media_projection_entry_cast_permission_dialog_warning_entire_screen" msgid="1986212276016817231">"Kapag nagka-cast ka, may access ang Android sa kahit anong nakikita sa iyong screen o pine-play sa device mo. Kaya mag-ingat sa mga bagay-bagay tulad ng mga password, detalye ng pagbabayad, mensahe, larawan, at audio at video."</string>
<string name="media_projection_entry_cast_permission_dialog_warning_single_app" msgid="9900961380294292">"Kapag nagka-cast ka ng app, may access ang Android sa kahit anong ipinapakita o pine-play sa app na iyon. Kaya mag-ingat sa mga bagay-bagay tulad ng mga password, detalye ng pagbabayad, mensahe, larawan, at audio at video."</string>
@@ -865,8 +871,7 @@
<string name="magnification_mode_switch_state_window" msgid="8597100249594076965">"I-magnify ang isang bahagi ng screen"</string>
<string name="magnification_open_settings_click_label" msgid="6151849212725923363">"Buksan ang mga setting ng pag-magnify"</string>
<string name="magnification_close_settings_click_label" msgid="4642477260651704517">"Isara ang mga setting ng pag-magnify"</string>
- <!-- no translation found for magnification_exit_edit_mode_click_label (1664818325144887117) -->
- <skip />
+ <string name="magnification_exit_edit_mode_click_label" msgid="1664818325144887117">"Lumabas sa edit mode"</string>
<string name="magnification_drag_corner_to_resize" msgid="1249766311052418130">"I-drag ang sulok para i-resize"</string>
<string name="accessibility_allow_diagonal_scrolling" msgid="3258050349191496398">"Payagan ang diagonal na pag-scroll"</string>
<string name="accessibility_resize" msgid="5733759136600611551">"I-resize"</string>
diff --git a/packages/SystemUI/res/values-tr/strings.xml b/packages/SystemUI/res/values-tr/strings.xml
index 7ba95ec..f217f13 100644
--- a/packages/SystemUI/res/values-tr/strings.xml
+++ b/packages/SystemUI/res/values-tr/strings.xml
@@ -57,6 +57,8 @@
<string name="hdmi_cec_set_menu_language_description" msgid="8176716678074126619">"Başka bir cihaz tarafından sistem dilinin değiştirilmesi istendi"</string>
<string name="hdmi_cec_set_menu_language_accept" msgid="2513689457281009578">"Dili değiştir"</string>
<string name="hdmi_cec_set_menu_language_decline" msgid="7650721096558646011">"Mevcut dili koru"</string>
+ <!-- no translation found for share_wifi_button_text (1285273973812029240) -->
+ <skip />
<string name="wifi_debugging_title" msgid="7300007687492186076">"Bu ağda kablosuz hata ayıklamaya izin verilsin mi?"</string>
<string name="wifi_debugging_message" msgid="5461204211731802995">"Ağ Adı (SSID)\n<xliff:g id="SSID_0">%1$s</xliff:g>\n\nKablosuz Adresi (BSSID)\n<xliff:g id="BSSID_1">%2$s</xliff:g>"</string>
<string name="wifi_debugging_always" msgid="2968383799517975155">"Bu ağda her zaman izin ver"</string>
@@ -158,6 +160,8 @@
<string name="biometric_dialog_wrong_pattern" msgid="8954812279840889029">"Yanlış desen"</string>
<string name="biometric_dialog_wrong_password" msgid="69477929306843790">"Yanlış şifre"</string>
<string name="biometric_dialog_credential_too_many_attempts" msgid="3083141271737748716">"Çok fazla yanlış giriş yapıldı.\n<xliff:g id="NUMBER">%d</xliff:g> saniye içinde tekrar deneyin."</string>
+ <!-- no translation found for work_challenge_emergency_button_text (8946588434515599288) -->
+ <skip />
<string name="biometric_dialog_credential_attempts_before_wipe" msgid="6751859711975516999">"Tekrar deneyin. Deneme sayısı: <xliff:g id="ATTEMPTS_0">%1$d</xliff:g>/<xliff:g id="MAX_ATTEMPTS">%2$d</xliff:g>."</string>
<string name="biometric_dialog_last_attempt_before_wipe_dialog_title" msgid="2874250099278693477">"Verileriniz silinecek"</string>
<string name="biometric_dialog_last_pattern_attempt_before_wipe_device" msgid="6562299244825817598">"Bir sonraki denemenizde yanlış desen girerseniz bu cihazın verileri silinir."</string>
@@ -411,6 +415,8 @@
<string name="media_projection_entry_app_permission_dialog_warning_entire_screen" msgid="8736391633234144237">"Paylaşma, kaydetme ve yayınlama özelliklerini kullandığınızda <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g>, ekranınızda görünen veya cihazınızda oynatılan her şeye erişebilir. Bu nedenle şifre, ödeme ayrıntıları, mesaj, fotoğraf, ses ve video gibi öğeler konusunda dikkatli olun."</string>
<string name="media_projection_entry_app_permission_dialog_warning_single_app" msgid="5211695779082563959">"Bir uygulamayı paylaştığınızda, kaydettiğinizde veya yayınladığınızda <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g>, söz konusu uygulamada gösterilen veya oynatılan her şeye erişebilir. Bu nedenle şifre, ödeme ayrıntıları, mesaj, fotoğraf, ses ve video gibi öğeler konusunda dikkatli olun."</string>
<string name="media_projection_entry_app_permission_dialog_continue" msgid="295463518195075840">"Başlat"</string>
+ <!-- no translation found for media_projection_entry_app_permission_dialog_single_app_disabled (8999903044874669995) -->
+ <skip />
<string name="media_projection_entry_cast_permission_dialog_title" msgid="8860150223172993547">"Yayın başlatılsın mı?"</string>
<string name="media_projection_entry_cast_permission_dialog_warning_entire_screen" msgid="1986212276016817231">"Yayınlama özelliğini kullandığınızda Android, ekranınızda görünen veya cihazınızda oynatılan her şeye erişebilir. Bu nedenle şifre, ödeme ayrıntıları, mesaj, fotoğraf, ses ve video gibi öğeler konusunda dikkatli olun."</string>
<string name="media_projection_entry_cast_permission_dialog_warning_single_app" msgid="9900961380294292">"Bir uygulamayı yayınladığınızda Android, söz konusu uygulamada gösterilen veya oynatılan her şeye erişebilir. Bu nedenle şifre, ödeme ayrıntıları, mesaj, fotoğraf, ses ve video gibi öğeler konusunda dikkatli olun."</string>
@@ -865,8 +871,7 @@
<string name="magnification_mode_switch_state_window" msgid="8597100249594076965">"Ekranın bir parçasını büyütün"</string>
<string name="magnification_open_settings_click_label" msgid="6151849212725923363">"Büyütme ayarlarını aç"</string>
<string name="magnification_close_settings_click_label" msgid="4642477260651704517">"Büyütme ayarlarını kapat"</string>
- <!-- no translation found for magnification_exit_edit_mode_click_label (1664818325144887117) -->
- <skip />
+ <string name="magnification_exit_edit_mode_click_label" msgid="1664818325144887117">"Düzenleme modundan çık"</string>
<string name="magnification_drag_corner_to_resize" msgid="1249766311052418130">"Yeniden boyutlandırmak için köşeyi sürükleyin"</string>
<string name="accessibility_allow_diagonal_scrolling" msgid="3258050349191496398">"Çapraz kaydırmaya izin ver"</string>
<string name="accessibility_resize" msgid="5733759136600611551">"Yeniden boyutlandır"</string>
diff --git a/packages/SystemUI/res/values-uk/strings.xml b/packages/SystemUI/res/values-uk/strings.xml
index 3feefef..83bfd63 100644
--- a/packages/SystemUI/res/values-uk/strings.xml
+++ b/packages/SystemUI/res/values-uk/strings.xml
@@ -57,6 +57,8 @@
<string name="hdmi_cec_set_menu_language_description" msgid="8176716678074126619">"Запит на змінення мови системи надіслано з іншого пристрою"</string>
<string name="hdmi_cec_set_menu_language_accept" msgid="2513689457281009578">"Змінити мову"</string>
<string name="hdmi_cec_set_menu_language_decline" msgid="7650721096558646011">"Залишити поточну мову"</string>
+ <!-- no translation found for share_wifi_button_text (1285273973812029240) -->
+ <skip />
<string name="wifi_debugging_title" msgid="7300007687492186076">"Дозволити налагодження через Wi-Fi у цій мережі?"</string>
<string name="wifi_debugging_message" msgid="5461204211731802995">"Ім\'я мережі (SSID)\n<xliff:g id="SSID_0">%1$s</xliff:g>\n\nАдреса Wi‑Fi (BSSID)\n<xliff:g id="BSSID_1">%2$s</xliff:g>"</string>
<string name="wifi_debugging_always" msgid="2968383799517975155">"Завжди дозволяти в цій мережі"</string>
@@ -158,6 +160,8 @@
<string name="biometric_dialog_wrong_pattern" msgid="8954812279840889029">"Неправильний ключ"</string>
<string name="biometric_dialog_wrong_password" msgid="69477929306843790">"Неправильний пароль"</string>
<string name="biometric_dialog_credential_too_many_attempts" msgid="3083141271737748716">"Забагато невдалих спроб.\nПовторіть за <xliff:g id="NUMBER">%d</xliff:g> с."</string>
+ <!-- no translation found for work_challenge_emergency_button_text (8946588434515599288) -->
+ <skip />
<string name="biometric_dialog_credential_attempts_before_wipe" msgid="6751859711975516999">"Спробуйте ще. Спроба <xliff:g id="ATTEMPTS_0">%1$d</xliff:g> із <xliff:g id="MAX_ATTEMPTS">%2$d</xliff:g>."</string>
<string name="biometric_dialog_last_attempt_before_wipe_dialog_title" msgid="2874250099278693477">"Ваші дані буде видалено"</string>
<string name="biometric_dialog_last_pattern_attempt_before_wipe_device" msgid="6562299244825817598">"Якщо наступного разу ви введете неправильний ключ, дані на цьому пристрої буде видалено."</string>
@@ -411,6 +415,8 @@
<string name="media_projection_entry_app_permission_dialog_warning_entire_screen" msgid="8736391633234144237">"Коли ви показуєте, записуєте або транслюєте екран, додаток <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> отримує доступ до всього, що відображається на екрані чи відтворюється на пристрої. Тому будьте уважні з паролями, повідомленнями, фотографіями, аудіо, відео, платіжною інформацією тощо."</string>
<string name="media_projection_entry_app_permission_dialog_warning_single_app" msgid="5211695779082563959">"Коли ви показуєте, записуєте або транслюєте додаток, <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> отримує доступ до всього, що відображається або відтворюється в цьому додатку. Тому будьте уважні з паролями, повідомленнями, фотографіями, аудіо, відео, платіжною інформацією тощо."</string>
<string name="media_projection_entry_app_permission_dialog_continue" msgid="295463518195075840">"Почати"</string>
+ <!-- no translation found for media_projection_entry_app_permission_dialog_single_app_disabled (8999903044874669995) -->
+ <skip />
<string name="media_projection_entry_cast_permission_dialog_title" msgid="8860150223172993547">"Почати трансляцію?"</string>
<string name="media_projection_entry_cast_permission_dialog_warning_entire_screen" msgid="1986212276016817231">"Під час трансляції ОС Android отримує доступ до всього, що відображається на екрані чи відтворюється на пристрої. Тому будьте уважні з паролями, повідомленнями, фотографіями, аудіо, відео, платіжною інформацією тощо."</string>
<string name="media_projection_entry_cast_permission_dialog_warning_single_app" msgid="9900961380294292">"Коли ви транслюєте додаток, ОС Android отримує доступ до всього, що відображається або відтворюється в ньому. Тому будьте уважні з паролями, повідомленнями, фотографіями, аудіо, відео, платіжною інформацією тощо."</string>
@@ -865,8 +871,7 @@
<string name="magnification_mode_switch_state_window" msgid="8597100249594076965">"Збільшити частину екрана"</string>
<string name="magnification_open_settings_click_label" msgid="6151849212725923363">"Відкрити налаштування збільшення"</string>
<string name="magnification_close_settings_click_label" msgid="4642477260651704517">"Закрити налаштування збільшення"</string>
- <!-- no translation found for magnification_exit_edit_mode_click_label (1664818325144887117) -->
- <skip />
+ <string name="magnification_exit_edit_mode_click_label" msgid="1664818325144887117">"Вийти з режиму редагування"</string>
<string name="magnification_drag_corner_to_resize" msgid="1249766311052418130">"Потягніть кут, щоб змінити розмір"</string>
<string name="accessibility_allow_diagonal_scrolling" msgid="3258050349191496398">"Дозволити прокручування по діагоналі"</string>
<string name="accessibility_resize" msgid="5733759136600611551">"Змінити розмір"</string>
@@ -1180,10 +1185,10 @@
<string name="privacy_dialog_manage_permissions" msgid="2543451567190470413">"Керувати доступом"</string>
<string name="privacy_dialog_active_call_usage" msgid="7858746847946397562">"Використовується (телефонний дзвінок)"</string>
<string name="privacy_dialog_recent_call_usage" msgid="1214810644978167344">"Нещодавно використано (телефонний дзвінок)"</string>
- <string name="privacy_dialog_active_app_usage" msgid="631997836335929880">"Використовується (<xliff:g id="APP_NAME">%1$s</xliff:g>)"</string>
- <string name="privacy_dialog_recent_app_usage" msgid="4883417856848222450">"Нещодавно використано (<xliff:g id="APP_NAME">%1$s</xliff:g>)"</string>
- <string name="privacy_dialog_active_app_usage_1" msgid="9047570143069220911">"Використовується (<xliff:g id="APP_NAME">%1$s</xliff:g>, <xliff:g id="ATTRIBUTION_LABEL">%2$s</xliff:g>)"</string>
+ <string name="privacy_dialog_active_app_usage" msgid="631997836335929880">"Використовується додатком <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
+ <string name="privacy_dialog_recent_app_usage" msgid="4883417856848222450">"Нещодавно використано додатком <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
+ <string name="privacy_dialog_active_app_usage_1" msgid="9047570143069220911">"Використовується додатком <xliff:g id="APP_NAME">%1$s</xliff:g> (<xliff:g id="ATTRIBUTION_LABEL">%2$s</xliff:g>)"</string>
<string name="privacy_dialog_recent_app_usage_1" msgid="2551340497722370109">"Нещодавно використано (<xliff:g id="APP_NAME">%1$s</xliff:g>, <xliff:g id="ATTRIBUTION_LABEL">%2$s</xliff:g>)"</string>
- <string name="privacy_dialog_active_app_usage_2" msgid="2770926061339921767">"Використовується (<xliff:g id="APP_NAME">%1$s</xliff:g>, <xliff:g id="ATTRIBUTION_LABEL">%2$s</xliff:g> • <xliff:g id="PROXY_LABEL">%3$s</xliff:g>)"</string>
+ <string name="privacy_dialog_active_app_usage_2" msgid="2770926061339921767">"Використовується додатком <xliff:g id="APP_NAME">%1$s</xliff:g> (<xliff:g id="ATTRIBUTION_LABEL">%2$s</xliff:g> • <xliff:g id="PROXY_LABEL">%3$s</xliff:g>)"</string>
<string name="privacy_dialog_recent_app_usage_2" msgid="2874689735085367167">"Нещодавно використано (<xliff:g id="APP_NAME">%1$s</xliff:g>, <xliff:g id="ATTRIBUTION_LABEL">%2$s</xliff:g> • <xliff:g id="PROXY_LABEL">%3$s</xliff:g>)"</string>
</resources>
diff --git a/packages/SystemUI/res/values-ur/strings.xml b/packages/SystemUI/res/values-ur/strings.xml
index 29d3762..eac34ce 100644
--- a/packages/SystemUI/res/values-ur/strings.xml
+++ b/packages/SystemUI/res/values-ur/strings.xml
@@ -57,6 +57,8 @@
<string name="hdmi_cec_set_menu_language_description" msgid="8176716678074126619">"کسی دوسرے آلے کے ذریعے سسٹم کی زبان میں تبدیلی کی درخواست کی گئی"</string>
<string name="hdmi_cec_set_menu_language_accept" msgid="2513689457281009578">"زبان تبدیل کریں"</string>
<string name="hdmi_cec_set_menu_language_decline" msgid="7650721096558646011">"موجودہ زبان برقرار رکھیں"</string>
+ <!-- no translation found for share_wifi_button_text (1285273973812029240) -->
+ <skip />
<string name="wifi_debugging_title" msgid="7300007687492186076">"اس نیٹ ورک پر وائرلیس ڈیبگنگ کرنے کی اجازت دیں؟"</string>
<string name="wifi_debugging_message" msgid="5461204211731802995">"نیٹ ورک کا نام (SSID)\n<xliff:g id="SSID_0">%1$s</xliff:g>\n\n Wi-Fi کا پتہ (BSSID)\n<xliff:g id="BSSID_1">%2$s</xliff:g>"</string>
<string name="wifi_debugging_always" msgid="2968383799517975155">"اس نیٹ ورک پر ہمیشہ اجازت دیں"</string>
@@ -158,6 +160,8 @@
<string name="biometric_dialog_wrong_pattern" msgid="8954812279840889029">"غلط پیٹرن"</string>
<string name="biometric_dialog_wrong_password" msgid="69477929306843790">"غلط پاس ورڈ"</string>
<string name="biometric_dialog_credential_too_many_attempts" msgid="3083141271737748716">"کافی زیادہ غلط کوششیں کی گئیں۔\n <xliff:g id="NUMBER">%d</xliff:g> سیکنڈ بعد دوبارہ کوشش کریں۔"</string>
+ <!-- no translation found for work_challenge_emergency_button_text (8946588434515599288) -->
+ <skip />
<string name="biometric_dialog_credential_attempts_before_wipe" msgid="6751859711975516999">"دوبارہ کوشش کریں۔ کوشش <xliff:g id="ATTEMPTS_0">%1$d</xliff:g> از <xliff:g id="MAX_ATTEMPTS">%2$d</xliff:g>۔"</string>
<string name="biometric_dialog_last_attempt_before_wipe_dialog_title" msgid="2874250099278693477">"آپ کا ڈیٹا حذف کر دیا جائے گا"</string>
<string name="biometric_dialog_last_pattern_attempt_before_wipe_device" msgid="6562299244825817598">"اگر آپ نے اگلی کوشش میں غلط پیٹرن درج کیا تو اس آلے کا ڈیٹا حذف کر دیا جائے گا۔"</string>
@@ -411,6 +415,8 @@
<string name="media_projection_entry_app_permission_dialog_warning_entire_screen" msgid="8736391633234144237">"جب آپ اشتراک، ریکارڈنگ یا کاسٹ کر رہے ہوتے ہیں تو <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> کو آپ کی اسکرین پر دکھائی دینے والی یا آپ کے آلے پر چلائی گئی ہر چیز تک رسائی حاصل ہوتی ہے۔ لہذا، پاس ورڈز، ادائیگی کی تفصیلات، پیغامات، تصاویر، ساتھ ہی آڈیو اور ویڈیو جیسی چیزوں کے سلسلے میں محتاط رہیں۔"</string>
<string name="media_projection_entry_app_permission_dialog_warning_single_app" msgid="5211695779082563959">"جب آپ اشتراک، ریکارڈنگ یا کسی ایپ کو کاسٹ کر رہے ہوتے ہیں تو <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> کو اس ایپ پر دکھائی گئی یا چلائی گئی ہر چیز تک رسائی حاصل ہوتی ہے۔ لہذا، پاس ورڈز، ادائیگی کی تفصیلات، پیغامات، تصاویر، ساتھ ہی آڈیو اور ویڈیو جیسی چیزوں کے سلسلے میں محتاط رہیں۔"</string>
<string name="media_projection_entry_app_permission_dialog_continue" msgid="295463518195075840">"شروع کریں"</string>
+ <!-- no translation found for media_projection_entry_app_permission_dialog_single_app_disabled (8999903044874669995) -->
+ <skip />
<string name="media_projection_entry_cast_permission_dialog_title" msgid="8860150223172993547">"کاسٹ کرنا شروع کریں؟"</string>
<string name="media_projection_entry_cast_permission_dialog_warning_entire_screen" msgid="1986212276016817231">"جب آپ کاسٹ کر رہے ہوتے ہیں، تو Android کو آپ کی اسکرین پر دکھائی دینے والی یا آپ کے آلے پر چلائی گئی ہر چیز تک رسائی حاصل ہوتی ہے۔ لہذا، پاس ورڈز، ادائیگی کی تفصیلات، پیغامات، تصاویر، ساتھ ہی آڈیو اور ویڈیو جیسی چیزوں کے سلسلے میں محتاط رہیں۔"</string>
<string name="media_projection_entry_cast_permission_dialog_warning_single_app" msgid="9900961380294292">"جب آپ کسی ایپ کو کاسٹ کر رہے ہوتے ہیں تو Android کو اس ایپ پر دکھائی گئی یا چلائی گئی ہر چیز تک رسائی حاصل ہوتی ہے۔ لہذا، پاس ورڈز، ادائیگی کی تفصیلات، پیغامات، تصاویر، ساتھ ہی آڈیو اور ویڈیو جیسی چیزوں کے سلسلے میں محتاط رہیں۔"</string>
@@ -865,8 +871,7 @@
<string name="magnification_mode_switch_state_window" msgid="8597100249594076965">"اسکرین کا حصہ بڑا کریں"</string>
<string name="magnification_open_settings_click_label" msgid="6151849212725923363">"میگنیفکیشن کی ترتیبات کھولیں"</string>
<string name="magnification_close_settings_click_label" msgid="4642477260651704517">"میگنیفکیشن کی ترتیبات بند کریں"</string>
- <!-- no translation found for magnification_exit_edit_mode_click_label (1664818325144887117) -->
- <skip />
+ <string name="magnification_exit_edit_mode_click_label" msgid="1664818325144887117">"ترمیم موڈ سے باہر نکلیں"</string>
<string name="magnification_drag_corner_to_resize" msgid="1249766311052418130">"سائز تبدیل کرنے کے لیے کونے کو گھسیٹیں"</string>
<string name="accessibility_allow_diagonal_scrolling" msgid="3258050349191496398">"وتری سکرولنگ کی اجازت دیں"</string>
<string name="accessibility_resize" msgid="5733759136600611551">"سائز تبدیل کریں"</string>
diff --git a/packages/SystemUI/res/values-uz/strings.xml b/packages/SystemUI/res/values-uz/strings.xml
index b7defbb..66c874d 100644
--- a/packages/SystemUI/res/values-uz/strings.xml
+++ b/packages/SystemUI/res/values-uz/strings.xml
@@ -57,6 +57,8 @@
<string name="hdmi_cec_set_menu_language_description" msgid="8176716678074126619">"Tizim tilini oʻzgartirishni boshqa qurilma soʻragan"</string>
<string name="hdmi_cec_set_menu_language_accept" msgid="2513689457281009578">"Tilni almashtirish"</string>
<string name="hdmi_cec_set_menu_language_decline" msgid="7650721096558646011">"Joriy tilni qoldirish"</string>
+ <!-- no translation found for share_wifi_button_text (1285273973812029240) -->
+ <skip />
<string name="wifi_debugging_title" msgid="7300007687492186076">"Wi-Fi orqali debagging uchun ruxsat berilsinmi?"</string>
<string name="wifi_debugging_message" msgid="5461204211731802995">"Tarmoq nomi (SSID):\n<xliff:g id="SSID_0">%1$s</xliff:g>\n\nWi‑Fi Manzil (BSSID):\n<xliff:g id="BSSID_1">%2$s</xliff:g>"</string>
<string name="wifi_debugging_always" msgid="2968383799517975155">"Bu tarmoqda doim ruxsat etilsin"</string>
@@ -158,6 +160,8 @@
<string name="biometric_dialog_wrong_pattern" msgid="8954812279840889029">"Grafik kalit xato"</string>
<string name="biometric_dialog_wrong_password" msgid="69477929306843790">"Parol xato"</string>
<string name="biometric_dialog_credential_too_many_attempts" msgid="3083141271737748716">"Xato urinishlar soni oshib ketdi! \n <xliff:g id="NUMBER">%d</xliff:g> soniyadan keyin qayta urining."</string>
+ <!-- no translation found for work_challenge_emergency_button_text (8946588434515599288) -->
+ <skip />
<string name="biometric_dialog_credential_attempts_before_wipe" msgid="6751859711975516999">"Qaytadan urining. Urinish: <xliff:g id="ATTEMPTS_0">%1$d</xliff:g> / <xliff:g id="MAX_ATTEMPTS">%2$d</xliff:g>."</string>
<string name="biometric_dialog_last_attempt_before_wipe_dialog_title" msgid="2874250099278693477">"Bitta urinish qoldi"</string>
<string name="biometric_dialog_last_pattern_attempt_before_wipe_device" msgid="6562299244825817598">"Agar grafik kalitni xato kiritsangiz, bu qurilmadagi maʼlumotlar oʻchirib tashlanadi."</string>
@@ -411,6 +415,8 @@
<string name="media_projection_entry_app_permission_dialog_warning_entire_screen" msgid="8736391633234144237">"Ulashish, yozib olish va translatsiya qilish vaqtida <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> ilovasi ekranda chiqadigan yoki qurilmada ijro qilinadigan kontentni koʻra oladi. Shu sababli parollar, toʻlov tafsilotlari, xabarlar, suratlar, audio va video chiqmasligi uchun ehtiyot boʻling."</string>
<string name="media_projection_entry_app_permission_dialog_warning_single_app" msgid="5211695779082563959">"Ulashish, yozib olish va translatsiya qilish vaqtida <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> ilovasi ekranda chiqadigan yoki qurilmada ijro qilinadigan kontentni koʻra oladi. Shu sababli parollar, toʻlov tafsilotlari, xabarlar, suratlar, audio va video chiqmasligi uchun ehtiyot boʻling."</string>
<string name="media_projection_entry_app_permission_dialog_continue" msgid="295463518195075840">"Boshlash"</string>
+ <!-- no translation found for media_projection_entry_app_permission_dialog_single_app_disabled (8999903044874669995) -->
+ <skip />
<string name="media_projection_entry_cast_permission_dialog_title" msgid="8860150223172993547">"Translatsiya boshlansinmi?"</string>
<string name="media_projection_entry_cast_permission_dialog_warning_entire_screen" msgid="1986212276016817231">"Translatsiya qilayotganingizda Android ekranda chiqadigan yoki qurilmada ijro qilinadigan kontentni koʻra oladi. Shu sababli parollar, toʻlov tafsilotlari, xabarlar, suratlar, audio va video chiqmasligi uchun ehtiyot boʻling."</string>
<string name="media_projection_entry_cast_permission_dialog_warning_single_app" msgid="9900961380294292">"Translatsiya qilayotganingizda Android ekranda chiqadigan yoki qurilmada ijro qilinadigan kontentni koʻra oladi. Shu sababli parollar, toʻlov tafsilotlari, xabarlar, suratlar, audio va video chiqmasligi uchun ehtiyot boʻling."</string>
@@ -865,8 +871,7 @@
<string name="magnification_mode_switch_state_window" msgid="8597100249594076965">"Ekran qismini kattalashtirish"</string>
<string name="magnification_open_settings_click_label" msgid="6151849212725923363">"Kattalashtirish sozlamalarini ochish"</string>
<string name="magnification_close_settings_click_label" msgid="4642477260651704517">"Kattalashtirish sozlamalarini yopish"</string>
- <!-- no translation found for magnification_exit_edit_mode_click_label (1664818325144887117) -->
- <skip />
+ <string name="magnification_exit_edit_mode_click_label" msgid="1664818325144887117">"Tahrirlash rejimidan chiqish"</string>
<string name="magnification_drag_corner_to_resize" msgid="1249766311052418130">"Oʻlchamini oʻzgartirish uchun burchakni torting"</string>
<string name="accessibility_allow_diagonal_scrolling" msgid="3258050349191496398">"Diagonal aylantirishga ruxsat berish"</string>
<string name="accessibility_resize" msgid="5733759136600611551">"Oʻlchamini oʻzgartirish"</string>
diff --git a/packages/SystemUI/res/values-vi/strings.xml b/packages/SystemUI/res/values-vi/strings.xml
index 2d49b02..2648d78 100644
--- a/packages/SystemUI/res/values-vi/strings.xml
+++ b/packages/SystemUI/res/values-vi/strings.xml
@@ -57,6 +57,8 @@
<string name="hdmi_cec_set_menu_language_description" msgid="8176716678074126619">"Thiết bị khác yêu cầu thay đổi ngôn ngữ hệ thống"</string>
<string name="hdmi_cec_set_menu_language_accept" msgid="2513689457281009578">"Thay đổi ngôn ngữ"</string>
<string name="hdmi_cec_set_menu_language_decline" msgid="7650721096558646011">"Giữ ngôn ngữ hiện tại"</string>
+ <!-- no translation found for share_wifi_button_text (1285273973812029240) -->
+ <skip />
<string name="wifi_debugging_title" msgid="7300007687492186076">"Cho phép gỡ lỗi qua Wi-Fi trên mạng này?"</string>
<string name="wifi_debugging_message" msgid="5461204211731802995">"Tên mạng (SSID)\n<xliff:g id="SSID_0">%1$s</xliff:g>\n\nĐịa chỉ Wi‑Fi (BSSID)\n<xliff:g id="BSSID_1">%2$s</xliff:g>"</string>
<string name="wifi_debugging_always" msgid="2968383799517975155">"Luôn cho phép trên mạng này"</string>
@@ -158,6 +160,8 @@
<string name="biometric_dialog_wrong_pattern" msgid="8954812279840889029">"Hình mở khóa không chính xác"</string>
<string name="biometric_dialog_wrong_password" msgid="69477929306843790">"Mật khẩu sai"</string>
<string name="biometric_dialog_credential_too_many_attempts" msgid="3083141271737748716">"Bạn đã nhập sai quá nhiều lần.\nHãy thử lại sau <xliff:g id="NUMBER">%d</xliff:g> giây."</string>
+ <!-- no translation found for work_challenge_emergency_button_text (8946588434515599288) -->
+ <skip />
<string name="biometric_dialog_credential_attempts_before_wipe" msgid="6751859711975516999">"Thử lại. Lần thử <xliff:g id="ATTEMPTS_0">%1$d</xliff:g>/<xliff:g id="MAX_ATTEMPTS">%2$d</xliff:g>."</string>
<string name="biometric_dialog_last_attempt_before_wipe_dialog_title" msgid="2874250099278693477">"Dữ liệu của bạn sẽ bị xóa"</string>
<string name="biometric_dialog_last_pattern_attempt_before_wipe_device" msgid="6562299244825817598">"Nếu bạn nhập hình mở khóa không chính xác vào lần thử tiếp theo, thì dữ liệu trên thiết bị này sẽ bị xóa."</string>
@@ -411,6 +415,8 @@
<string name="media_projection_entry_app_permission_dialog_warning_entire_screen" msgid="8736391633234144237">"Khi bạn chia sẻ, ghi hoặc truyền, <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> sẽ có quyền truy cập vào mọi nội dung xuất hiện trên màn hình hoặc phát trên thiết bị của bạn. Vì vậy, hãy thận trọng để không làm lộ thông tin như mật khẩu, thông tin thanh toán, tin nhắn, ảnh, âm thanh và video."</string>
<string name="media_projection_entry_app_permission_dialog_warning_single_app" msgid="5211695779082563959">"Khi bạn chia sẻ, ghi hoặc truyền ứng dụng, <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> sẽ có quyền truy cập vào mọi nội dung xuất hiện hoặc phát trên ứng dụng đó. Vì vậy, hãy thận trọng để không làm lộ các thông tin như mật khẩu, thông tin thanh toán, tin nhắn, ảnh, âm thanh và video."</string>
<string name="media_projection_entry_app_permission_dialog_continue" msgid="295463518195075840">"Bắt đầu"</string>
+ <!-- no translation found for media_projection_entry_app_permission_dialog_single_app_disabled (8999903044874669995) -->
+ <skip />
<string name="media_projection_entry_cast_permission_dialog_title" msgid="8860150223172993547">"Bắt đầu truyền?"</string>
<string name="media_projection_entry_cast_permission_dialog_warning_entire_screen" msgid="1986212276016817231">"Khi bạn truyền, Android sẽ có quyền truy cập vào mọi nội dung xuất hiện trên màn hình hoặc phát trên thiết bị của bạn. Vì vậy, hãy thận trọng để không làm lộ thông tin như mật khẩu, thông tin thanh toán, tin nhắn, ảnh, âm thanh và video."</string>
<string name="media_projection_entry_cast_permission_dialog_warning_single_app" msgid="9900961380294292">"Khi bạn truyền một ứng dụng, Android sẽ có quyền truy cập vào mọi nội dung xuất hiện hoặc phát trên ứng dụng đó. Vì vậy, hãy thận trọng để không làm lộ các thông tin như mật khẩu, thông tin thanh toán, tin nhắn, ảnh, âm thanh và video."</string>
@@ -865,8 +871,7 @@
<string name="magnification_mode_switch_state_window" msgid="8597100249594076965">"Phóng to một phần màn hình"</string>
<string name="magnification_open_settings_click_label" msgid="6151849212725923363">"Mở chế độ cài đặt phóng to"</string>
<string name="magnification_close_settings_click_label" msgid="4642477260651704517">"Đóng bảng cài đặt tính năng phóng to"</string>
- <!-- no translation found for magnification_exit_edit_mode_click_label (1664818325144887117) -->
- <skip />
+ <string name="magnification_exit_edit_mode_click_label" msgid="1664818325144887117">"Thoát chế độ chỉnh sửa"</string>
<string name="magnification_drag_corner_to_resize" msgid="1249766311052418130">"Kéo góc để thay đổi kích thước"</string>
<string name="accessibility_allow_diagonal_scrolling" msgid="3258050349191496398">"Cho phép cuộn chéo"</string>
<string name="accessibility_resize" msgid="5733759136600611551">"Đổi kích thước"</string>
diff --git a/packages/SystemUI/res/values-zh-rCN/strings.xml b/packages/SystemUI/res/values-zh-rCN/strings.xml
index 8d2b89b..3072fa4 100644
--- a/packages/SystemUI/res/values-zh-rCN/strings.xml
+++ b/packages/SystemUI/res/values-zh-rCN/strings.xml
@@ -57,6 +57,8 @@
<string name="hdmi_cec_set_menu_language_description" msgid="8176716678074126619">"另一台设备请求更改系统语言"</string>
<string name="hdmi_cec_set_menu_language_accept" msgid="2513689457281009578">"更改语言"</string>
<string name="hdmi_cec_set_menu_language_decline" msgid="7650721096558646011">"保持当前语言"</string>
+ <!-- no translation found for share_wifi_button_text (1285273973812029240) -->
+ <skip />
<string name="wifi_debugging_title" msgid="7300007687492186076">"要允许通过此网络进行无线调试吗?"</string>
<string name="wifi_debugging_message" msgid="5461204211731802995">"网络名称 (SSID)\n<xliff:g id="SSID_0">%1$s</xliff:g>\n\nWLAN 地址 (BSSID)\n<xliff:g id="BSSID_1">%2$s</xliff:g>"</string>
<string name="wifi_debugging_always" msgid="2968383799517975155">"始终允许通过此网络进行调试"</string>
@@ -158,6 +160,8 @@
<string name="biometric_dialog_wrong_pattern" msgid="8954812279840889029">"图案错误"</string>
<string name="biometric_dialog_wrong_password" msgid="69477929306843790">"密码错误"</string>
<string name="biometric_dialog_credential_too_many_attempts" msgid="3083141271737748716">"输错次数过多。\n请在 <xliff:g id="NUMBER">%d</xliff:g> 秒后重试。"</string>
+ <!-- no translation found for work_challenge_emergency_button_text (8946588434515599288) -->
+ <skip />
<string name="biometric_dialog_credential_attempts_before_wipe" msgid="6751859711975516999">"请重试。您目前已尝试 <xliff:g id="ATTEMPTS_0">%1$d</xliff:g> 次,最多可尝试 <xliff:g id="MAX_ATTEMPTS">%2$d</xliff:g> 次。"</string>
<string name="biometric_dialog_last_attempt_before_wipe_dialog_title" msgid="2874250099278693477">"您的数据将会被删除"</string>
<string name="biometric_dialog_last_pattern_attempt_before_wipe_device" msgid="6562299244825817598">"如果您下次绘制的解锁图案仍然有误,此设备上的数据将会被删除。"</string>
@@ -411,6 +415,8 @@
<string name="media_projection_entry_app_permission_dialog_warning_entire_screen" msgid="8736391633234144237">"在分享、录制或投放内容时,<xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g>可以访问屏幕上显示或设备中播放的所有内容。因此,请务必小心操作,谨防密码、付款信息、消息、照片、音频和视频等内容遭到泄露。"</string>
<string name="media_projection_entry_app_permission_dialog_warning_single_app" msgid="5211695779082563959">"在分享、录制或投放内容时,<xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g>可以访问通过此应用显示或播放的所有内容。因此,请务必小心操作,谨防密码、付款信息、消息、照片、音频和视频等内容遭到泄露。"</string>
<string name="media_projection_entry_app_permission_dialog_continue" msgid="295463518195075840">"开始"</string>
+ <!-- no translation found for media_projection_entry_app_permission_dialog_single_app_disabled (8999903044874669995) -->
+ <skip />
<string name="media_projection_entry_cast_permission_dialog_title" msgid="8860150223172993547">"开始投放?"</string>
<string name="media_projection_entry_cast_permission_dialog_warning_entire_screen" msgid="1986212276016817231">"在投放内容时,Android 可以访问屏幕上显示或设备中播放的所有内容。因此,请务必小心操作,谨防密码、付款信息、消息、照片、音频和视频等内容遭到泄露。"</string>
<string name="media_projection_entry_cast_permission_dialog_warning_single_app" msgid="9900961380294292">"在投放某个应用时,Android 可以访问此应用显示或播放的所有内容。因此,请务必小心操作,谨防密码、付款信息、消息、照片、音频和视频等内容遭到泄露。"</string>
diff --git a/packages/SystemUI/res/values-zh-rHK/strings.xml b/packages/SystemUI/res/values-zh-rHK/strings.xml
index 9e408f2..8f4adc0 100644
--- a/packages/SystemUI/res/values-zh-rHK/strings.xml
+++ b/packages/SystemUI/res/values-zh-rHK/strings.xml
@@ -57,6 +57,8 @@
<string name="hdmi_cec_set_menu_language_description" msgid="8176716678074126619">"另一部裝置要求變更系統語言"</string>
<string name="hdmi_cec_set_menu_language_accept" msgid="2513689457281009578">"變更語言"</string>
<string name="hdmi_cec_set_menu_language_decline" msgid="7650721096558646011">"保留目前語言"</string>
+ <!-- no translation found for share_wifi_button_text (1285273973812029240) -->
+ <skip />
<string name="wifi_debugging_title" msgid="7300007687492186076">"要在此網絡上允許無線偵錯功能嗎?"</string>
<string name="wifi_debugging_message" msgid="5461204211731802995">"網絡名稱 (SSID)\n<xliff:g id="SSID_0">%1$s</xliff:g>\n\nWi‑Fi 地址 (BSSID)\n<xliff:g id="BSSID_1">%2$s</xliff:g>"</string>
<string name="wifi_debugging_always" msgid="2968383799517975155">"一律允許在此網絡上執行"</string>
@@ -158,6 +160,8 @@
<string name="biometric_dialog_wrong_pattern" msgid="8954812279840889029">"圖案錯誤"</string>
<string name="biometric_dialog_wrong_password" msgid="69477929306843790">"密碼錯誤"</string>
<string name="biometric_dialog_credential_too_many_attempts" msgid="3083141271737748716">"輸入錯誤的次數太多,\n請於 <xliff:g id="NUMBER">%d</xliff:g> 秒後再試。"</string>
+ <!-- no translation found for work_challenge_emergency_button_text (8946588434515599288) -->
+ <skip />
<string name="biometric_dialog_credential_attempts_before_wipe" msgid="6751859711975516999">"請再試一次。你已嘗試 <xliff:g id="ATTEMPTS_0">%1$d</xliff:g> 次,最多可試 <xliff:g id="MAX_ATTEMPTS">%2$d</xliff:g> 次。"</string>
<string name="biometric_dialog_last_attempt_before_wipe_dialog_title" msgid="2874250099278693477">"你的資料將會刪除"</string>
<string name="biometric_dialog_last_pattern_attempt_before_wipe_device" msgid="6562299244825817598">"如果你下次畫出錯誤的上鎖圖案,系統將會刪除此裝置上的資料。"</string>
@@ -411,6 +415,8 @@
<string name="media_projection_entry_app_permission_dialog_warning_entire_screen" msgid="8736391633234144237">"當你分享、錄影或投放時,「<xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g>」可存取顯示在螢幕畫面上或在裝置上播放的所有內容。因此,請謹慎處理密碼、付款資料、訊息、相片、音訊和影片等。"</string>
<string name="media_projection_entry_app_permission_dialog_warning_single_app" msgid="5211695779082563959">"當你分享、錄影或投放應用程式時,「<xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g>」可存取顯示在該應用程式中顯示或播放的所有內容。因此,請謹慎處理密碼、付款資料、訊息、相片、音訊和影片等。"</string>
<string name="media_projection_entry_app_permission_dialog_continue" msgid="295463518195075840">"開始"</string>
+ <!-- no translation found for media_projection_entry_app_permission_dialog_single_app_disabled (8999903044874669995) -->
+ <skip />
<string name="media_projection_entry_cast_permission_dialog_title" msgid="8860150223172993547">"要開始投放嗎?"</string>
<string name="media_projection_entry_cast_permission_dialog_warning_entire_screen" msgid="1986212276016817231">"當你投放時,Android 可存取顯示在螢幕畫面上或在裝置上播放的所有內容。因此,請謹慎處理密碼、付款資料、訊息、相片、音訊和影片等。"</string>
<string name="media_projection_entry_cast_permission_dialog_warning_single_app" msgid="9900961380294292">"當你投放應用程式時,Android 可存取在該應用程式中顯示或播放的所有內容。因此,請謹慎處理密碼、付款資料、訊息、相片、音訊和影片等。"</string>
@@ -865,7 +871,7 @@
<string name="magnification_mode_switch_state_window" msgid="8597100249594076965">"放大部分螢幕畫面"</string>
<string name="magnification_open_settings_click_label" msgid="6151849212725923363">"開啟放大設定"</string>
<string name="magnification_close_settings_click_label" msgid="4642477260651704517">"關閉放大設定"</string>
- <string name="magnification_exit_edit_mode_click_label" msgid="1664818325144887117">"結束編輯模式"</string>
+ <string name="magnification_exit_edit_mode_click_label" msgid="1664818325144887117">"離開編輯模式"</string>
<string name="magnification_drag_corner_to_resize" msgid="1249766311052418130">"拖曳角落即可調整大小"</string>
<string name="accessibility_allow_diagonal_scrolling" msgid="3258050349191496398">"允許斜角捲動"</string>
<string name="accessibility_resize" msgid="5733759136600611551">"調整大小"</string>
diff --git a/packages/SystemUI/res/values-zh-rTW/strings.xml b/packages/SystemUI/res/values-zh-rTW/strings.xml
index 5831ac5..35b7231b 100644
--- a/packages/SystemUI/res/values-zh-rTW/strings.xml
+++ b/packages/SystemUI/res/values-zh-rTW/strings.xml
@@ -57,6 +57,8 @@
<string name="hdmi_cec_set_menu_language_description" msgid="8176716678074126619">"另一部裝置要求變更系統語言"</string>
<string name="hdmi_cec_set_menu_language_accept" msgid="2513689457281009578">"變更語言"</string>
<string name="hdmi_cec_set_menu_language_decline" msgid="7650721096558646011">"繼續使用目前的語言"</string>
+ <!-- no translation found for share_wifi_button_text (1285273973812029240) -->
+ <skip />
<string name="wifi_debugging_title" msgid="7300007687492186076">"要允許透過這個網路執行無線偵錯嗎?"</string>
<string name="wifi_debugging_message" msgid="5461204211731802995">"網路名稱 (SSID)\n<xliff:g id="SSID_0">%1$s</xliff:g>\n\nWi‑Fi 位址 (BSSID)\n<xliff:g id="BSSID_1">%2$s</xliff:g>"</string>
<string name="wifi_debugging_always" msgid="2968383799517975155">"一律允許透過這個網路執行"</string>
@@ -158,6 +160,8 @@
<string name="biometric_dialog_wrong_pattern" msgid="8954812279840889029">"圖案錯誤"</string>
<string name="biometric_dialog_wrong_password" msgid="69477929306843790">"密碼錯誤"</string>
<string name="biometric_dialog_credential_too_many_attempts" msgid="3083141271737748716">"錯誤次數過多,\n請於 <xliff:g id="NUMBER">%d</xliff:g> 秒後再試。"</string>
+ <!-- no translation found for work_challenge_emergency_button_text (8946588434515599288) -->
+ <skip />
<string name="biometric_dialog_credential_attempts_before_wipe" msgid="6751859711975516999">"請再試一次。你目前已嘗試 <xliff:g id="ATTEMPTS_0">%1$d</xliff:g> 次,最多可試 <xliff:g id="MAX_ATTEMPTS">%2$d</xliff:g> 次。"</string>
<string name="biometric_dialog_last_attempt_before_wipe_dialog_title" msgid="2874250099278693477">"你的資料將遭到刪除"</string>
<string name="biometric_dialog_last_pattern_attempt_before_wipe_device" msgid="6562299244825817598">"如果下次輸入的解鎖圖案仍不正確,系統將刪除這部裝置中的資料。"</string>
@@ -411,6 +415,8 @@
<string name="media_projection_entry_app_permission_dialog_warning_entire_screen" msgid="8736391633234144237">"當你分享、錄製或投放內容時,「<xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g>」可存取畫面上顯示的任何資訊或裝置播放的任何內容。因此,請謹慎處理密碼、付款資料、訊息、相片和影音內容等資訊。"</string>
<string name="media_projection_entry_app_permission_dialog_warning_single_app" msgid="5211695779082563959">"當你分享、錄製或投放應用程式內容時,「<xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g>」可存取應用程式中顯示的任何資訊或播放的任何內容。因此,請謹慎處理密碼、付款資料、訊息、相片和影音內容等資訊。"</string>
<string name="media_projection_entry_app_permission_dialog_continue" msgid="295463518195075840">"開始"</string>
+ <!-- no translation found for media_projection_entry_app_permission_dialog_single_app_disabled (8999903044874669995) -->
+ <skip />
<string name="media_projection_entry_cast_permission_dialog_title" msgid="8860150223172993547">"要開始投放嗎?"</string>
<string name="media_projection_entry_cast_permission_dialog_warning_entire_screen" msgid="1986212276016817231">"當你投放內容時,Android 可存取畫面上顯示的任何資訊或裝置播放的任何內容。因此,請謹慎處理密碼、付款資料、訊息、相片和影音內容等資訊。"</string>
<string name="media_projection_entry_cast_permission_dialog_warning_single_app" msgid="9900961380294292">"當你投放應用程式內容時,Android 可存取應用程式中顯示的任何資訊或播放的任何內容。因此,請謹慎處理密碼、付款資料、訊息、相片和影音內容等資訊。"</string>
diff --git a/packages/SystemUI/res/values-zu/strings.xml b/packages/SystemUI/res/values-zu/strings.xml
index 5013f8d..19c00e6 100644
--- a/packages/SystemUI/res/values-zu/strings.xml
+++ b/packages/SystemUI/res/values-zu/strings.xml
@@ -57,6 +57,8 @@
<string name="hdmi_cec_set_menu_language_description" msgid="8176716678074126619">"Ushintsho lolimi lwesistimu lucelwe enye idivayisi"</string>
<string name="hdmi_cec_set_menu_language_accept" msgid="2513689457281009578">"Shintsha ulimi"</string>
<string name="hdmi_cec_set_menu_language_decline" msgid="7650721096558646011">"Gcina ulimi lwamanje"</string>
+ <!-- no translation found for share_wifi_button_text (1285273973812029240) -->
+ <skip />
<string name="wifi_debugging_title" msgid="7300007687492186076">"Vumela ukulungisa amaphutha okungenantambo kule nethiwekhi?"</string>
<string name="wifi_debugging_message" msgid="5461204211731802995">"Igama Lenethiwekhi (SSID)\n<xliff:g id="SSID_0">%1$s</xliff:g>\n\nIkheli le-Wi‑Fi (BSSID)\n<xliff:g id="BSSID_1">%2$s</xliff:g>"</string>
<string name="wifi_debugging_always" msgid="2968383799517975155">"Njalo nje vumela le nethiwekhi"</string>
@@ -158,6 +160,8 @@
<string name="biometric_dialog_wrong_pattern" msgid="8954812279840889029">"Iphethini engalungile"</string>
<string name="biometric_dialog_wrong_password" msgid="69477929306843790">"Iphasiwedi engalungile"</string>
<string name="biometric_dialog_credential_too_many_attempts" msgid="3083141271737748716">"Imizamo eminingi kakhulu engalungile.\nZama futhi kumasekhondi angu-<xliff:g id="NUMBER">%d</xliff:g>."</string>
+ <!-- no translation found for work_challenge_emergency_button_text (8946588434515599288) -->
+ <skip />
<string name="biometric_dialog_credential_attempts_before_wipe" msgid="6751859711975516999">"Zama futhi. Umzamo ongu-<xliff:g id="ATTEMPTS_0">%1$d</xliff:g> kwengu-<xliff:g id="MAX_ATTEMPTS">%2$d</xliff:g>."</string>
<string name="biometric_dialog_last_attempt_before_wipe_dialog_title" msgid="2874250099278693477">"Idatha yakho izosuswa"</string>
<string name="biometric_dialog_last_pattern_attempt_before_wipe_device" msgid="6562299244825817598">"Uma ufaka iphethini engalungile kumzamo olandelayo, idatha yale divayisi izosuswa."</string>
@@ -411,6 +415,8 @@
<string name="media_projection_entry_app_permission_dialog_warning_entire_screen" msgid="8736391633234144237">"Uma wabelana, urekhoda, noma usakaza, i-<xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> inokufinyelela kunoma yini ebonakalayo kusikrini sakho noma edlalwa kudivayisi yakho. Ngakho-ke qaphela ngezinto ezifana namaphasiwedi, imininingwane yokukhokha, imilayezo, izithombe, nomsindo nevidiyo."</string>
<string name="media_projection_entry_app_permission_dialog_warning_single_app" msgid="5211695779082563959">"Uma wabelana, ukurekhoda, noma ukusakaza ku-app, <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> inokufinyelela kunoma yini eboniswayo noma edlalwa kuleyo app. Ngakho-ke qaphela ngezinto ezfana namaphasiwedi, imininingwane yokukhokha, imilayezo, izithombe, nomsindo nevidiyo."</string>
<string name="media_projection_entry_app_permission_dialog_continue" msgid="295463518195075840">"Qala"</string>
+ <!-- no translation found for media_projection_entry_app_permission_dialog_single_app_disabled (8999903044874669995) -->
+ <skip />
<string name="media_projection_entry_cast_permission_dialog_title" msgid="8860150223172993547">"Qala ukusakaza?"</string>
<string name="media_projection_entry_cast_permission_dialog_warning_entire_screen" msgid="1986212276016817231">"Uma usakaza, i-Android inokufinyelela kunoma yini ebonakalayo kusikrini sakho noma edlalwa kudivayisi yakho. Ngakho-ke qaphela ngezinto ezifana namaphasiwedi, imininingwane yokukhokha, imilayezo, izithombe, nomsindo nevidiyo."</string>
<string name="media_projection_entry_cast_permission_dialog_warning_single_app" msgid="9900961380294292">"Uma usakaza i-app, i-Android inokufinyelela kunoma yini eboniswayo noma edlalwa kuleyo app. Ngakho-ke qaphela ngezinto ezifana namaphasiwedi, imininingwane yenkokhelo, imilayezo, izithombe, nomsindo nevidiyo."</string>
@@ -865,8 +871,7 @@
<string name="magnification_mode_switch_state_window" msgid="8597100249594076965">"Khulisa ingxenye eyesikrini"</string>
<string name="magnification_open_settings_click_label" msgid="6151849212725923363">"Vula amasethingi okukhuliswa"</string>
<string name="magnification_close_settings_click_label" msgid="4642477260651704517">"Vala amasethingi okukhuliswa"</string>
- <!-- no translation found for magnification_exit_edit_mode_click_label (1664818325144887117) -->
- <skip />
+ <string name="magnification_exit_edit_mode_click_label" msgid="1664818325144887117">"Phuma kumodi yokuhlela"</string>
<string name="magnification_drag_corner_to_resize" msgid="1249766311052418130">"Hudula ikhona ukuze usayize kabusha"</string>
<string name="accessibility_allow_diagonal_scrolling" msgid="3258050349191496398">"Vumela ukuskrola oku-diagonal"</string>
<string name="accessibility_resize" msgid="5733759136600611551">"Shintsha usayizi"</string>
diff --git a/packages/SystemUI/res/values/dimens.xml b/packages/SystemUI/res/values/dimens.xml
index 2dc1b45..62c4424 100644
--- a/packages/SystemUI/res/values/dimens.xml
+++ b/packages/SystemUI/res/values/dimens.xml
@@ -857,6 +857,8 @@
<dimen name="keyguard_security_container_padding_top">20dp</dimen>
+ <dimen name="keyguard_translate_distance_on_swipe_up">-200dp</dimen>
+
<dimen name="keyguard_indication_margin_bottom">32dp</dimen>
<dimen name="lock_icon_margin_bottom">74dp</dimen>
<dimen name="ambient_indication_margin_bottom">71dp</dimen>
diff --git a/packages/SystemUI/res/values/ids.xml b/packages/SystemUI/res/values/ids.xml
index 04eae64..579358f 100644
--- a/packages/SystemUI/res/values/ids.xml
+++ b/packages/SystemUI/res/values/ids.xml
@@ -212,6 +212,8 @@
<item type="id" name="keyguard_indication_text" />
<item type="id" name="keyguard_indication_text_bottom" />
<item type="id" name="nssl_guideline" />
+ <item type="id" name="nssl_top_barrier" />
+ <item type="id" name="nssl_bottom_barrier" />
<item type="id" name="split_shade_guideline" />
<item type="id" name="lock_icon" />
<item type="id" name="lock_icon_bg" />
diff --git a/packages/SystemUI/res/values/strings.xml b/packages/SystemUI/res/values/strings.xml
index b37aeee..6840108 100644
--- a/packages/SystemUI/res/values/strings.xml
+++ b/packages/SystemUI/res/values/strings.xml
@@ -3184,6 +3184,12 @@
<!-- Label for a button that, when clicked, sends the user to the app store to install an app. [CHAR LIMIT=64]. -->
<string name="install_app">Install app</string>
+ <!--- Title of the dialog appearing when an external display is connected, asking whether to start mirroring [CHAR LIMIT=NONE]-->
+ <string name="connected_display_dialog_start_mirroring">Mirror to external display?</string>
+
+ <!--- Label of the "enable display" button of the dialog appearing when an external display is connected [CHAR LIMIT=NONE]-->
+ <string name="enable_display">Enable display</string>
+
<!-- Title of the privacy dialog, shown for active / recent app usage of some phone sensors [CHAR LIMIT=30] -->
<string name="privacy_dialog_title">Microphone & Camera</string>
<!-- Subtitle of the privacy dialog, shown for active / recent app usage of some phone sensors [CHAR LIMIT=NONE] -->
diff --git a/packages/SystemUI/shared/Android.bp b/packages/SystemUI/shared/Android.bp
index ca30e15..a6517c1 100644
--- a/packages/SystemUI/shared/Android.bp
+++ b/packages/SystemUI/shared/Android.bp
@@ -50,6 +50,7 @@
"SystemUIAnimationLib",
"SystemUIPluginLib",
"SystemUIUnfoldLib",
+ "SystemUISharedLib-Keyguard",
"androidx.dynamicanimation_dynamicanimation",
"androidx.concurrent_concurrent-futures",
"androidx.lifecycle_lifecycle-runtime-ktx",
diff --git a/packages/SystemUI/shared/keyguard/Android.bp b/packages/SystemUI/shared/keyguard/Android.bp
new file mode 100644
index 0000000..2181439
--- /dev/null
+++ b/packages/SystemUI/shared/keyguard/Android.bp
@@ -0,0 +1,16 @@
+package {
+ // See: http://go/android-license-faq
+ // A large-scale-change added 'default_applicable_licenses' to import
+ // all of the 'license_kinds' from "frameworks_base_packages_SystemUI_license"
+ // to get the below license kinds:
+ // SPDX-license-identifier-Apache-2.0
+ default_applicable_licenses: ["frameworks_base_packages_SystemUI_license"],
+}
+
+android_library {
+ name: "SystemUISharedLib-Keyguard",
+ srcs: [
+ "src/**/*.java",
+ ],
+ min_sdk_version: "current",
+}
diff --git a/packages/SystemUI/shared/keyguard/AndroidManifest.xml b/packages/SystemUI/shared/keyguard/AndroidManifest.xml
new file mode 100644
index 0000000..49bee08
--- /dev/null
+++ b/packages/SystemUI/shared/keyguard/AndroidManifest.xml
@@ -0,0 +1,20 @@
+<?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.
+ -->
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="com.android.systemui.shared.keyguard">
+</manifest>
\ No newline at end of file
diff --git a/packages/SystemUI/shared/keyguard/src/com/android/keyguard/BasePasswordTextView.java b/packages/SystemUI/shared/keyguard/src/com/android/keyguard/BasePasswordTextView.java
new file mode 100644
index 0000000..fe12134
--- /dev/null
+++ b/packages/SystemUI/shared/keyguard/src/com/android/keyguard/BasePasswordTextView.java
@@ -0,0 +1,239 @@
+/*
+ * 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.keyguard;
+
+import android.annotation.CallSuper;
+import android.content.Context;
+import android.text.InputType;
+import android.text.TextUtils;
+import android.util.AttributeSet;
+import android.view.accessibility.AccessibilityEvent;
+import android.view.accessibility.AccessibilityManager;
+import android.view.accessibility.AccessibilityNodeInfo;
+import android.widget.EditText;
+import android.widget.FrameLayout;
+
+/**
+ * A View similar to a textView which contains password text and can animate when the text is
+ * changed
+ */
+public abstract class BasePasswordTextView extends FrameLayout {
+ private String mText = "";
+ private UserActivityListener mUserActivityListener;
+ protected boolean mIsPinHinting;
+ protected PinShapeInput mPinShapeInput;
+ protected boolean mShowPassword = true;
+ protected boolean mUsePinShapes = false;
+ protected static final char DOT = '\u2022';
+
+ /** Listens to user activities like appending, deleting and resetting PIN text */
+ public interface UserActivityListener {
+
+ /** Listens to user activities. */
+ void onUserActivity();
+ }
+
+ public BasePasswordTextView(Context context) {
+ this(context, null);
+ }
+
+ public BasePasswordTextView(Context context, AttributeSet attrs) {
+ this(context, attrs, 0);
+ }
+
+ public BasePasswordTextView(Context context, AttributeSet attrs, int defStyleAttr) {
+ this(context, attrs, defStyleAttr, 0);
+ }
+
+ public BasePasswordTextView(
+ Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
+ super(context, attrs, defStyleAttr, defStyleRes);
+ }
+
+ protected abstract PinShapeInput inflatePinShapeInput(boolean isPinHinting);
+
+ protected abstract boolean shouldSendAccessibilityEvent();
+
+ protected void onAppend(char c, int newLength) {}
+
+ protected void onDelete(int index) {}
+
+ protected void onReset(boolean animated) {}
+
+ @CallSuper
+ protected void onUserActivity() {
+ if (mUserActivityListener != null) {
+ mUserActivityListener.onUserActivity();
+ }
+ }
+
+ @Override
+ public boolean hasOverlappingRendering() {
+ return false;
+ }
+
+ /** Appends a PIN text */
+ public void append(char c) {
+ CharSequence textbefore = getTransformedText();
+
+ mText = mText + c;
+ int newLength = mText.length();
+ onAppend(c, newLength);
+
+ if (mPinShapeInput != null) {
+ mPinShapeInput.append();
+ }
+
+ onUserActivity();
+
+ sendAccessibilityEventTypeViewTextChanged(textbefore, textbefore.length(), 0, 1);
+ }
+
+ /** Sets a listener who is notified on user activity */
+ public void setUserActivityListener(UserActivityListener userActivityListener) {
+ mUserActivityListener = userActivityListener;
+ }
+
+ /** Deletes the last PIN text */
+ public void deleteLastChar() {
+ int length = mText.length();
+ if (length > 0) {
+ CharSequence textbefore = getTransformedText();
+
+ mText = mText.substring(0, length - 1);
+ onDelete(length - 1);
+
+ if (mPinShapeInput != null) {
+ mPinShapeInput.delete();
+ }
+
+ sendAccessibilityEventTypeViewTextChanged(textbefore, textbefore.length() - 1, 1, 0);
+ }
+ onUserActivity();
+ }
+
+ /** Gets entered PIN text */
+ public String getText() {
+ return mText;
+ }
+
+ /** Gets a transformed text for accessibility event. Called before text changed. */
+ protected CharSequence getTransformedText() {
+ return String.valueOf(DOT).repeat(mText.length());
+ }
+
+ /** Gets a transformed text for accessibility event. Called after text changed. */
+ protected CharSequence getTransformedText(int fromIndex, int removedCount, int addedCount) {
+ return getTransformedText();
+ }
+
+ /** Reset PIN text without error */
+ public void reset(boolean animated, boolean announce) {
+ reset(false /* error */, animated, announce);
+ }
+
+ /** Reset PIN text */
+ public void reset(boolean error, boolean animated, boolean announce) {
+ CharSequence textbefore = getTransformedText();
+
+ mText = "";
+
+ onReset(animated);
+ if (animated) {
+ onUserActivity();
+ }
+
+ if (mPinShapeInput != null) {
+ if (error) {
+ mPinShapeInput.resetWithError();
+ } else {
+ mPinShapeInput.reset();
+ }
+ }
+
+ if (announce) {
+ sendAccessibilityEventTypeViewTextChanged(textbefore, 0, textbefore.length(), 0);
+ }
+ }
+
+ void sendAccessibilityEventTypeViewTextChanged(
+ CharSequence beforeText, int fromIndex, int removedCount, int addedCount) {
+ if (AccessibilityManager.getInstance(mContext).isEnabled()
+ && shouldSendAccessibilityEvent()) {
+ AccessibilityEvent event =
+ AccessibilityEvent.obtain(AccessibilityEvent.TYPE_VIEW_TEXT_CHANGED);
+ event.setFromIndex(fromIndex);
+ event.setRemovedCount(removedCount);
+ event.setAddedCount(addedCount);
+ event.setBeforeText(beforeText);
+ CharSequence transformedText = getTransformedText(fromIndex, removedCount, addedCount);
+ if (!TextUtils.isEmpty(transformedText)) {
+ event.getText().add(transformedText);
+ }
+ event.setPassword(true);
+ sendAccessibilityEventUnchecked(event);
+ }
+ }
+
+ /** Sets whether to use pin shapes. */
+ public void setUsePinShapes(boolean usePinShapes) {
+ mUsePinShapes = usePinShapes;
+ }
+
+ /** Determines whether AutoConfirmation feature is on. */
+ public void setIsPinHinting(boolean isPinHinting) {
+ // Do not reinflate the view if we are using the same one.
+ if (mPinShapeInput != null && mIsPinHinting == isPinHinting) {
+ return;
+ }
+ mIsPinHinting = isPinHinting;
+
+ if (mPinShapeInput != null) {
+ removeView(mPinShapeInput.getView());
+ mPinShapeInput = null;
+ }
+
+ mPinShapeInput = inflatePinShapeInput(isPinHinting);
+ addView(mPinShapeInput.getView());
+ }
+
+ /** Controls whether the last entered digit is briefly shown after being entered */
+ public void setShowPassword(boolean enabled) {
+ mShowPassword = enabled;
+ }
+
+ @Override
+ public void onInitializeAccessibilityEvent(AccessibilityEvent event) {
+ super.onInitializeAccessibilityEvent(event);
+
+ event.setClassName(EditText.class.getName());
+ event.setPassword(true);
+ }
+
+ @Override
+ public void onInitializeAccessibilityNodeInfo(AccessibilityNodeInfo info) {
+ super.onInitializeAccessibilityNodeInfo(info);
+
+ info.setClassName(EditText.class.getName());
+ info.setPassword(true);
+ info.setText(getTransformedText());
+
+ info.setEditable(true);
+
+ info.setInputType(InputType.TYPE_NUMBER_VARIATION_PASSWORD);
+ }
+}
diff --git a/packages/SystemUI/src/com/android/keyguard/PinShapeInput.java b/packages/SystemUI/shared/keyguard/src/com/android/keyguard/PinShapeInput.java
similarity index 85%
rename from packages/SystemUI/src/com/android/keyguard/PinShapeInput.java
rename to packages/SystemUI/shared/keyguard/src/com/android/keyguard/PinShapeInput.java
index 52ae6ba..d2b4066 100644
--- a/packages/SystemUI/src/com/android/keyguard/PinShapeInput.java
+++ b/packages/SystemUI/shared/keyguard/src/com/android/keyguard/PinShapeInput.java
@@ -44,6 +44,14 @@
void reset();
/**
+ * This is the method that is triggered for resetting the view with error If it doesn't have to
+ * show something regarding error, just reset
+ */
+ default void resetWithError() {
+ reset();
+ }
+
+ /**
* This is the method that is triggered for getting the view
*/
View getView();
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/plugins/PluginInstance.java b/packages/SystemUI/shared/src/com/android/systemui/shared/plugins/PluginInstance.java
index 6b67c09..7719e95 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/plugins/PluginInstance.java
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/plugins/PluginInstance.java
@@ -55,7 +55,9 @@
private final PluginListener<T> mListener;
private final ComponentName mComponentName;
private final PluginFactory<T> mPluginFactory;
+ private final String mTag;
+ private boolean mIsDebug = false;
private Context mPluginContext;
private T mPlugin;
@@ -71,27 +73,51 @@
mComponentName = componentName;
mPluginFactory = pluginFactory;
mPlugin = plugin;
+ mTag = TAG + mComponentName.toShortString()
+ + '@' + Integer.toHexString(hashCode());
if (mPlugin != null) {
mPluginContext = mPluginFactory.createPluginContext();
}
}
+ @Override
+ public String toString() {
+ return mTag;
+ }
+
+ public boolean getIsDebug() {
+ return mIsDebug;
+ }
+
+ public void setIsDebug(boolean debug) {
+ mIsDebug = debug;
+ }
+
+ private void logDebug(String message) {
+ if (mIsDebug) {
+ Log.i(mTag, message);
+ }
+ }
+
/** Alerts listener and plugin that the plugin has been created. */
public void onCreate() {
boolean loadPlugin = mListener.onPluginAttached(this);
if (!loadPlugin) {
if (mPlugin != null) {
+ logDebug("onCreate: auto-unload");
unloadPlugin();
}
return;
}
if (mPlugin == null) {
+ logDebug("onCreate auto-load");
loadPlugin();
return;
}
+ logDebug("onCreate: load callbacks");
mPluginFactory.checkVersion(mPlugin);
if (!(mPlugin instanceof PluginFragment)) {
// Only call onCreate for plugins that aren't fragments, as fragments
@@ -103,6 +129,7 @@
/** Alerts listener and plugin that the plugin is being shutdown. */
public void onDestroy() {
+ logDebug("onDestroy");
unloadPlugin();
mListener.onPluginDetached(this);
}
@@ -118,15 +145,18 @@
*/
public void loadPlugin() {
if (mPlugin != null) {
+ logDebug("Load request when already loaded");
return;
}
mPlugin = mPluginFactory.createPlugin();
mPluginContext = mPluginFactory.createPluginContext();
if (mPlugin == null || mPluginContext == null) {
+ Log.e(mTag, "Requested load, but failed");
return;
}
+ logDebug("Loaded plugin; running callbacks");
mPluginFactory.checkVersion(mPlugin);
if (!(mPlugin instanceof PluginFragment)) {
// Only call onCreate for plugins that aren't fragments, as fragments
@@ -143,9 +173,11 @@
*/
public void unloadPlugin() {
if (mPlugin == null) {
+ logDebug("Unload request when already unloaded");
return;
}
+ logDebug("Unloading plugin, running callbacks");
mListener.onPluginUnloaded(mPlugin, this);
if (!(mPlugin instanceof PluginFragment)) {
// Only call onDestroy for plugins that aren't fragments, as fragments
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitch.java b/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitch.java
index 06b6692..1703b30 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitch.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitch.java
@@ -48,6 +48,7 @@
private static final long STATUS_AREA_MOVE_UP_MILLIS = 967;
private static final long STATUS_AREA_MOVE_DOWN_MILLIS = 467;
private static final float SMARTSPACE_TRANSLATION_CENTER_MULTIPLIER = 1.4f;
+ private static final float SMARTSPACE_TOP_PADDING_MULTIPLIER = 2.625f;
@IntDef({LARGE, SMALL})
@Retention(RetentionPolicy.SOURCE)
@@ -96,6 +97,14 @@
private KeyguardClockFrame mLargeClockFrame;
private ClockController mClock;
+ // It's bc_smartspace_view, assigned by KeyguardClockSwitchController
+ // to get the top padding for translating smartspace for weather clock
+ private View mSmartspace;
+
+ // Smartspace in weather clock is translated by this value
+ // to compensate for the position invisible dateWeatherView
+ private int mSmartspaceTop = -1;
+
private KeyguardStatusAreaView mStatusArea;
private int mSmartspaceTopOffset;
private float mWeatherClockSmartspaceScaling = 1f;
@@ -134,8 +143,11 @@
public void onConfigChanged() {
mClockSwitchYAmount = mContext.getResources().getDimensionPixelSize(
R.dimen.keyguard_clock_switch_y_shift);
- mSmartspaceTopOffset = mContext.getResources().getDimensionPixelSize(
- R.dimen.keyguard_smartspace_top_offset);
+ mSmartspaceTopOffset = (int) (mContext.getResources().getDimensionPixelSize(
+ R.dimen.keyguard_smartspace_top_offset)
+ * mContext.getResources().getConfiguration().fontScale
+ / mContext.getResources().getDisplayMetrics().density
+ * SMARTSPACE_TOP_PADDING_MULTIPLIER);
mWeatherClockSmartspaceScaling = ResourcesCompat.getFloat(
mContext.getResources(), R.dimen.weather_clock_smartspace_scale);
mWeatherClockSmartspaceTranslateX = mContext.getResources().getDimensionPixelSize(
@@ -145,6 +157,12 @@
updateStatusArea(/* animate= */false);
}
+ /** Get bc_smartspace_view from KeyguardClockSwitchController
+ * Use its top to decide the translation value */
+ public void setSmartspace(View smartspace) {
+ mSmartspace = smartspace;
+ }
+
/** Sets whether the large clock is being shown on a connected display. */
public void setLargeClockOnSecondaryDisplay(boolean onSecondaryDisplay) {
if (mClock != null) {
@@ -295,7 +313,7 @@
&& mClock.getLargeClock().getConfig().getHasCustomWeatherDataDisplay()) {
statusAreaClockScale = mWeatherClockSmartspaceScaling;
statusAreaClockTranslateX = mWeatherClockSmartspaceTranslateX;
- statusAreaClockTranslateY = mWeatherClockSmartspaceTranslateY;
+ statusAreaClockTranslateY = mWeatherClockSmartspaceTranslateY - mSmartspaceTop;
if (mSplitShadeCentered) {
statusAreaClockTranslateX *= SMARTSPACE_TRANSLATION_CENTER_MULTIPLIER;
}
@@ -418,10 +436,14 @@
post(() -> updateClockTargetRegions());
}
- if (mDisplayedClockSize != null && !mChildrenAreLaidOut) {
+ if (mSmartspace != null && mSmartspaceTop != mSmartspace.getTop()) {
+ mSmartspaceTop = mSmartspace.getTop();
post(() -> updateClockViews(mDisplayedClockSize == LARGE, mAnimateOnLayout));
}
+ if (mDisplayedClockSize != null && !mChildrenAreLaidOut) {
+ post(() -> updateClockViews(mDisplayedClockSize == LARGE, mAnimateOnLayout));
+ }
mChildrenAreLaidOut = true;
}
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitchController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitchController.java
index 6d2880e..d897960 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitchController.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitchController.java
@@ -372,6 +372,7 @@
mSmartspaceView.setPaddingRelative(startPadding, 0, endPadding, 0);
mKeyguardUnlockAnimationController.setLockscreenSmartspace(mSmartspaceView);
+ mView.setSmartspace(mSmartspaceView);
}
/**
diff --git a/packages/SystemUI/src/com/android/keyguard/LockIconView.java b/packages/SystemUI/src/com/android/keyguard/LockIconView.java
index 8611dbbb..1d37809 100644
--- a/packages/SystemUI/src/com/android/keyguard/LockIconView.java
+++ b/packages/SystemUI/src/com/android/keyguard/LockIconView.java
@@ -142,11 +142,13 @@
mLockIconCenter.y + mRadius);
final FrameLayout.LayoutParams lp = (FrameLayout.LayoutParams) getLayoutParams();
- lp.width = (int) (mSensorRect.right - mSensorRect.left);
- lp.height = (int) (mSensorRect.bottom - mSensorRect.top);
- lp.topMargin = (int) mSensorRect.top;
- lp.setMarginStart((int) mSensorRect.left);
- setLayoutParams(lp);
+ if (lp != null) {
+ lp.width = (int) (mSensorRect.right - mSensorRect.left);
+ lp.height = (int) (mSensorRect.bottom - mSensorRect.top);
+ lp.topMargin = (int) mSensorRect.top;
+ lp.setMarginStart((int) mSensorRect.left);
+ setLayoutParams(lp);
+ }
}
@Override
diff --git a/packages/SystemUI/src/com/android/keyguard/LockIconViewController.java b/packages/SystemUI/src/com/android/keyguard/LockIconViewController.java
index 951a6ae..ab9b647 100644
--- a/packages/SystemUI/src/com/android/keyguard/LockIconViewController.java
+++ b/packages/SystemUI/src/com/android/keyguard/LockIconViewController.java
@@ -28,6 +28,7 @@
import static com.android.systemui.flags.Flags.ONE_WAY_HAPTICS_API_MIGRATION;
import static com.android.systemui.util.kotlin.JavaAdapterKt.collectFlow;
+import android.content.Context;
import android.content.res.Configuration;
import android.content.res.Resources;
import android.graphics.Point;
@@ -74,7 +75,6 @@
import com.android.systemui.statusbar.VibratorHelper;
import com.android.systemui.statusbar.policy.ConfigurationController;
import com.android.systemui.statusbar.policy.KeyguardStateController;
-import com.android.systemui.util.ViewController;
import com.android.systemui.util.concurrency.DelayableExecutor;
import java.io.PrintWriter;
@@ -90,7 +90,7 @@
* icon will show a set distance from the bottom of the device.
*/
@SysUISingleton
-public class LockIconViewController extends ViewController<LockIconView> implements Dumpable {
+public class LockIconViewController implements Dumpable {
private static final String TAG = "LockIconViewController";
private static final float sDefaultDensity =
(float) DisplayMetrics.DENSITY_DEVICE_STABLE / (float) DisplayMetrics.DENSITY_DEFAULT;
@@ -109,6 +109,8 @@
@NonNull private final ConfigurationController mConfigurationController;
@NonNull private final DelayableExecutor mExecutor;
private boolean mUdfpsEnrolled;
+ private Resources mResources;
+ private Context mContext;
@NonNull private final AnimatedStateListDrawable mIcon;
@@ -120,6 +122,7 @@
@NonNull private final PrimaryBouncerInteractor mPrimaryBouncerInteractor;
@NonNull private final KeyguardTransitionInteractor mTransitionInteractor;
@NonNull private final KeyguardInteractor mKeyguardInteractor;
+ @NonNull private final View.AccessibilityDelegate mAccessibilityDelegate;
// Tracks the velocity of a touch to help filter out the touches that move too fast.
private VelocityTracker mVelocityTracker;
@@ -154,6 +157,7 @@
private boolean mDownDetected;
private final Rect mSensorTouchLocation = new Rect();
+ private LockIconView mView;
@VisibleForTesting
final Consumer<TransitionStep> mDozeTransitionCallback = (TransitionStep step) -> {
@@ -178,7 +182,6 @@
@Inject
public LockIconViewController(
- @Nullable LockIconView view,
@NonNull StatusBarStateController statusBarStateController,
@NonNull KeyguardUpdateMonitor keyguardUpdateMonitor,
@NonNull KeyguardViewController keyguardViewController,
@@ -195,9 +198,9 @@
@NonNull KeyguardTransitionInteractor transitionInteractor,
@NonNull KeyguardInteractor keyguardInteractor,
@NonNull FeatureFlags featureFlags,
- PrimaryBouncerInteractor primaryBouncerInteractor
+ PrimaryBouncerInteractor primaryBouncerInteractor,
+ Context context
) {
- super(view);
mStatusBarStateController = statusBarStateController;
mKeyguardUpdateMonitor = keyguardUpdateMonitor;
mAuthController = authController;
@@ -218,16 +221,40 @@
mMaxBurnInOffsetY = resources.getDimensionPixelSize(R.dimen.udfps_burn_in_offset_y);
mIcon = (AnimatedStateListDrawable)
- resources.getDrawable(R.drawable.super_lock_icon, mView.getContext().getTheme());
- mView.setImageDrawable(mIcon);
+ resources.getDrawable(R.drawable.super_lock_icon, context.getTheme());
mUnlockedLabel = resources.getString(R.string.accessibility_unlock_button);
mLockedLabel = resources.getString(R.string.accessibility_lock_icon);
mLongPressTimeout = resources.getInteger(R.integer.config_lockIconLongPress);
dumpManager.registerDumpable(TAG, this);
+ mResources = resources;
+ mContext = context;
+
+ mAccessibilityDelegate = new View.AccessibilityDelegate() {
+ private final AccessibilityNodeInfo.AccessibilityAction mAccessibilityAuthenticateHint =
+ new AccessibilityNodeInfo.AccessibilityAction(
+ AccessibilityNodeInfoCompat.ACTION_CLICK,
+ mResources.getString(R.string.accessibility_authenticate_hint));
+ private final AccessibilityNodeInfo.AccessibilityAction mAccessibilityEnterHint =
+ new AccessibilityNodeInfo.AccessibilityAction(
+ AccessibilityNodeInfoCompat.ACTION_CLICK,
+ mResources.getString(R.string.accessibility_enter_hint));
+ public void onInitializeAccessibilityNodeInfo(View v, AccessibilityNodeInfo info) {
+ super.onInitializeAccessibilityNodeInfo(v, info);
+ if (isActionable()) {
+ if (mShowLockIcon) {
+ info.addAction(mAccessibilityAuthenticateHint);
+ } else if (mShowUnlockIcon) {
+ info.addAction(mAccessibilityEnterHint);
+ }
+ }
+ }
+ };
}
- @Override
- protected void onInit() {
+ /** Sets the LockIconView to the controller and rebinds any that depend on it. */
+ public void setLockIconView(LockIconView lockIconView) {
+ mView = lockIconView;
+ mView.setImageDrawable(mIcon);
mView.setAccessibilityDelegate(mAccessibilityDelegate);
if (mFeatureFlags.isEnabled(DOZING_MIGRATION_1)) {
@@ -240,10 +267,7 @@
collectFlow(mView, mKeyguardInteractor.isActiveDreamLockscreenHosted(),
mIsActiveDreamLockscreenHostedCallback);
}
- }
- @Override
- protected void onViewAttached() {
updateIsUdfpsEnrolled();
updateConfiguration();
updateKeyguardShowing();
@@ -256,19 +280,49 @@
mStatusBarState = mStatusBarStateController.getState();
updateColors();
- mConfigurationController.addCallback(mConfigurationListener);
-
- mAuthController.addCallback(mAuthControllerCallback);
- mKeyguardUpdateMonitor.registerCallback(mKeyguardUpdateMonitorCallback);
- mStatusBarStateController.addCallback(mStatusBarStateListener);
- mKeyguardStateController.addCallback(mKeyguardStateCallback);
mDownDetected = false;
updateBurnInOffsets();
updateVisibility();
+ updateAccessibility();
+
+ lockIconView.addOnAttachStateChangeListener(new View.OnAttachStateChangeListener() {
+ @Override
+ public void onViewAttachedToWindow(View view) {
+ registerCallbacks();
+ }
+
+ @Override
+ public void onViewDetachedFromWindow(View view) {
+ unregisterCallbacks();
+ }
+ });
+
+ if (lockIconView.isAttachedToWindow()) {
+ registerCallbacks();
+ }
+ }
+
+ private void registerCallbacks() {
+ mConfigurationController.addCallback(mConfigurationListener);
+ mAuthController.addCallback(mAuthControllerCallback);
+ mKeyguardUpdateMonitor.registerCallback(mKeyguardUpdateMonitorCallback);
+ mStatusBarStateController.addCallback(mStatusBarStateListener);
+ mKeyguardStateController.addCallback(mKeyguardStateCallback);
mAccessibilityManager.addAccessibilityStateChangeListener(
mAccessibilityStateChangeListener);
- updateAccessibility();
+
+ }
+
+ private void unregisterCallbacks() {
+ mAuthController.removeCallback(mAuthControllerCallback);
+ mConfigurationController.removeCallback(mConfigurationListener);
+ mKeyguardUpdateMonitor.removeCallback(mKeyguardUpdateMonitorCallback);
+ mStatusBarStateController.removeCallback(mStatusBarStateListener);
+ mKeyguardStateController.removeCallback(mKeyguardStateCallback);
+ mAccessibilityManager.removeAccessibilityStateChangeListener(
+ mAccessibilityStateChangeListener);
+
}
private void updateAccessibility() {
@@ -279,18 +333,6 @@
}
}
- @Override
- protected void onViewDetached() {
- mAuthController.removeCallback(mAuthControllerCallback);
- mConfigurationController.removeCallback(mConfigurationListener);
- mKeyguardUpdateMonitor.removeCallback(mKeyguardUpdateMonitorCallback);
- mStatusBarStateController.removeCallback(mStatusBarStateListener);
- mKeyguardStateController.removeCallback(mKeyguardStateCallback);
-
- mAccessibilityManager.removeAccessibilityStateChangeListener(
- mAccessibilityStateChangeListener);
- }
-
public float getTop() {
return mView.getLocationTop();
}
@@ -363,28 +405,6 @@
}
}
- private final View.AccessibilityDelegate mAccessibilityDelegate =
- new View.AccessibilityDelegate() {
- private final AccessibilityNodeInfo.AccessibilityAction mAccessibilityAuthenticateHint =
- new AccessibilityNodeInfo.AccessibilityAction(
- AccessibilityNodeInfoCompat.ACTION_CLICK,
- getResources().getString(R.string.accessibility_authenticate_hint));
- private final AccessibilityNodeInfo.AccessibilityAction mAccessibilityEnterHint =
- new AccessibilityNodeInfo.AccessibilityAction(
- AccessibilityNodeInfoCompat.ACTION_CLICK,
- getResources().getString(R.string.accessibility_enter_hint));
- public void onInitializeAccessibilityNodeInfo(View v, AccessibilityNodeInfo info) {
- super.onInitializeAccessibilityNodeInfo(v, info);
- if (isActionable()) {
- if (mShowLockIcon) {
- info.addAction(mAccessibilityAuthenticateHint);
- } else if (mShowUnlockIcon) {
- info.addAction(mAccessibilityEnterHint);
- }
- }
- }
- };
-
private boolean isLockScreen() {
return !mIsDozing
&& !mIsBouncerShowing
@@ -401,18 +421,15 @@
}
private void updateConfiguration() {
- WindowManager windowManager = getContext().getSystemService(WindowManager.class);
+ WindowManager windowManager = mContext.getSystemService(WindowManager.class);
Rect bounds = windowManager.getCurrentWindowMetrics().getBounds();
mWidthPixels = bounds.right;
mHeightPixels = bounds.bottom;
- mBottomPaddingPx = getResources().getDimensionPixelSize(R.dimen.lock_icon_margin_bottom);
- mDefaultPaddingPx =
- getResources().getDimensionPixelSize(R.dimen.lock_icon_padding);
-
- mUnlockedLabel = mView.getContext().getResources().getString(
+ mBottomPaddingPx = mResources.getDimensionPixelSize(R.dimen.lock_icon_margin_bottom);
+ mDefaultPaddingPx = mResources.getDimensionPixelSize(R.dimen.lock_icon_padding);
+ mUnlockedLabel = mResources.getString(
R.string.accessibility_unlock_button);
- mLockedLabel = mView.getContext()
- .getResources().getString(R.string.accessibility_lock_icon);
+ mLockedLabel = mResources.getString(R.string.accessibility_lock_icon);
updateLockIconLocation();
}
@@ -755,7 +772,7 @@
} else {
mVibrator.vibrate(
Process.myUid(),
- getContext().getOpPackageName(),
+ mContext.getOpPackageName(),
UdfpsController.EFFECT_CLICK,
"lock-icon-down",
TOUCH_VIBRATION_ATTRIBUTES);
@@ -769,7 +786,7 @@
} else {
mVibrator.vibrate(
Process.myUid(),
- getContext().getOpPackageName(),
+ mContext.getOpPackageName(),
UdfpsController.EFFECT_CLICK,
"lock-screen-lock-icon-longpress",
TOUCH_VIBRATION_ATTRIBUTES);
diff --git a/packages/SystemUI/src/com/android/keyguard/PasswordTextView.java b/packages/SystemUI/src/com/android/keyguard/PasswordTextView.java
index 8e8ee48..9a2ffe0 100644
--- a/packages/SystemUI/src/com/android/keyguard/PasswordTextView.java
+++ b/packages/SystemUI/src/com/android/keyguard/PasswordTextView.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2014 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.
@@ -11,7 +11,7 @@
* 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
+ * limitations under the License.
*/
package com.android.keyguard;
@@ -30,18 +30,11 @@
import android.graphics.Typeface;
import android.os.PowerManager;
import android.os.SystemClock;
-import android.text.InputType;
-import android.text.TextUtils;
import android.util.AttributeSet;
import android.view.Gravity;
import android.view.LayoutInflater;
-import android.view.accessibility.AccessibilityEvent;
-import android.view.accessibility.AccessibilityManager;
-import android.view.accessibility.AccessibilityNodeInfo;
import android.view.animation.AnimationUtils;
import android.view.animation.Interpolator;
-import android.widget.EditText;
-import android.widget.FrameLayout;
import com.android.settingslib.Utils;
import com.android.systemui.R;
@@ -52,12 +45,11 @@
* A View similar to a textView which contains password text and can animate when the text is
* changed
*/
-public class PasswordTextView extends FrameLayout {
-
- private static final float DOT_OVERSHOOT_FACTOR = 1.5f;
- private static final long DOT_APPEAR_DURATION_OVERSHOOT = 320;
+public class PasswordTextView extends BasePasswordTextView {
public static final long APPEAR_DURATION = 160;
public static final long DISAPPEAR_DURATION = 160;
+ private static final float DOT_OVERSHOOT_FACTOR = 1.5f;
+ private static final long DOT_APPEAR_DURATION_OVERSHOOT = 320;
private static final long RESET_DELAY_PER_ELEMENT = 40;
private static final long RESET_MAX_DELAY = 200;
@@ -82,15 +74,12 @@
*/
private static final float OVERSHOOT_TIME_POSITION = 0.5f;
- private static char DOT = '\u2022';
-
/**
* The raw text size, will be multiplied by the scaled density when drawn
*/
private int mTextHeightRaw;
private final int mGravity;
private ArrayList<CharState> mTextChars = new ArrayList<>();
- private String mText = "";
private int mDotSize;
private PowerManager mPM;
private int mCharPadding;
@@ -99,15 +88,6 @@
private Interpolator mAppearInterpolator;
private Interpolator mDisappearInterpolator;
private Interpolator mFastOutSlowInInterpolator;
- private boolean mShowPassword = true;
- private UserActivityListener mUserActivityListener;
- private boolean mIsPinHinting;
- private PinShapeInput mPinShapeInput;
- private boolean mUsePinShapes = false;
-
- public interface UserActivityListener {
- void onUserActivity();
- }
public PasswordTextView(Context context) {
this(context, null);
@@ -145,8 +125,7 @@
mCharPadding = a.getDimensionPixelSize(R.styleable.PasswordTextView_charPadding,
getContext().getResources().getDimensionPixelSize(
R.dimen.password_char_padding));
- mDrawColor = a.getColor(R.styleable.PasswordTextView_android_textColor,
- Color.WHITE);
+ mDrawColor = a.getColor(R.styleable.PasswordTextView_android_textColor, Color.WHITE);
mDrawPaint.setColor(mDrawColor);
} finally {
@@ -156,8 +135,7 @@
mDrawPaint.setFlags(Paint.SUBPIXEL_TEXT_FLAG | Paint.ANTI_ALIAS_FLAG);
mDrawPaint.setTextAlign(Paint.Align.CENTER);
mDrawPaint.setTypeface(Typeface.create(
- context.getString(com.android.internal.R.string.config_headlineFontFamily),
- 0));
+ context.getString(com.android.internal.R.string.config_headlineFontFamily), 0));
mAppearInterpolator = AnimationUtils.loadInterpolator(mContext,
android.R.interpolator.linear_out_slow_in);
mDisappearInterpolator = AnimationUtils.loadInterpolator(mContext,
@@ -169,9 +147,19 @@
}
@Override
- protected void onConfigurationChanged(Configuration newConfig) {
- mTextHeightRaw = getContext().getResources().getInteger(
- R.integer.scaled_password_text_size);
+ protected PinShapeInput inflatePinShapeInput(boolean isPinHinting) {
+ if (isPinHinting) {
+ return (PinShapeInput) LayoutInflater.from(mContext).inflate(
+ R.layout.keyguard_pin_shape_hinting_view, null);
+ } else {
+ return (PinShapeInput) LayoutInflater.from(mContext).inflate(
+ R.layout.keyguard_pin_shape_non_hinting_view, null);
+ }
+ }
+
+ @Override
+ protected boolean shouldSendAccessibilityEvent() {
+ return isFocused() || isSelected() && isShown();
}
@Override
@@ -201,8 +189,8 @@
int charHeight = (bounds.bottom - bounds.top);
float yPosition =
(getHeight() - getPaddingBottom() - getPaddingTop()) / 2 + getPaddingTop();
- canvas.clipRect(getPaddingLeft(), getPaddingTop(),
- getWidth() - getPaddingRight(), getHeight() - getPaddingBottom());
+ canvas.clipRect(getPaddingLeft(), getPaddingTop(), getWidth() - getPaddingRight(),
+ getHeight() - getPaddingBottom());
float charLength = bounds.right - bounds.left;
for (int i = 0; i < length; i++) {
CharState charState = mTextChars.get(i);
@@ -212,6 +200,67 @@
}
}
+ @Override
+ protected void onAppend(char c, int newLength) {
+ int visibleChars = mTextChars.size();
+ CharState charState;
+ if (newLength > visibleChars) {
+ charState = obtainCharState(c);
+ mTextChars.add(charState);
+ } else {
+ charState = mTextChars.get(newLength - 1);
+ charState.whichChar = c;
+ }
+ charState.startAppearAnimation();
+
+ // ensure that the previous element is being swapped
+ if (newLength > 1) {
+ CharState previousState = mTextChars.get(newLength - 2);
+ if (previousState.isDotSwapPending) {
+ previousState.swapToDotWhenAppearFinished();
+ }
+ }
+ }
+
+ @Override
+ protected void onDelete(int index) {
+ CharState charState = mTextChars.get(index);
+ charState.startRemoveAnimation(0, 0);
+ }
+
+ @Override
+ protected void onReset(boolean animated) {
+ if (animated) {
+ int length = mTextChars.size();
+ int middleIndex = (length - 1) / 2;
+ long delayPerElement = RESET_DELAY_PER_ELEMENT;
+ for (int i = 0; i < length; i++) {
+ CharState charState = mTextChars.get(i);
+ int delayIndex;
+ if (i <= middleIndex) {
+ delayIndex = i * 2;
+ } else {
+ int distToMiddle = i - middleIndex;
+ delayIndex = (length - 1) - (distToMiddle - 1) * 2;
+ }
+ long startDelay = delayIndex * delayPerElement;
+ startDelay = Math.min(startDelay, RESET_MAX_DELAY);
+ long maxDelay = delayPerElement * (length - 1);
+ maxDelay = Math.min(maxDelay, RESET_MAX_DELAY) + DISAPPEAR_DURATION;
+ charState.startRemoveAnimation(startDelay, maxDelay);
+ charState.removeDotSwapCallbacks();
+ }
+ } else {
+ mTextChars.clear();
+ }
+ }
+
+ @Override
+ protected void onUserActivity() {
+ mPM.userActivity(SystemClock.uptimeMillis(), false);
+ super.onUserActivity();
+ }
+
/**
* Reload colors from resources.
**/
@@ -225,8 +274,9 @@
}
@Override
- public boolean hasOverlappingRendering() {
- return false;
+ protected void onConfigurationChanged(Configuration newConfig) {
+ mTextHeightRaw = getContext().getResources().getInteger(
+ R.integer.scaled_password_text_size);
}
private Rect getCharBounds() {
@@ -252,67 +302,14 @@
return width;
}
-
- public void append(char c) {
- int visibleChars = mTextChars.size();
- CharSequence textbefore = getTransformedText();
- mText = mText + c;
- int newLength = mText.length();
- CharState charState;
- if (newLength > visibleChars) {
- charState = obtainCharState(c);
- mTextChars.add(charState);
- } else {
- charState = mTextChars.get(newLength - 1);
- charState.whichChar = c;
- }
- if (mPinShapeInput != null) {
- mPinShapeInput.append();
- }
- charState.startAppearAnimation();
-
- // ensure that the previous element is being swapped
- if (newLength > 1) {
- CharState previousState = mTextChars.get(newLength - 2);
- if (previousState.isDotSwapPending) {
- previousState.swapToDotWhenAppearFinished();
- }
- }
- userActivity();
- sendAccessibilityEventTypeViewTextChanged(textbefore, textbefore.length(), 0, 1);
+ private CharState obtainCharState(char c) {
+ CharState charState = new CharState();
+ charState.whichChar = c;
+ return charState;
}
- public void setUserActivityListener(UserActivityListener userActivityListener) {
- mUserActivityListener = userActivityListener;
- }
-
- private void userActivity() {
- mPM.userActivity(SystemClock.uptimeMillis(), false);
- if (mUserActivityListener != null) {
- mUserActivityListener.onUserActivity();
- }
- }
-
- public void deleteLastChar() {
- int length = mText.length();
- CharSequence textbefore = getTransformedText();
- if (length > 0) {
- mText = mText.substring(0, length - 1);
- CharState charState = mTextChars.get(length - 1);
- charState.startRemoveAnimation(0, 0);
- sendAccessibilityEventTypeViewTextChanged(textbefore, textbefore.length() - 1, 1, 0);
- if (mPinShapeInput != null) {
- mPinShapeInput.delete();
- }
- }
- userActivity();
- }
-
- public String getText() {
- return mText;
- }
-
- private CharSequence getTransformedText() {
+ @Override
+ protected CharSequence getTransformedText() {
int textLength = mTextChars.size();
StringBuilder stringBuilder = new StringBuilder(textLength);
for (int i = 0; i < textLength; i++) {
@@ -327,130 +324,6 @@
return stringBuilder;
}
- private CharState obtainCharState(char c) {
- CharState charState = new CharState();
- charState.whichChar = c;
- return charState;
- }
-
- public void reset(boolean animated, boolean announce) {
- CharSequence textbefore = getTransformedText();
- mText = "";
- int length = mTextChars.size();
- int middleIndex = (length - 1) / 2;
- long delayPerElement = RESET_DELAY_PER_ELEMENT;
- for (int i = 0; i < length; i++) {
- CharState charState = mTextChars.get(i);
- if (animated) {
- int delayIndex;
- if (i <= middleIndex) {
- delayIndex = i * 2;
- } else {
- int distToMiddle = i - middleIndex;
- delayIndex = (length - 1) - (distToMiddle - 1) * 2;
- }
- long startDelay = delayIndex * delayPerElement;
- startDelay = Math.min(startDelay, RESET_MAX_DELAY);
- long maxDelay = delayPerElement * (length - 1);
- maxDelay = Math.min(maxDelay, RESET_MAX_DELAY) + DISAPPEAR_DURATION;
- charState.startRemoveAnimation(startDelay, maxDelay);
- charState.removeDotSwapCallbacks();
- }
- }
- if (!animated) {
- mTextChars.clear();
- } else {
- userActivity();
- }
- if (mPinShapeInput != null) {
- mPinShapeInput.reset();
- }
- if (announce) {
- sendAccessibilityEventTypeViewTextChanged(textbefore, 0, textbefore.length(), 0);
- }
- }
-
- void sendAccessibilityEventTypeViewTextChanged(CharSequence beforeText, int fromIndex,
- int removedCount, int addedCount) {
- if (AccessibilityManager.getInstance(mContext).isEnabled() &&
- (isFocused() || isSelected() && isShown())) {
- AccessibilityEvent event =
- AccessibilityEvent.obtain(AccessibilityEvent.TYPE_VIEW_TEXT_CHANGED);
- event.setFromIndex(fromIndex);
- event.setRemovedCount(removedCount);
- event.setAddedCount(addedCount);
- event.setBeforeText(beforeText);
- CharSequence transformedText = getTransformedText();
- if (!TextUtils.isEmpty(transformedText)) {
- event.getText().add(transformedText);
- }
- event.setPassword(true);
- sendAccessibilityEventUnchecked(event);
- }
- }
-
- @Override
- public void onInitializeAccessibilityEvent(AccessibilityEvent event) {
- super.onInitializeAccessibilityEvent(event);
-
- event.setClassName(EditText.class.getName());
- event.setPassword(true);
- }
-
- @Override
- public void onInitializeAccessibilityNodeInfo(AccessibilityNodeInfo info) {
- super.onInitializeAccessibilityNodeInfo(info);
-
- info.setClassName(EditText.class.getName());
- info.setPassword(true);
- info.setText(getTransformedText());
-
- info.setEditable(true);
-
- info.setInputType(InputType.TYPE_NUMBER_VARIATION_PASSWORD);
- }
-
- /**
- * Sets whether to use pin shapes.
- */
- public void setUsePinShapes(boolean usePinShapes) {
- mUsePinShapes = usePinShapes;
- }
-
- /**
- * Determines whether AutoConfirmation feature is on.
- *
- * @param isPinHinting
- */
- public void setIsPinHinting(boolean isPinHinting) {
- // Do not reinflate the view if we are using the same one.
- if (mPinShapeInput != null && mIsPinHinting == isPinHinting) {
- return;
- }
- mIsPinHinting = isPinHinting;
-
- if (mPinShapeInput != null) {
- removeView(mPinShapeInput.getView());
- mPinShapeInput = null;
- }
-
- if (isPinHinting) {
- mPinShapeInput = (PinShapeInput) LayoutInflater.from(mContext).inflate(
- R.layout.keyguard_pin_shape_hinting_view, null);
- } else {
- mPinShapeInput = (PinShapeInput) LayoutInflater.from(mContext).inflate(
- R.layout.keyguard_pin_shape_non_hinting_view, null);
- }
- addView(mPinShapeInput.getView());
- }
-
- /**
- * Controls whether the last entered digit is briefly shown after being entered
- */
- public void setShowPassword(boolean enabled) {
- mShowPassword = enabled;
- }
-
private class CharState {
char whichChar;
ValueAnimator textAnimator;
@@ -468,6 +341,7 @@
Animator.AnimatorListener removeEndListener = new AnimatorListenerAdapter() {
private boolean mCancelled;
+
@Override
public void onAnimationCancel(Animator animation) {
mCancelled = true;
@@ -516,53 +390,53 @@
}
};
- private ValueAnimator.AnimatorUpdateListener dotSizeUpdater
- = new ValueAnimator.AnimatorUpdateListener() {
- @Override
- public void onAnimationUpdate(ValueAnimator animation) {
- currentDotSizeFactor = (float) animation.getAnimatedValue();
- invalidate();
- }
- };
-
- private ValueAnimator.AnimatorUpdateListener textSizeUpdater
- = new ValueAnimator.AnimatorUpdateListener() {
- @Override
- public void onAnimationUpdate(ValueAnimator animation) {
- boolean textVisibleBefore = isCharVisibleForA11y();
- float beforeTextSizeFactor = currentTextSizeFactor;
- currentTextSizeFactor = (float) animation.getAnimatedValue();
- if (textVisibleBefore != isCharVisibleForA11y()) {
- currentTextSizeFactor = beforeTextSizeFactor;
- CharSequence beforeText = getTransformedText();
- currentTextSizeFactor = (float) animation.getAnimatedValue();
- int indexOfThisChar = mTextChars.indexOf(CharState.this);
- if (indexOfThisChar >= 0) {
- sendAccessibilityEventTypeViewTextChanged(
- beforeText, indexOfThisChar, 1, 1);
+ private ValueAnimator.AnimatorUpdateListener mDotSizeUpdater =
+ new ValueAnimator.AnimatorUpdateListener() {
+ @Override
+ public void onAnimationUpdate(ValueAnimator animation) {
+ currentDotSizeFactor = (float) animation.getAnimatedValue();
+ invalidate();
}
- }
- invalidate();
- }
- };
+ };
- private ValueAnimator.AnimatorUpdateListener textTranslationUpdater
- = new ValueAnimator.AnimatorUpdateListener() {
- @Override
- public void onAnimationUpdate(ValueAnimator animation) {
- currentTextTranslationY = (float) animation.getAnimatedValue();
- invalidate();
- }
- };
+ private ValueAnimator.AnimatorUpdateListener mTextSizeUpdater =
+ new ValueAnimator.AnimatorUpdateListener() {
+ @Override
+ public void onAnimationUpdate(ValueAnimator animation) {
+ boolean textVisibleBefore = isCharVisibleForA11y();
+ float beforeTextSizeFactor = currentTextSizeFactor;
+ currentTextSizeFactor = (float) animation.getAnimatedValue();
+ if (textVisibleBefore != isCharVisibleForA11y()) {
+ currentTextSizeFactor = beforeTextSizeFactor;
+ CharSequence beforeText = getTransformedText();
+ currentTextSizeFactor = (float) animation.getAnimatedValue();
+ int indexOfThisChar = mTextChars.indexOf(CharState.this);
+ if (indexOfThisChar >= 0) {
+ sendAccessibilityEventTypeViewTextChanged(beforeText,
+ indexOfThisChar, 1, 1);
+ }
+ }
+ invalidate();
+ }
+ };
- private ValueAnimator.AnimatorUpdateListener widthUpdater
- = new ValueAnimator.AnimatorUpdateListener() {
- @Override
- public void onAnimationUpdate(ValueAnimator animation) {
- currentWidthFactor = (float) animation.getAnimatedValue();
- invalidate();
- }
- };
+ private ValueAnimator.AnimatorUpdateListener mTextTranslationUpdater =
+ new ValueAnimator.AnimatorUpdateListener() {
+ @Override
+ public void onAnimationUpdate(ValueAnimator animation) {
+ currentTextTranslationY = (float) animation.getAnimatedValue();
+ invalidate();
+ }
+ };
+
+ private ValueAnimator.AnimatorUpdateListener mWidthUpdater =
+ new ValueAnimator.AnimatorUpdateListener() {
+ @Override
+ public void onAnimationUpdate(ValueAnimator animation) {
+ currentWidthFactor = (float) animation.getAnimatedValue();
+ invalidate();
+ }
+ };
private Runnable dotSwapperRunnable = new Runnable() {
@Override
@@ -573,12 +447,15 @@
};
void startRemoveAnimation(long startDelay, long widthDelay) {
- boolean dotNeedsAnimation = (currentDotSizeFactor > 0.0f && dotAnimator == null)
- || (dotAnimator != null && dotAnimationIsGrowing);
- boolean textNeedsAnimation = (currentTextSizeFactor > 0.0f && textAnimator == null)
- || (textAnimator != null && textAnimationIsGrowing);
- boolean widthNeedsAnimation = (currentWidthFactor > 0.0f && widthAnimator == null)
- || (widthAnimator != null && widthAnimationIsGrowing);
+ boolean dotNeedsAnimation =
+ (currentDotSizeFactor > 0.0f && dotAnimator == null) || (dotAnimator != null
+ && dotAnimationIsGrowing);
+ boolean textNeedsAnimation =
+ (currentTextSizeFactor > 0.0f && textAnimator == null) || (textAnimator != null
+ && textAnimationIsGrowing);
+ boolean widthNeedsAnimation =
+ (currentWidthFactor > 0.0f && widthAnimator == null) || (widthAnimator != null
+ && widthAnimationIsGrowing);
if (dotNeedsAnimation) {
startDotDisappearAnimation(startDelay);
}
@@ -591,10 +468,10 @@
}
void startAppearAnimation() {
- boolean dotNeedsAnimation = !mShowPassword
- && (dotAnimator == null || !dotAnimationIsGrowing);
- boolean textNeedsAnimation = mShowPassword
- && (textAnimator == null || !textAnimationIsGrowing);
+ boolean dotNeedsAnimation =
+ !mShowPassword && (dotAnimator == null || !dotAnimationIsGrowing);
+ boolean textNeedsAnimation =
+ mShowPassword && (textAnimator == null || !textAnimationIsGrowing);
boolean widthNeedsAnimation = (widthAnimator == null || !widthAnimationIsGrowing);
if (dotNeedsAnimation) {
startDotAppearAnimation(0);
@@ -628,8 +505,8 @@
void swapToDotWhenAppearFinished() {
removeDotSwapCallbacks();
if (textAnimator != null) {
- long remainingDuration = textAnimator.getDuration()
- - textAnimator.getCurrentPlayTime();
+ long remainingDuration =
+ textAnimator.getDuration() - textAnimator.getCurrentPlayTime();
postDotSwap(remainingDuration + TEXT_REST_DURATION_AFTER_APPEAR);
} else {
performSwap();
@@ -638,14 +515,14 @@
private void performSwap() {
startTextDisappearAnimation(0);
- startDotAppearAnimation(DISAPPEAR_DURATION
- - DOT_APPEAR_TEXT_DISAPPEAR_OVERLAP_DURATION);
+ startDotAppearAnimation(
+ DISAPPEAR_DURATION - DOT_APPEAR_TEXT_DISAPPEAR_OVERLAP_DURATION);
}
private void startWidthDisappearAnimation(long widthDelay) {
cancelAnimator(widthAnimator);
widthAnimator = ValueAnimator.ofFloat(currentWidthFactor, 0.0f);
- widthAnimator.addUpdateListener(widthUpdater);
+ widthAnimator.addUpdateListener(mWidthUpdater);
widthAnimator.addListener(widthFinishListener);
widthAnimator.addListener(removeEndListener);
widthAnimator.setDuration((long) (DISAPPEAR_DURATION * currentWidthFactor));
@@ -657,7 +534,7 @@
private void startTextDisappearAnimation(long startDelay) {
cancelAnimator(textAnimator);
textAnimator = ValueAnimator.ofFloat(currentTextSizeFactor, 0.0f);
- textAnimator.addUpdateListener(textSizeUpdater);
+ textAnimator.addUpdateListener(mTextSizeUpdater);
textAnimator.addListener(textFinishListener);
textAnimator.setInterpolator(mDisappearInterpolator);
textAnimator.setDuration((long) (DISAPPEAR_DURATION * currentTextSizeFactor));
@@ -669,7 +546,7 @@
private void startDotDisappearAnimation(long startDelay) {
cancelAnimator(dotAnimator);
ValueAnimator animator = ValueAnimator.ofFloat(currentDotSizeFactor, 0.0f);
- animator.addUpdateListener(dotSizeUpdater);
+ animator.addUpdateListener(mDotSizeUpdater);
animator.addListener(dotFinishListener);
animator.setInterpolator(mDisappearInterpolator);
long duration = (long) (DISAPPEAR_DURATION * Math.min(currentDotSizeFactor, 1.0f));
@@ -683,7 +560,7 @@
private void startWidthAppearAnimation() {
cancelAnimator(widthAnimator);
widthAnimator = ValueAnimator.ofFloat(currentWidthFactor, 1.0f);
- widthAnimator.addUpdateListener(widthUpdater);
+ widthAnimator.addUpdateListener(mWidthUpdater);
widthAnimator.addListener(widthFinishListener);
widthAnimator.setDuration((long) (APPEAR_DURATION * (1f - currentWidthFactor)));
widthAnimator.start();
@@ -693,7 +570,7 @@
private void startTextAppearAnimation() {
cancelAnimator(textAnimator);
textAnimator = ValueAnimator.ofFloat(currentTextSizeFactor, 1.0f);
- textAnimator.addUpdateListener(textSizeUpdater);
+ textAnimator.addUpdateListener(mTextSizeUpdater);
textAnimator.addListener(textFinishListener);
textAnimator.setInterpolator(mAppearInterpolator);
textAnimator.setDuration((long) (APPEAR_DURATION * (1f - currentTextSizeFactor)));
@@ -703,7 +580,7 @@
// handle translation
if (textTranslateAnimator == null) {
textTranslateAnimator = ValueAnimator.ofFloat(1.0f, 0.0f);
- textTranslateAnimator.addUpdateListener(textTranslationUpdater);
+ textTranslateAnimator.addUpdateListener(mTextTranslationUpdater);
textTranslateAnimator.addListener(textTranslateFinishListener);
textTranslateAnimator.setInterpolator(mAppearInterpolator);
textTranslateAnimator.setDuration(APPEAR_DURATION);
@@ -717,14 +594,14 @@
// We perform an overshoot animation
ValueAnimator overShootAnimator = ValueAnimator.ofFloat(currentDotSizeFactor,
DOT_OVERSHOOT_FACTOR);
- overShootAnimator.addUpdateListener(dotSizeUpdater);
+ overShootAnimator.addUpdateListener(mDotSizeUpdater);
overShootAnimator.setInterpolator(mAppearInterpolator);
- long overShootDuration = (long) (DOT_APPEAR_DURATION_OVERSHOOT
- * OVERSHOOT_TIME_POSITION);
+ long overShootDuration =
+ (long) (DOT_APPEAR_DURATION_OVERSHOOT * OVERSHOOT_TIME_POSITION);
overShootAnimator.setDuration(overShootDuration);
ValueAnimator settleBackAnimator = ValueAnimator.ofFloat(DOT_OVERSHOOT_FACTOR,
1.0f);
- settleBackAnimator.addUpdateListener(dotSizeUpdater);
+ settleBackAnimator.addUpdateListener(mDotSizeUpdater);
settleBackAnimator.setDuration(DOT_APPEAR_DURATION_OVERSHOOT - overShootDuration);
settleBackAnimator.addListener(dotFinishListener);
AnimatorSet animatorSet = new AnimatorSet();
@@ -734,7 +611,7 @@
dotAnimator = animatorSet;
} else {
ValueAnimator growAnimator = ValueAnimator.ofFloat(currentDotSizeFactor, 1.0f);
- growAnimator.addUpdateListener(dotSizeUpdater);
+ growAnimator.addUpdateListener(mDotSizeUpdater);
growAnimator.setDuration((long) (APPEAR_DURATION * (1.0f - currentDotSizeFactor)));
growAnimator.addListener(dotFinishListener);
growAnimator.setStartDelay(delay);
diff --git a/packages/SystemUI/src/com/android/systemui/accessibility/aconfig/accessibility.aconfig b/packages/SystemUI/src/com/android/systemui/accessibility/aconfig/accessibility.aconfig
new file mode 100644
index 0000000..91c5551
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/accessibility/aconfig/accessibility.aconfig
@@ -0,0 +1,7 @@
+package: "com.android.systemui.aconfig"
+flag {
+ name: "floating_menu_overlaps_nav_bars_flag"
+ namespace: "accessibility"
+ description: "Adjusts bounds to allow the floating menu to render on top of navigation bars."
+ bug: "283768342"
+}
\ No newline at end of file
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 47770fa..f29077d 100644
--- a/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/MenuView.java
+++ b/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/MenuView.java
@@ -38,6 +38,7 @@
import androidx.recyclerview.widget.RecyclerViewAccessibilityDelegate;
import com.android.internal.accessibility.dialog.AccessibilityTarget;
+import com.android.systemui.aconfig.Flags;
import java.util.ArrayList;
import java.util.Collections;
@@ -284,6 +285,22 @@
void updateMenuMoveToTucked(boolean isMoveToTucked) {
mIsMoveToTucked = isMoveToTucked;
mMenuViewModel.updateMenuMoveToTucked(isMoveToTucked);
+
+ if (Flags.floatingMenuOverlapsNavBarsFlag()) {
+ 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/MenuViewAppearance.java b/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/MenuViewAppearance.java
index 3cd250f..3822936 100644
--- a/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/MenuViewAppearance.java
+++ b/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/MenuViewAppearance.java
@@ -35,6 +35,7 @@
import androidx.annotation.DimenRes;
import com.android.systemui.R;
+import com.android.systemui.aconfig.Flags;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
@@ -154,8 +155,10 @@
final int margin = getMenuMargin();
final Rect draggableBounds = new Rect(getWindowAvailableBounds());
- // Initializes start position for mapping the translation of the menu view.
- draggableBounds.offsetTo(/* newLeft= */ 0, /* newTop= */ 0);
+ if (!Flags.floatingMenuOverlapsNavBarsFlag()) {
+ // Initializes start position for mapping the translation of the menu view.
+ draggableBounds.offsetTo(/* newLeft= */ 0, /* newTop= */ 0);
+ }
draggableBounds.top += margin;
draggableBounds.right -= getMenuWidth();
diff --git a/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/MenuViewLayerController.java b/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/MenuViewLayerController.java
index c52ecc5..cc18c30 100644
--- a/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/MenuViewLayerController.java
+++ b/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/MenuViewLayerController.java
@@ -24,6 +24,7 @@
import android.view.WindowManager;
import android.view.accessibility.AccessibilityManager;
+import com.android.systemui.aconfig.Flags;
import com.android.systemui.util.settings.SecureSettings;
/**
@@ -77,8 +78,15 @@
params.receiveInsetsIgnoringZOrder = true;
params.privateFlags |= PRIVATE_FLAG_EXCLUDE_FROM_SCREEN_MAGNIFICATION;
params.windowAnimations = android.R.style.Animation_Translucent;
- params.setFitInsetsTypes(
- WindowInsets.Type.systemBars() | WindowInsets.Type.displayCutout());
+ // Insets are configured to allow the menu to display over navigation and system bars.
+ if (Flags.floatingMenuOverlapsNavBarsFlag()) {
+ params.setFitInsetsTypes(0);
+ params.layoutInDisplayCutoutMode =
+ WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS;
+ } else {
+ params.setFitInsetsTypes(
+ WindowInsets.Type.systemBars() | WindowInsets.Type.displayCutout());
+ }
return params;
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/appops/AppOpsControllerImpl.java b/packages/SystemUI/src/com/android/systemui/appops/AppOpsControllerImpl.java
index eec16e6..c9801d7 100644
--- a/packages/SystemUI/src/com/android/systemui/appops/AppOpsControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/appops/AppOpsControllerImpl.java
@@ -52,6 +52,7 @@
import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.List;
+import java.util.Map;
import java.util.Set;
import javax.inject.Inject;
@@ -184,6 +185,10 @@
protected void setListening(boolean listening) {
mListening = listening;
if (listening) {
+ // System UI could be restarted while ops are active, so fetch the currently active ops
+ // once System UI starts listening again.
+ fetchCurrentActiveOps();
+
mAppOps.startWatchingActive(OPS, this);
mAppOps.startWatchingNoted(OPS, this);
mAudioManager.registerAudioRecordingCallback(mAudioRecordingCallback, mBGHandler);
@@ -216,6 +221,29 @@
}
}
+ private void fetchCurrentActiveOps() {
+ List<AppOpsManager.PackageOps> packageOps = mAppOps.getPackagesForOps(OPS);
+ for (AppOpsManager.PackageOps op : packageOps) {
+ for (AppOpsManager.OpEntry entry : op.getOps()) {
+ for (Map.Entry<String, AppOpsManager.AttributedOpEntry> attributedOpEntry :
+ entry.getAttributedOpEntries().entrySet()) {
+ if (attributedOpEntry.getValue().isRunning()) {
+ onOpActiveChanged(
+ entry.getOpStr(),
+ op.getUid(),
+ op.getPackageName(),
+ /* attributionTag= */ attributedOpEntry.getKey(),
+ /* active= */ true,
+ // AppOpsManager doesn't have a way to fetch attribution flags or
+ // chain ID given an op entry, so default them to none.
+ AppOpsManager.ATTRIBUTION_FLAGS_NONE,
+ AppOpsManager.ATTRIBUTION_CHAIN_ID_NONE);
+ }
+ }
+ }
+ }
+ }
+
/**
* Adds a callback that will get notifified when an AppOp of the type the controller tracks
* changes
diff --git a/packages/SystemUI/src/com/android/systemui/back/domain/interactor/BackActionInteractor.kt b/packages/SystemUI/src/com/android/systemui/back/domain/interactor/BackActionInteractor.kt
index 3c74bf4..066cba23 100644
--- a/packages/SystemUI/src/com/android/systemui/back/domain/interactor/BackActionInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/back/domain/interactor/BackActionInteractor.kt
@@ -16,24 +16,69 @@
package com.android.systemui.back.domain.interactor
+import android.window.BackEvent
+import android.window.OnBackAnimationCallback
+import android.window.OnBackInvokedCallback
+import android.window.OnBackInvokedDispatcher
+import android.window.WindowOnBackInvokedDispatcher
+import com.android.systemui.CoreStartable
import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.dagger.qualifiers.Application
+import com.android.systemui.flags.FeatureFlags
+import com.android.systemui.flags.Flags
import com.android.systemui.plugins.statusbar.StatusBarStateController
+import com.android.systemui.scene.domain.interactor.WindowRootViewVisibilityInteractor
import com.android.systemui.shade.QuickSettingsController
import com.android.systemui.shade.ShadeController
import com.android.systemui.shade.ShadeViewController
+import com.android.systemui.statusbar.NotificationShadeWindowController
import com.android.systemui.statusbar.StatusBarState
import com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager
import javax.inject.Inject
+import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.launch
/** Handles requests to go back either from a button or gesture. */
@SysUISingleton
class BackActionInteractor
@Inject
constructor(
+ @Application private val scope: CoroutineScope,
private val statusBarStateController: StatusBarStateController,
private val statusBarKeyguardViewManager: StatusBarKeyguardViewManager,
- private val shadeController: ShadeController
-) {
+ private val shadeController: ShadeController,
+ private val notificationShadeWindowController: NotificationShadeWindowController,
+ private val windowRootViewVisibilityInteractor: WindowRootViewVisibilityInteractor,
+ featureFlags: FeatureFlags,
+) : CoreStartable {
+
+ private var isCallbackRegistered = false
+
+ private val callback =
+ if (featureFlags.isEnabled(Flags.WM_SHADE_ANIMATE_BACK_GESTURE)) {
+ /**
+ * New callback that handles back gesture invoked, cancel, progress and provides
+ * feedback via Shade animation.
+ */
+ object : OnBackAnimationCallback {
+ override fun onBackInvoked() {
+ onBackRequested()
+ }
+
+ override fun onBackProgressed(backEvent: BackEvent) {
+ if (shouldBackBeHandled() && shadeViewController.canBeCollapsed()) {
+ shadeViewController.onBackProgressed(backEvent.progress)
+ }
+ }
+ }
+ } else {
+ OnBackInvokedCallback { onBackRequested() }
+ }
+
+ private val onBackInvokedDispatcher: WindowOnBackInvokedDispatcher?
+ get() =
+ notificationShadeWindowController.windowRootView?.viewRootImpl?.onBackInvokedDispatcher
+
private lateinit var shadeViewController: ShadeViewController
private lateinit var qsController: QuickSettingsController
@@ -42,6 +87,19 @@
this.shadeViewController = svController
}
+ override fun start() {
+ scope.launch {
+ windowRootViewVisibilityInteractor.isLockscreenOrShadeVisibleAndInteractive.collect {
+ visible ->
+ if (visible) {
+ registerBackCallback()
+ } else {
+ unregisterBackCallback()
+ }
+ }
+ }
+ }
+
fun shouldBackBeHandled(): Boolean {
return statusBarStateController.state != StatusBarState.KEYGUARD &&
statusBarStateController.state != StatusBarState.SHADE_LOCKED &&
@@ -74,4 +132,24 @@
}
return false
}
+
+ private fun registerBackCallback() {
+ if (isCallbackRegistered) {
+ return
+ }
+ onBackInvokedDispatcher?.let {
+ it.registerOnBackInvokedCallback(OnBackInvokedDispatcher.PRIORITY_DEFAULT, callback)
+ isCallbackRegistered = true
+ }
+ }
+
+ private fun unregisterBackCallback() {
+ if (!isCallbackRegistered) {
+ return
+ }
+ onBackInvokedDispatcher?.let {
+ it.unregisterOnBackInvokedCallback(callback)
+ isCallbackRegistered = false
+ }
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java
index b23e085..a368703 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java
@@ -586,16 +586,18 @@
if (shouldTryToDismissKeyguard()) {
tryDismissingKeyguard();
}
- onFingerDown(requestId,
- data.getPointerId(),
- data.getX(),
- data.getY(),
- data.getMinor(),
- data.getMajor(),
- data.getOrientation(),
- data.getTime(),
- data.getGestureStart(),
- mStatusBarStateController.isDozing());
+ if (!mOnFingerDown) {
+ onFingerDown(requestId,
+ data.getPointerId(),
+ data.getX(),
+ data.getY(),
+ data.getMinor(),
+ data.getMajor(),
+ data.getOrientation(),
+ data.getTime(),
+ data.getGestureStart(),
+ mStatusBarStateController.isDozing());
+ }
// Pilfer if valid overlap, don't allow following events to reach keyguard
shouldPilfer = true;
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/ui/CredentialPasswordView.kt b/packages/SystemUI/src/com/android/systemui/biometrics/ui/CredentialPasswordView.kt
index 6c5cc48..a7ecf38 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/ui/CredentialPasswordView.kt
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/ui/CredentialPasswordView.kt
@@ -52,7 +52,7 @@
titleView.ellipsize = TextUtils.TruncateAt.MARQUEE
titleView.marqueeRepeatLimit = -1
// select to enable marquee unless a screen reader is enabled
- titleView.isSelected = accessibilityManager.shouldMarquee()
+ titleView.isSelected = accessibilityManager?.shouldMarquee() ?: false
} else {
titleView.isSingleLine = false
titleView.ellipsize = null
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/ui/viewmodel/PromptViewModel.kt b/packages/SystemUI/src/com/android/systemui/biometrics/ui/viewmodel/PromptViewModel.kt
index e8b8f54..be08932 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/ui/viewmodel/PromptViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/ui/viewmodel/PromptViewModel.kt
@@ -78,17 +78,6 @@
private val _isOverlayTouched: MutableStateFlow<Boolean> = MutableStateFlow(false)
- /**
- * If the API caller or the user's personal preferences require explicit confirmation after
- * successful authentication.
- */
- val isConfirmationRequired: Flow<Boolean> =
- combine(_isOverlayTouched, promptSelectorInteractor.isConfirmationRequired) {
- isOverlayTouched,
- isConfirmationRequired ->
- !isOverlayTouched && isConfirmationRequired
- }
-
/** The kind of credential the user has. */
val credentialKind: Flow<PromptKind> = promptSelectorInteractor.credentialKind
@@ -137,6 +126,15 @@
}
.distinctUntilChanged()
+ /**
+ * If the API caller or the user's personal preferences require explicit confirmation after
+ * successful authentication. Confirmation always required when in explicit flow.
+ */
+ val isConfirmationRequired: Flow<Boolean> =
+ combine(_isOverlayTouched, size) { isOverlayTouched, size ->
+ !isOverlayTouched && size.isNotSmall
+ }
+
/** Title for the prompt. */
val title: Flow<String> =
promptSelectorInteractor.prompt.map { it?.title ?: "" }.distinctUntilChanged()
@@ -170,12 +168,7 @@
.distinctUntilChanged()
/** If the icon can be used as a confirmation button. */
- val isIconConfirmButton: Flow<Boolean> =
- combine(size, promptSelectorInteractor.isConfirmationRequired) {
- size,
- isConfirmationRequired ->
- size.isNotSmall && isConfirmationRequired
- }
+ val isIconConfirmButton: Flow<Boolean> = size.map { it.isNotSmall }.distinctUntilChanged()
/** If the negative button should be shown. */
val isNegativeButtonVisible: Flow<Boolean> =
diff --git a/packages/SystemUI/src/com/android/systemui/bouncer/domain/interactor/BouncerInteractor.kt b/packages/SystemUI/src/com/android/systemui/bouncer/domain/interactor/BouncerInteractor.kt
index 1bf3a9e..fc32f4c 100644
--- a/packages/SystemUI/src/com/android/systemui/bouncer/domain/interactor/BouncerInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/bouncer/domain/interactor/BouncerInteractor.kt
@@ -22,6 +22,8 @@
import com.android.systemui.authentication.domain.model.AuthenticationMethodModel
import com.android.systemui.authentication.shared.model.AuthenticationThrottlingModel
import com.android.systemui.bouncer.data.repository.BouncerRepository
+import com.android.systemui.classifier.FalsingClassifier
+import com.android.systemui.classifier.domain.interactor.FalsingInteractor
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Application
import com.android.systemui.flags.FeatureFlags
@@ -50,6 +52,7 @@
private val authenticationInteractor: AuthenticationInteractor,
private val sceneInteractor: SceneInteractor,
featureFlags: FeatureFlags,
+ private val falsingInteractor: FalsingInteractor,
) {
/** The user-facing message to show in the bouncer. */
@@ -103,6 +106,34 @@
}
}
+ /** Notifies that the user has places down a pointer, not necessarily dragging just yet. */
+ fun onDown() {
+ falsingInteractor.avoidGesture()
+ }
+
+ /**
+ * Notifies of "intentional" (i.e. non-false) user interaction with the UI which is very likely
+ * to be real user interaction with the bouncer and not the result of a false touch in the
+ * user's pocket or by the user's face while holding their device up to their ear.
+ */
+ fun onIntentionalUserInput() {
+ falsingInteractor.updateFalseConfidence(FalsingClassifier.Result.passed(0.6))
+ }
+
+ /**
+ * Notifies of false input which is very likely to be the result of a false touch in the user's
+ * pocket or by the user's face while holding their device up to their ear.
+ */
+ fun onFalseUserInput() {
+ falsingInteractor.updateFalseConfidence(
+ FalsingClassifier.Result.falsed(
+ /* confidence= */ 0.7,
+ /* context= */ javaClass.simpleName,
+ /* reason= */ "empty pattern input",
+ )
+ )
+ }
+
/**
* Either shows the bouncer or unlocks the device, if the bouncer doesn't need to be shown.
*
diff --git a/packages/SystemUI/src/com/android/systemui/bouncer/ui/viewmodel/PasswordBouncerViewModel.kt b/packages/SystemUI/src/com/android/systemui/bouncer/ui/viewmodel/PasswordBouncerViewModel.kt
index ca15f4e..80a41ce 100644
--- a/packages/SystemUI/src/com/android/systemui/bouncer/ui/viewmodel/PasswordBouncerViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/bouncer/ui/viewmodel/PasswordBouncerViewModel.kt
@@ -34,6 +34,7 @@
) {
private val _password = MutableStateFlow("")
+
/** The password entered so far. */
val password: StateFlow<String> = _password.asStateFlow()
@@ -48,6 +49,10 @@
interactor.clearMessage()
}
+ if (password.isNotEmpty()) {
+ interactor.onIntentionalUserInput()
+ }
+
_password.value = password
}
diff --git a/packages/SystemUI/src/com/android/systemui/bouncer/ui/viewmodel/PatternBouncerViewModel.kt b/packages/SystemUI/src/com/android/systemui/bouncer/ui/viewmodel/PatternBouncerViewModel.kt
index 4425f9f..85eaf0b 100644
--- a/packages/SystemUI/src/com/android/systemui/bouncer/ui/viewmodel/PatternBouncerViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/bouncer/ui/viewmodel/PatternBouncerViewModel.kt
@@ -46,10 +46,12 @@
/** The number of columns in the dot grid. */
val columnCount = 3
+
/** The number of rows in the dot grid. */
val rowCount = 3
private val _selectedDots = MutableStateFlow<LinkedHashSet<PatternDotViewModel>>(linkedSetOf())
+
/** The dots that were selected by the user, in the order of selection. */
val selectedDots: StateFlow<List<PatternDotViewModel>> =
_selectedDots
@@ -61,10 +63,12 @@
)
private val _currentDot = MutableStateFlow<PatternDotViewModel?>(null)
+
/** The most-recently selected dot that the user selected. */
val currentDot: StateFlow<PatternDotViewModel?> = _currentDot.asStateFlow()
private val _dots = MutableStateFlow(defaultDots())
+
/** All dots on the grid. */
val dots: StateFlow<List<PatternDotViewModel>> = _dots.asStateFlow()
@@ -76,6 +80,11 @@
interactor.resetMessage()
}
+ /** Notifies that the user has placed down a pointer, not necessarily dragging just yet. */
+ fun onDown() {
+ interactor.onDown()
+ }
+
/** Notifies that the user has started a drag gesture across the dot grid. */
fun onDragStart() {
interactor.clearMessage()
@@ -124,11 +133,13 @@
dot =
PatternDotViewModel(
x =
- if (hitDot.x > dot.x) dot.x + 1
- else if (hitDot.x < dot.x) dot.x - 1 else dot.x,
+ if (hitDot.x > dot.x) {
+ dot.x + 1
+ } else if (hitDot.x < dot.x) dot.x - 1 else dot.x,
y =
- if (hitDot.y > dot.y) dot.y + 1
- else if (hitDot.y < dot.y) dot.y - 1 else dot.y,
+ if (hitDot.y > dot.y) {
+ dot.y + 1
+ } else if (hitDot.y < dot.y) dot.y - 1 else dot.y,
)
}
}
@@ -148,6 +159,12 @@
/** Notifies that the user has ended the drag gesture across the dot grid. */
fun onDragEnd() {
val pattern = _selectedDots.value.map { it.toCoordinate() }
+
+ if (pattern.size == 1) {
+ // Single dot patterns are treated as erroneous/false taps:
+ interactor.onFalseUserInput()
+ }
+
_dots.value = defaultDots()
_currentDot.value = null
_selectedDots.value = linkedSetOf()
diff --git a/packages/SystemUI/src/com/android/systemui/bouncer/ui/viewmodel/PinBouncerViewModel.kt b/packages/SystemUI/src/com/android/systemui/bouncer/ui/viewmodel/PinBouncerViewModel.kt
index 844cf02..ebf939b 100644
--- a/packages/SystemUI/src/com/android/systemui/bouncer/ui/viewmodel/PinBouncerViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/bouncer/ui/viewmodel/PinBouncerViewModel.kt
@@ -88,6 +88,11 @@
interactor.resetMessage()
}
+ /** Notifies that the user has placed down a pointer. */
+ fun onDown() {
+ interactor.onDown()
+ }
+
/** Notifies that the user clicked on a PIN button with the given digit value. */
fun onPinButtonClicked(input: Int) {
val pinInput = mutablePinInput.value
@@ -95,6 +100,8 @@
interactor.clearMessage()
}
+ interactor.onIntentionalUserInput()
+
mutablePinInput.value = pinInput.append(input)
tryAuthenticate(useAutoConfirm = true)
}
@@ -148,8 +155,10 @@
enum class ActionButtonAppearance {
/** Button must not be shown. */
Hidden,
+
/** Button is shown, but with no background to make it less prominent. */
Subtle,
+
/** Button is shown. */
Shown,
}
diff --git a/packages/SystemUI/src/com/android/systemui/camera/CameraGestureHelper.kt b/packages/SystemUI/src/com/android/systemui/camera/CameraGestureHelper.kt
index 4227a7a..b268095 100644
--- a/packages/SystemUI/src/com/android/systemui/camera/CameraGestureHelper.kt
+++ b/packages/SystemUI/src/com/android/systemui/camera/CameraGestureHelper.kt
@@ -29,12 +29,14 @@
import android.view.WindowManager
import com.android.keyguard.KeyguardUpdateMonitor
import com.android.systemui.ActivityIntentHelper
+import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Main
import com.android.systemui.plugins.ActivityStarter
import com.android.systemui.settings.UserTracker
import com.android.systemui.shared.system.ActivityManagerKt.isInForeground
import com.android.systemui.statusbar.StatusBarState
import com.android.systemui.statusbar.phone.CentralSurfaces
+import com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager
import com.android.systemui.statusbar.policy.KeyguardStateController
import java.util.concurrent.Executor
import javax.inject.Inject
@@ -43,10 +45,12 @@
* Helps with handling camera-related gestures (for example, double-tap the power button to launch
* the camera).
*/
+@SysUISingleton
class CameraGestureHelper @Inject constructor(
private val context: Context,
private val centralSurfaces: CentralSurfaces,
private val keyguardStateController: KeyguardStateController,
+ private val statusBarKeyguardViewManager: StatusBarKeyguardViewManager,
private val packageManager: PackageManager,
private val activityManager: ActivityManager,
private val activityStarter: ActivityStarter,
@@ -133,7 +137,7 @@
centralSurfaces.startLaunchTransitionTimeout()
// Call this to make sure the keyguard is ready to be dismissed once the next intent is
// handled by the OS (in our case it is the activity we started right above)
- centralSurfaces.readyForKeyguardDone()
+ statusBarKeyguardViewManager.readyForKeyguardDone()
}
/**
diff --git a/packages/SystemUI/src/com/android/systemui/classifier/FalsingCollector.java b/packages/SystemUI/src/com/android/systemui/classifier/FalsingCollector.java
index 08e1e9a..f77f989 100644
--- a/packages/SystemUI/src/com/android/systemui/classifier/FalsingCollector.java
+++ b/packages/SystemUI/src/com/android/systemui/classifier/FalsingCollector.java
@@ -29,78 +29,21 @@
void setShowingAod(boolean showingAod);
/** */
- void onNotificationStartDraggingDown();
-
- /** */
- void onNotificationStopDraggingDown();
-
- /** */
- void setNotificationExpanded();
-
- /** */
- void onQsDown();
-
- /** */
boolean shouldEnforceBouncer();
/** */
- void onTrackingStarted(boolean secure);
-
- /** */
- void onTrackingStopped();
-
- /** */
- void onLeftAffordanceOn();
-
- /** */
- void onCameraOn();
-
- /** */
- void onAffordanceSwipingStarted(boolean rightCorner);
-
- /** */
- void onAffordanceSwipingAborted();
-
- /** */
- void onStartExpandingFromPulse();
-
- /** */
- void onExpansionFromPulseStopped();
-
- /** */
void onScreenOnFromTouch();
/** */
boolean isReportingEnabled();
/** */
- void onUnlockHintStarted();
-
- /** */
- void onCameraHintStarted();
-
- /** */
- void onLeftAffordanceHintStarted();
-
- /** */
void onScreenTurningOn();
/** */
void onScreenOff();
/** */
- void onNotificationStopDismissing();
-
- /** */
- void onNotificationDismissed();
-
- /** */
- void onNotificationStartDismissing();
-
- /** */
- void onNotificationDoubleTap(boolean accepted, float dx, float dy);
-
- /** */
void onBouncerShown();
/** */
diff --git a/packages/SystemUI/src/com/android/systemui/classifier/FalsingCollectorActual.kt b/packages/SystemUI/src/com/android/systemui/classifier/FalsingCollectorActual.kt
new file mode 100644
index 0000000..3eaad1f
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/classifier/FalsingCollectorActual.kt
@@ -0,0 +1,24 @@
+/*
+ * 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.
+ */
+
+package com.android.systemui.classifier
+
+import javax.inject.Qualifier
+
+@Qualifier
+@MustBeDocumented
+@Retention(AnnotationRetention.RUNTIME)
+annotation class FalsingCollectorActual
diff --git a/packages/SystemUI/src/com/android/systemui/classifier/FalsingCollectorFake.java b/packages/SystemUI/src/com/android/systemui/classifier/FalsingCollectorFake.java
index f335d1d..c0ee71c 100644
--- a/packages/SystemUI/src/com/android/systemui/classifier/FalsingCollectorFake.java
+++ b/packages/SystemUI/src/com/android/systemui/classifier/FalsingCollectorFake.java
@@ -29,59 +29,11 @@
}
@Override
- public void onNotificationStartDraggingDown() {
- }
-
- @Override
- public void onNotificationStopDraggingDown() {
- }
-
- @Override
- public void setNotificationExpanded() {
- }
-
- @Override
- public void onQsDown() {
- }
-
- @Override
public boolean shouldEnforceBouncer() {
return false;
}
@Override
- public void onTrackingStarted(boolean secure) {
- }
-
- @Override
- public void onTrackingStopped() {
- }
-
- @Override
- public void onLeftAffordanceOn() {
- }
-
- @Override
- public void onCameraOn() {
- }
-
- @Override
- public void onAffordanceSwipingStarted(boolean rightCorner) {
- }
-
- @Override
- public void onAffordanceSwipingAborted() {
- }
-
- @Override
- public void onStartExpandingFromPulse() {
- }
-
- @Override
- public void onExpansionFromPulseStopped() {
- }
-
- @Override
public void onScreenOnFromTouch() {
}
@@ -91,18 +43,6 @@
}
@Override
- public void onUnlockHintStarted() {
- }
-
- @Override
- public void onCameraHintStarted() {
- }
-
- @Override
- public void onLeftAffordanceHintStarted() {
- }
-
- @Override
public void onScreenTurningOn() {
}
@@ -111,22 +51,6 @@
}
@Override
- public void onNotificationStopDismissing() {
- }
-
- @Override
- public void onNotificationDismissed() {
- }
-
- @Override
- public void onNotificationStartDismissing() {
- }
-
- @Override
- public void onNotificationDoubleTap(boolean accepted, float dx, float dy) {
- }
-
- @Override
public void onBouncerShown() {
}
diff --git a/packages/SystemUI/src/com/android/systemui/classifier/FalsingCollectorImpl.java b/packages/SystemUI/src/com/android/systemui/classifier/FalsingCollectorImpl.java
index 6a021f6..39c01f7 100644
--- a/packages/SystemUI/src/com/android/systemui/classifier/FalsingCollectorImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/classifier/FalsingCollectorImpl.java
@@ -103,10 +103,6 @@
private final BatteryStateChangeCallback mBatteryListener = new BatteryStateChangeCallback() {
@Override
- public void onBatteryLevelChanged(int level, boolean pluggedIn, boolean charging) {
- }
-
- @Override
public void onWirelessChargingChanged(boolean isWirelessCharging) {
if (isWirelessCharging || mDockManager.isDocked()) {
mProximitySensor.pause();
@@ -169,34 +165,21 @@
@Override
public void onSuccessfulUnlock() {
+ logDebug("REAL: onSuccessfulUnlock");
mFalsingManager.onSuccessfulUnlock();
sessionEnd();
}
@Override
public void setShowingAod(boolean showingAod) {
+ logDebug("REAL: setShowingAod(" + showingAod + ")");
mShowingAod = showingAod;
updateSessionActive();
}
- @Override
- public void onNotificationStartDraggingDown() {
- }
-
- @Override
- public void onNotificationStopDraggingDown() {
- }
-
- @Override
- public void setNotificationExpanded() {
- }
-
- @Override
- public void onQsDown() {
- }
-
@VisibleForTesting
void onQsExpansionChanged(Boolean expanded) {
+ logDebug("REAL: onQsExpansionChanged(" + expanded + ")");
if (expanded) {
unregisterSensors();
} else if (mSessionStarted) {
@@ -210,39 +193,8 @@
}
@Override
- public void onTrackingStarted(boolean secure) {
- }
-
- @Override
- public void onTrackingStopped() {
- }
-
- @Override
- public void onLeftAffordanceOn() {
- }
-
- @Override
- public void onCameraOn() {
- }
-
- @Override
- public void onAffordanceSwipingStarted(boolean rightCorner) {
- }
-
- @Override
- public void onAffordanceSwipingAborted() {
- }
-
- @Override
- public void onStartExpandingFromPulse() {
- }
-
- @Override
- public void onExpansionFromPulseStopped() {
- }
-
- @Override
public void onScreenOnFromTouch() {
+ logDebug("REAL: onScreenOnFromTouch");
onScreenTurningOn();
}
@@ -252,52 +204,28 @@
}
@Override
- public void onUnlockHintStarted() {
- }
-
- @Override
- public void onCameraHintStarted() {
- }
-
- @Override
- public void onLeftAffordanceHintStarted() {
- }
-
- @Override
public void onScreenTurningOn() {
+ logDebug("REAL: onScreenTurningOn");
mScreenOn = true;
updateSessionActive();
}
@Override
public void onScreenOff() {
+ logDebug("REAL: onScreenOff");
mScreenOn = false;
updateSessionActive();
}
@Override
- public void onNotificationStopDismissing() {
- }
-
- @Override
- public void onNotificationDismissed() {
- }
-
- @Override
- public void onNotificationStartDismissing() {
- }
-
- @Override
- public void onNotificationDoubleTap(boolean accepted, float dx, float dy) {
- }
-
- @Override
public void onBouncerShown() {
+ logDebug("REAL: onBouncerShown");
unregisterSensors();
}
@Override
public void onBouncerHidden() {
+ logDebug("REAL: onBouncerHidden");
if (mSessionStarted) {
registerSensors();
}
@@ -305,6 +233,7 @@
@Override
public void onTouchEvent(MotionEvent ev) {
+ logDebug("REAL: onTouchEvent(" + ev.getActionMasked() + ")");
if (!mKeyguardStateController.isShowing()) {
avoidGesture();
return;
@@ -334,6 +263,7 @@
@Override
public void onMotionEventComplete() {
+ logDebug("REAL: onMotionEventComplete");
// We must delay processing the completion because of the way Android handles click events.
// It generally delays executing them immediately, instead choosing to give the UI a chance
// to respond to touch events before acknowledging the click. As such, we must also delay,
@@ -350,6 +280,7 @@
@Override
public void avoidGesture() {
+ logDebug("REAL: avoidGesture");
mAvoidGesture = true;
if (mPendingDownEvent != null) {
mPendingDownEvent.recycle();
@@ -359,6 +290,7 @@
@Override
public void cleanup() {
+ logDebug("REAL: cleanup");
unregisterSensors();
mKeyguardUpdateMonitor.removeCallback(mKeyguardUpdateCallback);
mStatusBarStateController.removeCallback(mStatusBarStateListener);
@@ -368,11 +300,13 @@
@Override
public void updateFalseConfidence(FalsingClassifier.Result result) {
+ logDebug("REAL: updateFalseConfidence(" + result.isFalse() + ")");
mHistoryTracker.addResults(Collections.singleton(result), mSystemClock.uptimeMillis());
}
@Override
public void onA11yAction() {
+ logDebug("REAL: onA11yAction");
if (mPendingDownEvent != null) {
mPendingDownEvent.recycle();
mPendingDownEvent = null;
@@ -427,17 +361,13 @@
static void logDebug(String msg) {
- logDebug(msg, null);
- }
-
- static void logDebug(String msg, Throwable throwable) {
if (DEBUG) {
- Log.d(TAG, msg, throwable);
+ logDebug(msg);
}
}
private static class ProximityEventImpl implements FalsingManager.ProximityEvent {
- private ThresholdSensorEvent mThresholdSensorEvent;
+ private final ThresholdSensorEvent mThresholdSensorEvent;
ProximityEventImpl(ThresholdSensorEvent thresholdSensorEvent) {
mThresholdSensorEvent = thresholdSensorEvent;
diff --git a/packages/SystemUI/src/com/android/systemui/classifier/FalsingCollectorNoOp.kt b/packages/SystemUI/src/com/android/systemui/classifier/FalsingCollectorNoOp.kt
new file mode 100644
index 0000000..e5b404f
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/classifier/FalsingCollectorNoOp.kt
@@ -0,0 +1,81 @@
+/*
+ * 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.
+ */
+
+package com.android.systemui.classifier
+
+import android.view.MotionEvent
+import com.android.systemui.classifier.FalsingCollectorImpl.logDebug
+import com.android.systemui.dagger.SysUISingleton
+import javax.inject.Inject
+
+@SysUISingleton
+class FalsingCollectorNoOp @Inject constructor() : FalsingCollector {
+ override fun onSuccessfulUnlock() {
+ logDebug("NOOP: onSuccessfulUnlock")
+ }
+
+ override fun setShowingAod(showingAod: Boolean) {
+ logDebug("NOOP: setShowingAod($showingAod)")
+ }
+
+ override fun shouldEnforceBouncer(): Boolean = false
+
+ override fun onScreenOnFromTouch() {
+ logDebug("NOOP: onScreenOnFromTouch")
+ }
+
+ override fun isReportingEnabled(): Boolean = false
+
+ override fun onScreenTurningOn() {
+ logDebug("NOOP: onScreenTurningOn")
+ }
+
+ override fun onScreenOff() {
+ logDebug("NOOP: onScreenOff")
+ }
+
+ override fun onBouncerShown() {
+ logDebug("NOOP: onBouncerShown")
+ }
+
+ override fun onBouncerHidden() {
+ logDebug("NOOP: onBouncerHidden")
+ }
+
+ override fun onTouchEvent(ev: MotionEvent) {
+ logDebug("NOOP: onTouchEvent(${ev.actionMasked})")
+ }
+
+ override fun onMotionEventComplete() {
+ logDebug("NOOP: onMotionEventComplete")
+ }
+
+ override fun avoidGesture() {
+ logDebug("NOOP: avoidGesture")
+ }
+
+ override fun cleanup() {
+ logDebug("NOOP: cleanup")
+ }
+
+ override fun updateFalseConfidence(result: FalsingClassifier.Result) {
+ logDebug("NOOP: updateFalseConfidence(${result.isFalse})")
+ }
+
+ override fun onA11yAction() {
+ logDebug("NOOP: onA11yAction")
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/classifier/FalsingModule.java b/packages/SystemUI/src/com/android/systemui/classifier/FalsingModule.java
index c7f3b2d..3195d09 100644
--- a/packages/SystemUI/src/com/android/systemui/classifier/FalsingModule.java
+++ b/packages/SystemUI/src/com/android/systemui/classifier/FalsingModule.java
@@ -22,19 +22,21 @@
import com.android.systemui.R;
import com.android.systemui.dagger.SysUISingleton;
import com.android.systemui.dagger.qualifiers.Main;
+import com.android.systemui.flags.FeatureFlagsClassic;
+import com.android.systemui.flags.Flags;
import com.android.systemui.statusbar.phone.NotificationTapHelper;
+import dagger.Binds;
+import dagger.Module;
+import dagger.Provides;
+import dagger.multibindings.ElementsIntoSet;
+
import java.util.Arrays;
import java.util.HashSet;
import java.util.Set;
import javax.inject.Named;
-import dagger.Binds;
-import dagger.Module;
-import dagger.Provides;
-import dagger.multibindings.ElementsIntoSet;
-
/** Dagger Module for Falsing. */
@Module
public interface FalsingModule {
@@ -45,10 +47,20 @@
String DOUBLE_TAP_TIMEOUT_MS = "falsing_double_tap_timeout_ms";
String IS_FOLDABLE_DEVICE = "falsing_foldable_device";
- /** */
- @Binds
+ /** Provides the actual {@link FalsingCollector} if the scene container feature is off. */
+ @Provides
@SysUISingleton
- FalsingCollector bindsFalsingCollector(FalsingCollectorImpl impl);
+ static FalsingCollector providesFalsingCollectorLegacy(
+ FalsingCollectorImpl impl,
+ FalsingCollectorNoOp noOp,
+ FeatureFlagsClassic featureFlags) {
+ return featureFlags.isEnabled(Flags.SCENE_CONTAINER) ? noOp : impl;
+ }
+
+ /** Provides the actual {@link FalsingCollector}. */
+ @Binds
+ @FalsingCollectorActual
+ FalsingCollector bindsFalsingCollectorActual(FalsingCollectorImpl impl);
/** */
@Provides
diff --git a/packages/SystemUI/src/com/android/systemui/classifier/domain/interactor/FalsingInteractor.kt b/packages/SystemUI/src/com/android/systemui/classifier/domain/interactor/FalsingInteractor.kt
new file mode 100644
index 0000000..2e861c3
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/classifier/domain/interactor/FalsingInteractor.kt
@@ -0,0 +1,65 @@
+/*
+ * 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.
+ */
+
+package com.android.systemui.classifier.domain.interactor
+
+import android.view.MotionEvent
+import com.android.systemui.classifier.FalsingClassifier
+import com.android.systemui.classifier.FalsingCollector
+import com.android.systemui.classifier.FalsingCollectorActual
+import com.android.systemui.dagger.SysUISingleton
+import javax.inject.Inject
+
+/**
+ * Exposes the subset of the [FalsingCollector] API that's required by external callers.
+ *
+ * E.g. methods of [FalsingCollector] that are not exposed by this class don't need to be invoked by
+ * external callers as they're already called by the scene framework.
+ */
+@SysUISingleton
+class FalsingInteractor
+@Inject
+constructor(
+ @FalsingCollectorActual private val collector: FalsingCollector,
+) {
+ /**
+ * Notifies of a [MotionEvent] that passed through the UI.
+ *
+ * Must call [onMotionEventComplete] when done with this event.
+ */
+ fun onTouchEvent(event: MotionEvent) = collector.onTouchEvent(event)
+
+ /**
+ * Notifies that a [MotionEvent] has finished being dispatched through the UI.
+ *
+ * Must be called after each call to [onTouchEvent].
+ */
+ fun onMotionEventComplete() = collector.onMotionEventComplete()
+
+ /**
+ * Instructs the falsing system to ignore the rest of the current input gesture; automatically
+ * resets when another gesture is started (with the next down event).
+ */
+ fun avoidGesture() = collector.avoidGesture()
+
+ /**
+ * Inserts the given [result] into the falsing system, affecting future runs of the classifier
+ * as if this was a result that had organically happened before.
+ */
+ fun updateFalseConfidence(
+ result: FalsingClassifier.Result,
+ ) = collector.updateFalseConfidence(result)
+}
diff --git a/packages/SystemUI/src/com/android/systemui/common/shared/model/SharedNotificationContainerPosition.kt b/packages/SystemUI/src/com/android/systemui/common/shared/model/SharedNotificationContainerPosition.kt
new file mode 100644
index 0000000..b2bc06f
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/common/shared/model/SharedNotificationContainerPosition.kt
@@ -0,0 +1,23 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.common.shared.model
+
+/** Positioning info for the shared notification container */
+data class SharedNotificationContainerPosition(
+ val top: Float = 0f,
+ val bottom: Float = 0f,
+)
diff --git a/packages/SystemUI/src/com/android/systemui/common/ui/data/repository/ConfigurationRepository.kt b/packages/SystemUI/src/com/android/systemui/common/ui/data/repository/ConfigurationRepository.kt
index c0d1951..b8de8d8 100644
--- a/packages/SystemUI/src/com/android/systemui/common/ui/data/repository/ConfigurationRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/common/ui/data/repository/ConfigurationRepository.kt
@@ -43,6 +43,9 @@
val scaleForResolution: Flow<Float>
fun getResolutionScale(): Float
+
+ /** Convience to context.resources.getDimensionPixelSize() */
+ fun getDimensionPixelSize(id: Int): Int
}
@ExperimentalCoroutinesApi
@@ -115,4 +118,8 @@
}
return 1f
}
+
+ override fun getDimensionPixelSize(id: Int): Int {
+ return context.resources.getDimensionPixelSize(id)
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/communal/ui/view/layout/blueprints/DefaultCommunalBlueprint.kt b/packages/SystemUI/src/com/android/systemui/communal/ui/view/layout/blueprints/DefaultCommunalBlueprint.kt
index c3369da..970b475 100644
--- a/packages/SystemUI/src/com/android/systemui/communal/ui/view/layout/blueprints/DefaultCommunalBlueprint.kt
+++ b/packages/SystemUI/src/com/android/systemui/communal/ui/view/layout/blueprints/DefaultCommunalBlueprint.kt
@@ -16,10 +16,10 @@
package com.android.systemui.communal.ui.view.layout.blueprints
-import androidx.constraintlayout.widget.ConstraintSet
import com.android.systemui.communal.ui.view.layout.sections.DefaultCommunalWidgetSection
import com.android.systemui.dagger.SysUISingleton
-import com.android.systemui.keyguard.data.repository.KeyguardBlueprint
+import com.android.systemui.keyguard.shared.model.KeyguardBlueprint
+import com.android.systemui.keyguard.shared.model.KeyguardSection
import javax.inject.Inject
/** Blueprint for communal mode. */
@@ -28,13 +28,10 @@
class DefaultCommunalBlueprint
@Inject
constructor(
- private val defaultCommunalWidgetSection: DefaultCommunalWidgetSection,
+ defaultCommunalWidgetSection: DefaultCommunalWidgetSection,
) : KeyguardBlueprint {
override val id: String = COMMUNAL
-
- override fun apply(constraintSet: ConstraintSet) {
- defaultCommunalWidgetSection.apply(constraintSet)
- }
+ override val sections: Array<KeyguardSection> = arrayOf(defaultCommunalWidgetSection)
companion object {
const val COMMUNAL = "communal"
diff --git a/packages/SystemUI/src/com/android/systemui/communal/ui/view/layout/sections/DefaultCommunalWidgetSection.kt b/packages/SystemUI/src/com/android/systemui/communal/ui/view/layout/sections/DefaultCommunalWidgetSection.kt
index b0e3132..4fb9384 100644
--- a/packages/SystemUI/src/com/android/systemui/communal/ui/view/layout/sections/DefaultCommunalWidgetSection.kt
+++ b/packages/SystemUI/src/com/android/systemui/communal/ui/view/layout/sections/DefaultCommunalWidgetSection.kt
@@ -17,18 +17,47 @@
package com.android.systemui.communal.ui.view.layout.sections
import android.view.ViewGroup.LayoutParams.WRAP_CONTENT
+import androidx.constraintlayout.widget.ConstraintLayout
import androidx.constraintlayout.widget.ConstraintSet
import androidx.constraintlayout.widget.ConstraintSet.BOTTOM
import androidx.constraintlayout.widget.ConstraintSet.END
import androidx.constraintlayout.widget.ConstraintSet.PARENT_ID
import com.android.systemui.R
-import com.android.systemui.keyguard.data.repository.KeyguardSection
+import com.android.systemui.communal.ui.adapter.CommunalWidgetViewAdapter
+import com.android.systemui.communal.ui.binder.CommunalWidgetViewBinder
+import com.android.systemui.communal.ui.viewmodel.CommunalWidgetViewModel
+import com.android.systemui.flags.FeatureFlags
+import com.android.systemui.flags.Flags
+import com.android.systemui.keyguard.domain.interactor.KeyguardBlueprintInteractor
+import com.android.systemui.keyguard.shared.model.KeyguardSection
+import com.android.systemui.keyguard.ui.view.KeyguardRootView
+import dagger.Lazy
import javax.inject.Inject
-class DefaultCommunalWidgetSection @Inject constructor() : KeyguardSection {
+class DefaultCommunalWidgetSection
+@Inject
+constructor(
+ private val featureFlags: FeatureFlags,
+ private val keyguardRootView: KeyguardRootView,
+ private val communalWidgetViewModel: CommunalWidgetViewModel,
+ private val communalWidgetViewAdapter: CommunalWidgetViewAdapter,
+ private val keyguardBlueprintInteractor: Lazy<KeyguardBlueprintInteractor>,
+) : KeyguardSection {
private val widgetAreaViewId = R.id.communal_widget_wrapper
+ override fun addViews(constraintLayout: ConstraintLayout) {
+ if (!featureFlags.isEnabled(Flags.WIDGET_ON_KEYGUARD)) {
+ return
+ }
- override fun apply(constraintSet: ConstraintSet) {
+ CommunalWidgetViewBinder.bind(
+ keyguardRootView,
+ communalWidgetViewModel,
+ communalWidgetViewAdapter,
+ keyguardBlueprintInteractor.get(),
+ )
+ }
+
+ override fun applyConstraints(constraintSet: ConstraintSet) {
constraintSet.apply {
constrainWidth(widgetAreaViewId, WRAP_CONTENT)
constrainHeight(widgetAreaViewId, WRAP_CONTENT)
diff --git a/packages/SystemUI/src/com/android/systemui/controls/ControlsServiceInfo.kt b/packages/SystemUI/src/com/android/systemui/controls/ControlsServiceInfo.kt
index d3174f1..adb0bf3 100644
--- a/packages/SystemUI/src/com/android/systemui/controls/ControlsServiceInfo.kt
+++ b/packages/SystemUI/src/com/android/systemui/controls/ControlsServiceInfo.kt
@@ -71,14 +71,9 @@
private var resolved: Boolean = false
@WorkerThread
- fun resolvePanelActivity(
- allowAllApps: Boolean = false
- ) {
+ fun resolvePanelActivity() {
if (resolved) return
resolved = true
- val validPackages = context.resources
- .getStringArray(R.array.config_controlsPreferredPackages)
- if (componentName.packageName !in validPackages && !allowAllApps) return
panelActivity = _panelActivity?.let {
val resolveInfos = mPm.queryIntentActivitiesAsUser(
Intent().setComponent(it),
diff --git a/packages/SystemUI/src/com/android/systemui/controls/dagger/ControlsComponent.kt b/packages/SystemUI/src/com/android/systemui/controls/dagger/ControlsComponent.kt
index 94e5633..88b9612 100644
--- a/packages/SystemUI/src/com/android/systemui/controls/dagger/ControlsComponent.kt
+++ b/packages/SystemUI/src/com/android/systemui/controls/dagger/ControlsComponent.kt
@@ -16,7 +16,6 @@
package com.android.systemui.controls.dagger
-import android.content.Context
import com.android.internal.widget.LockPatternUtils
import com.android.internal.widget.LockPatternUtils.StrongAuthTracker.STRONG_AUTH_REQUIRED_AFTER_BOOT
import com.android.systemui.controls.controller.ControlsController
@@ -44,10 +43,9 @@
@Inject
constructor(
@ControlsFeatureEnabled private val featureEnabled: Boolean,
- private val context: Context,
- private val lazyControlsController: Lazy<ControlsController>,
- private val lazyControlsUiController: Lazy<ControlsUiController>,
- private val lazyControlsListingController: Lazy<ControlsListingController>,
+ lazyControlsController: Lazy<ControlsController>,
+ lazyControlsUiController: Lazy<ControlsUiController>,
+ lazyControlsListingController: Lazy<ControlsListingController>,
private val lockPatternUtils: LockPatternUtils,
private val keyguardStateController: KeyguardStateController,
private val userTracker: UserTracker,
@@ -55,27 +53,25 @@
optionalControlsTileResourceConfiguration: Optional<ControlsTileResourceConfiguration>
) {
+ private val controlsController: Optional<ControlsController> =
+ optionalIf(isEnabled(), lazyControlsController)
+ private val controlsUiController: Optional<ControlsUiController> =
+ optionalIf(isEnabled(), lazyControlsUiController)
+ private val controlsListingController: Optional<ControlsListingController> =
+ optionalIf(isEnabled(), lazyControlsListingController)
+
val canShowWhileLockedSetting: StateFlow<Boolean> =
controlsSettingsRepository.canShowControlsInLockscreen
private val controlsTileResourceConfiguration: ControlsTileResourceConfiguration =
optionalControlsTileResourceConfiguration.orElse(ControlsTileResourceConfigurationImpl())
- fun getControlsController(): Optional<ControlsController> {
- return if (featureEnabled) Optional.of(lazyControlsController.get()) else Optional.empty()
- }
+ fun getControlsController(): Optional<ControlsController> = controlsController
- fun getControlsUiController(): Optional<ControlsUiController> {
- return if (featureEnabled) Optional.of(lazyControlsUiController.get()) else Optional.empty()
- }
+ fun getControlsUiController(): Optional<ControlsUiController> = controlsUiController
- fun getControlsListingController(): Optional<ControlsListingController> {
- return if (featureEnabled) {
- Optional.of(lazyControlsListingController.get())
- } else {
- Optional.empty()
- }
- }
+ fun getControlsListingController(): Optional<ControlsListingController> =
+ controlsListingController
/** @return true if controls are feature-enabled and the user has the setting enabled */
fun isEnabled() = featureEnabled
@@ -118,4 +114,11 @@
fun getTileImageId(): Int {
return controlsTileResourceConfiguration.getTileImageId()
}
+
+ private fun <T : Any> optionalIf(condition: Boolean, provider: Lazy<T>): Optional<T> =
+ if (condition) {
+ Optional.of(provider.get())
+ } else {
+ Optional.empty()
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/controls/management/ControlsListingControllerImpl.kt b/packages/SystemUI/src/com/android/systemui/controls/management/ControlsListingControllerImpl.kt
index 83bec66..74e1dc0 100644
--- a/packages/SystemUI/src/com/android/systemui/controls/management/ControlsListingControllerImpl.kt
+++ b/packages/SystemUI/src/com/android/systemui/controls/management/ControlsListingControllerImpl.kt
@@ -33,7 +33,6 @@
import com.android.systemui.dagger.qualifiers.Background
import com.android.systemui.dump.DumpManager
import com.android.systemui.flags.FeatureFlags
-import com.android.systemui.flags.Flags
import com.android.systemui.settings.UserTracker
import com.android.systemui.util.ActivityTaskManagerProxy
import com.android.systemui.util.asIndenting
@@ -125,9 +124,8 @@
private fun updateServices(newServices: List<ControlsServiceInfo>) {
if (activityTaskManagerProxy.supportsMultiWindow(context)) {
- val allowAllApps = featureFlags.isEnabled(Flags.APP_PANELS_ALL_APPS_ALLOWED)
newServices.forEach {
- it.resolvePanelActivity(allowAllApps) }
+ it.resolvePanelActivity() }
}
if (newServices != availableServices) {
diff --git a/packages/SystemUI/src/com/android/systemui/dagger/SysUIComponent.java b/packages/SystemUI/src/com/android/systemui/dagger/SysUIComponent.java
index 6fdb4ca..dcacd09 100644
--- a/packages/SystemUI/src/com/android/systemui/dagger/SysUIComponent.java
+++ b/packages/SystemUI/src/com/android/systemui/dagger/SysUIComponent.java
@@ -22,6 +22,7 @@
import com.android.systemui.InitController;
import com.android.systemui.SystemUIAppComponentFactoryBase;
import com.android.systemui.dagger.qualifiers.PerUser;
+import com.android.systemui.display.ui.viewmodel.ConnectingDisplayViewModel;
import com.android.systemui.dump.DumpManager;
import com.android.systemui.keyguard.KeyguardSliceProvider;
import com.android.systemui.media.muteawait.MediaMuteAwaitConnectionCli;
@@ -140,6 +141,7 @@
getMediaMuteAwaitConnectionCli();
getNearbyMediaDevicesManager();
getUnfoldLatencyTracker().init();
+ getConnectingDisplayViewModel().init();
getFoldStateLoggingProvider().ifPresent(FoldStateLoggingProvider::init);
getFoldStateLogger().ifPresent(FoldStateLogger::init);
getUnfoldTransitionProgressProvider().ifPresent((progressProvider) ->
@@ -229,6 +231,11 @@
NearbyMediaDevicesManager getNearbyMediaDevicesManager();
/**
+ * Creates a ConnectingDisplayViewModel
+ */
+ ConnectingDisplayViewModel getConnectingDisplayViewModel();
+
+ /**
* Returns {@link CoreStartable}s that should be started with the application.
*/
Map<Class<?>, Provider<CoreStartable>> getStartables();
diff --git a/packages/SystemUI/src/com/android/systemui/dagger/SystemUICoreStartableModule.kt b/packages/SystemUI/src/com/android/systemui/dagger/SystemUICoreStartableModule.kt
index 1b2a9eb..7ce7ce94 100644
--- a/packages/SystemUI/src/com/android/systemui/dagger/SystemUICoreStartableModule.kt
+++ b/packages/SystemUI/src/com/android/systemui/dagger/SystemUICoreStartableModule.kt
@@ -23,6 +23,7 @@
import com.android.systemui.SliceBroadcastRelayHandler
import com.android.systemui.accessibility.SystemActions
import com.android.systemui.accessibility.WindowMagnification
+import com.android.systemui.back.domain.interactor.BackActionInteractor
import com.android.systemui.biometrics.AuthController
import com.android.systemui.biometrics.BiometricNotificationService
import com.android.systemui.clipboardoverlay.ClipboardListener
@@ -346,4 +347,9 @@
abstract fun bindStatusBarHeadsUpChangeListener(
impl: StatusBarHeadsUpChangeListener
): CoreStartable
+
+ @Binds
+ @IntoMap
+ @ClassKey(BackActionInteractor::class)
+ abstract fun bindBackActionInteractor(impl: BackActionInteractor): CoreStartable
}
diff --git a/packages/SystemUI/src/com/android/systemui/display/data/repository/DisplayRepository.kt b/packages/SystemUI/src/com/android/systemui/display/data/repository/DisplayRepository.kt
index b18f7bf..e7bbf97 100644
--- a/packages/SystemUI/src/com/android/systemui/display/data/repository/DisplayRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/display/data/repository/DisplayRepository.kt
@@ -22,6 +22,7 @@
import android.hardware.display.DisplayManager.EVENT_FLAG_DISPLAY_CHANGED
import android.hardware.display.DisplayManager.EVENT_FLAG_DISPLAY_REMOVED
import android.os.Handler
+import android.util.Log
import android.view.Display
import com.android.systemui.common.coroutine.ConflatedCallbackFlow.conflatedCallbackFlow
import com.android.systemui.dagger.SysUISingleton
@@ -34,6 +35,7 @@
import kotlinx.coroutines.channels.awaitClose
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.SharingStarted
+import kotlinx.coroutines.flow.distinctUntilChanged
import kotlinx.coroutines.flow.flowOn
import kotlinx.coroutines.flow.stateIn
@@ -41,6 +43,13 @@
interface DisplayRepository {
/** Provides a nullable set of displays. */
val displays: Flow<Set<Display>>
+
+ /**
+ * Pending display id that can be enabled/disabled.
+ *
+ * When `null`, it means there is no pending display waiting to be enabled.
+ */
+ val pendingDisplayId: Flow<Int?>
}
@SysUISingleton
@@ -85,8 +94,60 @@
initialValue = getDisplays()
)
- fun getDisplays(): Set<Display> =
+ private fun getDisplays(): Set<Display> =
traceSection("DisplayRepository#getDisplays()") {
displayManager.displays?.toSet() ?: emptySet()
}
+
+ override val pendingDisplayId: Flow<Int?> =
+ conflatedCallbackFlow {
+ val callback =
+ object : DisplayConnectionListener {
+ private val pendingIds = mutableSetOf<Int>()
+ override fun onDisplayConnected(id: Int) {
+ pendingIds += id
+ trySend(id)
+ }
+
+ override fun onDisplayDisconnected(id: Int) {
+ if (id in pendingIds) {
+ pendingIds -= id
+ trySend(null)
+ } else {
+ Log.e(
+ TAG,
+ "onDisplayDisconnected received for unknown display. " +
+ "id=$id, knownIds=$pendingIds"
+ )
+ }
+ }
+ }
+ displayManager.registerDisplayListener(
+ callback,
+ backgroundHandler,
+ DisplayManager.EVENT_FLAG_DISPLAY_CONNECTION_CHANGED,
+ )
+ awaitClose { displayManager.unregisterDisplayListener(callback) }
+ }
+ .distinctUntilChanged()
+ .flowOn(backgroundCoroutineDispatcher)
+ .stateIn(
+ applicationScope,
+ started = SharingStarted.WhileSubscribed(),
+ initialValue = null
+ )
+
+ private companion object {
+ const val TAG = "DisplayRepository"
+ }
+}
+
+/** Used to provide default implementations for all methods. */
+private interface DisplayConnectionListener : DisplayListener {
+
+ override fun onDisplayConnected(id: Int) {}
+ override fun onDisplayDisconnected(id: Int) {}
+ override fun onDisplayAdded(id: Int) {}
+ override fun onDisplayRemoved(id: Int) {}
+ override fun onDisplayChanged(id: Int) {}
}
diff --git a/packages/SystemUI/src/com/android/systemui/display/domain/interactor/ConnectedDisplayInteractor.kt b/packages/SystemUI/src/com/android/systemui/display/domain/interactor/ConnectedDisplayInteractor.kt
index 4b957c7..ef6fa26 100644
--- a/packages/SystemUI/src/com/android/systemui/display/domain/interactor/ConnectedDisplayInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/display/domain/interactor/ConnectedDisplayInteractor.kt
@@ -16,12 +16,17 @@
package com.android.systemui.display.domain.interactor
+import android.hardware.display.DisplayManager
import android.view.Display
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.display.data.repository.DisplayRepository
+import com.android.systemui.display.domain.interactor.ConnectedDisplayInteractor.PendingDisplay
import com.android.systemui.display.domain.interactor.ConnectedDisplayInteractor.State
+import com.android.systemui.keyguard.data.repository.KeyguardRepository
+import com.android.systemui.util.traceSection
import javax.inject.Inject
import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.combine
import kotlinx.coroutines.flow.distinctUntilChanged
import kotlinx.coroutines.flow.map
@@ -37,18 +42,32 @@
*/
val connectedDisplayState: Flow<State>
+ /** Pending display that can be enabled to be used by the system. */
+ val pendingDisplay: Flow<PendingDisplay?>
+
/** Possible connected display state. */
enum class State {
DISCONNECTED,
CONNECTED,
CONNECTED_SECURE,
}
+
+ /** Represents a connected display that has not been enabled yet. */
+ interface PendingDisplay {
+ /** Enables the display, making it available to the system. */
+ fun enable()
+
+ /** Disables the display, making it unavailable to the system. */
+ fun disable()
+ }
}
@SysUISingleton
class ConnectedDisplayInteractorImpl
@Inject
constructor(
+ private val displayManager: DisplayManager,
+ keyguardRepository: KeyguardRepository,
displayRepository: DisplayRepository,
) : ConnectedDisplayInteractor {
@@ -70,4 +89,31 @@
}
}
.distinctUntilChanged()
+
+ // Provides the pending display only if the lockscreen is unlocked
+ override val pendingDisplay: Flow<PendingDisplay?> =
+ displayRepository.pendingDisplayId.combine(keyguardRepository.isKeyguardUnlocked) {
+ pendingDisplayId,
+ keyguardUnlocked ->
+ if (pendingDisplayId != null && keyguardUnlocked) {
+ pendingDisplayId.toPendingDisplay()
+ } else {
+ null
+ }
+ }
+
+ private fun Int.toPendingDisplay() =
+ object : PendingDisplay {
+ val id = this@toPendingDisplay
+ override fun enable() {
+ traceSection("DisplayRepository#enable($id)") {
+ displayManager.enableConnectedDisplay(id)
+ }
+ }
+ override fun disable() {
+ traceSection("DisplayRepository#enable($id)") {
+ displayManager.disableConnectedDisplay(id)
+ }
+ }
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/display/ui/view/MirroringConfirmationDialog.kt b/packages/SystemUI/src/com/android/systemui/display/ui/view/MirroringConfirmationDialog.kt
new file mode 100644
index 0000000..174c6ff
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/display/ui/view/MirroringConfirmationDialog.kt
@@ -0,0 +1,54 @@
+/*
+ * 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.display.ui.view
+
+import android.app.Dialog
+import android.content.Context
+import android.os.Bundle
+import android.view.Gravity
+import android.view.View
+import android.view.WindowManager
+import android.widget.TextView
+import com.android.systemui.R
+
+/** Dialog used to decide what to do with a connected display. */
+class MirroringConfirmationDialog(
+ context: Context,
+ private val onStartMirroringClickListener: View.OnClickListener,
+ private val onDismissClickListener: View.OnClickListener,
+) : Dialog(context, R.style.Theme_SystemUI_Dialog) {
+
+ private lateinit var mirrorButton: TextView
+ private lateinit var dismissButton: TextView
+ override fun onCreate(savedInstanceState: Bundle?) {
+ super.onCreate(savedInstanceState)
+ window?.apply {
+ setType(WindowManager.LayoutParams.TYPE_STATUS_BAR_SUB_PANEL)
+ addPrivateFlags(WindowManager.LayoutParams.SYSTEM_FLAG_SHOW_FOR_ALL_USERS)
+ setGravity(Gravity.BOTTOM)
+ }
+ setContentView(R.layout.connected_display_dialog)
+ setCanceledOnTouchOutside(true)
+ mirrorButton =
+ requireViewById<TextView>(R.id.enable_display).apply {
+ setOnClickListener(onStartMirroringClickListener)
+ }
+ dismissButton =
+ requireViewById<TextView>(R.id.cancel).apply {
+ setOnClickListener(onDismissClickListener)
+ }
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/display/ui/viewmodel/ConnectingDisplayViewModel.kt b/packages/SystemUI/src/com/android/systemui/display/ui/viewmodel/ConnectingDisplayViewModel.kt
new file mode 100644
index 0000000..ece33b7
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/display/ui/viewmodel/ConnectingDisplayViewModel.kt
@@ -0,0 +1,76 @@
+/*
+ * 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.display.ui.viewmodel
+
+import android.app.Dialog
+import android.content.Context
+import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.dagger.qualifiers.Application
+import com.android.systemui.display.domain.interactor.ConnectedDisplayInteractor
+import com.android.systemui.display.domain.interactor.ConnectedDisplayInteractor.PendingDisplay
+import com.android.systemui.display.ui.view.MirroringConfirmationDialog
+import javax.inject.Inject
+import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.flow.launchIn
+import kotlinx.coroutines.flow.onEach
+
+/**
+ * Shows/hides a dialog to allow the user to decide whether to use the external display for
+ * mirroring.
+ */
+@SysUISingleton
+class ConnectingDisplayViewModel
+@Inject
+constructor(
+ private val context: Context,
+ private val connectedDisplayInteractor: ConnectedDisplayInteractor,
+ @Application private val scope: CoroutineScope,
+) {
+
+ private var dialog: Dialog? = null
+
+ /** Starts listening for pending displays. */
+ fun init() {
+ connectedDisplayInteractor.pendingDisplay
+ .onEach { pendingDisplay ->
+ if (pendingDisplay == null) {
+ hideDialog()
+ } else {
+ showDialog(pendingDisplay)
+ }
+ }
+ .launchIn(scope)
+ }
+
+ private fun showDialog(pendingDisplay: PendingDisplay) {
+ hideDialog()
+ dialog =
+ MirroringConfirmationDialog(
+ context,
+ onStartMirroringClickListener = {
+ pendingDisplay.enable()
+ hideDialog()
+ },
+ onDismissClickListener = { hideDialog() }
+ )
+ .apply { show() }
+ }
+
+ private fun hideDialog() {
+ dialog?.hide()
+ dialog = null
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/flags/Flags.kt b/packages/SystemUI/src/com/android/systemui/flags/Flags.kt
index dfe6711..74b9b09 100644
--- a/packages/SystemUI/src/com/android/systemui/flags/Flags.kt
+++ b/packages/SystemUI/src/com/android/systemui/flags/Flags.kt
@@ -39,6 +39,10 @@
@JvmField val TEAMFOOD = unreleasedFlag("teamfood")
// 100 - notification
+ // TODO(b/297792660): Tracking Bug
+ val ADD_TRANSIENT_HUN_IN_STACK_STATE_ANIMATOR =
+ unreleasedFlag("add_transient_hun_in_stack_state_animator", teamfood = false)
+
// TODO(b/254512751): Tracking Bug
val NOTIFICATION_PIPELINE_DEVELOPER_LOGGING =
unreleasedFlag("notification_pipeline_developer_logging")
@@ -174,7 +178,7 @@
/** Flag to control the migration of face auth to modern architecture. */
// TODO(b/262838215): Tracking bug
- @JvmField val FACE_AUTH_REFACTOR = unreleasedFlag("face_auth_refactor")
+ @JvmField val FACE_AUTH_REFACTOR = unreleasedFlag("face_auth_refactor", teamfood = true)
/** Flag to control the revamp of keyguard biometrics progress animation */
// TODO(b/244313043): Tracking bug
@@ -204,6 +208,10 @@
@JvmField
val LOCK_SCREEN_LONG_PRESS_ENABLED = releasedFlag("lock_screen_long_press_enabled")
+ /** Inflate and bind views upon emitting a blueprint value . */
+ // TODO(b/297365780): Tracking Bug
+ @JvmField val LAZY_INFLATE_KEYGUARD = unreleasedFlag("lazy_inflate_keyguard")
+
/** Enables UI updates for AI wallpapers in the wallpaper picker. */
// TODO(b/267722622): Tracking Bug
@JvmField val WALLPAPER_PICKER_UI_FOR_AIWP = releasedFlag("wallpaper_picker_ui_for_aiwp")
@@ -223,7 +231,7 @@
// TODO(b/291710220): Tracking bug.
@JvmField
val WALLPAPER_PICKER_PAGE_TRANSITIONS =
- unreleasedFlag("wallpaper_picker_page_transitions")
+ unreleasedFlag("wallpaper_picker_page_transitions", teamfood = true)
/** Add "Apply" button to wall paper picker's grid preview page. */
// TODO(b/294866904): Tracking bug.
@@ -370,6 +378,9 @@
// 600- status bar
+ // TODO(b/291315866): Tracking Bug
+ @JvmField val SIGNAL_CALLBACK_DEPRECATION = unreleasedFlag("signal_callback_deprecation")
+
// TODO(b/265892345): Tracking Bug
val PLUG_IN_STATUS_BAR_CHIP = releasedFlag("plug_in_status_bar_chip")
@@ -391,7 +402,7 @@
// TODO(b/290676905): Tracking Bug
val NEW_SHADE_CARRIER_GROUP_MOBILE_ICONS =
- unreleasedFlag("new_shade_carrier_group_mobile_icons")
+ unreleasedFlag("new_shade_carrier_group_mobile_icons", teamfood = true)
// 700 - dialer/calls
// TODO(b/254512734): Tracking Bug
@@ -489,11 +500,6 @@
@Keep
@JvmField
- val WM_DESKTOP_WINDOWING =
- sysPropBooleanFlag("persist.wm.debug.desktop_mode", default = false)
-
- @Keep
- @JvmField
val WM_CAPTION_ON_SHELL =
sysPropBooleanFlag("persist.wm.debug.caption_on_shell", default = true)
@@ -631,9 +637,6 @@
// 1900
@JvmField val NOTE_TASKS = releasedFlag("keycode_flag")
- // 2000 - device controls
- @JvmField val APP_PANELS_ALL_APPS_ALLOWED = releasedFlag("app_panels_all_apps_allowed")
-
// 2200 - biometrics (udfps, sfps, BiometricPrompt, etc.)
// TODO(b/259264861): Tracking Bug
@JvmField val UDFPS_NEW_TOUCH_DETECTION = releasedFlag("udfps_new_touch_detection")
@@ -654,6 +657,10 @@
val WARN_ON_BLOCKING_BINDER_TRANSACTIONS =
unreleasedFlag("warn_on_blocking_binder_transactions")
+ @JvmField
+ val COROUTINE_TRACING =
+ unreleasedFlag("coroutine_tracing")
+
// TODO(b/283071711): Tracking bug
@JvmField
val TRIM_RESOURCES_WITH_BACKGROUND_TRIM_AT_LOCK =
diff --git a/packages/SystemUI/src/com/android/systemui/graphics/ImageLoader.kt b/packages/SystemUI/src/com/android/systemui/graphics/ImageLoader.kt
index c41b5e4..2856011 100644
--- a/packages/SystemUI/src/com/android/systemui/graphics/ImageLoader.kt
+++ b/packages/SystemUI/src/com/android/systemui/graphics/ImageLoader.kt
@@ -366,6 +366,52 @@
}
}
+ /**
+ * Obtains the image size from the image header, without decoding the full image.
+ *
+ * @param icon an [Icon] representing the source of the image
+ * @return the [Size] if it could be determined from the image header, or `null` otherwise
+ */
+ suspend fun loadSize(icon: Icon, context: Context): Size? =
+ withContext(backgroundDispatcher) { loadSizeSync(icon, context) }
+
+ /**
+ * Obtains the image size from the image header, without decoding the full image.
+ *
+ * @param icon an [Icon] representing the source of the image
+ * @return the [Size] if it could be determined from the image header, or `null` otherwise
+ */
+ @WorkerThread
+ fun loadSizeSync(icon: Icon, context: Context): Size? {
+ return when (icon.type) {
+ Icon.TYPE_URI,
+ Icon.TYPE_URI_ADAPTIVE_BITMAP -> {
+ val source = ImageDecoder.createSource(context.contentResolver, icon.uri)
+ loadSizeSync(source)
+ }
+ else -> null
+ }
+ }
+
+ /**
+ * Obtains the image size from the image header, without decoding the full image.
+ *
+ * @param source [ImageDecoder.Source] of the image
+ * @return the [Size] if it could be determined from the image header, or `null` otherwise
+ */
+ @WorkerThread
+ fun loadSizeSync(source: ImageDecoder.Source): Size? {
+ return try {
+ ImageDecoder.decodeHeader(source).size
+ } catch (e: IOException) {
+ Log.w(TAG, "Failed to load source $source", e)
+ return null
+ } catch (e: DecodeException) {
+ Log.w(TAG, "Failed to decode source $source", e)
+ return null
+ }
+ }
+
companion object {
const val TAG = "ImageLoader"
@@ -452,7 +498,7 @@
* originate from other processes so we need to make sure we load them from the right
* package source.
*
- * @return [Resources] to load the icon drawble or null if icon doesn't carry a resource or
+ * @return [Resources] to load the icon drawable or null if icon doesn't carry a resource or
* the resource package couldn't be resolved.
*/
@WorkerThread
diff --git a/packages/SystemUI/src/com/android/systemui/haptics/slider/SeekableSliderEventProducer.kt b/packages/SystemUI/src/com/android/systemui/haptics/slider/SeekableSliderEventProducer.kt
new file mode 100644
index 0000000..629b361
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/haptics/slider/SeekableSliderEventProducer.kt
@@ -0,0 +1,68 @@
+/*
+ * 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.haptics.slider
+
+import android.widget.SeekBar
+import android.widget.SeekBar.OnSeekBarChangeListener
+import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.MutableStateFlow
+import kotlinx.coroutines.flow.asStateFlow
+import kotlinx.coroutines.flow.update
+
+/** An event producer for a Seekable element such as the Android [SeekBar] */
+class SeekableSliderEventProducer : SliderEventProducer, OnSeekBarChangeListener {
+
+ /** The current event reported by a SeekBar */
+ private val _currentEvent = MutableStateFlow(SliderEvent(SliderEventType.NOTHING, 0f))
+
+ override fun produceEvents(): Flow<SliderEvent> = _currentEvent.asStateFlow()
+
+ override fun onProgressChanged(seekBar: SeekBar, progress: Int, fromUser: Boolean) {
+ val eventType =
+ if (fromUser) SliderEventType.PROGRESS_CHANGE_BY_USER
+ else SliderEventType.PROGRESS_CHANGE_BY_PROGRAM
+
+ _currentEvent.value = SliderEvent(eventType, normalizeProgress(seekBar, progress))
+ }
+
+ /**
+ * Normalize the integer progress of a SeekBar to the range from 0F to 1F.
+ *
+ * @param[seekBar] The SeekBar that reports a progress.
+ * @param[progress] The integer progress of the SeekBar within its min and max values.
+ * @return The progress in the range from 0F to 1F.
+ */
+ private fun normalizeProgress(seekBar: SeekBar, progress: Int): Float {
+ if (seekBar.max == seekBar.min) {
+ return 1.0f
+ }
+ val range = seekBar.max - seekBar.min
+ return (progress - seekBar.min) / range.toFloat()
+ }
+
+ override fun onStartTrackingTouch(seekBar: SeekBar) {
+ _currentEvent.update { previousEvent ->
+ SliderEvent(SliderEventType.STARTED_TRACKING_TOUCH, previousEvent.currentProgress)
+ }
+ }
+
+ override fun onStopTrackingTouch(seekBar: SeekBar) {
+ _currentEvent.update { previousEvent ->
+ SliderEvent(SliderEventType.STOPPED_TRACKING_TOUCH, previousEvent.currentProgress)
+ }
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/haptics/slider/SliderEvent.kt b/packages/SystemUI/src/com/android/systemui/haptics/slider/SliderEvent.kt
new file mode 100644
index 0000000..1377b29
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/haptics/slider/SliderEvent.kt
@@ -0,0 +1,31 @@
+/*
+ * 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.haptics.slider
+
+import androidx.annotation.FloatRange
+
+/**
+ * An event arising from a slider.
+ *
+ * @property[type] The type of event. Must be one of [SliderEventType].
+ * @property[currentProgress] The current progress of the slider normalized to the range between 0F
+ * and 1F (inclusive).
+ */
+data class SliderEvent(
+ val type: SliderEventType,
+ @FloatRange(from = 0.0, to = 1.0) val currentProgress: Float
+)
diff --git a/packages/SystemUI/src/com/android/systemui/haptics/slider/SliderEventProducer.kt b/packages/SystemUI/src/com/android/systemui/haptics/slider/SliderEventProducer.kt
new file mode 100644
index 0000000..8b17e86
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/haptics/slider/SliderEventProducer.kt
@@ -0,0 +1,30 @@
+/*
+ * 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.haptics.slider
+
+import kotlinx.coroutines.flow.Flow
+
+/** Defines a producer of [SliderEvent] to be consumed as a [Flow] */
+interface SliderEventProducer {
+
+ /**
+ * Produce a stream of [SliderEvent]
+ *
+ * @return A [Flow] of [SliderEvent] produced from the operation of a slider.
+ */
+ fun produceEvents(): Flow<SliderEvent>
+}
diff --git a/packages/SystemUI/src/com/android/systemui/haptics/slider/SliderEventType.kt b/packages/SystemUI/src/com/android/systemui/haptics/slider/SliderEventType.kt
new file mode 100644
index 0000000..413e277
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/haptics/slider/SliderEventType.kt
@@ -0,0 +1,33 @@
+/*
+ * 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.haptics.slider
+
+/** The type of a [SliderEvent]. */
+enum class SliderEventType {
+ /* No event. */
+ NOTHING,
+ /* The slider has captured a touch input and is tracking touch events. */
+ STARTED_TRACKING_TOUCH,
+ /* The slider progress is changing due to user touch input. */
+ PROGRESS_CHANGE_BY_USER,
+ /* The slider progress is changing programmatically. */
+ PROGRESS_CHANGE_BY_PROGRAM,
+ /* The slider has stopped tracking touch events. */
+ STOPPED_TRACKING_TOUCH,
+ /* The external (not touch) stimulus that was modifying the slider progress has stopped. */
+ EXTERNAL_STIMULUS_RELEASE,
+}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardService.java b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardService.java
index 9d2771e..f6add9c 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardService.java
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardService.java
@@ -298,18 +298,18 @@
}
@Inject
- public KeyguardService(KeyguardViewMediator keyguardViewMediator,
- KeyguardLifecyclesDispatcher keyguardLifecyclesDispatcher,
- ScreenOnCoordinator screenOnCoordinator,
- ShellTransitions shellTransitions,
- DisplayTracker displayTracker,
- WindowManagerLockscreenVisibilityViewModel
- wmLockscreenVisibilityViewModel,
- WindowManagerLockscreenVisibilityManager wmLockscreenVisibilityManager,
- KeyguardSurfaceBehindViewModel keyguardSurfaceBehindViewModel,
- KeyguardSurfaceBehindParamsApplier keyguardSurfaceBehindAnimator,
- @Application CoroutineScope scope,
- FeatureFlags featureFlags) {
+ public KeyguardService(
+ KeyguardViewMediator keyguardViewMediator,
+ KeyguardLifecyclesDispatcher keyguardLifecyclesDispatcher,
+ ScreenOnCoordinator screenOnCoordinator,
+ ShellTransitions shellTransitions,
+ DisplayTracker displayTracker,
+ WindowManagerLockscreenVisibilityViewModel wmLockscreenVisibilityViewModel,
+ WindowManagerLockscreenVisibilityManager wmLockscreenVisibilityManager,
+ KeyguardSurfaceBehindViewModel keyguardSurfaceBehindViewModel,
+ KeyguardSurfaceBehindParamsApplier keyguardSurfaceBehindAnimator,
+ @Application CoroutineScope scope,
+ FeatureFlags featureFlags) {
super();
mKeyguardViewMediator = keyguardViewMediator;
mKeyguardLifecyclesDispatcher = keyguardLifecyclesDispatcher;
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewConfigurator.kt b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewConfigurator.kt
index 9b323ee..6bc9abf 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewConfigurator.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewConfigurator.kt
@@ -17,10 +17,15 @@
package com.android.systemui.keyguard
+import android.content.Context
import android.content.res.Configuration
+import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
+import com.android.keyguard.KeyguardStatusView
import com.android.keyguard.KeyguardStatusViewController
+import com.android.keyguard.LockIconView
+import com.android.keyguard.LockIconViewController
import com.android.keyguard.dagger.KeyguardStatusViewComponent
import com.android.systemui.CoreStartable
import com.android.systemui.R
@@ -37,6 +42,7 @@
import com.android.systemui.keyguard.ui.binder.KeyguardQuickAffordanceViewBinder
import com.android.systemui.keyguard.ui.binder.KeyguardRootViewBinder
import com.android.systemui.keyguard.ui.binder.KeyguardSettingsViewBinder
+import com.android.systemui.keyguard.ui.view.KeyguardIndicationArea
import com.android.systemui.keyguard.ui.view.KeyguardRootView
import com.android.systemui.keyguard.ui.view.layout.KeyguardBlueprintCommandListener
import com.android.systemui.keyguard.ui.viewmodel.KeyguardAmbientIndicationViewModel
@@ -51,6 +57,7 @@
import com.android.systemui.shade.NotificationShadeWindowView
import com.android.systemui.statusbar.KeyguardIndicationController
import com.android.systemui.statusbar.VibratorHelper
+import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayoutController
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.SharedNotificationContainerViewModel
@@ -90,6 +97,10 @@
private val keyguardBlueprintInteractor: KeyguardBlueprintInteractor,
private val communalWidgetViewModel: CommunalWidgetViewModel,
private val communalWidgetViewAdapter: CommunalWidgetViewAdapter,
+ private val notificationStackScrollerLayoutController: NotificationStackScrollLayoutController,
+ private val context: Context,
+ private val keyguardIndicationController: KeyguardIndicationController,
+ private val lockIconViewController: LockIconViewController,
) : CoreStartable {
private var rootViewHandle: DisposableHandle? = null
@@ -98,22 +109,41 @@
private var rightShortcutHandle: KeyguardQuickAffordanceViewBinder.Binding? = null
private var ambientIndicationAreaHandle: KeyguardAmbientIndicationAreaViewBinder.Binding? = null
private var settingsPopupMenuHandle: DisposableHandle? = null
- private var keyguardStatusViewController: KeyguardStatusViewController? = null
+ var keyguardStatusViewController: KeyguardStatusViewController? = null
+ get() {
+ if (field == null) {
+ val statusViewComponent =
+ keyguardStatusViewComponentFactory.build(
+ LayoutInflater.from(context).inflate(R.layout.keyguard_status_view, null)
+ as KeyguardStatusView
+ )
+ val controller = statusViewComponent.keyguardStatusViewController
+ controller.init()
+ field = controller
+ }
+
+ return field
+ }
override fun start() {
- bindKeyguardRootView()
- val notificationPanel =
- notificationShadeWindowView.requireViewById(R.id.notification_panel) as ViewGroup
- unbindKeyguardBottomArea(notificationPanel)
- bindIndicationArea()
- bindLockIconView(notificationPanel)
- bindKeyguardStatusView(notificationPanel)
- setupNotificationStackScrollLayout(notificationPanel)
- bindLeftShortcut()
- bindRightShortcut()
- bindAmbientIndicationArea()
- bindSettingsPopupMenu()
- bindCommunalWidgetArea()
+ if (featureFlags.isEnabled(Flags.LAZY_INFLATE_KEYGUARD)) {
+ keyguardRootView.removeAllViews()
+ initializeViews()
+ } else {
+ bindKeyguardRootView()
+ val notificationPanel =
+ notificationShadeWindowView.requireViewById(R.id.notification_panel) as ViewGroup
+ unbindKeyguardBottomArea(notificationPanel)
+ bindIndicationArea()
+ bindLockIconView(notificationPanel)
+ bindKeyguardStatusView(notificationPanel)
+ setupNotificationStackScrollLayout(notificationPanel)
+ bindLeftShortcut()
+ bindRightShortcut()
+ bindAmbientIndicationArea()
+ bindSettingsPopupMenu()
+ bindCommunalWidgetArea()
+ }
KeyguardBlueprintViewBinder.bind(keyguardRootView, keyguardBlueprintViewModel)
keyguardBlueprintCommandListener.start()
@@ -131,6 +161,7 @@
SharedNotificationContainerBinder.bind(
sharedNotificationContainer,
sharedNotificationContainerViewModel,
+ notificationStackScrollerLayoutController,
)
}
}
@@ -161,6 +192,14 @@
)
}
+ /** Initialize views so that corresponding controllers have a view set. */
+ private fun initializeViews() {
+ val indicationArea = KeyguardIndicationArea(context, null)
+ keyguardIndicationController.setIndicationArea(indicationArea)
+
+ lockIconViewController.setLockIconView(LockIconView(context, null))
+ }
+
private fun bindKeyguardRootView() {
rootViewHandle?.dispose()
rootViewHandle =
@@ -183,6 +222,9 @@
keyguardRootView.findViewById<View?>(R.id.lock_icon_view)?.let {
keyguardRootView.removeView(it)
}
+ legacyParent.requireViewById<LockIconView>(R.id.lock_icon_view).let {
+ lockIconViewController.setLockIconView(it)
+ }
}
}
@@ -304,6 +346,5 @@
* Temporary, to allow NotificationPanelViewController to use the same instance while code is
* migrated: b/288242803
*/
- fun getKeyguardStatusViewController() = keyguardStatusViewController
fun getKeyguardRootView() = keyguardRootView
}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
index e983a05..2b4dc81 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
@@ -3207,7 +3207,7 @@
* visible
*/
public void exitKeyguardAndFinishSurfaceBehindRemoteAnimation(boolean showKeyguard) {
- Log.d(TAG, "onKeyguardExitRemoteAnimationFinished");
+ Log.d(TAG, "exitKeyguardAndFinishSurfaceBehindRemoteAnimation");
if (!mSurfaceBehindRemoteAnimationRunning && !mSurfaceBehindRemoteAnimationRequested) {
Log.d(TAG, "skip onKeyguardExitRemoteAnimationFinished showKeyguard=" + showKeyguard
+ " surfaceAnimationRunning=" + mSurfaceBehindRemoteAnimationRunning
@@ -3222,6 +3222,13 @@
// Post layout changes to the next frame, so we don't hang at the end of the animation.
DejankUtils.postAfterTraversal(() -> {
+ if (!mPM.isInteractive()) {
+ Log.e(TAG, "exitKeyguardAndFinishSurfaceBehindRemoteAnimation#postAfterTraversal" +
+ "Not interactive after traversal. Don't hide the keyguard. This means we " +
+ "re-locked the device during unlock.");
+ return;
+ }
+
onKeyguardExitFinished();
if (mKeyguardStateController.isDismissingFromSwipe() || wasShowing) {
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/KeyguardBlueprintRepository.kt b/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/KeyguardBlueprintRepository.kt
index 7234757..f91ae74 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/KeyguardBlueprintRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/KeyguardBlueprintRepository.kt
@@ -17,13 +17,10 @@
package com.android.systemui.keyguard.data.repository
-import android.view.View
-import androidx.constraintlayout.widget.ConstraintLayout
-import androidx.constraintlayout.widget.ConstraintSet
-import androidx.core.view.children
import com.android.systemui.common.ui.data.repository.ConfigurationRepository
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Application
+import com.android.systemui.keyguard.shared.model.KeyguardBlueprint
import com.android.systemui.keyguard.ui.view.layout.blueprints.DefaultKeyguardBlueprint.Companion.DEFAULT
import com.android.systemui.keyguard.ui.view.layout.blueprints.KeyguardBlueprintModule
import java.io.PrintWriter
@@ -95,26 +92,3 @@
blueprintIdMap.forEach { entry -> pw.println("${entry.key}") }
}
}
-
-/** Determines the constraints for the ConstraintSet in the lockscreen root view. */
-interface KeyguardBlueprint {
- val id: String
- val shouldRemoveUnconstrainedViews: Boolean
- get() = true
-
- fun apply(constraintLayout: ConstraintSet)
- fun removeUnConstrainedViews(constraintLayout: ConstraintLayout, constraintSet: ConstraintSet) {
- constraintLayout.children
- .map { it.id }
- .filterNot { constraintSet.knownIds.contains(it) }
- .forEach { constraintSet.setVisibility(it, View.GONE) }
- }
-}
-
-/**
- * Lower level modules that determine constraints for a particular section in the lockscreen root
- * view.
- */
-interface KeyguardSection {
- fun apply(constraintSet: ConstraintSet)
-}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/KeyguardRepository.kt b/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/KeyguardRepository.kt
index 42cd3a5..d399e4c 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/KeyguardRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/KeyguardRepository.kt
@@ -158,7 +158,7 @@
val lastDozeTapToWakePosition: StateFlow<Point?>
/** Observable for the [StatusBarState] */
- val statusBarState: Flow<StatusBarState>
+ val statusBarState: StateFlow<StatusBarState>
/** Observable for device wake/sleep state */
val wakefulness: StateFlow<WakefulnessModel>
@@ -520,23 +520,29 @@
return keyguardBypassController.bypassEnabled
}
- override val statusBarState: Flow<StatusBarState> = conflatedCallbackFlow {
- val callback =
- object : StatusBarStateController.StateListener {
- override fun onStateChanged(state: Int) {
- trySendWithFailureLogging(statusBarStateIntToObject(state), TAG, "state")
- }
+ // TODO(b/297345631): Expose this at the interactor level instead so that it can be powered by
+ // [SceneInteractor] when scenes are ready.
+ override val statusBarState: StateFlow<StatusBarState> =
+ conflatedCallbackFlow {
+ val callback =
+ object : StatusBarStateController.StateListener {
+ override fun onStateChanged(state: Int) {
+ trySendWithFailureLogging(
+ statusBarStateIntToObject(state),
+ TAG,
+ "state"
+ )
+ }
+ }
+
+ statusBarStateController.addCallback(callback)
+ awaitClose { statusBarStateController.removeCallback(callback) }
}
-
- statusBarStateController.addCallback(callback)
- trySendWithFailureLogging(
- statusBarStateIntToObject(statusBarStateController.getState()),
- TAG,
- "initial state"
- )
-
- awaitClose { statusBarStateController.removeCallback(callback) }
- }
+ .stateIn(
+ scope,
+ SharingStarted.Eagerly,
+ statusBarStateIntToObject(statusBarStateController.state)
+ )
override val biometricUnlockState: Flow<BiometricUnlockModel> = conflatedCallbackFlow {
fun dispatchUpdate() {
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromOccludedTransitionInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromOccludedTransitionInteractor.kt
index ff0db34..714add4 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromOccludedTransitionInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromOccludedTransitionInteractor.kt
@@ -50,14 +50,28 @@
listenForOccludedToAodOrDozing()
listenForOccludedToGone()
listenForOccludedToAlternateBouncer()
+ listenForOccludedToPrimaryBouncer()
+ }
+
+ private fun listenForOccludedToPrimaryBouncer() {
+ scope.launch {
+ keyguardInteractor.primaryBouncerShowing
+ .sample(transitionInteractor.startedKeyguardTransitionStep, ::Pair)
+ .collect { (isBouncerShowing, lastStartedTransitionStep) ->
+ if (
+ isBouncerShowing && lastStartedTransitionStep.to == KeyguardState.OCCLUDED
+ ) {
+ startTransitionTo(KeyguardState.PRIMARY_BOUNCER)
+ }
+ }
+ }
}
private fun listenForOccludedToDreaming() {
scope.launch {
keyguardInteractor.isAbleToDream
.sample(transitionInteractor.finishedKeyguardState, ::Pair)
- .collect { pair ->
- val (isAbleToDream, keyguardState) = pair
+ .collect { (isAbleToDream, keyguardState) ->
if (isAbleToDream && keyguardState == KeyguardState.OCCLUDED) {
startTransitionTo(KeyguardState.DREAMING)
}
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 ed84884..0c05a0e 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
@@ -19,10 +19,14 @@
import android.app.StatusBarManager
import android.graphics.Point
+import android.util.MathUtils
+import com.android.app.animation.Interpolators
+import com.android.systemui.R
import com.android.systemui.bouncer.data.repository.KeyguardBouncerRepository
import com.android.systemui.common.coroutine.ChannelExt.trySendWithFailureLogging
import com.android.systemui.common.coroutine.ConflatedCallbackFlow.conflatedCallbackFlow
import com.android.systemui.common.shared.model.Position
+import com.android.systemui.common.shared.model.SharedNotificationContainerPosition
import com.android.systemui.common.ui.data.repository.ConfigurationRepository
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.flags.FeatureFlags
@@ -38,12 +42,14 @@
import com.android.systemui.keyguard.shared.model.ScreenModel
import com.android.systemui.keyguard.shared.model.StatusBarState
import com.android.systemui.keyguard.shared.model.WakefulnessModel
+import com.android.systemui.shade.data.repository.ShadeRepository
import com.android.systemui.statusbar.CommandQueue
import com.android.systemui.util.kotlin.sample
import javax.inject.Inject
import kotlinx.coroutines.channels.awaitClose
import kotlinx.coroutines.delay
import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.combine
import kotlinx.coroutines.flow.distinctUntilChanged
@@ -51,6 +57,7 @@
import kotlinx.coroutines.flow.flatMapLatest
import kotlinx.coroutines.flow.flow
import kotlinx.coroutines.flow.flowOf
+import kotlinx.coroutines.flow.map
import kotlinx.coroutines.flow.merge
import kotlinx.coroutines.flow.onStart
@@ -66,29 +73,42 @@
featureFlags: FeatureFlags,
bouncerRepository: KeyguardBouncerRepository,
configurationRepository: ConfigurationRepository,
+ shadeRepository: ShadeRepository,
) {
+ /** Position information for the shared notification container. */
+ val sharedNotificationContainerPosition =
+ MutableStateFlow(SharedNotificationContainerPosition())
+
/**
* The amount of doze the system is in, where `1.0` is fully dozing and `0.0` is not dozing at
* all.
*/
val dozeAmount: Flow<Float> = repository.linearDozeAmount
+
/** Whether the system is in doze mode. */
val isDozing: Flow<Boolean> = repository.isDozing
+
/** Receive an event for doze time tick */
val dozeTimeTick: Flow<Long> = repository.dozeTimeTick
+
/** Whether Always-on Display mode is available. */
val isAodAvailable: Flow<Boolean> = repository.isAodAvailable
+
/** Doze transition information. */
val dozeTransitionModel: Flow<DozeTransitionModel> = repository.dozeTransitionModel
+
/**
* Whether the system is dreaming. [isDreaming] will be always be true when [isDozing] is true,
* but not vice-versa.
*/
val isDreaming: Flow<Boolean> = repository.isDreaming
+
/** Whether the system is dreaming with an overlay active */
val isDreamingWithOverlay: Flow<Boolean> = repository.isDreamingWithOverlay
+
/** Whether the system is dreaming and the active dream is hosted in lockscreen */
val isActiveDreamLockscreenHosted: StateFlow<Boolean> = repository.isActiveDreamLockscreenHosted
+
/** Event for when the camera gesture is detected */
val onCameraLaunchDetected: Flow<CameraLaunchSourceModel> = conflatedCallbackFlow {
val callback =
@@ -137,18 +157,25 @@
/** Whether the keyguard is showing or not. */
val isKeyguardShowing: Flow<Boolean> = repository.isKeyguardShowing
+
/** Whether the keyguard is unlocked or not. */
val isKeyguardUnlocked: Flow<Boolean> = repository.isKeyguardUnlocked
+
/** Whether the keyguard is occluded (covered by an activity). */
val isKeyguardOccluded: Flow<Boolean> = repository.isKeyguardOccluded
+
/** Whether the keyguard is going away. */
val isKeyguardGoingAway: Flow<Boolean> = repository.isKeyguardGoingAway
+
/** Whether the primary bouncer is showing or not. */
val primaryBouncerShowing: Flow<Boolean> = bouncerRepository.primaryBouncerShow
+
/** Whether the alternate bouncer is showing or not. */
val alternateBouncerShowing: Flow<Boolean> = bouncerRepository.alternateBouncerVisible
+
/** Observable for the [StatusBarState] */
val statusBarState: Flow<StatusBarState> = repository.statusBarState
+
/**
* Observable for [BiometricUnlockModel] when biometrics like face or any fingerprint (rear,
* side, under display) is used to unlock the device.
@@ -196,6 +223,22 @@
val keyguardAlpha: Flow<Float> = repository.keyguardAlpha
+ val keyguardTranslationY: Flow<Float> =
+ configurationChange.flatMapLatest {
+ val translationDistance =
+ configurationRepository.getDimensionPixelSize(
+ R.dimen.keyguard_translate_distance_on_swipe_up
+ )
+ shadeRepository.shadeModel.map {
+ // On swipe up, translate the keyguard to reveal the bouncer
+ MathUtils.lerp(
+ translationDistance,
+ 0,
+ Interpolators.FAST_OUT_LINEAR_IN.getInterpolation(it.expansionAmount)
+ )
+ }
+ }
+
/** Whether to animate the next doze mode transition. */
val animateDozingTransitions: Flow<Boolean> = repository.animateBottomAreaDozingTransitions
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/shared/model/KeyguardBlueprint.kt b/packages/SystemUI/src/com/android/systemui/keyguard/shared/model/KeyguardBlueprint.kt
new file mode 100644
index 0000000..659c5f3
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/shared/model/KeyguardBlueprint.kt
@@ -0,0 +1,38 @@
+/*
+ * 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.keyguard.shared.model
+
+import androidx.constraintlayout.widget.ConstraintLayout
+import androidx.constraintlayout.widget.ConstraintSet
+
+/** Determines the constraints for the ConstraintSet in the lockscreen root view. */
+interface KeyguardBlueprint {
+ val id: String
+ val sections: Array<KeyguardSection>
+
+ fun addViews(constraintLayout: ConstraintLayout) {
+ sections.forEach { it.addViews(constraintLayout) }
+ }
+
+ fun applyConstraints(constraintSet: ConstraintSet) {
+ sections.forEach { it.applyConstraints(constraintSet) }
+ }
+
+ fun onDestroy() {
+ sections.forEach { it.onDestroy() }
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/shared/model/KeyguardSection.kt b/packages/SystemUI/src/com/android/systemui/keyguard/shared/model/KeyguardSection.kt
new file mode 100644
index 0000000..19f50de
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/shared/model/KeyguardSection.kt
@@ -0,0 +1,30 @@
+/*
+ * 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.
+ */
+
+package com.android.systemui.keyguard.shared.model
+
+import androidx.constraintlayout.widget.ConstraintLayout
+import androidx.constraintlayout.widget.ConstraintSet
+
+/**
+ * Lower level modules that determine constraints for a particular section in the lockscreen root
+ * view.
+ */
+interface KeyguardSection {
+ fun addViews(constraintLayout: ConstraintLayout)
+ fun applyConstraints(constraintSet: ConstraintSet)
+ fun onDestroy() {}
+}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/shared/model/WakeSleepReason.kt b/packages/SystemUI/src/com/android/systemui/keyguard/shared/model/WakeSleepReason.kt
index fb685da..c8a04fd 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/shared/model/WakeSleepReason.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/shared/model/WakeSleepReason.kt
@@ -19,18 +19,20 @@
import android.os.PowerManager
/** The reason we're waking up or going to sleep, such as pressing the power button. */
-enum class WakeSleepReason {
+enum class WakeSleepReason(
+ val isTouch: Boolean,
+) {
/** The physical power button was pressed to wake up or sleep the device. */
- POWER_BUTTON,
+ POWER_BUTTON(isTouch = false),
/** The user has tapped or double tapped to wake the screen. */
- TAP,
+ TAP(isTouch = true),
/** The user performed some sort of gesture to wake the screen. */
- GESTURE,
+ GESTURE(isTouch = true),
/** Something else happened to wake up or sleep the device. */
- OTHER;
+ OTHER(isTouch = false);
companion object {
fun fromPowerManagerWakeReason(reason: Int): WakeSleepReason {
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardBlueprintViewBinder.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardBlueprintViewBinder.kt
index e40c279..c340e5d 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardBlueprintViewBinder.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardBlueprintViewBinder.kt
@@ -21,6 +21,7 @@
import android.util.Log
import androidx.constraintlayout.widget.ConstraintLayout
import androidx.constraintlayout.widget.ConstraintSet
+import androidx.core.view.children
import androidx.lifecycle.Lifecycle
import androidx.lifecycle.repeatOnLifecycle
import com.android.systemui.keyguard.ui.viewmodel.KeyguardBlueprintViewModel
@@ -36,16 +37,28 @@
repeatOnLifecycle(Lifecycle.State.CREATED) {
launch {
viewModel.blueprint.collect { blueprint ->
- Trace.beginSection("KeyguardBlueprintController#applyBlueprint")
+ Trace.beginSection("KeyguardBlueprint#applyBlueprint")
Log.d(TAG, "applying blueprint: $blueprint")
- ConstraintSet().apply {
- clone(constraintLayout)
- val emptyLayout = ConstraintSet.Layout()
- knownIds.forEach { getConstraint(it).layout.copyFrom(emptyLayout) }
- blueprint?.apply(this)
- blueprint?.removeUnConstrainedViews(constraintLayout, this)
- applyTo(constraintLayout)
+ if (blueprint != viewModel.currentBluePrint) {
+ viewModel.currentBluePrint?.onDestroy()
}
+ val constraintSet =
+ ConstraintSet().apply {
+ clone(constraintLayout)
+ val emptyLayout = ConstraintSet.Layout()
+ knownIds.forEach {
+ getConstraint(it).layout.copyFrom(emptyLayout)
+ }
+ blueprint.addViews(constraintLayout)
+ blueprint.applyConstraints(this)
+ applyTo(constraintLayout)
+ }
+ // Remove all unconstrained views.
+ constraintLayout.children
+ .filterNot { constraintSet.knownIds.contains(it.id) }
+ .forEach { constraintLayout.removeView(it) }
+
+ viewModel.currentBluePrint = blueprint
Trace.endSection()
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardRootViewBinder.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardRootViewBinder.kt
index 19f622b..2814732 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardRootViewBinder.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardRootViewBinder.kt
@@ -18,6 +18,7 @@
import android.annotation.DrawableRes
import android.view.View
+import android.view.View.OnLayoutChangeListener
import android.view.ViewGroup
import androidx.lifecycle.Lifecycle
import androidx.lifecycle.repeatOnLifecycle
@@ -43,6 +44,9 @@
/** Bind occludingAppDeviceEntryMessageViewModel to run whenever the keyguard view is attached. */
@ExperimentalCoroutinesApi
object KeyguardRootViewBinder {
+
+ private var onLayoutChangeListener: OnLayoutChange? = null
+
@JvmStatic
fun bind(
view: ViewGroup,
@@ -54,8 +58,8 @@
): DisposableHandle {
val disposableHandle =
view.repeatWhenAttached {
- if (featureFlags.isEnabled(Flags.FP_LISTEN_OCCLUDING_APPS)) {
- repeatOnLifecycle(Lifecycle.State.CREATED) {
+ repeatOnLifecycle(Lifecycle.State.CREATED) {
+ if (featureFlags.isEnabled(Flags.FP_LISTEN_OCCLUDING_APPS)) {
launch {
occludingAppDeviceEntryMessageViewModel.message.collect {
biometricMessage ->
@@ -72,10 +76,8 @@
}
}
}
- }
- if (featureFlags.isEnabled(Flags.MIGRATE_SPLIT_KEYGUARD_BOTTOM_AREA)) {
- repeatOnLifecycle(Lifecycle.State.STARTED) {
+ if (featureFlags.isEnabled(Flags.MIGRATE_SPLIT_KEYGUARD_BOTTOM_AREA)) {
launch {
viewModel.keyguardRootViewVisibilityState.collect { visibilityState ->
view.animate().cancel()
@@ -84,16 +86,21 @@
val isOcclusionTransitionRunning =
visibilityState.occlusionTransitionRunning
if (goingToFullShade) {
- view.animate().alpha(0f).setStartDelay(
- keyguardStateController.keyguardFadingAwayDelay
- ).setDuration(
- keyguardStateController.shortenedFadingAwayDuration
- ).setInterpolator(
- Interpolators.ALPHA_OUT
- ).withEndAction { view.visibility = View.GONE }.start()
+ view
+ .animate()
+ .alpha(0f)
+ .setStartDelay(
+ keyguardStateController.keyguardFadingAwayDelay
+ )
+ .setDuration(
+ keyguardStateController.shortenedFadingAwayDuration
+ )
+ .setInterpolator(Interpolators.ALPHA_OUT)
+ .withEndAction { view.visibility = View.GONE }
+ .start()
} else if (
statusBarState == StatusBarState.KEYGUARD ||
- statusBarState == StatusBarState.SHADE_LOCKED
+ statusBarState == StatusBarState.SHADE_LOCKED
) {
view.visibility = View.VISIBLE
if (!isOcclusionTransitionRunning) {
@@ -105,15 +112,30 @@
}
}
+ launch { viewModel.alpha.collect { alpha -> view.alpha = alpha } }
+ }
+
+ if (featureFlags.isEnabled(Flags.MIGRATE_KEYGUARD_STATUS_VIEW)) {
launch {
- viewModel.alpha.collect { alpha ->
- view.alpha = alpha
+ viewModel.translationY.collect {
+ val statusView =
+ view.requireViewById<View>(R.id.keyguard_status_view)
+ statusView.translationY = it
}
}
}
}
}
- return disposableHandle
+
+ onLayoutChangeListener = OnLayoutChange(viewModel)
+ view.addOnLayoutChangeListener(onLayoutChangeListener)
+
+ return object : DisposableHandle {
+ override fun dispose() {
+ disposableHandle.dispose()
+ view.removeOnLayoutChangeListener(onLayoutChangeListener)
+ }
+ }
}
/**
@@ -122,10 +144,10 @@
private fun createChipbarInfo(message: String, @DrawableRes icon: Int): ChipbarInfo {
return ChipbarInfo(
startIcon =
- TintedIcon(
- Icon.Resource(icon, null),
- ChipbarInfo.DEFAULT_ICON_TINT,
- ),
+ TintedIcon(
+ Icon.Resource(icon, null),
+ ChipbarInfo.DEFAULT_ICON_TINT,
+ ),
text = Text.Loaded(message),
endItem = null,
vibrationEffect = null,
@@ -138,5 +160,31 @@
)
}
+ private class OnLayoutChange(private val viewModel: KeyguardRootViewModel) :
+ OnLayoutChangeListener {
+ override fun onLayoutChange(
+ v: View,
+ left: Int,
+ top: Int,
+ right: Int,
+ bottom: Int,
+ oldLeft: Int,
+ oldTop: Int,
+ oldRight: Int,
+ oldBottom: Int
+ ) {
+ val ksv = v.findViewById(R.id.keyguard_status_view) as View?
+ val lockIcon = v.findViewById(R.id.lock_icon_view) as View?
+
+ if (ksv != null && lockIcon != null) {
+ // After layout, ensure the notifications are positioned correctly
+ viewModel.onSharedNotificationContainerPositionChanged(
+ ksv!!.top.toFloat() + ksv!!.height,
+ lockIcon!!.y
+ )
+ }
+ }
+ }
+
private const val ID = "occluding_app_device_entry_unlock_msg"
}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/preview/KeyguardPreviewRenderer.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/preview/KeyguardPreviewRenderer.kt
index 41c1c96..2cfc478 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/preview/KeyguardPreviewRenderer.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/preview/KeyguardPreviewRenderer.kt
@@ -34,6 +34,7 @@
import android.view.ViewGroup
import android.view.WindowManager
import android.widget.FrameLayout
+import androidx.constraintlayout.widget.ConstraintLayout
import androidx.core.view.isInvisible
import com.android.keyguard.ClockEventController
import com.android.keyguard.KeyguardClockSwitch
@@ -45,12 +46,12 @@
import com.android.systemui.flags.FeatureFlags
import com.android.systemui.flags.Flags
import com.android.systemui.keyguard.domain.interactor.KeyguardBlueprintInteractor
-import com.android.systemui.keyguard.ui.binder.KeyguardBlueprintViewBinder
import com.android.systemui.keyguard.ui.binder.KeyguardPreviewClockViewBinder
import com.android.systemui.keyguard.ui.binder.KeyguardPreviewSmartspaceViewBinder
import com.android.systemui.keyguard.ui.binder.KeyguardQuickAffordanceViewBinder
import com.android.systemui.keyguard.ui.binder.KeyguardRootViewBinder
import com.android.systemui.keyguard.ui.view.KeyguardRootView
+import com.android.systemui.keyguard.ui.view.layout.sections.DefaultShortcutsSection
import com.android.systemui.keyguard.ui.viewmodel.KeyguardBlueprintViewModel
import com.android.systemui.keyguard.ui.viewmodel.KeyguardBottomAreaViewModel
import com.android.systemui.keyguard.ui.viewmodel.KeyguardPreviewClockViewModel
@@ -109,6 +110,7 @@
private val occludingAppDeviceEntryMessageViewModel: OccludingAppDeviceEntryMessageViewModel,
private val chipbarCoordinator: ChipbarCoordinator,
private val keyguardStateController: KeyguardStateController,
+ private val defaultShortcutsSection: DefaultShortcutsSection,
) {
val hostToken: IBinder? = bundle.getBinder(KEY_HOST_TOKEN)
@@ -119,6 +121,7 @@
KeyguardPreviewConstants.KEY_HIGHLIGHT_QUICK_AFFORDANCES,
false,
)
+
/** [shouldHideClock] here means that we never create and bind the clock views */
private val shouldHideClock: Boolean =
bundle.getBoolean(ClockPreviewConstants.KEY_HIDE_CLOCK, false)
@@ -176,7 +179,6 @@
if (featureFlags.isEnabled(Flags.MIGRATE_SPLIT_KEYGUARD_BOTTOM_AREA)) {
setupKeyguardRootView(rootView)
- setupShortcuts(rootView)
} else {
setUpBottomArea(rootView)
}
@@ -348,14 +350,14 @@
FrameLayout.LayoutParams.MATCH_PARENT,
),
)
- KeyguardBlueprintViewBinder.bind(keyguardRootView, keyguardBlueprintViewModel)
- keyguardBlueprintInteractor.refreshBlueprint()
+ setupShortcuts(keyguardRootView)
}
- private fun setupShortcuts(rootView: FrameLayout) {
+ private fun setupShortcuts(keyguardRootView: ConstraintLayout) {
+ defaultShortcutsSection.addShortcutViews(keyguardRootView)
shortcutsBindings.add(
KeyguardQuickAffordanceViewBinder.bind(
- rootView.requireViewById(R.id.start_button),
+ keyguardRootView.requireViewById(R.id.start_button),
quickAffordancesCombinedViewModel.startButton,
keyguardRootViewModel.alpha,
falsingManager,
@@ -367,7 +369,7 @@
shortcutsBindings.add(
KeyguardQuickAffordanceViewBinder.bind(
- rootView.requireViewById(R.id.end_button),
+ keyguardRootView.requireViewById(R.id.end_button),
quickAffordancesCombinedViewModel.endButton,
keyguardRootViewModel.alpha,
falsingManager,
@@ -516,11 +518,11 @@
// is dark or a light.
// TODO(b/277832214) we can potentially simplify this code by checking for
// wallpaperColors being null in the if clause above and removing the many ?.
- val wallpaperColorScheme =
- wallpaperColors?.let { ColorScheme(it, /* darkTheme= */ false) }
+ val wallpaperColorScheme = wallpaperColors?.let { ColorScheme(it, darkTheme = false) }
val lightClockColor = wallpaperColorScheme?.accent1?.s100
val darkClockColor = wallpaperColorScheme?.accent2?.s600
- /** Note that when [wallpaperColors] is null, isWallpaperDark is true. */
+
+ // Note that when [wallpaperColors] is null, isWallpaperDark is true.
val isWallpaperDark: Boolean =
(wallpaperColors?.colorHints?.and(WallpaperColors.HINT_SUPPORTS_DARK_TEXT)) == 0
clock.events.onSeedColorChanged(
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/blueprints/DefaultKeyguardBlueprint.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/blueprints/DefaultKeyguardBlueprint.kt
index 518df07..5a15fc2 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/blueprints/DefaultKeyguardBlueprint.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/blueprints/DefaultKeyguardBlueprint.kt
@@ -17,12 +17,15 @@
package com.android.systemui.keyguard.ui.view.layout.blueprints
-import androidx.constraintlayout.widget.ConstraintSet
+import androidx.constraintlayout.widget.ConstraintLayout
import com.android.systemui.dagger.SysUISingleton
-import com.android.systemui.keyguard.data.repository.KeyguardBlueprint
+import com.android.systemui.flags.FeatureFlags
+import com.android.systemui.flags.Flags
+import com.android.systemui.keyguard.shared.model.KeyguardBlueprint
import com.android.systemui.keyguard.ui.view.layout.sections.DefaultAmbientIndicationAreaSection
import com.android.systemui.keyguard.ui.view.layout.sections.DefaultIndicationAreaSection
import com.android.systemui.keyguard.ui.view.layout.sections.DefaultLockIconSection
+import com.android.systemui.keyguard.ui.view.layout.sections.DefaultNotificationStackScrollLayoutSection
import com.android.systemui.keyguard.ui.view.layout.sections.DefaultSettingsPopupMenuSection
import com.android.systemui.keyguard.ui.view.layout.sections.DefaultShortcutsSection
import com.android.systemui.keyguard.ui.view.layout.sections.DefaultStatusViewSection
@@ -39,24 +42,34 @@
class DefaultKeyguardBlueprint
@Inject
constructor(
- private val defaultIndicationAreaSection: DefaultIndicationAreaSection,
- private val defaultLockIconSection: DefaultLockIconSection,
- private val defaultShortcutsSection: DefaultShortcutsSection,
- private val defaultAmbientIndicationAreaSection: DefaultAmbientIndicationAreaSection,
- private val defaultSettingsPopupMenuSection: DefaultSettingsPopupMenuSection,
- private val defaultStatusViewSection: DefaultStatusViewSection,
- private val splitShadeGuidelines: SplitShadeGuidelines,
+ defaultIndicationAreaSection: DefaultIndicationAreaSection,
+ defaultLockIconSection: DefaultLockIconSection,
+ defaultShortcutsSection: DefaultShortcutsSection,
+ defaultAmbientIndicationAreaSection: DefaultAmbientIndicationAreaSection,
+ defaultSettingsPopupMenuSection: DefaultSettingsPopupMenuSection,
+ defaultStatusViewSection: DefaultStatusViewSection,
+ defaultNotificationStackScrollLayoutSection: DefaultNotificationStackScrollLayoutSection,
+ splitShadeGuidelines: SplitShadeGuidelines,
+ private val featureFlags: FeatureFlags,
) : KeyguardBlueprint {
override val id: String = DEFAULT
- override fun apply(constraintSet: ConstraintSet) {
- defaultIndicationAreaSection.apply(constraintSet)
- defaultLockIconSection.apply(constraintSet)
- defaultShortcutsSection.apply(constraintSet)
- defaultAmbientIndicationAreaSection.apply(constraintSet)
- defaultSettingsPopupMenuSection.apply(constraintSet)
- defaultStatusViewSection.apply(constraintSet)
- splitShadeGuidelines.apply(constraintSet)
+ override val sections =
+ arrayOf(
+ defaultIndicationAreaSection,
+ defaultLockIconSection,
+ defaultShortcutsSection,
+ defaultAmbientIndicationAreaSection,
+ defaultSettingsPopupMenuSection,
+ defaultStatusViewSection,
+ defaultNotificationStackScrollLayoutSection,
+ splitShadeGuidelines,
+ )
+
+ override fun addViews(constraintLayout: ConstraintLayout) {
+ if (featureFlags.isEnabled(Flags.LAZY_INFLATE_KEYGUARD)) {
+ super.addViews(constraintLayout)
+ }
}
companion object {
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/blueprints/KeyguardBlueprintModule.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/blueprints/KeyguardBlueprintModule.kt
index 07f316b..fda4c3d 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/blueprints/KeyguardBlueprintModule.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/blueprints/KeyguardBlueprintModule.kt
@@ -18,7 +18,7 @@
package com.android.systemui.keyguard.ui.view.layout.blueprints
import com.android.systemui.communal.ui.view.layout.blueprints.DefaultCommunalBlueprint
-import com.android.systemui.keyguard.data.repository.KeyguardBlueprint
+import com.android.systemui.keyguard.shared.model.KeyguardBlueprint
import dagger.Binds
import dagger.Module
import dagger.multibindings.IntoSet
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/blueprints/ShortcutsBesideUdfpsKeyguardBlueprint.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/blueprints/ShortcutsBesideUdfpsKeyguardBlueprint.kt
index 54c2796..5ef625e 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/blueprints/ShortcutsBesideUdfpsKeyguardBlueprint.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/blueprints/ShortcutsBesideUdfpsKeyguardBlueprint.kt
@@ -17,16 +17,14 @@
package com.android.systemui.keyguard.ui.view.layout.blueprints
-import androidx.constraintlayout.widget.ConstraintSet
-import com.android.keyguard.KeyguardUpdateMonitor
import com.android.systemui.dagger.SysUISingleton
-import com.android.systemui.keyguard.data.repository.KeyguardBlueprint
+import com.android.systemui.keyguard.shared.model.KeyguardBlueprint
import com.android.systemui.keyguard.ui.view.layout.sections.AlignShortcutsToUdfpsSection
import com.android.systemui.keyguard.ui.view.layout.sections.DefaultAmbientIndicationAreaSection
import com.android.systemui.keyguard.ui.view.layout.sections.DefaultIndicationAreaSection
import com.android.systemui.keyguard.ui.view.layout.sections.DefaultLockIconSection
+import com.android.systemui.keyguard.ui.view.layout.sections.DefaultNotificationStackScrollLayoutSection
import com.android.systemui.keyguard.ui.view.layout.sections.DefaultSettingsPopupMenuSection
-import com.android.systemui.keyguard.ui.view.layout.sections.DefaultShortcutsSection
import com.android.systemui.keyguard.ui.view.layout.sections.DefaultStatusViewSection
import com.android.systemui.keyguard.ui.view.layout.sections.SplitShadeGuidelines
import javax.inject.Inject
@@ -36,31 +34,28 @@
class ShortcutsBesideUdfpsKeyguardBlueprint
@Inject
constructor(
- private val keyguardUpdateMonitor: KeyguardUpdateMonitor,
- private val defaultIndicationAreaSection: DefaultIndicationAreaSection,
- private val defaultLockIconSection: DefaultLockIconSection,
- private val defaultAmbientIndicationAreaSection: DefaultAmbientIndicationAreaSection,
- private val defaultSettingsPopupMenuSection: DefaultSettingsPopupMenuSection,
- private val alignShortcutsToUdfpsSection: AlignShortcutsToUdfpsSection,
- private val defaultShortcutsSection: DefaultShortcutsSection,
- private val defaultStatusViewSection: DefaultStatusViewSection,
- private val splitShadeGuidelines: SplitShadeGuidelines,
+ defaultIndicationAreaSection: DefaultIndicationAreaSection,
+ defaultLockIconSection: DefaultLockIconSection,
+ defaultAmbientIndicationAreaSection: DefaultAmbientIndicationAreaSection,
+ defaultSettingsPopupMenuSection: DefaultSettingsPopupMenuSection,
+ alignShortcutsToUdfpsSection: AlignShortcutsToUdfpsSection,
+ defaultStatusViewSection: DefaultStatusViewSection,
+ splitShadeGuidelines: SplitShadeGuidelines,
+ defaultNotificationStackScrollLayoutSection: DefaultNotificationStackScrollLayoutSection,
) : KeyguardBlueprint {
override val id: String = SHORTCUTS_BESIDE_UDFPS
- override fun apply(constraintSet: ConstraintSet) {
- defaultIndicationAreaSection.apply(constraintSet)
- defaultLockIconSection.apply(constraintSet)
- defaultAmbientIndicationAreaSection.apply(constraintSet)
- defaultSettingsPopupMenuSection.apply(constraintSet)
- if (keyguardUpdateMonitor.isUdfpsSupported) {
- alignShortcutsToUdfpsSection.apply(constraintSet)
- } else {
- defaultShortcutsSection.apply(constraintSet)
- }
- defaultStatusViewSection.apply(constraintSet)
- splitShadeGuidelines.apply(constraintSet)
- }
+ override val sections =
+ arrayOf(
+ defaultIndicationAreaSection,
+ defaultLockIconSection,
+ defaultAmbientIndicationAreaSection,
+ defaultSettingsPopupMenuSection,
+ alignShortcutsToUdfpsSection,
+ defaultStatusViewSection,
+ defaultNotificationStackScrollLayoutSection,
+ splitShadeGuidelines,
+ )
companion object {
const val SHORTCUTS_BESIDE_UDFPS = "shortcutsBesideUdfps"
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/AlignShortcutsToUdfpsSection.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/AlignShortcutsToUdfpsSection.kt
index 156b9f3..587c6b7 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/AlignShortcutsToUdfpsSection.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/AlignShortcutsToUdfpsSection.kt
@@ -18,6 +18,7 @@
package com.android.systemui.keyguard.ui.view.layout.sections
import android.content.res.Resources
+import androidx.constraintlayout.widget.ConstraintLayout
import androidx.constraintlayout.widget.ConstraintSet
import androidx.constraintlayout.widget.ConstraintSet.BOTTOM
import androidx.constraintlayout.widget.ConstraintSet.LEFT
@@ -26,12 +27,58 @@
import androidx.constraintlayout.widget.ConstraintSet.TOP
import com.android.systemui.R
import com.android.systemui.dagger.qualifiers.Main
-import com.android.systemui.keyguard.data.repository.KeyguardSection
+import com.android.systemui.flags.FeatureFlags
+import com.android.systemui.flags.Flags
+import com.android.systemui.keyguard.shared.model.KeyguardSection
+import com.android.systemui.keyguard.ui.binder.KeyguardQuickAffordanceViewBinder
+import com.android.systemui.keyguard.ui.viewmodel.KeyguardQuickAffordancesCombinedViewModel
+import com.android.systemui.keyguard.ui.viewmodel.KeyguardRootViewModel
+import com.android.systemui.plugins.FalsingManager
+import com.android.systemui.statusbar.KeyguardIndicationController
+import com.android.systemui.statusbar.VibratorHelper
import javax.inject.Inject
-class AlignShortcutsToUdfpsSection @Inject constructor(@Main private val resources: Resources) :
- KeyguardSection {
- override fun apply(constraintSet: ConstraintSet) {
+class AlignShortcutsToUdfpsSection
+@Inject
+constructor(
+ @Main private val resources: Resources,
+ private val featureFlags: FeatureFlags,
+ private val keyguardQuickAffordancesCombinedViewModel:
+ KeyguardQuickAffordancesCombinedViewModel,
+ private val keyguardRootViewModel: KeyguardRootViewModel,
+ private val falsingManager: FalsingManager,
+ private val indicationController: KeyguardIndicationController,
+ private val vibratorHelper: VibratorHelper,
+) : BaseShortcutsSection(), KeyguardSection {
+
+ override fun addViews(constraintLayout: ConstraintLayout) {
+ if (featureFlags.isEnabled(Flags.MIGRATE_SPLIT_KEYGUARD_BOTTOM_AREA)) {
+ addLeftShortcut(constraintLayout)
+ addRightShortcut(constraintLayout)
+ leftShortcutHandle =
+ KeyguardQuickAffordanceViewBinder.bind(
+ constraintLayout.requireViewById(R.id.start_button),
+ keyguardQuickAffordancesCombinedViewModel.startButton,
+ keyguardRootViewModel.alpha,
+ falsingManager,
+ vibratorHelper,
+ ) {
+ indicationController.showTransientIndication(it)
+ }
+ rightShortcutHandle =
+ KeyguardQuickAffordanceViewBinder.bind(
+ constraintLayout.requireViewById(R.id.end_button),
+ keyguardQuickAffordancesCombinedViewModel.endButton,
+ keyguardRootViewModel.alpha,
+ falsingManager,
+ vibratorHelper,
+ ) {
+ indicationController.showTransientIndication(it)
+ }
+ }
+ }
+
+ override fun applyConstraints(constraintSet: ConstraintSet) {
val width = resources.getDimensionPixelSize(R.dimen.keyguard_affordance_fixed_width)
val height = resources.getDimensionPixelSize(R.dimen.keyguard_affordance_fixed_height)
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/BaseShortcutsSection.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/BaseShortcutsSection.kt
new file mode 100644
index 0000000..db0cf5a
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/BaseShortcutsSection.kt
@@ -0,0 +1,101 @@
+/*
+ * 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.keyguard.ui.view.layout.sections
+
+import android.view.View
+import android.widget.ImageView
+import androidx.constraintlayout.widget.ConstraintLayout
+import androidx.constraintlayout.widget.ConstraintSet
+import androidx.core.content.res.ResourcesCompat
+import com.android.systemui.R
+import com.android.systemui.animation.view.LaunchableImageView
+import com.android.systemui.keyguard.shared.model.KeyguardSection
+import com.android.systemui.keyguard.ui.binder.KeyguardQuickAffordanceViewBinder
+
+/** Base class for sections that add lockscreen shortcuts. */
+abstract class BaseShortcutsSection : KeyguardSection {
+ protected open var leftShortcutHandle: KeyguardQuickAffordanceViewBinder.Binding? = null
+ protected open var rightShortcutHandle: KeyguardQuickAffordanceViewBinder.Binding? = null
+
+ override fun addViews(constraintLayout: ConstraintLayout) {}
+
+ override fun applyConstraints(constraintSet: ConstraintSet) {}
+
+ override fun onDestroy() {
+ leftShortcutHandle?.destroy()
+ rightShortcutHandle?.destroy()
+ }
+
+ protected open fun addLeftShortcut(constraintLayout: ConstraintLayout) {
+ if (constraintLayout.findViewById<View>(R.id.start_button) != null) return
+
+ val padding =
+ constraintLayout.resources.getDimensionPixelSize(
+ R.dimen.keyguard_affordance_fixed_padding
+ )
+ val view =
+ LaunchableImageView(constraintLayout.context, null).apply {
+ id = R.id.start_button
+ scaleType = ImageView.ScaleType.FIT_CENTER
+ background =
+ ResourcesCompat.getDrawable(
+ context.resources,
+ R.drawable.keyguard_bottom_affordance_bg,
+ context.theme
+ )
+ foreground =
+ ResourcesCompat.getDrawable(
+ context.resources,
+ R.drawable.keyguard_bottom_affordance_selected_border,
+ context.theme
+ )
+ visibility = View.INVISIBLE
+ setPadding(padding, padding, padding, padding)
+ }
+ constraintLayout.addView(view)
+ }
+
+ protected open fun addRightShortcut(constraintLayout: ConstraintLayout) {
+ if (constraintLayout.findViewById<View>(R.id.end_button) != null) return
+
+ val padding =
+ constraintLayout.resources.getDimensionPixelSize(
+ R.dimen.keyguard_affordance_fixed_padding
+ )
+ val view =
+ LaunchableImageView(constraintLayout.context, null).apply {
+ id = R.id.end_button
+ scaleType = ImageView.ScaleType.FIT_CENTER
+ background =
+ ResourcesCompat.getDrawable(
+ context.resources,
+ R.drawable.keyguard_bottom_affordance_bg,
+ context.theme
+ )
+ foreground =
+ ResourcesCompat.getDrawable(
+ context.resources,
+ R.drawable.keyguard_bottom_affordance_selected_border,
+ context.theme
+ )
+ visibility = View.INVISIBLE
+ setPadding(padding, padding, padding, padding)
+ }
+ constraintLayout.addView(view)
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/DefaultAmbientIndicationAreaSection.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/DefaultAmbientIndicationAreaSection.kt
index abf25a2..f8455c5 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/DefaultAmbientIndicationAreaSection.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/DefaultAmbientIndicationAreaSection.kt
@@ -17,7 +17,10 @@
package com.android.systemui.keyguard.ui.view.layout.sections
+import android.view.LayoutInflater
+import android.view.View
import android.view.ViewGroup.LayoutParams.MATCH_PARENT
+import androidx.constraintlayout.widget.ConstraintLayout
import androidx.constraintlayout.widget.ConstraintSet
import androidx.constraintlayout.widget.ConstraintSet.BOTTOM
import androidx.constraintlayout.widget.ConstraintSet.END
@@ -28,13 +31,44 @@
import androidx.constraintlayout.widget.ConstraintSet.WRAP_CONTENT
import com.android.keyguard.KeyguardUpdateMonitor
import com.android.systemui.R
-import com.android.systemui.keyguard.data.repository.KeyguardSection
+import com.android.systemui.flags.FeatureFlags
+import com.android.systemui.flags.Flags
+import com.android.systemui.keyguard.shared.model.KeyguardSection
+import com.android.systemui.keyguard.ui.binder.KeyguardAmbientIndicationAreaViewBinder
+import com.android.systemui.keyguard.ui.viewmodel.KeyguardAmbientIndicationViewModel
+import com.android.systemui.keyguard.ui.viewmodel.KeyguardRootViewModel
import javax.inject.Inject
class DefaultAmbientIndicationAreaSection
@Inject
-constructor(private val keyguardUpdateMonitor: KeyguardUpdateMonitor) : KeyguardSection {
- override fun apply(constraintSet: ConstraintSet) {
+constructor(
+ private val keyguardUpdateMonitor: KeyguardUpdateMonitor,
+ private val featureFlags: FeatureFlags,
+ private val keyguardAmbientIndicationViewModel: KeyguardAmbientIndicationViewModel,
+ private val keyguardRootViewModel: KeyguardRootViewModel,
+) : KeyguardSection {
+ private var ambientIndicationAreaHandle: KeyguardAmbientIndicationAreaViewBinder.Binding? = null
+
+ override fun addViews(constraintLayout: ConstraintLayout) {
+ if (featureFlags.isEnabled(Flags.MIGRATE_SPLIT_KEYGUARD_BOTTOM_AREA)) {
+ if (constraintLayout.findViewById<View>(R.id.ambient_indication_container) == null) {
+ val view =
+ LayoutInflater.from(constraintLayout.context)
+ .inflate(R.layout.ambient_indication, constraintLayout, false)
+
+ constraintLayout.addView(view)
+ }
+
+ ambientIndicationAreaHandle =
+ KeyguardAmbientIndicationAreaViewBinder.bind(
+ constraintLayout,
+ keyguardAmbientIndicationViewModel,
+ keyguardRootViewModel,
+ )
+ }
+ }
+
+ override fun applyConstraints(constraintSet: ConstraintSet) {
constraintSet.apply {
constrainWidth(R.id.ambient_indication_container, MATCH_PARENT)
@@ -59,4 +93,8 @@
}
}
}
+
+ override fun onDestroy() {
+ ambientIndicationAreaHandle?.destroy()
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/DefaultIndicationAreaSection.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/DefaultIndicationAreaSection.kt
index dee7ed5..f04bfc6 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/DefaultIndicationAreaSection.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/DefaultIndicationAreaSection.kt
@@ -18,17 +18,53 @@
package com.android.systemui.keyguard.ui.view.layout.sections
import android.content.Context
+import android.view.View
import android.view.ViewGroup
+import androidx.constraintlayout.widget.ConstraintLayout
import androidx.constraintlayout.widget.ConstraintSet
import com.android.systemui.R
-import com.android.systemui.keyguard.data.repository.KeyguardSection
+import com.android.systemui.flags.FeatureFlags
+import com.android.systemui.flags.Flags
+import com.android.systemui.keyguard.shared.model.KeyguardSection
+import com.android.systemui.keyguard.ui.binder.KeyguardIndicationAreaBinder
+import com.android.systemui.keyguard.ui.view.KeyguardIndicationArea
+import com.android.systemui.keyguard.ui.viewmodel.KeyguardIndicationAreaViewModel
+import com.android.systemui.keyguard.ui.viewmodel.KeyguardRootViewModel
+import com.android.systemui.statusbar.KeyguardIndicationController
import javax.inject.Inject
+import kotlinx.coroutines.DisposableHandle
-class DefaultIndicationAreaSection @Inject constructor(private val context: Context) :
- KeyguardSection {
+class DefaultIndicationAreaSection
+@Inject
+constructor(
+ private val context: Context,
+ private val keyguardIndicationAreaViewModel: KeyguardIndicationAreaViewModel,
+ private val keyguardRootViewModel: KeyguardRootViewModel,
+ private val indicationController: KeyguardIndicationController,
+ private val featureFlags: FeatureFlags,
+) : KeyguardSection {
private val indicationAreaViewId = R.id.keyguard_indication_area
+ private var indicationAreaHandle: DisposableHandle? = null
- override fun apply(constraintSet: ConstraintSet) {
+ override fun addViews(constraintLayout: ConstraintLayout) {
+ if (featureFlags.isEnabled(Flags.MIGRATE_SPLIT_KEYGUARD_BOTTOM_AREA)) {
+ if (constraintLayout.findViewById<View>(indicationAreaViewId) == null) {
+ val view = KeyguardIndicationArea(context, null)
+ constraintLayout.addView(view)
+ }
+
+ indicationAreaHandle =
+ KeyguardIndicationAreaBinder.bind(
+ constraintLayout,
+ keyguardIndicationAreaViewModel,
+ keyguardRootViewModel,
+ indicationController,
+ featureFlags,
+ )
+ }
+ }
+
+ override fun applyConstraints(constraintSet: ConstraintSet) {
constraintSet.apply {
constrainWidth(indicationAreaViewId, ViewGroup.LayoutParams.MATCH_PARENT)
constrainHeight(indicationAreaViewId, ViewGroup.LayoutParams.WRAP_CONTENT)
@@ -53,4 +89,8 @@
)
}
}
+
+ override fun onDestroy() {
+ indicationAreaHandle?.dispose()
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/DefaultLockIconSection.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/DefaultLockIconSection.kt
index 461faec..3d62f3f 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/DefaultLockIconSection.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/DefaultLockIconSection.kt
@@ -21,13 +21,20 @@
import android.graphics.Point
import android.graphics.Rect
import android.util.DisplayMetrics
+import android.view.View
import android.view.WindowManager
import androidx.annotation.VisibleForTesting
+import androidx.constraintlayout.widget.ConstraintLayout
import androidx.constraintlayout.widget.ConstraintSet
import com.android.keyguard.KeyguardUpdateMonitor
+import com.android.keyguard.LockIconView
+import com.android.keyguard.LockIconViewController
import com.android.systemui.R
import com.android.systemui.biometrics.AuthController
-import com.android.systemui.keyguard.data.repository.KeyguardSection
+import com.android.systemui.flags.FeatureFlags
+import com.android.systemui.flags.Flags
+import com.android.systemui.keyguard.shared.model.KeyguardSection
+import com.android.systemui.shade.NotificationPanelView
import javax.inject.Inject
class DefaultLockIconSection
@@ -37,16 +44,30 @@
private val authController: AuthController,
private val windowManager: WindowManager,
private val context: Context,
+ private val notificationPanelView: NotificationPanelView,
+ private val featureFlags: FeatureFlags,
+ private val lockIconViewController: LockIconViewController,
) : KeyguardSection {
private val lockIconViewId = R.id.lock_icon_view
- override fun apply(constraintSet: ConstraintSet) {
+ override fun addViews(constraintLayout: ConstraintLayout) {
+ if (featureFlags.isEnabled(Flags.MIGRATE_LOCK_ICON)) {
+ notificationPanelView.findViewById<View>(R.id.lock_icon_view).let {
+ notificationPanelView.removeView(it)
+ }
+ if (constraintLayout.findViewById<View>(R.id.lock_icon_view) == null) {
+ val view = LockIconView(context, null).apply { id = R.id.lock_icon_view }
+ constraintLayout.addView(view)
+ lockIconViewController.setLockIconView(view)
+ }
+ }
+ }
+
+ override fun applyConstraints(constraintSet: ConstraintSet) {
val isUdfpsSupported = keyguardUpdateMonitor.isUdfpsSupported
val scaleFactor: Float = authController.scaleFactor
val mBottomPaddingPx =
context.resources.getDimensionPixelSize(R.dimen.lock_icon_margin_bottom)
- val mDefaultPaddingPx = context.resources.getDimensionPixelSize(R.dimen.lock_icon_padding)
- val scaledPadding: Int = (mDefaultPaddingPx * scaleFactor).toInt()
val bounds = windowManager.currentWindowMetrics.bounds
val widthPixels = bounds.right.toFloat()
val heightPixels = bounds.bottom.toFloat()
@@ -57,12 +78,7 @@
if (isUdfpsSupported) {
authController.udfpsLocation?.let { udfpsLocation ->
- centerLockIcon(
- udfpsLocation,
- authController.udfpsRadius,
- scaledPadding,
- constraintSet
- )
+ centerLockIcon(udfpsLocation, authController.udfpsRadius, constraintSet)
}
} else {
centerLockIcon(
@@ -71,19 +87,13 @@
(heightPixels - ((mBottomPaddingPx + lockIconRadiusPx) * scaleFactor)).toInt()
),
lockIconRadiusPx * scaleFactor,
- scaledPadding,
constraintSet,
)
}
}
@VisibleForTesting
- internal fun centerLockIcon(
- center: Point,
- radius: Float,
- drawablePadding: Int,
- constraintSet: ConstraintSet
- ) {
+ internal fun centerLockIcon(center: Point, radius: Float, constraintSet: ConstraintSet) {
val sensorRect =
Rect().apply {
set(
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/DefaultNotificationStackScrollLayoutSection.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/DefaultNotificationStackScrollLayoutSection.kt
new file mode 100644
index 0000000..a203e41d
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/DefaultNotificationStackScrollLayoutSection.kt
@@ -0,0 +1,61 @@
+/*
+ * 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.keyguard.ui.view.layout.sections
+
+import android.view.View
+import android.view.ViewGroup
+import androidx.constraintlayout.widget.ConstraintLayout
+import androidx.constraintlayout.widget.ConstraintSet
+import com.android.systemui.R
+import com.android.systemui.flags.FeatureFlags
+import com.android.systemui.flags.Flags
+import com.android.systemui.keyguard.shared.model.KeyguardSection
+import com.android.systemui.shade.NotificationPanelView
+import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayoutController
+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.SharedNotificationContainerViewModel
+import javax.inject.Inject
+
+class DefaultNotificationStackScrollLayoutSection
+@Inject
+constructor(
+ private val featureFlags: FeatureFlags,
+ private val notificationPanelView: NotificationPanelView,
+ private val sharedNotificationContainer: SharedNotificationContainer,
+ private val sharedNotificationContainerViewModel: SharedNotificationContainerViewModel,
+ private val controller: NotificationStackScrollLayoutController,
+) : KeyguardSection {
+ override fun addViews(constraintLayout: ConstraintLayout) {
+ if (featureFlags.isEnabled(Flags.MIGRATE_NSSL)) {
+ // This moves the existing NSSL view to a different parent, as the controller is a
+ // singleton and recreating it has other bad side effects
+ notificationPanelView.findViewById<View?>(R.id.notification_stack_scroller)?.let {
+ (it.parent as ViewGroup).removeView(it)
+ sharedNotificationContainer.addNotificationStackScrollLayout(it)
+ SharedNotificationContainerBinder.bind(
+ sharedNotificationContainer,
+ sharedNotificationContainerViewModel,
+ controller,
+ )
+ }
+ }
+ }
+
+ override fun applyConstraints(constraintSet: ConstraintSet) {}
+}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/DefaultSettingsPopupMenuSection.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/DefaultSettingsPopupMenuSection.kt
index ad1e4f8..660cc96 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/DefaultSettingsPopupMenuSection.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/DefaultSettingsPopupMenuSection.kt
@@ -18,20 +18,65 @@
package com.android.systemui.keyguard.ui.view.layout.sections
import android.content.res.Resources
+import android.view.LayoutInflater
+import android.view.View
+import androidx.constraintlayout.widget.ConstraintLayout
import androidx.constraintlayout.widget.ConstraintSet
import androidx.constraintlayout.widget.ConstraintSet.BOTTOM
import androidx.constraintlayout.widget.ConstraintSet.END
import androidx.constraintlayout.widget.ConstraintSet.PARENT_ID
import androidx.constraintlayout.widget.ConstraintSet.START
import androidx.constraintlayout.widget.ConstraintSet.WRAP_CONTENT
+import androidx.core.view.isVisible
import com.android.systemui.R
+import com.android.systemui.animation.view.LaunchableLinearLayout
import com.android.systemui.dagger.qualifiers.Main
-import com.android.systemui.keyguard.data.repository.KeyguardSection
+import com.android.systemui.flags.FeatureFlags
+import com.android.systemui.flags.Flags
+import com.android.systemui.keyguard.shared.model.KeyguardSection
+import com.android.systemui.keyguard.ui.binder.KeyguardSettingsViewBinder
+import com.android.systemui.keyguard.ui.viewmodel.KeyguardSettingsMenuViewModel
+import com.android.systemui.plugins.ActivityStarter
+import com.android.systemui.statusbar.VibratorHelper
import javax.inject.Inject
+import kotlinx.coroutines.DisposableHandle
-class DefaultSettingsPopupMenuSection @Inject constructor(@Main private val resources: Resources) :
- KeyguardSection {
- override fun apply(constraintSet: ConstraintSet) {
+class DefaultSettingsPopupMenuSection
+@Inject
+constructor(
+ @Main private val resources: Resources,
+ private val featureFlags: FeatureFlags,
+ private val keyguardSettingsMenuViewModel: KeyguardSettingsMenuViewModel,
+ private val vibratorHelper: VibratorHelper,
+ private val activityStarter: ActivityStarter,
+) : KeyguardSection {
+ private var settingsPopupMenuHandle: DisposableHandle? = null
+
+ override fun addViews(constraintLayout: ConstraintLayout) {
+ if (featureFlags.isEnabled(Flags.MIGRATE_SPLIT_KEYGUARD_BOTTOM_AREA)) {
+ if (constraintLayout.findViewById<View?>(R.id.keyguard_settings_button) == null) {
+ val view =
+ LayoutInflater.from(constraintLayout.context)
+ .inflate(R.layout.keyguard_settings_popup_menu, constraintLayout, false)
+ .apply {
+ id = R.id.keyguard_settings_button
+ isVisible = false
+ alpha = 0f
+ } as LaunchableLinearLayout
+ constraintLayout.addView(view)
+ }
+
+ settingsPopupMenuHandle =
+ KeyguardSettingsViewBinder.bind(
+ constraintLayout.requireViewById<View>(R.id.keyguard_settings_button),
+ keyguardSettingsMenuViewModel,
+ vibratorHelper,
+ activityStarter,
+ )
+ }
+ }
+
+ override fun applyConstraints(constraintSet: ConstraintSet) {
val horizontalOffsetMargin =
resources.getDimensionPixelSize(R.dimen.keyguard_affordance_horizontal_offset)
@@ -51,6 +96,11 @@
BOTTOM,
resources.getDimensionPixelSize(R.dimen.keyguard_affordance_vertical_offset)
)
+ setVisibility(R.id.keyguard_settings_button, View.GONE)
}
}
+
+ override fun onDestroy() {
+ settingsPopupMenuHandle?.dispose()
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/DefaultShortcutsSection.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/DefaultShortcutsSection.kt
index db4653d..965910a 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/DefaultShortcutsSection.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/DefaultShortcutsSection.kt
@@ -18,6 +18,7 @@
package com.android.systemui.keyguard.ui.view.layout.sections
import android.content.res.Resources
+import androidx.constraintlayout.widget.ConstraintLayout
import androidx.constraintlayout.widget.ConstraintSet
import androidx.constraintlayout.widget.ConstraintSet.BOTTOM
import androidx.constraintlayout.widget.ConstraintSet.LEFT
@@ -25,12 +26,58 @@
import androidx.constraintlayout.widget.ConstraintSet.RIGHT
import com.android.systemui.R
import com.android.systemui.dagger.qualifiers.Main
-import com.android.systemui.keyguard.data.repository.KeyguardSection
+import com.android.systemui.flags.FeatureFlags
+import com.android.systemui.flags.Flags
+import com.android.systemui.keyguard.shared.model.KeyguardSection
+import com.android.systemui.keyguard.ui.binder.KeyguardQuickAffordanceViewBinder
+import com.android.systemui.keyguard.ui.viewmodel.KeyguardQuickAffordancesCombinedViewModel
+import com.android.systemui.keyguard.ui.viewmodel.KeyguardRootViewModel
+import com.android.systemui.plugins.FalsingManager
+import com.android.systemui.statusbar.KeyguardIndicationController
+import com.android.systemui.statusbar.VibratorHelper
import javax.inject.Inject
-class DefaultShortcutsSection @Inject constructor(@Main private val resources: Resources) :
- KeyguardSection {
- override fun apply(constraintSet: ConstraintSet) {
+class DefaultShortcutsSection
+@Inject
+constructor(
+ @Main private val resources: Resources,
+ private val featureFlags: FeatureFlags,
+ private val keyguardQuickAffordancesCombinedViewModel:
+ KeyguardQuickAffordancesCombinedViewModel,
+ private val keyguardRootViewModel: KeyguardRootViewModel,
+ private val falsingManager: FalsingManager,
+ private val indicationController: KeyguardIndicationController,
+ private val vibratorHelper: VibratorHelper,
+) : BaseShortcutsSection(), KeyguardSection {
+
+ override fun addViews(constraintLayout: ConstraintLayout) {
+ if (featureFlags.isEnabled(Flags.MIGRATE_SPLIT_KEYGUARD_BOTTOM_AREA)) {
+ addLeftShortcut(constraintLayout)
+ addRightShortcut(constraintLayout)
+ leftShortcutHandle =
+ KeyguardQuickAffordanceViewBinder.bind(
+ constraintLayout.requireViewById(R.id.start_button),
+ keyguardQuickAffordancesCombinedViewModel.startButton,
+ keyguardRootViewModel.alpha,
+ falsingManager,
+ vibratorHelper,
+ ) {
+ indicationController.showTransientIndication(it)
+ }
+ rightShortcutHandle =
+ KeyguardQuickAffordanceViewBinder.bind(
+ constraintLayout.requireViewById(R.id.end_button),
+ keyguardQuickAffordancesCombinedViewModel.endButton,
+ keyguardRootViewModel.alpha,
+ falsingManager,
+ vibratorHelper,
+ ) {
+ indicationController.showTransientIndication(it)
+ }
+ }
+ }
+
+ override fun applyConstraints(constraintSet: ConstraintSet) {
val width = resources.getDimensionPixelSize(R.dimen.keyguard_affordance_fixed_width)
val height = resources.getDimensionPixelSize(R.dimen.keyguard_affordance_fixed_height)
val horizontalOffsetMargin =
@@ -50,4 +97,15 @@
connect(R.id.end_button, BOTTOM, PARENT_ID, BOTTOM, verticalOffsetMargin)
}
}
+
+ /** Method to add shortcuts without applying any data binding. */
+ fun addShortcutViews(constraintLayout: ConstraintLayout) {
+ addLeftShortcut(constraintLayout)
+ addRightShortcut(constraintLayout)
+ ConstraintSet().apply {
+ clone(constraintLayout)
+ applyConstraints(this)
+ applyTo(constraintLayout)
+ }
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/DefaultStatusViewSection.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/DefaultStatusViewSection.kt
index 3f319ba..321d7a7 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/DefaultStatusViewSection.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/DefaultStatusViewSection.kt
@@ -18,29 +18,96 @@
package com.android.systemui.keyguard.ui.view.layout.sections
import android.content.Context
+import android.view.LayoutInflater
+import android.view.View
import android.view.ViewGroup
-import androidx.constraintlayout.widget.ConstraintSet
-import com.android.systemui.R
-import com.android.systemui.keyguard.data.repository.KeyguardSection
-import javax.inject.Inject
import android.view.ViewGroup.LayoutParams.WRAP_CONTENT
+import androidx.constraintlayout.widget.ConstraintLayout
+import androidx.constraintlayout.widget.ConstraintSet
+import androidx.constraintlayout.widget.ConstraintSet.END
import androidx.constraintlayout.widget.ConstraintSet.MATCH_CONSTRAINT
import androidx.constraintlayout.widget.ConstraintSet.PARENT_ID
import androidx.constraintlayout.widget.ConstraintSet.START
import androidx.constraintlayout.widget.ConstraintSet.TOP
-import androidx.constraintlayout.widget.ConstraintSet.END
+import com.android.keyguard.KeyguardStatusView
+import com.android.keyguard.dagger.KeyguardStatusViewComponent
+import com.android.systemui.R
+import com.android.systemui.flags.FeatureFlags
+import com.android.systemui.flags.Flags
+import com.android.systemui.keyguard.KeyguardViewConfigurator
+import com.android.systemui.keyguard.shared.model.KeyguardSection
+import com.android.systemui.media.controls.ui.KeyguardMediaController
+import com.android.systemui.shade.NotificationPanelView
+import com.android.systemui.shade.NotificationPanelViewController
+import com.android.systemui.util.LargeScreenUtils
+import com.android.systemui.util.Utils
+import dagger.Lazy
+import javax.inject.Inject
+import kotlinx.coroutines.ExperimentalCoroutinesApi
-class DefaultStatusViewSection @Inject constructor(private val context: Context) :
- KeyguardSection {
+class DefaultStatusViewSection
+@Inject
+constructor(
+ private val context: Context,
+ private val featureFlags: FeatureFlags,
+ private val notificationPanelView: NotificationPanelView,
+ private val keyguardStatusViewComponentFactory: KeyguardStatusViewComponent.Factory,
+ private val keyguardViewConfigurator: Lazy<KeyguardViewConfigurator>,
+ private val notificationPanelViewController: Lazy<NotificationPanelViewController>,
+ private val keyguardMediaController: KeyguardMediaController,
+) : KeyguardSection {
private val statusViewId = R.id.keyguard_status_view
- override fun apply(constraintSet: ConstraintSet) {
+ @OptIn(ExperimentalCoroutinesApi::class)
+ override fun addViews(constraintLayout: ConstraintLayout) {
+ // At startup, 2 views with the ID `R.id.keyguard_status_view` will be available.
+ // Disable one of them
+ if (featureFlags.isEnabled(Flags.MIGRATE_KEYGUARD_STATUS_VIEW)) {
+ notificationPanelView.findViewById<View>(statusViewId)?.let {
+ notificationPanelView.removeView(it)
+ }
+ if (constraintLayout.findViewById<View>(statusViewId) == null) {
+ val keyguardStatusView =
+ (LayoutInflater.from(context)
+ .inflate(R.layout.keyguard_status_view, constraintLayout, false)
+ as KeyguardStatusView)
+ .apply { clipChildren = false }
+
+ val statusViewComponent =
+ keyguardStatusViewComponentFactory.build(keyguardStatusView)
+ val controller = statusViewComponent.keyguardStatusViewController
+ controller.init()
+ constraintLayout.addView(keyguardStatusView)
+ keyguardMediaController.attachSplitShadeContainer(
+ keyguardStatusView.requireViewById<ViewGroup>(R.id.status_view_media_container)
+ )
+ keyguardViewConfigurator.get().keyguardStatusViewController = controller
+ notificationPanelViewController.get().updateStatusBarViewController()
+ }
+ }
+ }
+
+ override fun applyConstraints(constraintSet: ConstraintSet) {
constraintSet.apply {
constrainWidth(statusViewId, MATCH_CONSTRAINT)
constrainHeight(statusViewId, WRAP_CONTENT)
connect(statusViewId, TOP, PARENT_ID, TOP)
connect(statusViewId, START, PARENT_ID, START)
connect(statusViewId, END, PARENT_ID, END)
+
+ val margin =
+ if (LargeScreenUtils.shouldUseSplitNotificationShade(context.resources)) {
+ context.resources.getDimensionPixelSize(R.dimen.keyguard_split_shade_top_margin)
+ } else {
+ context.resources.getDimensionPixelSize(R.dimen.keyguard_clock_top_margin) +
+ Utils.getStatusBarHeaderHeightKeyguard(context)
+ }
+ setMargin(statusViewId, TOP, margin)
}
}
+
+ @OptIn(ExperimentalCoroutinesApi::class)
+ override fun onDestroy() {
+ keyguardViewConfigurator.get().keyguardStatusViewController = null
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/SplitShadeGuidelines.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/SplitShadeGuidelines.kt
index 668b17f..bd629d5 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/SplitShadeGuidelines.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/SplitShadeGuidelines.kt
@@ -18,23 +18,17 @@
package com.android.systemui.keyguard.ui.view.layout.sections
import android.content.Context
-import android.view.ViewGroup
+import androidx.constraintlayout.widget.ConstraintLayout
import androidx.constraintlayout.widget.ConstraintSet
-import com.android.systemui.R
-import com.android.systemui.keyguard.data.repository.KeyguardSection
-import javax.inject.Inject
-import android.view.ViewGroup.LayoutParams.WRAP_CONTENT
-import androidx.constraintlayout.widget.ConstraintSet.MATCH_CONSTRAINT
-import androidx.constraintlayout.widget.ConstraintSet.PARENT_ID
-import androidx.constraintlayout.widget.ConstraintSet.START
-import androidx.constraintlayout.widget.ConstraintSet.TOP
-import androidx.constraintlayout.widget.ConstraintSet.END
import androidx.constraintlayout.widget.ConstraintSet.VERTICAL
+import com.android.systemui.R
+import com.android.systemui.keyguard.shared.model.KeyguardSection
+import javax.inject.Inject
-class SplitShadeGuidelines @Inject constructor(private val context: Context) :
- KeyguardSection {
+class SplitShadeGuidelines @Inject constructor(private val context: Context) : KeyguardSection {
+ override fun addViews(constraintLayout: ConstraintLayout) {}
- override fun apply(constraintSet: ConstraintSet) {
+ override fun applyConstraints(constraintSet: ConstraintSet) {
constraintSet.apply {
// For use on large screens, it will provide a guideline vertically in the center to
// enable items to be aligned on the left or right sides
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardBlueprintViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardBlueprintViewModel.kt
index 5e9e553..e2bfc36 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardBlueprintViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardBlueprintViewModel.kt
@@ -17,13 +17,13 @@
package com.android.systemui.keyguard.ui.viewmodel
-import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.keyguard.domain.interactor.KeyguardBlueprintInteractor
+import com.android.systemui.keyguard.shared.model.KeyguardBlueprint
import javax.inject.Inject
-@SysUISingleton
class KeyguardBlueprintViewModel
@Inject
constructor(keyguardBlueprintInteractor: KeyguardBlueprintInteractor) {
+ var currentBluePrint: KeyguardBlueprint? = null
val blueprint = keyguardBlueprintInteractor.blueprint
}
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 92b9ee4..c49af4d 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
@@ -17,6 +17,7 @@
package com.android.systemui.keyguard.ui.viewmodel
+import com.android.systemui.common.shared.model.SharedNotificationContainerPosition
import com.android.systemui.keyguard.domain.interactor.KeyguardInteractor
import com.android.systemui.keyguard.shared.model.KeyguardRootViewVisibilityState
import javax.inject.Inject
@@ -57,6 +58,8 @@
}
}
+ val translationY: Flow<Float> = keyguardInteractor.keyguardTranslationY
+
/**
* Puts this view-model in "preview mode", which means it's being used for UI that is rendering
* the lock screen preview in wallpaper picker / settings and not the real experience on the
@@ -66,4 +69,9 @@
val newPreviewMode = PreviewMode(true)
previewMode.value = newPreviewMode
}
+
+ fun onSharedNotificationContainerPositionChanged(top: Float, bottom: Float) {
+ keyguardInteractor.sharedNotificationContainerPosition.value =
+ SharedNotificationContainerPosition(top, bottom)
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenSceneViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenSceneViewModel.kt
index 93c4902..05c9323 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenSceneViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenSceneViewModel.kt
@@ -17,7 +17,6 @@
package com.android.systemui.keyguard.ui.viewmodel
import com.android.systemui.authentication.domain.interactor.AuthenticationInteractor
-import com.android.systemui.bouncer.domain.interactor.BouncerInteractor
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.scene.shared.model.SceneKey
import javax.inject.Inject
@@ -30,7 +29,7 @@
@Inject
constructor(
authenticationInteractor: AuthenticationInteractor,
- private val bouncerInteractor: BouncerInteractor,
+ val longPress: KeyguardLongPressViewModel,
) {
/** The key of the scene we should switch to when swiping up. */
val upDestinationSceneKey: Flow<SceneKey> =
@@ -41,9 +40,4 @@
SceneKey.Bouncer
}
}
-
- /** Notifies that the lock button on the lock screen was clicked. */
- fun onLockButtonClicked() {
- bouncerInteractor.showOrUnlockDevice()
- }
}
diff --git a/packages/SystemUI/src/com/android/systemui/media/MediaProjectionAppSelectorActivity.kt b/packages/SystemUI/src/com/android/systemui/media/MediaProjectionAppSelectorActivity.kt
index 8f884d24..053c9b5 100644
--- a/packages/SystemUI/src/com/android/systemui/media/MediaProjectionAppSelectorActivity.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/MediaProjectionAppSelectorActivity.kt
@@ -206,6 +206,11 @@
override fun bind(recentTasks: List<RecentTask>) {
recentsViewController.bind(recentTasks)
+ if (!hasWorkProfile()) {
+ // Make sure to refresh the adapter, to show/hide the recents view depending on whether
+ // there are recents or not.
+ mMultiProfilePagerAdapter.personalListAdapter.notifyDataSetChanged()
+ }
}
override fun returnSelectedApp(launchCookie: IBinder) {
@@ -248,9 +253,20 @@
override fun shouldGetOnlyDefaultActivities() = false
- override fun shouldShowContentPreview() = true
+ override fun shouldShowContentPreview() =
+ if (hasWorkProfile()) {
+ // When the user has a work profile, we can always set this to true, and the layout is
+ // adjusted automatically, and hide the recents view.
+ true
+ } else {
+ // When there is no work profile, we should only show the content preview if there are
+ // recents, otherwise the collapsed app selector will look empty.
+ recentsViewController.hasRecentTasks
+ }
- override fun shouldShowContentPreviewWhenEmpty(): Boolean = true
+ override fun shouldShowContentPreviewWhenEmpty() = shouldShowContentPreview()
+
+ private fun hasWorkProfile() = mMultiProfilePagerAdapter.count > 1
override fun createMyUserIdProvider(): MyUserIdProvider =
object : MyUserIdProvider() {
diff --git a/packages/SystemUI/src/com/android/systemui/media/controls/ui/MediaCarouselController.kt b/packages/SystemUI/src/com/android/systemui/media/controls/ui/MediaCarouselController.kt
index 398dcf2..38a6a8f 100644
--- a/packages/SystemUI/src/com/android/systemui/media/controls/ui/MediaCarouselController.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/controls/ui/MediaCarouselController.kt
@@ -39,7 +39,6 @@
import com.android.keyguard.KeyguardUpdateMonitorCallback
import com.android.systemui.Dumpable
import com.android.systemui.R
-import com.android.systemui.classifier.FalsingCollector
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Main
import com.android.systemui.dump.DumpManager
@@ -59,6 +58,11 @@
import com.android.systemui.plugins.FalsingManager
import com.android.systemui.qs.PageIndicator
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
+import com.android.systemui.shared.system.SysUiStatsLog.SMART_SPACE_CARD_REPORTED__DISPLAY_SURFACE__DREAM_OVERLAY as SSPACE_CARD_REPORTED__DREAM_OVERLAY
+import com.android.systemui.shared.system.SysUiStatsLog.SMART_SPACE_CARD_REPORTED__DISPLAY_SURFACE__LOCKSCREEN as SSPACE_CARD_REPORTED__LOCKSCREEN
+import com.android.systemui.shared.system.SysUiStatsLog.SMART_SPACE_CARD_REPORTED__DISPLAY_SURFACE__SHADE
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
@@ -100,7 +104,6 @@
@Main executor: DelayableExecutor,
private val mediaManager: MediaDataManager,
configurationController: ConfigurationController,
- falsingCollector: FalsingCollector,
falsingManager: FalsingManager,
dumpManager: DumpManager,
private val logger: MediaUiEventLogger,
@@ -122,6 +125,7 @@
/** Is the player currently visible (at the end of the transformation */
private var playersVisible: Boolean = false
+
/**
* The desired location where we'll be at the end of the transformation. Usually this matches
* the end location, except when we're still waiting on a state update call.
@@ -152,6 +156,7 @@
@VisibleForTesting var mediaCarousel: MediaScrollView
val mediaCarouselScrollHandler: MediaCarouselScrollHandler
val mediaFrame: ViewGroup
+
@VisibleForTesting
lateinit var settingsButton: View
private set
@@ -280,7 +285,6 @@
this::updatePageIndicatorLocation,
this::updateSeekbarListening,
this::closeGuts,
- falsingCollector,
falsingManager,
this::logSmartspaceImpression,
logger
@@ -327,23 +331,18 @@
if (addOrUpdatePlayer(key, oldKey, data, isSsReactivated)) {
// Log card received if a new resumable media card is added
MediaPlayerData.getMediaPlayer(key)?.let {
- /* ktlint-disable max-line-length */
logSmartspaceCardReported(
759, // SMARTSPACE_CARD_RECEIVED
it.mSmartspaceId,
it.mUid,
surfaces =
intArrayOf(
- SysUiStatsLog
- .SMART_SPACE_CARD_REPORTED__DISPLAY_SURFACE__SHADE,
- SysUiStatsLog
- .SMART_SPACE_CARD_REPORTED__DISPLAY_SURFACE__LOCKSCREEN,
- SysUiStatsLog
- .SMART_SPACE_CARD_REPORTED__DISPLAY_SURFACE__DREAM_OVERLAY
+ SMART_SPACE_CARD_REPORTED__DISPLAY_SURFACE__SHADE,
+ SSPACE_CARD_REPORTED__LOCKSCREEN,
+ SSPACE_CARD_REPORTED__DREAM_OVERLAY,
),
rank = MediaPlayerData.getMediaPlayerIndex(key)
)
- /* ktlint-disable max-line-length */
}
if (
mediaCarouselScrollHandler.visibleToUser &&
@@ -362,24 +361,20 @@
it.mUid + systemClock.currentTimeMillis().toInt()
)
it.mIsImpressed = false
- /* ktlint-disable max-line-length */
+
logSmartspaceCardReported(
759, // SMARTSPACE_CARD_RECEIVED
it.mSmartspaceId,
it.mUid,
surfaces =
intArrayOf(
- SysUiStatsLog
- .SMART_SPACE_CARD_REPORTED__DISPLAY_SURFACE__SHADE,
- SysUiStatsLog
- .SMART_SPACE_CARD_REPORTED__DISPLAY_SURFACE__LOCKSCREEN,
- SysUiStatsLog
- .SMART_SPACE_CARD_REPORTED__DISPLAY_SURFACE__DREAM_OVERLAY
+ SMART_SPACE_CARD_REPORTED__DISPLAY_SURFACE__SHADE,
+ SSPACE_CARD_REPORTED__LOCKSCREEN,
+ SSPACE_CARD_REPORTED__DREAM_OVERLAY,
),
rank = index,
receivedLatencyMillis = receivedSmartspaceCardLatency
)
- /* ktlint-disable max-line-length */
}
}
// If media container area already visible to the user, log impression for
@@ -431,19 +426,16 @@
it.mUid + systemClock.currentTimeMillis().toInt()
)
it.mIsImpressed = false
- /* ktlint-disable max-line-length */
+
logSmartspaceCardReported(
759, // SMARTSPACE_CARD_RECEIVED
it.mSmartspaceId,
it.mUid,
surfaces =
intArrayOf(
- SysUiStatsLog
- .SMART_SPACE_CARD_REPORTED__DISPLAY_SURFACE__SHADE,
- SysUiStatsLog
- .SMART_SPACE_CARD_REPORTED__DISPLAY_SURFACE__LOCKSCREEN,
- SysUiStatsLog
- .SMART_SPACE_CARD_REPORTED__DISPLAY_SURFACE__DREAM_OVERLAY
+ SMART_SPACE_CARD_REPORTED__DISPLAY_SURFACE__SHADE,
+ SSPACE_CARD_REPORTED__LOCKSCREEN,
+ SSPACE_CARD_REPORTED__DREAM_OVERLAY,
),
rank = index,
receivedLatencyMillis =
@@ -451,25 +443,20 @@
data.headphoneConnectionTimeMillis)
.toInt()
)
- /* ktlint-disable max-line-length */
}
}
}
addSmartspaceMediaRecommendations(key, data, shouldPrioritize)
MediaPlayerData.getMediaPlayer(key)?.let {
- /* ktlint-disable max-line-length */
logSmartspaceCardReported(
759, // SMARTSPACE_CARD_RECEIVED
it.mSmartspaceId,
it.mUid,
surfaces =
intArrayOf(
- SysUiStatsLog
- .SMART_SPACE_CARD_REPORTED__DISPLAY_SURFACE__SHADE,
- SysUiStatsLog
- .SMART_SPACE_CARD_REPORTED__DISPLAY_SURFACE__LOCKSCREEN,
- SysUiStatsLog
- .SMART_SPACE_CARD_REPORTED__DISPLAY_SURFACE__DREAM_OVERLAY
+ SMART_SPACE_CARD_REPORTED__DISPLAY_SURFACE__SHADE,
+ SSPACE_CARD_REPORTED__LOCKSCREEN,
+ SSPACE_CARD_REPORTED__DREAM_OVERLAY,
),
rank = MediaPlayerData.getMediaPlayerIndex(key),
receivedLatencyMillis =
@@ -477,7 +464,6 @@
data.headphoneConnectionTimeMillis)
.toInt()
)
- /* ktlint-disable max-line-length */
}
if (
mediaCarouselScrollHandler.visibleToUser &&
@@ -560,7 +546,10 @@
mediaCarouselScrollHandler.onSettingsButtonUpdated(settings)
settingsButton.setOnClickListener {
logger.logCarouselSettings()
- activityStarter.startActivity(settingsIntent, true /* dismissShade */)
+ activityStarter.startActivity(
+ settingsIntent,
+ /* dismissShade= */ true,
+ )
}
}
@@ -1108,7 +1097,6 @@
}
}
- @JvmOverloads
/**
* Log Smartspace events
*
@@ -1127,6 +1115,7 @@
* between headphone connection to sysUI displays media recommendation card
* @param isSwipeToDismiss whether is to log swipe-to-dismiss event
*/
+ @JvmOverloads
fun logSmartspaceCardReported(
eventId: Int,
instanceId: Int,
@@ -1154,21 +1143,24 @@
val cardinality = mediaContent.getChildCount()
surfaces.forEach { surface ->
- /* ktlint-disable max-line-length */
SysUiStatsLog.write(
- SysUiStatsLog.SMARTSPACE_CARD_REPORTED,
+ SMARTSPACE_CARD_REPORTED,
eventId,
instanceId,
// Deprecated, replaced with AiAi feature type so we don't need to create logging
// card type for each new feature.
- SysUiStatsLog.SMART_SPACE_CARD_REPORTED__CARD_TYPE__UNKNOWN_CARD,
+ SMART_SPACE_CARD_REPORTED__CARD_TYPE__UNKNOWN_CARD,
surface,
// Use -1 as rank value to indicate user swipe to dismiss the card
if (isSwipeToDismiss) -1 else rank,
cardinality,
- if (mediaControlKey.isSsMediaRec) 15 // MEDIA_RECOMMENDATION
- else if (mediaControlKey.isSsReactivated) 43 // MEDIA_RESUME_SS_ACTIVATED
- else 31, // MEDIA_RESUME
+ if (mediaControlKey.isSsMediaRec) {
+ 15 // MEDIA_RECOMMENDATION
+ } else if (mediaControlKey.isSsReactivated) {
+ 43 // MEDIA_RESUME_SS_ACTIVATED
+ } else {
+ 31
+ }, // MEDIA_RESUME
uid,
interactedSubcardRank,
interactedSubcardCardinality,
@@ -1176,7 +1168,7 @@
null, // Media cards cannot have subcards.
null // Media cards don't have dimensions today.
)
- /* ktlint-disable max-line-length */
+
if (DEBUG) {
Log.d(
TAG,
@@ -1259,6 +1251,7 @@
instanceId = InstanceId.fakeInstanceId(-1),
appUid = -1
)
+
// Whether should prioritize Smartspace card.
internal var shouldPrioritizeSs: Boolean = false
private set
@@ -1291,6 +1284,7 @@
private val mediaPlayers = TreeMap<MediaSortKey, MediaControlPanel>(comparator)
private val mediaData: MutableMap<String, MediaSortKey> = mutableMapOf()
+
// A map that tracks order of visible media players before they get reordered.
private val visibleMediaPlayers = LinkedHashMap<String, MediaSortKey>()
diff --git a/packages/SystemUI/src/com/android/systemui/media/controls/ui/MediaCarouselScrollHandler.kt b/packages/SystemUI/src/com/android/systemui/media/controls/ui/MediaCarouselScrollHandler.kt
index ce50a11..bbb61b4 100644
--- a/packages/SystemUI/src/com/android/systemui/media/controls/ui/MediaCarouselScrollHandler.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/controls/ui/MediaCarouselScrollHandler.kt
@@ -30,7 +30,6 @@
import com.android.systemui.Gefingerpoken
import com.android.systemui.R
import com.android.systemui.classifier.Classifier.NOTIFICATION_DISMISS
-import com.android.systemui.classifier.FalsingCollector
import com.android.systemui.media.controls.util.MediaUiEventLogger
import com.android.systemui.plugins.FalsingManager
import com.android.systemui.qs.PageIndicator
@@ -59,7 +58,6 @@
private var translationChangedListener: () -> Unit,
private var seekBarUpdateListener: (visibleToUser: Boolean) -> Unit,
private val closeGuts: (immediate: Boolean) -> Unit,
- private val falsingCollector: FalsingCollector,
private val falsingManager: FalsingManager,
private val logSmartspaceImpression: (Boolean) -> Unit,
private val logger: MediaUiEventLogger
@@ -67,8 +65,10 @@
/** Is the view in RTL */
val isRtl: Boolean
get() = scrollView.isLayoutRtl
+
/** Do we need falsing protection? */
var falsingProtectionNeeded: Boolean = false
+
/** The width of the carousel */
private var carouselWidth: Int = 0
@@ -80,6 +80,7 @@
/** The content where the players are added */
private var mediaContent: ViewGroup
+
/** The gesture detector to detect touch gestures */
private val gestureDetector: GestureDetectorCompat
@@ -140,9 +141,6 @@
) = onScroll(down!!, lastMotion, distanceX)
override fun onDown(e: MotionEvent): Boolean {
- if (falsingProtectionNeeded) {
- falsingCollector.onNotificationStartDismissing()
- }
return false
}
}
@@ -263,9 +261,6 @@
private fun onTouch(motionEvent: MotionEvent): Boolean {
val isUp = motionEvent.action == MotionEvent.ACTION_UP
- if (isUp && falsingProtectionNeeded) {
- falsingCollector.onNotificationStopDismissing()
- }
if (gestureDetector.onTouchEvent(motionEvent)) {
if (isUp) {
// If this is an up and we're flinging, we don't want to have this touch reach
@@ -482,8 +477,11 @@
}
val relativeLocation =
visibleMediaIndex.toFloat() +
- if (playerWidthPlusPadding > 0) scrollInAmount.toFloat() / playerWidthPlusPadding
- else 0f
+ if (playerWidthPlusPadding > 0) {
+ scrollInAmount.toFloat() / playerWidthPlusPadding
+ } else {
+ 0f
+ }
// Fix the location, because PageIndicator does not handle RTL internally
val location =
if (isRtl) {
diff --git a/packages/SystemUI/src/com/android/systemui/mediaprojection/appselector/view/MediaProjectionRecentsViewController.kt b/packages/SystemUI/src/com/android/systemui/mediaprojection/appselector/view/MediaProjectionRecentsViewController.kt
index 64de9bd..5d732fb 100644
--- a/packages/SystemUI/src/com/android/systemui/mediaprojection/appselector/view/MediaProjectionRecentsViewController.kt
+++ b/packages/SystemUI/src/com/android/systemui/mediaprojection/appselector/view/MediaProjectionRecentsViewController.kt
@@ -35,8 +35,8 @@
import javax.inject.Inject
/**
- * Controller that handles view of the recent apps selector in the media projection activity.
- * It is responsible for creating and updating recent apps view.
+ * Controller that handles view of the recent apps selector in the media projection activity. It is
+ * responsible for creating and updating recent apps view.
*/
@MediaProjectionAppSelectorScope
class MediaProjectionRecentsViewController
@@ -51,15 +51,21 @@
private var views: Views? = null
private var lastBoundData: List<RecentTask>? = null
+ val hasRecentTasks: Boolean
+ get() = lastBoundData?.isNotEmpty() ?: false
+
init {
taskViewSizeProvider.addCallback(this)
}
fun createView(parent: ViewGroup): ViewGroup =
- views?.root ?: createRecentViews(parent).also {
- views = it
- lastBoundData?.let { recents -> bind(recents) }
- }.root
+ views?.root
+ ?: createRecentViews(parent)
+ .also {
+ views = it
+ lastBoundData?.let { recents -> bind(recents) }
+ }
+ .root
fun bind(recentTasks: List<RecentTask>) {
views?.apply {
@@ -88,7 +94,8 @@
.inflate(R.layout.media_projection_recent_tasks, parent, /* attachToRoot= */ false)
as ViewGroup
- val container = recentsRoot.requireViewById<View>(R.id.media_projection_recent_tasks_container)
+ val container =
+ recentsRoot.requireViewById<View>(R.id.media_projection_recent_tasks_container)
container.setTaskHeightSize()
val progress = recentsRoot.requireViewById<View>(R.id.media_projection_recent_tasks_loader)
diff --git a/packages/SystemUI/src/com/android/systemui/power/PowerUI.java b/packages/SystemUI/src/com/android/systemui/power/PowerUI.java
index 5a1ad96..e1e1aae 100644
--- a/packages/SystemUI/src/com/android/systemui/power/PowerUI.java
+++ b/packages/SystemUI/src/com/android/systemui/power/PowerUI.java
@@ -35,11 +35,14 @@
import android.os.Temperature;
import android.os.UserHandle;
import android.provider.Settings;
+import android.service.vr.IVrManager;
+import android.service.vr.IVrStateCallbacks;
import android.text.format.DateUtils;
import android.util.Log;
import android.util.Slog;
import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
import com.android.internal.annotations.VisibleForTesting;
import com.android.settingslib.fuelgauge.Estimate;
@@ -51,17 +54,13 @@
import com.android.systemui.keyguard.WakefulnessLifecycle;
import com.android.systemui.settings.UserTracker;
import com.android.systemui.statusbar.CommandQueue;
-import com.android.systemui.statusbar.phone.CentralSurfaces;
import java.io.PrintWriter;
import java.util.Arrays;
-import java.util.Optional;
import java.util.concurrent.Future;
import javax.inject.Inject;
-import dagger.Lazy;
-
@SysUISingleton
public class PowerUI implements CoreStartable, CommandQueue.Callbacks {
@@ -107,12 +106,15 @@
@VisibleForTesting int mBatteryLevel = 100;
@VisibleForTesting int mBatteryStatus = BatteryManager.BATTERY_STATUS_UNKNOWN;
+ private boolean mInVrMode;
+
private IThermalEventListener mSkinThermalEventListener;
private IThermalEventListener mUsbThermalEventListener;
private final Context mContext;
private final BroadcastDispatcher mBroadcastDispatcher;
private final CommandQueue mCommandQueue;
- private final Lazy<Optional<CentralSurfaces>> mCentralSurfacesOptionalLazy;
+ @Nullable
+ private final IVrManager mVrManager;
private final WakefulnessLifecycle.Observer mWakefulnessObserver =
new WakefulnessLifecycle.Observer() {
@Override
@@ -134,17 +136,28 @@
}
};
+ private final IVrStateCallbacks mVrStateCallbacks = new IVrStateCallbacks.Stub() {
+ @Override
+ public void onVrStateChanged(boolean enabled) {
+ mInVrMode = enabled;
+ }
+ };
+
@Inject
- public PowerUI(Context context, BroadcastDispatcher broadcastDispatcher,
- CommandQueue commandQueue, Lazy<Optional<CentralSurfaces>> centralSurfacesOptionalLazy,
- WarningsUI warningsUI, EnhancedEstimates enhancedEstimates,
+ public PowerUI(
+ Context context,
+ BroadcastDispatcher broadcastDispatcher,
+ CommandQueue commandQueue,
+ @Nullable IVrManager vrManager,
+ WarningsUI warningsUI,
+ EnhancedEstimates enhancedEstimates,
WakefulnessLifecycle wakefulnessLifecycle,
PowerManager powerManager,
UserTracker userTracker) {
mContext = context;
mBroadcastDispatcher = broadcastDispatcher;
mCommandQueue = commandQueue;
- mCentralSurfacesOptionalLazy = centralSurfacesOptionalLazy;
+ mVrManager = vrManager;
mWarnings = warningsUI;
mEnhancedEstimates = enhancedEstimates;
mPowerManager = powerManager;
@@ -164,7 +177,7 @@
};
final ContentResolver resolver = mContext.getContentResolver();
resolver.registerContentObserver(Settings.Global.getUriFor(
- Settings.Global.LOW_POWER_MODE_TRIGGER_LEVEL),
+ Settings.Global.LOW_POWER_MODE_TRIGGER_LEVEL),
false, obs, UserHandle.USER_ALL);
updateBatteryWarningLevels();
mReceiver.init();
@@ -199,6 +212,14 @@
});
initThermalEventListeners();
mCommandQueue.addCallback(this);
+
+ if (mVrManager != null) {
+ try {
+ mVrManager.registerListener(mVrStateCallbacks);
+ } catch (RemoteException e) {
+ Slog.e(TAG, "Failed to register VR mode state listener: " + e);
+ }
+ }
}
@Override
@@ -718,10 +739,7 @@
int status = temp.getStatus();
if (status >= Temperature.THROTTLING_EMERGENCY) {
- final Optional<CentralSurfaces> centralSurfacesOptional =
- mCentralSurfacesOptionalLazy.get();
- if (!centralSurfacesOptional.map(CentralSurfaces::isDeviceInVrMode)
- .orElse(false)) {
+ if (!mInVrMode) {
mWarnings.showHighTemperatureWarning();
Slog.d(TAG, "SkinThermalEventListener: notifyThrottling was called "
+ ", current skin status = " + status
diff --git a/packages/SystemUI/src/com/android/systemui/power/data/repository/PowerRepository.kt b/packages/SystemUI/src/com/android/systemui/power/data/repository/PowerRepository.kt
index 69cb611..b2a8719 100644
--- a/packages/SystemUI/src/com/android/systemui/power/data/repository/PowerRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/power/data/repository/PowerRepository.kt
@@ -39,6 +39,9 @@
/** Wakes up the device. */
fun wakeUp(why: String, @PowerManager.WakeReason wakeReason: Int)
+
+ /** Notifies the power repository that a user touch happened. */
+ fun userTouch()
}
@SysUISingleton
@@ -83,6 +86,14 @@
)
}
+ override fun userTouch() {
+ manager.userActivity(
+ systemClock.uptimeMillis(),
+ PowerManager.USER_ACTIVITY_EVENT_TOUCH,
+ /* flags= */ 0,
+ )
+ }
+
companion object {
private const val TAG = "PowerRepository"
}
diff --git a/packages/SystemUI/src/com/android/systemui/power/domain/interactor/PowerInteractor.kt b/packages/SystemUI/src/com/android/systemui/power/domain/interactor/PowerInteractor.kt
index 809edc0..16885ed 100644
--- a/packages/SystemUI/src/com/android/systemui/power/domain/interactor/PowerInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/power/domain/interactor/PowerInteractor.kt
@@ -19,6 +19,7 @@
import android.os.PowerManager
import com.android.systemui.classifier.FalsingCollector
+import com.android.systemui.classifier.FalsingCollectorActual
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.keyguard.data.repository.KeyguardRepository
import com.android.systemui.plugins.statusbar.StatusBarStateController
@@ -34,7 +35,7 @@
constructor(
private val repository: PowerRepository,
private val keyguardRepository: KeyguardRepository,
- private val falsingCollector: FalsingCollector,
+ @FalsingCollectorActual private val falsingCollector: FalsingCollector,
private val screenOffAnimationController: ScreenOffAnimationController,
private val statusBarStateController: StatusBarStateController,
) {
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/CastTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/CastTile.java
index 54376fe..e9a2428 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/CastTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/CastTile.java
@@ -18,6 +18,8 @@
import static android.media.MediaRouter.ROUTE_TYPE_REMOTE_DISPLAY;
+import static com.android.systemui.flags.Flags.SIGNAL_CALLBACK_DEPRECATION;
+
import android.annotation.NonNull;
import android.app.Dialog;
import android.content.Intent;
@@ -42,6 +44,7 @@
import com.android.systemui.animation.DialogLaunchAnimator;
import com.android.systemui.dagger.qualifiers.Background;
import com.android.systemui.dagger.qualifiers.Main;
+import com.android.systemui.flags.FeatureFlags;
import com.android.systemui.plugins.ActivityStarter;
import com.android.systemui.plugins.FalsingManager;
import com.android.systemui.plugins.qs.QSTile.BooleanState;
@@ -54,6 +57,8 @@
import com.android.systemui.statusbar.connectivity.SignalCallback;
import com.android.systemui.statusbar.connectivity.WifiIndicators;
import com.android.systemui.statusbar.phone.SystemUIDialog;
+import com.android.systemui.statusbar.pipeline.wifi.domain.interactor.WifiInteractor;
+import com.android.systemui.statusbar.pipeline.wifi.shared.model.WifiNetworkModel;
import com.android.systemui.statusbar.policy.CastController;
import com.android.systemui.statusbar.policy.CastController.CastDevice;
import com.android.systemui.statusbar.policy.HotspotController;
@@ -61,6 +66,7 @@
import java.util.ArrayList;
import java.util.List;
+import java.util.function.Consumer;
import javax.inject.Inject;
@@ -79,6 +85,9 @@
private final NetworkController mNetworkController;
private final DialogLaunchAnimator mDialogLaunchAnimator;
private final Callback mCallback = new Callback();
+ private final WifiInteractor mWifiInteractor;
+ private final TileJavaAdapter mJavaAdapter;
+ private final FeatureFlags mFeatureFlags;
private boolean mWifiConnected;
private boolean mHotspotConnected;
@@ -97,7 +106,10 @@
KeyguardStateController keyguardStateController,
NetworkController networkController,
HotspotController hotspotController,
- DialogLaunchAnimator dialogLaunchAnimator
+ DialogLaunchAnimator dialogLaunchAnimator,
+ WifiInteractor wifiInteractor,
+ TileJavaAdapter javaAdapter,
+ FeatureFlags featureFlags
) {
super(host, uiEventLogger, backgroundLooper, mainHandler, falsingManager, metricsLogger,
statusBarStateController, activityStarter, qsLogger);
@@ -105,9 +117,16 @@
mKeyguard = keyguardStateController;
mNetworkController = networkController;
mDialogLaunchAnimator = dialogLaunchAnimator;
+ mWifiInteractor = wifiInteractor;
+ mJavaAdapter = javaAdapter;
+ mFeatureFlags = featureFlags;
mController.observe(this, mCallback);
mKeyguard.observe(this, mCallback);
- mNetworkController.observe(this, mSignalCallback);
+ if (!mFeatureFlags.isEnabled(SIGNAL_CALLBACK_DEPRECATION)) {
+ mNetworkController.observe(this, mSignalCallback);
+ } else {
+ mJavaAdapter.bind(this, mWifiInteractor.getWifiNetwork(), mNetworkModelConsumer);
+ }
hotspotController.observe(this, mHotspotCallback);
}
@@ -293,19 +312,37 @@
return mWifiConnected || mHotspotConnected;
}
+ private void setWifiConnected(boolean connected) {
+ if (connected != mWifiConnected) {
+ mWifiConnected = connected;
+ // Hotspot is not connected, so changes here should update
+ if (!mHotspotConnected) {
+ refreshState();
+ }
+ }
+ }
+
+ private void setHotspotConnected(boolean connected) {
+ if (connected != mHotspotConnected) {
+ mHotspotConnected = connected;
+ // Wifi is not connected, so changes here should update
+ if (!mWifiConnected) {
+ refreshState();
+ }
+ }
+ }
+
+ private final Consumer<WifiNetworkModel> mNetworkModelConsumer = (model) -> {
+ setWifiConnected(model instanceof WifiNetworkModel.Active);
+ };
+
private final SignalCallback mSignalCallback = new SignalCallback() {
@Override
public void setWifiIndicators(@NonNull WifiIndicators indicators) {
// statusIcon.visible has the connected status information
boolean enabledAndConnected = indicators.enabled
- && (indicators.qsIcon == null ? false : indicators.qsIcon.visible);
- if (enabledAndConnected != mWifiConnected) {
- mWifiConnected = enabledAndConnected;
- // Hotspot is not connected, so changes here should update
- if (!mHotspotConnected) {
- refreshState();
- }
- }
+ && (indicators.qsIcon != null && indicators.qsIcon.visible);
+ setWifiConnected(enabledAndConnected);
}
};
@@ -314,13 +351,7 @@
@Override
public void onHotspotChanged(boolean enabled, int numDevices) {
boolean enabledAndConnected = enabled && numDevices > 0;
- if (enabledAndConnected != mHotspotConnected) {
- mHotspotConnected = enabledAndConnected;
- // Wifi is not connected, so changes here should update
- if (!mWifiConnected) {
- refreshState();
- }
- }
+ setHotspotConnected(enabledAndConnected);
}
};
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/InternetTileNewImpl.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/InternetTileNewImpl.kt
new file mode 100644
index 0000000..3b2f8b7
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/InternetTileNewImpl.kt
@@ -0,0 +1,114 @@
+/*
+ * 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.qs.tiles
+
+import android.content.Context
+import android.content.Intent
+import android.os.Handler
+import android.os.Looper
+import android.provider.Settings
+import android.view.View
+import com.android.internal.logging.MetricsLogger
+import com.android.systemui.R
+import com.android.systemui.dagger.qualifiers.Background
+import com.android.systemui.dagger.qualifiers.Main
+import com.android.systemui.plugins.ActivityStarter
+import com.android.systemui.plugins.FalsingManager
+import com.android.systemui.plugins.qs.QSIconView
+import com.android.systemui.plugins.qs.QSTile
+import com.android.systemui.plugins.statusbar.StatusBarStateController
+import com.android.systemui.qs.AlphaControlledSignalTileView
+import com.android.systemui.qs.QSHost
+import com.android.systemui.qs.QsEventLogger
+import com.android.systemui.qs.logging.QSLogger
+import com.android.systemui.qs.tileimpl.QSTileImpl
+import com.android.systemui.qs.tiles.dialog.InternetDialogFactory
+import com.android.systemui.statusbar.connectivity.AccessPointController
+import com.android.systemui.statusbar.pipeline.shared.ui.binder.InternetTileBinder
+import com.android.systemui.statusbar.pipeline.shared.ui.model.InternetTileModel
+import com.android.systemui.statusbar.pipeline.shared.ui.viewmodel.InternetTileViewModel
+import javax.inject.Inject
+
+class InternetTileNewImpl
+@Inject
+constructor(
+ host: QSHost,
+ uiEventLogger: QsEventLogger,
+ @Background backgroundLooper: Looper,
+ @Main private val mainHandler: Handler,
+ falsingManager: FalsingManager,
+ metricsLogger: MetricsLogger,
+ statusBarStateController: StatusBarStateController,
+ activityStarter: ActivityStarter,
+ qsLogger: QSLogger,
+ viewModel: InternetTileViewModel,
+ private val internetDialogFactory: InternetDialogFactory,
+ private val accessPointController: AccessPointController,
+) :
+ QSTileImpl<QSTile.SignalState>(
+ host,
+ uiEventLogger,
+ backgroundLooper,
+ mainHandler,
+ falsingManager,
+ metricsLogger,
+ statusBarStateController,
+ activityStarter,
+ qsLogger
+ ) {
+ private var model: InternetTileModel = viewModel.tileModel.value
+
+ init {
+ InternetTileBinder.bind(lifecycle, viewModel.tileModel) { newModel ->
+ model = newModel
+ refreshState()
+ }
+ }
+
+ override fun createTileView(context: Context): QSIconView =
+ AlphaControlledSignalTileView(context)
+
+ override fun getTileLabel(): CharSequence =
+ mContext.getString(R.string.quick_settings_internet_label)
+
+ override fun newTileState(): QSTile.SignalState {
+ return QSTile.SignalState().also { it.forceExpandIcon = true }
+ }
+
+ override fun handleClick(view: View?) {
+ mainHandler.post {
+ internetDialogFactory.create(
+ aboveStatusBar = true,
+ accessPointController.canConfigMobileData(),
+ accessPointController.canConfigWifi(),
+ view,
+ )
+ }
+ }
+
+ override fun handleUpdateState(state: QSTile.SignalState, arg: Any?) {
+ state.label = mContext.resources.getString(R.string.quick_settings_internet_label)
+
+ model.applyTo(state, mContext)
+ }
+
+ override fun getLongClickIntent(): Intent = WIFI_SETTINGS
+
+ companion object {
+ private val WIFI_SETTINGS = Intent(Settings.ACTION_WIFI_SETTINGS)
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/TileJavaAdapter.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/TileJavaAdapter.kt
new file mode 100644
index 0000000..a2430ad
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/TileJavaAdapter.kt
@@ -0,0 +1,47 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+package com.android.systemui.qs.tiles
+
+import androidx.lifecycle.Lifecycle
+import androidx.lifecycle.LifecycleOwner
+import androidx.lifecycle.lifecycleScope
+import androidx.lifecycle.repeatOnLifecycle
+import com.android.systemui.dagger.SysUISingleton
+import java.util.function.Consumer
+import javax.inject.Inject
+import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.collect
+import kotlinx.coroutines.launch
+
+/**
+ * Utility for binding tiles to kotlin flows. Similar to [JavaAdapter] and usable for QS tiles. We
+ * use [Lifecycle.State.RESUMED] here to match the implementation of [CallbackController.observe]
+ */
+@SysUISingleton
+class TileJavaAdapter @Inject constructor() {
+ fun <T> bind(
+ lifecycleOwner: LifecycleOwner,
+ flow: Flow<T>,
+ consumer: Consumer<T>,
+ ) {
+ lifecycleOwner.lifecycleScope.launch {
+ lifecycleOwner.repeatOnLifecycle(Lifecycle.State.RESUMED) {
+ flow.collect { consumer.accept(it) }
+ }
+ }
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/scene/data/repository/WindowRootViewVisibilityRepository.kt b/packages/SystemUI/src/com/android/systemui/scene/data/repository/WindowRootViewVisibilityRepository.kt
new file mode 100644
index 0000000..d833e56
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/scene/data/repository/WindowRootViewVisibilityRepository.kt
@@ -0,0 +1,74 @@
+/*
+ * 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.scene.data.repository
+
+import android.os.RemoteException
+import com.android.internal.statusbar.IStatusBarService
+import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.dagger.qualifiers.UiBackground
+import java.util.concurrent.Executor
+import javax.inject.Inject
+import kotlinx.coroutines.flow.MutableStateFlow
+import kotlinx.coroutines.flow.StateFlow
+import kotlinx.coroutines.flow.asStateFlow
+
+/** Source of truth for the visibility of various parts of the window root view. */
+@SysUISingleton
+class WindowRootViewVisibilityRepository
+@Inject
+constructor(
+ private val statusBarService: IStatusBarService,
+ @UiBackground private val uiBgExecutor: Executor,
+) {
+ private val _isLockscreenOrShadeVisible = MutableStateFlow(false)
+ val isLockscreenOrShadeVisible: StateFlow<Boolean> = _isLockscreenOrShadeVisible.asStateFlow()
+
+ fun setIsLockscreenOrShadeVisible(visible: Boolean) {
+ _isLockscreenOrShadeVisible.value = visible
+ }
+
+ /**
+ * Called when the lockscreen or shade has been shown and can be interacted with so that SysUI
+ * can notify external services.
+ */
+ fun onLockscreenOrShadeInteractive(
+ shouldClearNotificationEffects: Boolean,
+ notificationCount: Int,
+ ) {
+ executeServiceCallOnUiBg {
+ statusBarService.onPanelRevealed(shouldClearNotificationEffects, notificationCount)
+ }
+ }
+
+ /**
+ * Called when the lockscreen or shade no longer can be interactecd with so that SysUI can
+ * notify external services.
+ */
+ fun onLockscreenOrShadeNotInteractive() {
+ executeServiceCallOnUiBg { statusBarService.onPanelHidden() }
+ }
+
+ private fun executeServiceCallOnUiBg(runnable: () -> Unit) {
+ uiBgExecutor.execute {
+ try {
+ runnable.invoke()
+ } catch (ex: RemoteException) {
+ // Won't fail unless the world has ended
+ }
+ }
+ }
+}
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 a7434c6..45ee7be 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
@@ -18,6 +18,7 @@
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Application
+import com.android.systemui.power.data.repository.PowerRepository
import com.android.systemui.scene.data.repository.SceneContainerRepository
import com.android.systemui.scene.shared.logger.SceneLogger
import com.android.systemui.scene.shared.model.ObservableTransitionState
@@ -44,6 +45,7 @@
constructor(
@Application applicationScope: CoroutineScope,
private val repository: SceneContainerRepository,
+ private val powerRepository: PowerRepository,
private val logger: SceneLogger,
) {
@@ -153,6 +155,11 @@
repository.setTransitionState(transitionState)
}
+ /** Handles a user input event. */
+ fun onUserInput() {
+ powerRepository.userTouch()
+ }
+
/**
* Notifies that the UI has transitioned sufficiently to the given scene.
*
@@ -161,7 +168,7 @@
* Once a transition between one scene and another passes a threshold, the UI invokes this
* method to report it, updating the value in [desiredScene] to match what the UI shows.
*/
- internal fun onSceneChanged(scene: SceneModel, loggingReason: String) {
+ fun onSceneChanged(scene: SceneModel, loggingReason: String) {
updateDesiredScene(scene, loggingReason, logger::logSceneChangeCommitted)
}
diff --git a/packages/SystemUI/src/com/android/systemui/scene/domain/interactor/WindowRootViewVisibilityInteractor.kt b/packages/SystemUI/src/com/android/systemui/scene/domain/interactor/WindowRootViewVisibilityInteractor.kt
new file mode 100644
index 0000000..16ffcc2
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/scene/domain/interactor/WindowRootViewVisibilityInteractor.kt
@@ -0,0 +1,121 @@
+/*
+ * 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.scene.domain.interactor
+
+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.KeyguardRepository
+import com.android.systemui.keyguard.shared.model.StatusBarState
+import com.android.systemui.scene.data.repository.WindowRootViewVisibilityRepository
+import com.android.systemui.statusbar.NotificationPresenter
+import com.android.systemui.statusbar.notification.init.NotificationsController
+import com.android.systemui.statusbar.policy.HeadsUpManager
+import javax.inject.Inject
+import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.flow.SharingStarted
+import kotlinx.coroutines.flow.StateFlow
+import kotlinx.coroutines.flow.combine
+import kotlinx.coroutines.flow.stateIn
+import kotlinx.coroutines.launch
+
+/** Business logic about the visibility of various parts of the window root view. */
+@SysUISingleton
+class WindowRootViewVisibilityInteractor
+@Inject
+constructor(
+ @Application private val scope: CoroutineScope,
+ private val windowRootViewVisibilityRepository: WindowRootViewVisibilityRepository,
+ private val keyguardRepository: KeyguardRepository,
+ private val headsUpManager: HeadsUpManager,
+) : CoreStartable {
+
+ private var notificationPresenter: NotificationPresenter? = null
+ private var notificationsController: NotificationsController? = null
+
+ private val isNotifPresenterFullyCollapsed: Boolean
+ get() = notificationPresenter?.isPresenterFullyCollapsed ?: true
+
+ /**
+ * True if lockscreen (including AOD) or the shade is visible and false otherwise. Notably,
+ * false if the bouncer is visible.
+ *
+ * TODO(b/297080059): Use [SceneInteractor] as the source of truth if the scene flag is on.
+ */
+ val isLockscreenOrShadeVisible: StateFlow<Boolean> =
+ windowRootViewVisibilityRepository.isLockscreenOrShadeVisible
+
+ /**
+ * True if lockscreen (including AOD) or the shade is visible **and** the user is currently
+ * interacting with the device, false otherwise. Notably, false if the bouncer is visible and
+ * false if the device is asleep.
+ */
+ val isLockscreenOrShadeVisibleAndInteractive: StateFlow<Boolean> =
+ combine(
+ isLockscreenOrShadeVisible,
+ keyguardRepository.wakefulness,
+ ) { isKeyguardAodOrShadeVisible, wakefulness ->
+ isKeyguardAodOrShadeVisible && wakefulness.isDeviceInteractive()
+ }
+ .stateIn(scope, SharingStarted.Eagerly, initialValue = false)
+
+ /**
+ * Sets classes that aren't easily injectable on this class.
+ *
+ * TODO(b/277762009): Inject these directly instead.
+ */
+ fun setUp(
+ presenter: NotificationPresenter?,
+ notificationsController: NotificationsController?,
+ ) {
+ this.notificationPresenter = presenter
+ this.notificationsController = notificationsController
+ }
+
+ override fun start() {
+ scope.launch {
+ isLockscreenOrShadeVisibleAndInteractive.collect { interactive ->
+ if (interactive) {
+ windowRootViewVisibilityRepository.onLockscreenOrShadeInteractive(
+ getShouldClearNotificationEffects(keyguardRepository.statusBarState.value),
+ getNotificationLoad(),
+ )
+ } else {
+ windowRootViewVisibilityRepository.onLockscreenOrShadeNotInteractive()
+ }
+ }
+ }
+ }
+
+ fun setIsLockscreenOrShadeVisible(visible: Boolean) {
+ windowRootViewVisibilityRepository.setIsLockscreenOrShadeVisible(visible)
+ }
+
+ private fun getShouldClearNotificationEffects(statusBarState: StatusBarState): Boolean {
+ return !isNotifPresenterFullyCollapsed &&
+ (statusBarState == StatusBarState.SHADE ||
+ statusBarState == StatusBarState.SHADE_LOCKED)
+ }
+
+ private fun getNotificationLoad(): Int {
+ return if (headsUpManager.hasPinnedHeadsUp() && isNotifPresenterFullyCollapsed) {
+ 1
+ } else {
+ notificationsController?.getActiveNotificationsCount() ?: 0
+ }
+ }
+}
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 1747099..7f77acc 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
@@ -14,11 +14,15 @@
* limitations under the License.
*/
+@file:OptIn(ExperimentalCoroutinesApi::class)
+
package com.android.systemui.scene.domain.startable
import com.android.systemui.CoreStartable
import com.android.systemui.authentication.domain.interactor.AuthenticationInteractor
import com.android.systemui.authentication.domain.model.AuthenticationMethodModel
+import com.android.systemui.classifier.FalsingCollector
+import com.android.systemui.classifier.FalsingCollectorActual
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Application
import com.android.systemui.dagger.qualifiers.DisplayId
@@ -40,7 +44,12 @@
import com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_STATUS_BAR_KEYGUARD_SHOWING
import javax.inject.Inject
import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.flow.distinctUntilChanged
+import kotlinx.coroutines.flow.distinctUntilChangedBy
+import kotlinx.coroutines.flow.emptyFlow
+import kotlinx.coroutines.flow.filterNotNull
+import kotlinx.coroutines.flow.flatMapLatest
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.flow.mapNotNull
import kotlinx.coroutines.launch
@@ -61,6 +70,7 @@
private val sysUiState: SysUiState,
@DisplayId private val displayId: Int,
private val sceneLogger: SceneLogger,
+ @FalsingCollectorActual private val falsingCollector: FalsingCollector,
) : CoreStartable {
override fun start() {
@@ -69,6 +79,7 @@
hydrateVisibility()
automaticallySwitchScenes()
hydrateSystemUiState()
+ collectFalsingSignals()
} else {
sceneLogger.logFrameworkEnabled(isEnabled = false)
}
@@ -225,6 +236,66 @@
}
}
+ /** Collects and reports signals into the falsing system. */
+ private fun collectFalsingSignals() {
+ applicationScope.launch {
+ authenticationInteractor.isLockscreenDismissed.collect { isLockscreenDismissed ->
+ if (isLockscreenDismissed) {
+ falsingCollector.onSuccessfulUnlock()
+ }
+ }
+ }
+
+ applicationScope.launch {
+ keyguardInteractor.isDozing.distinctUntilChanged().collect { isDozing ->
+ falsingCollector.setShowingAod(isDozing)
+ }
+ }
+
+ applicationScope.launch {
+ keyguardInteractor.isAodAvailable
+ .flatMapLatest { isAodAvailable ->
+ if (!isAodAvailable) {
+ keyguardInteractor.wakefulnessModel
+ } else {
+ emptyFlow()
+ }
+ }
+ .map { wakefulnessModel ->
+ val wakeChange: Boolean? =
+ when (wakefulnessModel.state) {
+ WakefulnessState.STARTING_TO_WAKE -> true
+ WakefulnessState.ASLEEP -> false
+ else -> null
+ }
+ (wakeChange to wakefulnessModel.lastWakeReason).takeIf { wakeChange != null }
+ }
+ .filterNotNull()
+ .distinctUntilChangedBy { it.first }
+ .collect { (wakeChange, wakeReason) ->
+ when {
+ wakeChange == true && wakeReason.isTouch ->
+ falsingCollector.onScreenOnFromTouch()
+ wakeChange == true -> falsingCollector.onScreenTurningOn()
+ wakeChange == false -> falsingCollector.onScreenOff()
+ }
+ }
+ }
+
+ applicationScope.launch {
+ sceneInteractor.desiredScene
+ .map { it.key == SceneKey.Bouncer }
+ .distinctUntilChanged()
+ .collect { switchedToBouncerScene ->
+ if (switchedToBouncerScene) {
+ falsingCollector.onBouncerShown()
+ } else {
+ falsingCollector.onBouncerHidden()
+ }
+ }
+ }
+ }
+
private fun switchToScene(targetSceneKey: SceneKey, loggingReason: String) {
sceneInteractor.changeScene(
scene = SceneModel(targetSceneKey),
diff --git a/packages/SystemUI/src/com/android/systemui/scene/domain/startable/SceneContainerStartableModule.kt b/packages/SystemUI/src/com/android/systemui/scene/domain/startable/SceneContainerStartableModule.kt
index 8da1803..fcfdceb 100644
--- a/packages/SystemUI/src/com/android/systemui/scene/domain/startable/SceneContainerStartableModule.kt
+++ b/packages/SystemUI/src/com/android/systemui/scene/domain/startable/SceneContainerStartableModule.kt
@@ -17,6 +17,7 @@
package com.android.systemui.scene.domain.startable
import com.android.systemui.CoreStartable
+import com.android.systemui.scene.domain.interactor.WindowRootViewVisibilityInteractor
import dagger.Binds
import dagger.Module
import dagger.multibindings.ClassKey
@@ -29,4 +30,11 @@
@IntoMap
@ClassKey(SceneContainerStartable::class)
fun bind(impl: SceneContainerStartable): CoreStartable
+
+ @Binds
+ @IntoMap
+ @ClassKey(WindowRootViewVisibilityInteractor::class)
+ fun bindWindowRootViewVisibilityInteractor(
+ impl: WindowRootViewVisibilityInteractor
+ ): CoreStartable
}
diff --git a/packages/SystemUI/src/com/android/systemui/scene/ui/view/SceneWindowRootViewBinder.kt b/packages/SystemUI/src/com/android/systemui/scene/ui/view/SceneWindowRootViewBinder.kt
index f9324a9..3e76607 100644
--- a/packages/SystemUI/src/com/android/systemui/scene/ui/view/SceneWindowRootViewBinder.kt
+++ b/packages/SystemUI/src/com/android/systemui/scene/ui/view/SceneWindowRootViewBinder.kt
@@ -110,6 +110,7 @@
// SysUI altogether.
private fun createVisibilityToggleView(otherView: View): View {
val toggleView = View(otherView.context)
+ otherView.isVisible = false
toggleView.layoutParams = FrameLayout.LayoutParams(200, 200, Gravity.CENTER_HORIZONTAL)
toggleView.setOnClickListener {
val now = Instant.now()
diff --git a/packages/SystemUI/src/com/android/systemui/scene/ui/view/WindowRootView.kt b/packages/SystemUI/src/com/android/systemui/scene/ui/view/WindowRootView.kt
index c9a73e6..ef688a8 100644
--- a/packages/SystemUI/src/com/android/systemui/scene/ui/view/WindowRootView.kt
+++ b/packages/SystemUI/src/com/android/systemui/scene/ui/view/WindowRootView.kt
@@ -1,3 +1,19 @@
+/*
+ * 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.scene.ui.view
import android.annotation.SuppressLint
diff --git a/packages/SystemUI/src/com/android/systemui/scene/ui/viewmodel/SceneContainerViewModel.kt b/packages/SystemUI/src/com/android/systemui/scene/ui/viewmodel/SceneContainerViewModel.kt
index 5c16fb5..2431660 100644
--- a/packages/SystemUI/src/com/android/systemui/scene/ui/viewmodel/SceneContainerViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/scene/ui/viewmodel/SceneContainerViewModel.kt
@@ -16,6 +16,8 @@
package com.android.systemui.scene.ui.viewmodel
+import android.view.MotionEvent
+import com.android.systemui.classifier.domain.interactor.FalsingInteractor
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.scene.domain.interactor.SceneInteractor
import com.android.systemui.scene.shared.model.ObservableTransitionState
@@ -30,7 +32,8 @@
class SceneContainerViewModel
@Inject
constructor(
- private val interactor: SceneInteractor,
+ private val sceneInteractor: SceneInteractor,
+ private val falsingInteractor: FalsingInteractor,
) {
/**
* Keys of all scenes in the container.
@@ -38,17 +41,17 @@
* The scenes will be sorted in z-order such that the last one is the one that should be
* rendered on top of all previous ones.
*/
- val allSceneKeys: List<SceneKey> = interactor.allSceneKeys()
+ val allSceneKeys: List<SceneKey> = sceneInteractor.allSceneKeys()
/** The scene that should be rendered. */
- val currentScene: StateFlow<SceneModel> = interactor.desiredScene
+ val currentScene: StateFlow<SceneModel> = sceneInteractor.desiredScene
/** Whether the container is visible. */
- val isVisible: StateFlow<Boolean> = interactor.isVisible
+ val isVisible: StateFlow<Boolean> = sceneInteractor.isVisible
/** Notifies that the UI has transitioned sufficiently to the given scene. */
fun onSceneChanged(scene: SceneModel) {
- interactor.onSceneChanged(
+ sceneInteractor.onSceneChanged(
scene = scene,
loggingReason = SCENE_TRANSITION_LOGGING_REASON,
)
@@ -60,7 +63,27 @@
* Note that you must call is with `null` when the UI is done or risk a memory leak.
*/
fun setTransitionState(transitionState: Flow<ObservableTransitionState>?) {
- interactor.setTransitionState(transitionState)
+ sceneInteractor.setTransitionState(transitionState)
+ }
+
+ /**
+ * Notifies that a [MotionEvent] is first seen at the top of the scene container UI.
+ *
+ * Call this before the [MotionEvent] starts to propagate through the UI hierarchy.
+ */
+ fun onMotionEvent(event: MotionEvent) {
+ sceneInteractor.onUserInput()
+ falsingInteractor.onTouchEvent(event)
+ }
+
+ /**
+ * Notifies that a [MotionEvent] that was previously sent to [onMotionEvent] has passed through
+ * the scene container UI.
+ *
+ * Call this after the [MotionEvent] propagates completely through the UI hierarchy.
+ */
+ fun onMotionEventComplete() {
+ falsingInteractor.onMotionEventComplete()
}
companion object {
diff --git a/packages/SystemUI/src/com/android/systemui/screenrecord/BaseScreenSharePermissionDialog.kt b/packages/SystemUI/src/com/android/systemui/screenrecord/BaseScreenSharePermissionDialog.kt
index 7859fa0..8dd25bc 100644
--- a/packages/SystemUI/src/com/android/systemui/screenrecord/BaseScreenSharePermissionDialog.kt
+++ b/packages/SystemUI/src/com/android/systemui/screenrecord/BaseScreenSharePermissionDialog.kt
@@ -121,7 +121,7 @@
private fun createOptionsView(@LayoutRes layoutId: Int?) {
if (layoutId == null) return
- val stub = findViewById<View>(R.id.options_stub) as ViewStub
+ val stub = requireViewById<View>(R.id.options_stub) as ViewStub
stub.layoutResource = layoutId
stub.inflate()
}
@@ -144,8 +144,8 @@
override fun getDropDownView(position: Int, convertView: View?, parent: ViewGroup): View {
val inflater = LayoutInflater.from(parent.context)
val view = inflater.inflate(R.layout.screen_share_dialog_spinner_item_text, parent, false)
- val titleTextView = view.findViewById<TextView>(android.R.id.text1)
- val errorTextView = view.findViewById<TextView>(android.R.id.text2)
+ val titleTextView = view.requireViewById<TextView>(android.R.id.text1)
+ val errorTextView = view.requireViewById<TextView>(android.R.id.text2)
titleTextView.text = getItem(position)
errorTextView.text = options[position].spinnerDisabledText
if (isEnabled(position)) {
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/RequestProcessor.kt b/packages/SystemUI/src/com/android/systemui/screenshot/RequestProcessor.kt
index c0d807a..98f2fee 100644
--- a/packages/SystemUI/src/com/android/systemui/screenshot/RequestProcessor.kt
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/RequestProcessor.kt
@@ -28,8 +28,18 @@
import java.util.function.Consumer
import javax.inject.Inject
+/** Processes a screenshot request sent from [ScreenshotHelper]. */
+interface ScreenshotRequestProcessor {
+ /**
+ * Inspects the incoming ScreenshotData, potentially modifying it based upon policy.
+ *
+ * @param screenshot the screenshot to process
+ */
+ suspend fun process(screenshot: ScreenshotData): ScreenshotData
+}
+
/**
- * Processes a screenshot request sent from {@link ScreenshotHelper}.
+ * Implementation of [ScreenshotRequestProcessor]
*/
@SysUISingleton
class RequestProcessor @Inject constructor(
@@ -38,7 +48,7 @@
private val flags: FeatureFlags,
/** For the Java Async version, to invoke the callback. */
@Application private val mainScope: CoroutineScope
-) {
+) : ScreenshotRequestProcessor {
/**
* Inspects the incoming request, returning a potentially modified request depending on policy.
*
@@ -57,7 +67,6 @@
// regardless of the managed profile status.
if (request.type != TAKE_SCREENSHOT_PROVIDED_IMAGE) {
-
val info = policy.findPrimaryContent(policy.getDefaultDisplayId())
Log.d(TAG, "findPrimaryContent: $info")
@@ -99,12 +108,7 @@
}
}
- /**
- * Inspects the incoming ScreenshotData, potentially modifying it based upon policy.
- *
- * @param screenshot the screenshot to process
- */
- suspend fun process(screenshot: ScreenshotData): ScreenshotData {
+ override suspend fun process(screenshot: ScreenshotData): ScreenshotData {
var result = screenshot
// Apply work profile screenshots policy:
@@ -116,7 +120,7 @@
// regardless of the managed profile status.
if (screenshot.type != TAKE_SCREENSHOT_PROVIDED_IMAGE) {
- val info = policy.findPrimaryContent(policy.getDefaultDisplayId())
+ val info = policy.findPrimaryContent(screenshot.displayId)
Log.d(TAG, "findPrimaryContent: $info")
result.taskId = info.taskId
result.topComponent = info.component
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotController.java b/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotController.java
index b59106e..cf782b7 100644
--- a/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotController.java
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotController.java
@@ -100,11 +100,14 @@
import com.android.systemui.flags.Flags;
import com.android.systemui.screenshot.ScreenshotController.SavedImageData.ActionTransition;
import com.android.systemui.screenshot.TakeScreenshotService.RequestCallback;
-import com.android.systemui.settings.DisplayTracker;
import com.android.systemui.util.Assert;
import com.google.common.util.concurrent.ListenableFuture;
+import dagger.assisted.Assisted;
+import dagger.assisted.AssistedFactory;
+import dagger.assisted.AssistedInject;
+
import java.io.File;
import java.util.List;
import java.util.concurrent.CancellationException;
@@ -118,7 +121,6 @@
import java.util.function.Consumer;
import java.util.function.Supplier;
-import javax.inject.Inject;
/**
* Controls the state and flow for screenshots.
@@ -275,7 +277,7 @@
private final ScrollCaptureClient mScrollCaptureClient;
private final PhoneWindow mWindow;
private final DisplayManager mDisplayManager;
- private final DisplayTracker mDisplayTracker;
+ private final int mDisplayId;
private final ScrollCaptureController mScrollCaptureController;
private final LongScreenshotData mLongScreenshotHolder;
private final boolean mIsLowRamDevice;
@@ -314,7 +316,8 @@
| ActivityInfo.CONFIG_SCREEN_LAYOUT
| ActivityInfo.CONFIG_ASSETS_PATHS);
- @Inject
+
+ @AssistedInject
ScreenshotController(
Context context,
FeatureFlags flags,
@@ -335,7 +338,7 @@
UserManager userManager,
AssistContentRequester assistContentRequester,
MessageContainerController messageContainerController,
- DisplayTracker displayTracker
+ @Assisted int displayId
) {
mScreenshotSmartActions = screenshotSmartActions;
mNotificationsController = screenshotNotificationsController;
@@ -360,9 +363,9 @@
dismissScreenshot(SCREENSHOT_INTERACTION_TIMEOUT);
});
+ mDisplayId = displayId;
mDisplayManager = requireNonNull(context.getSystemService(DisplayManager.class));
- mDisplayTracker = displayTracker;
- final Context displayContext = context.createDisplayContext(getDefaultDisplay());
+ final Context displayContext = context.createDisplayContext(getDisplay());
mContext = (WindowContext) displayContext.createWindowContext(TYPE_SCREENSHOT, null);
mWindowManager = mContext.getSystemService(WindowManager.class);
mFlags = flags;
@@ -406,7 +409,7 @@
if (screenshot.getType() == WindowManager.TAKE_SCREENSHOT_FULLSCREEN) {
Rect bounds = getFullScreenRect();
screenshot.setBitmap(
- mImageCapture.captureDisplay(mDisplayTracker.getDefaultDisplayId(), bounds));
+ mImageCapture.captureDisplay(mDisplayId, bounds));
screenshot.setScreenBounds(bounds);
}
@@ -638,7 +641,7 @@
setWindowFocusable(false);
}
}, mActionExecutor, mFlags);
- mScreenshotView.setDefaultDisplay(mDisplayTracker.getDefaultDisplayId());
+ mScreenshotView.setDefaultDisplay(mDisplayId);
mScreenshotView.setDefaultTimeoutMillis(mScreenshotHandler.getDefaultTimeoutMillis());
mScreenshotView.setOnKeyListener((v, keyCode, event) -> {
@@ -727,8 +730,8 @@
if (mLastScrollCaptureRequest != null) {
mLastScrollCaptureRequest.cancel(true);
}
- final ListenableFuture<ScrollCaptureResponse> future =
- mScrollCaptureClient.request(mDisplayTracker.getDefaultDisplayId());
+ final ListenableFuture<ScrollCaptureResponse> future = mScrollCaptureClient.request(
+ mDisplayId);
mLastScrollCaptureRequest = future;
mLastScrollCaptureRequest.addListener(() ->
onScrollCaptureResponseReady(future, owner), mMainExecutor);
@@ -758,9 +761,8 @@
final ScrollCaptureResponse response = mLastScrollCaptureResponse;
mScreenshotView.showScrollChip(response.getPackageName(), /* onClick */ () -> {
DisplayMetrics displayMetrics = new DisplayMetrics();
- getDefaultDisplay().getRealMetrics(displayMetrics);
- Bitmap newScreenshot = mImageCapture.captureDisplay(
- mDisplayTracker.getDefaultDisplayId(),
+ getDisplay().getRealMetrics(displayMetrics);
+ Bitmap newScreenshot = mImageCapture.captureDisplay(mDisplayId,
new Rect(0, 0, displayMetrics.widthPixels, displayMetrics.heightPixels));
mScreenshotView.prepareScrollingTransition(response, mScreenBitmap, newScreenshot,
@@ -825,7 +827,7 @@
try {
WindowManagerGlobal.getWindowManagerService()
.overridePendingAppTransitionRemote(runner,
- mDisplayTracker.getDefaultDisplayId());
+ mDisplayId);
} catch (Exception e) {
Log.e(TAG, "Error overriding screenshot app transition", e);
}
@@ -1160,8 +1162,8 @@
}
}
- private Display getDefaultDisplay() {
- return mDisplayManager.getDisplay(mDisplayTracker.getDefaultDisplayId());
+ private Display getDisplay() {
+ return mDisplayManager.getDisplay(mDisplayId);
}
private boolean allowLongScreenshots() {
@@ -1170,7 +1172,7 @@
private Rect getFullScreenRect() {
DisplayMetrics displayMetrics = new DisplayMetrics();
- getDefaultDisplay().getRealMetrics(displayMetrics);
+ getDisplay().getRealMetrics(displayMetrics);
return new Rect(0, 0, displayMetrics.widthPixels, displayMetrics.heightPixels);
}
@@ -1229,4 +1231,11 @@
};
}
}
+
+ /** Injectable factory to create screenshot controller instances for a specific display. */
+ @AssistedFactory
+ public interface Factory {
+ /** Creates an instance of the controller for that specific displayId. */
+ ScreenshotController create(int displayId);
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotData.kt b/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotData.kt
index e9be88a..92e933a 100644
--- a/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotData.kt
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotData.kt
@@ -6,12 +6,13 @@
import android.graphics.Rect
import android.net.Uri
import android.os.UserHandle
+import android.view.Display
import android.view.WindowManager.ScreenshotSource
import android.view.WindowManager.ScreenshotType
import androidx.annotation.VisibleForTesting
import com.android.internal.util.ScreenshotRequest
-/** ScreenshotData represents the current state of a single screenshot being acquired. */
+/** [ScreenshotData] represents the current state of a single screenshot being acquired. */
data class ScreenshotData(
@ScreenshotType var type: Int,
@ScreenshotSource var source: Int,
@@ -23,6 +24,7 @@
var taskId: Int,
var insets: Insets,
var bitmap: Bitmap?,
+ var displayId: Int,
/** App-provided URL representing the content the user was looking at in the screenshot. */
var contextUrl: Uri? = null,
) {
@@ -31,22 +33,31 @@
companion object {
@JvmStatic
- fun fromRequest(request: ScreenshotRequest): ScreenshotData {
- return ScreenshotData(
- request.type,
- request.source,
- if (request.userId >= 0) UserHandle.of(request.userId) else null,
- request.topComponent,
- request.boundsInScreen,
- request.taskId,
- request.insets,
- request.bitmap,
+ fun fromRequest(request: ScreenshotRequest, displayId: Int = Display.DEFAULT_DISPLAY) =
+ ScreenshotData(
+ type = request.type,
+ source = request.source,
+ userHandle = if (request.userId >= 0) UserHandle.of(request.userId) else null,
+ topComponent = request.topComponent,
+ screenBounds = request.boundsInScreen,
+ taskId = request.taskId,
+ insets = request.insets,
+ bitmap = request.bitmap,
+ displayId = displayId,
)
- }
@VisibleForTesting
- fun forTesting(): ScreenshotData {
- return ScreenshotData(0, 0, null, null, null, 0, Insets.NONE, null)
- }
+ fun forTesting() =
+ ScreenshotData(
+ type = 0,
+ source = 0,
+ userHandle = null,
+ topComponent = null,
+ screenBounds = null,
+ taskId = 0,
+ insets = Insets.NONE,
+ bitmap = null,
+ displayId = Display.DEFAULT_DISPLAY,
+ )
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/TakeScreenshotExecutor.kt b/packages/SystemUI/src/com/android/systemui/screenshot/TakeScreenshotExecutor.kt
new file mode 100644
index 0000000..6c886fc
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/TakeScreenshotExecutor.kt
@@ -0,0 +1,201 @@
+package com.android.systemui.screenshot
+
+import android.net.Uri
+import android.os.Trace
+import android.util.Log
+import android.view.Display
+import com.android.internal.logging.UiEventLogger
+import com.android.internal.util.ScreenshotRequest
+import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.dagger.qualifiers.Application
+import com.android.systemui.display.data.repository.DisplayRepository
+import com.android.systemui.screenshot.TakeScreenshotService.RequestCallback
+import java.util.function.Consumer
+import javax.inject.Inject
+import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.Job
+import kotlinx.coroutines.flow.SharingStarted
+import kotlinx.coroutines.flow.StateFlow
+import kotlinx.coroutines.flow.stateIn
+import kotlinx.coroutines.launch
+
+/**
+ * Receives the signal to take a screenshot from [TakeScreenshotService], and calls back with the
+ * result.
+ *
+ * Captures a screenshot for each [Display] available.
+ */
+@SysUISingleton
+class TakeScreenshotExecutor
+@Inject
+constructor(
+ private val screenshotControllerFactory: ScreenshotController.Factory,
+ displayRepository: DisplayRepository,
+ @Application private val mainScope: CoroutineScope,
+ private val screenshotRequestProcessor: ScreenshotRequestProcessor,
+ private val uiEventLogger: UiEventLogger
+) {
+
+ private lateinit var displays: StateFlow<Set<Display>>
+ private val displaysCollectionJob: Job =
+ mainScope.launch {
+ displays = displayRepository.displays.stateIn(this, SharingStarted.Eagerly, emptySet())
+ }
+
+ private val screenshotControllers = mutableMapOf<Int, ScreenshotController>()
+
+ /**
+ * Executes the [ScreenshotRequest].
+ *
+ * [onSaved] is invoked only on the default display result. [RequestCallback.onFinish] is
+ * invoked only when both screenshot UIs are removed.
+ */
+ suspend fun executeScreenshots(
+ screenshotRequest: ScreenshotRequest,
+ onSaved: (Uri) -> Unit,
+ requestCallback: RequestCallback
+ ) {
+ val displayIds = getDisplaysToScreenshot()
+ val resultCallbackWrapper = MultiResultCallbackWrapper(requestCallback)
+ screenshotRequest.oneForEachDisplay(displayIds).forEach { screenshotData: ScreenshotData ->
+ dispatchToController(
+ screenshotData = screenshotData,
+ onSaved =
+ if (screenshotData.displayId == Display.DEFAULT_DISPLAY) onSaved else { _ -> },
+ callback = resultCallbackWrapper.createCallbackForId(screenshotData.displayId)
+ )
+ }
+ }
+
+ /** Creates a [ScreenshotData] for each display. */
+ private suspend fun ScreenshotRequest.oneForEachDisplay(
+ displayIds: List<Int>
+ ): List<ScreenshotData> {
+ return displayIds
+ .map { displayId -> ScreenshotData.fromRequest(this, displayId) }
+ .map { screenshotData: ScreenshotData ->
+ screenshotRequestProcessor.process(screenshotData)
+ }
+ }
+
+ private fun dispatchToController(
+ screenshotData: ScreenshotData,
+ onSaved: (Uri) -> Unit,
+ callback: RequestCallback
+ ) {
+ uiEventLogger.log(
+ ScreenshotEvent.getScreenshotSource(screenshotData.source),
+ 0,
+ screenshotData.packageNameString
+ )
+ Log.d(TAG, "Screenshot request: $screenshotData")
+ getScreenshotController(screenshotData.displayId)
+ .handleScreenshot(screenshotData, onSaved, callback)
+ }
+
+ private fun getDisplaysToScreenshot(): List<Int> {
+ return displays.value.filter { it.type in ALLOWED_DISPLAY_TYPES }.map { it.displayId }
+ }
+
+ /**
+ * Propagates the close system dialog signal to all controllers.
+ *
+ * TODO(b/295143676): Move the receiver in this class once the flag is flipped.
+ */
+ fun onCloseSystemDialogsReceived() {
+ screenshotControllers.forEach { (_, screenshotController) ->
+ if (!screenshotController.isPendingSharedTransition) {
+ screenshotController.dismissScreenshot(ScreenshotEvent.SCREENSHOT_DISMISSED_OTHER)
+ }
+ }
+ }
+
+ /** Removes all screenshot related windows. */
+ fun removeWindows() {
+ screenshotControllers.forEach { (_, screenshotController) ->
+ screenshotController.removeWindow()
+ }
+ }
+
+ /**
+ * Destroys the executor. Afterwards, this class is not expected to work as intended anymore.
+ */
+ fun onDestroy() {
+ screenshotControllers.forEach { (_, screenshotController) ->
+ screenshotController.onDestroy()
+ }
+ screenshotControllers.clear()
+ displaysCollectionJob.cancel()
+ }
+
+ private fun getScreenshotController(id: Int): ScreenshotController {
+ return screenshotControllers.computeIfAbsent(id) { screenshotControllerFactory.create(id) }
+ }
+
+ /** For java compatibility only. see [executeScreenshots] */
+ fun executeScreenshotsAsync(
+ screenshotRequest: ScreenshotRequest,
+ onSaved: Consumer<Uri>,
+ requestCallback: RequestCallback
+ ) {
+ mainScope.launch {
+ executeScreenshots(screenshotRequest, { uri -> onSaved.accept(uri) }, requestCallback)
+ }
+ }
+
+ /**
+ * Returns a [RequestCallback] that calls [RequestCallback.onFinish] only when all callbacks for
+ * id created have finished.
+ *
+ * If any callback created calls [reportError], then following [onFinish] are not considered.
+ */
+ private class MultiResultCallbackWrapper(
+ private val originalCallback: RequestCallback,
+ ) {
+ private val idsPending = mutableSetOf<Int>()
+ private var errorReported = false
+
+ /**
+ * Creates a callback for [id].
+ *
+ * [originalCallback]'s [onFinish] will be called only when this (and the other created)
+ * callback's [onFinish] have been called.
+ */
+ fun createCallbackForId(id: Int): RequestCallback {
+ Trace.asyncTraceForTrackBegin(Trace.TRACE_TAG_APP, TAG, "Waiting for id=$id", id)
+ idsPending += id
+ return object : RequestCallback {
+ override fun reportError() {
+ Log.d(TAG, "ReportError id=$id")
+ Trace.asyncTraceForTrackEnd(Trace.TRACE_TAG_APP, TAG, id)
+ Trace.instantForTrack(Trace.TRACE_TAG_APP, TAG, "reportError id=$id")
+ originalCallback.reportError()
+ errorReported = true
+ }
+
+ override fun onFinish() {
+ Log.d(TAG, "onFinish id=$id")
+ if (errorReported) return
+ idsPending -= id
+ Trace.instantForTrack(Trace.TRACE_TAG_APP, TAG, "onFinish id=$id")
+ if (idsPending.isEmpty()) {
+ Trace.asyncTraceForTrackEnd(Trace.TRACE_TAG_APP, TAG, id)
+ originalCallback.onFinish()
+ }
+ }
+ }
+ }
+ }
+
+ private companion object {
+ val TAG = LogConfig.logTag(TakeScreenshotService::class.java)
+
+ val ALLOWED_DISPLAY_TYPES =
+ listOf(
+ Display.TYPE_EXTERNAL,
+ Display.TYPE_INTERNAL,
+ Display.TYPE_OVERLAY,
+ Display.TYPE_WIFI
+ )
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/TakeScreenshotService.java b/packages/SystemUI/src/com/android/systemui/screenshot/TakeScreenshotService.java
index 1cdad83..1e8542f 100644
--- a/packages/SystemUI/src/com/android/systemui/screenshot/TakeScreenshotService.java
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/TakeScreenshotService.java
@@ -21,6 +21,7 @@
import static com.android.internal.util.ScreenshotHelper.SCREENSHOT_MSG_PROCESS_COMPLETE;
import static com.android.internal.util.ScreenshotHelper.SCREENSHOT_MSG_URI;
+import static com.android.systemui.flags.Flags.MULTI_DISPLAY_SCREENSHOT;
import static com.android.systemui.screenshot.LogConfig.DEBUG_CALLBACK;
import static com.android.systemui.screenshot.LogConfig.DEBUG_DISMISS;
import static com.android.systemui.screenshot.LogConfig.DEBUG_SERVICE;
@@ -46,6 +47,7 @@
import android.os.UserHandle;
import android.os.UserManager;
import android.util.Log;
+import android.view.Display;
import android.widget.Toast;
import com.android.internal.annotations.VisibleForTesting;
@@ -59,6 +61,7 @@
import java.util.function.Consumer;
import javax.inject.Inject;
+import javax.inject.Provider;
public class TakeScreenshotService extends Service {
private static final String TAG = logTag(TakeScreenshotService.class);
@@ -82,12 +85,17 @@
if (DEBUG_DISMISS) {
Log.d(TAG, "Received ACTION_CLOSE_SYSTEM_DIALOGS");
}
- if (!mScreenshot.isPendingSharedTransition()) {
+ if (mFeatureFlags.isEnabled(MULTI_DISPLAY_SCREENSHOT)) {
+ // TODO(b/295143676): move receiver inside executor when the flag is enabled.
+ mTakeScreenshotExecutor.get().onCloseSystemDialogsReceived();
+ } else if (!mScreenshot.isPendingSharedTransition()) {
mScreenshot.dismissScreenshot(SCREENSHOT_DISMISSED_OTHER);
}
}
}
};
+ private final Provider<TakeScreenshotExecutor> mTakeScreenshotExecutor;
+
/** Informs about coarse grained state of the Controller. */
public interface RequestCallback {
@@ -99,16 +107,15 @@
}
@Inject
- public TakeScreenshotService(ScreenshotController screenshotController, UserManager userManager,
- DevicePolicyManager devicePolicyManager, UiEventLogger uiEventLogger,
- ScreenshotNotificationsController notificationsController, Context context,
- @Background Executor bgExecutor, FeatureFlags featureFlags,
- RequestProcessor processor) {
+ public TakeScreenshotService(ScreenshotController.Factory screenshotControllerFactory,
+ UserManager userManager, DevicePolicyManager devicePolicyManager,
+ UiEventLogger uiEventLogger, ScreenshotNotificationsController notificationsController,
+ Context context, @Background Executor bgExecutor, FeatureFlags featureFlags,
+ RequestProcessor processor, Provider<TakeScreenshotExecutor> takeScreenshotExecutor) {
if (DEBUG_SERVICE) {
Log.d(TAG, "new " + this);
}
mHandler = new Handler(Looper.getMainLooper(), this::handleMessage);
- mScreenshot = screenshotController;
mUserManager = userManager;
mDevicePolicyManager = devicePolicyManager;
mUiEventLogger = uiEventLogger;
@@ -117,6 +124,12 @@
mBgExecutor = bgExecutor;
mFeatureFlags = featureFlags;
mProcessor = processor;
+ mTakeScreenshotExecutor = takeScreenshotExecutor;
+ if (mFeatureFlags.isEnabled(MULTI_DISPLAY_SCREENSHOT)) {
+ mScreenshot = null;
+ } else {
+ mScreenshot = screenshotControllerFactory.create(Display.DEFAULT_DISPLAY);
+ }
}
@Override
@@ -142,7 +155,11 @@
if (DEBUG_SERVICE) {
Log.d(TAG, "onUnbind");
}
- mScreenshot.removeWindow();
+ if (mFeatureFlags.isEnabled(MULTI_DISPLAY_SCREENSHOT)) {
+ mTakeScreenshotExecutor.get().removeWindows();
+ } else {
+ mScreenshot.removeWindow();
+ }
unregisterReceiver(mCloseSystemDialogs);
return false;
}
@@ -150,7 +167,11 @@
@Override
public void onDestroy() {
super.onDestroy();
- mScreenshot.onDestroy();
+ if (mFeatureFlags.isEnabled(MULTI_DISPLAY_SCREENSHOT)) {
+ mTakeScreenshotExecutor.get().onDestroy();
+ } else {
+ mScreenshot.onDestroy();
+ }
if (DEBUG_SERVICE) {
Log.d(TAG, "onDestroy");
}
@@ -218,10 +239,17 @@
}
Log.d(TAG, "Processing screenshot data");
- ScreenshotData screenshotData = ScreenshotData.fromRequest(request);
+
+
+ ScreenshotData screenshotData = ScreenshotData.fromRequest(
+ request, Display.DEFAULT_DISPLAY);
try {
- mProcessor.processAsync(screenshotData,
- (data) -> dispatchToController(data, onSaved, callback));
+ if (mFeatureFlags.isEnabled(MULTI_DISPLAY_SCREENSHOT)) {
+ mTakeScreenshotExecutor.get().executeScreenshotsAsync(request, onSaved, callback);
+ } else {
+ mProcessor.processAsync(screenshotData, (data) ->
+ dispatchToController(data, onSaved, callback));
+ }
} catch (IllegalStateException e) {
Log.e(TAG, "Failed to process screenshot request!", e);
logFailedRequest(request);
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/dagger/ScreenshotModule.java b/packages/SystemUI/src/com/android/systemui/screenshot/dagger/ScreenshotModule.java
index 22e238c0..7d17d4c 100644
--- a/packages/SystemUI/src/com/android/systemui/screenshot/dagger/ScreenshotModule.java
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/dagger/ScreenshotModule.java
@@ -20,9 +20,11 @@
import com.android.systemui.screenshot.ImageCapture;
import com.android.systemui.screenshot.ImageCaptureImpl;
+import com.android.systemui.screenshot.RequestProcessor;
import com.android.systemui.screenshot.ScreenshotPolicy;
import com.android.systemui.screenshot.ScreenshotPolicyImpl;
import com.android.systemui.screenshot.ScreenshotProxyService;
+import com.android.systemui.screenshot.ScreenshotRequestProcessor;
import com.android.systemui.screenshot.TakeScreenshotService;
import com.android.systemui.screenshot.appclips.AppClipsScreenshotHelperService;
import com.android.systemui.screenshot.appclips.AppClipsService;
@@ -63,4 +65,8 @@
@IntoMap
@ClassKey(AppClipsService.class)
abstract Service bindAppClipsService(AppClipsService service);
+
+ @Binds
+ abstract ScreenshotRequestProcessor bindScreenshotRequestProcessor(
+ RequestProcessor requestProcessor);
}
diff --git a/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java b/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java
index 014093d..8db7abf 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java
+++ b/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java
@@ -194,6 +194,7 @@
import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayoutController;
import com.android.systemui.statusbar.notification.stack.NotificationStackSizeCalculator;
import com.android.systemui.statusbar.notification.stack.StackStateAnimator;
+import com.android.systemui.statusbar.notification.stack.domain.interactor.SharedNotificationContainerInteractor;
import com.android.systemui.statusbar.phone.BounceInterpolator;
import com.android.systemui.statusbar.phone.CentralSurfaces;
import com.android.systemui.statusbar.phone.DozeParameters;
@@ -606,7 +607,7 @@
private final LockscreenToOccludedTransitionViewModel mLockscreenToOccludedTransitionViewModel;
private final PrimaryBouncerToGoneTransitionViewModel mPrimaryBouncerToGoneTransitionViewModel;
-
+ private final SharedNotificationContainerInteractor mSharedNotificationContainerInteractor;
private final KeyguardTransitionInteractor mKeyguardTransitionInteractor;
private final KeyguardInteractor mKeyguardInteractor;
private final KeyguardViewConfigurator mKeyguardViewConfigurator;
@@ -772,6 +773,7 @@
KeyguardLongPressViewModel keyguardLongPressViewModel,
KeyguardInteractor keyguardInteractor,
ActivityStarter activityStarter,
+ SharedNotificationContainerInteractor sharedNotificationContainerInteractor,
KeyguardViewConfigurator keyguardViewConfigurator,
KeyguardFaceAuthInteractor keyguardFaceAuthInteractor,
KeyguardRootView keyguardRootView) {
@@ -797,6 +799,7 @@
mLockscreenToOccludedTransitionViewModel = lockscreenToOccludedTransitionViewModel;
mPrimaryBouncerToGoneTransitionViewModel = primaryBouncerToGoneTransitionViewModel;
mKeyguardTransitionInteractor = keyguardTransitionInteractor;
+ mSharedNotificationContainerInteractor = sharedNotificationContainerInteractor;
mKeyguardInteractor = keyguardInteractor;
mKeyguardViewConfigurator = keyguardViewConfigurator;
mView.addOnAttachStateChangeListener(new View.OnAttachStateChangeListener() {
@@ -1223,37 +1226,7 @@
private void updateViewControllers(
FrameLayout userAvatarView,
KeyguardUserSwitcherView keyguardUserSwitcherView) {
- // Re-associate the KeyguardStatusViewController
- if (mKeyguardStatusViewController != null) {
- mKeyguardStatusViewController.onDestroy();
- }
-
- if (mFeatureFlags.isEnabled(Flags.MIGRATE_KEYGUARD_STATUS_VIEW)) {
- // Need a shared controller until mKeyguardStatusViewController can be removed from
- // here, due to important state being set in that controller. Rebind in order to pick
- // up config changes
- mKeyguardViewConfigurator.bindKeyguardStatusView(mView);
- mKeyguardStatusViewController =
- mKeyguardViewConfigurator.getKeyguardStatusViewController();
- } else {
- KeyguardStatusView keyguardStatusView = mView.getRootView().findViewById(
- R.id.keyguard_status_view);
- KeyguardStatusViewComponent statusViewComponent =
- mKeyguardStatusViewComponentFactory.build(keyguardStatusView);
- mKeyguardStatusViewController = statusViewComponent.getKeyguardStatusViewController();
- mKeyguardStatusViewController.init();
- }
- mKeyguardStatusViewController.setSplitShadeEnabled(mSplitShadeEnabled);
- mKeyguardStatusViewController.getView().addOnLayoutChangeListener(
- (v, left, top, right, bottom, oldLeft, oldTop, oldRight, oldBottom) -> {
- int oldHeight = oldBottom - oldTop;
- if (v.getHeight() != oldHeight) {
- mNotificationStackScrollLayoutController.animateNextTopPaddingChange();
- }
- });
-
- updateClockAppearance();
-
+ updateStatusBarViewController();
if (mKeyguardUserSwitcherController != null) {
// Try to close the switcher so that callbacks are triggered if necessary.
// Otherwise, NPV can get into a state where some of the views are still hidden
@@ -1283,6 +1256,40 @@
}
}
+ /** Updates the StatusBarViewController and updates any that depend on it. */
+ public void updateStatusBarViewController() {
+ // Re-associate the KeyguardStatusViewController
+ if (mKeyguardStatusViewController != null) {
+ mKeyguardStatusViewController.onDestroy();
+ }
+
+ if (mFeatureFlags.isEnabled(Flags.MIGRATE_KEYGUARD_STATUS_VIEW)) {
+ // Need a shared controller until mKeyguardStatusViewController can be removed from
+ // here, due to important state being set in that controller. Rebind in order to pick
+ // up config changes
+ mKeyguardStatusViewController =
+ mKeyguardViewConfigurator.getKeyguardStatusViewController();
+ } else {
+ KeyguardStatusView keyguardStatusView = mView.getRootView().findViewById(
+ R.id.keyguard_status_view);
+ KeyguardStatusViewComponent statusViewComponent =
+ mKeyguardStatusViewComponentFactory.build(keyguardStatusView);
+ mKeyguardStatusViewController = statusViewComponent.getKeyguardStatusViewController();
+ mKeyguardStatusViewController.init();
+ }
+
+ mKeyguardStatusViewController.setSplitShadeEnabled(mSplitShadeEnabled);
+ mKeyguardStatusViewController.getView().addOnLayoutChangeListener(
+ (v, left, top, right, bottom, oldLeft, oldTop, oldRight, oldBottom) -> {
+ int oldHeight = oldBottom - oldTop;
+ if (v.getHeight() != oldHeight) {
+ mNotificationStackScrollLayoutController.animateNextTopPaddingChange();
+ }
+ });
+
+ updateClockAppearance();
+ }
+
@Override
public void updateResources() {
final boolean newSplitShadeEnabled =
@@ -1308,15 +1315,17 @@
// Reset any left over overscroll state. It is a rare corner case but can happen.
mQsController.setOverScrollAmount(0);
mScrimController.setNotificationsOverScrollAmount(0);
- mNotificationStackScrollLayoutController.setOverExpansion(0);
- mNotificationStackScrollLayoutController.setOverScrollAmount(0);
+ if (!mFeatureFlags.isEnabled(Flags.MIGRATE_NSSL)) {
+ mNotificationStackScrollLayoutController.setOverExpansion(0);
+ mNotificationStackScrollLayoutController.setOverScrollAmount(0);
+ }
// when we switch between split shade and regular shade we want to enforce setting qs to
// the default state: expanded for split shade and collapsed otherwise
- if (!isOnKeyguard() && mPanelExpanded) {
+ if (!isKeyguardShowing() && mPanelExpanded) {
mQsController.setExpanded(mSplitShadeEnabled);
}
- if (isOnKeyguard() && mQsController.getExpanded() && mSplitShadeEnabled) {
+ if (isKeyguardShowing() && mQsController.getExpanded() && mSplitShadeEnabled) {
// In single column keyguard - when you swipe from the top - QS is fully expanded and
// StatusBarState is KEYGUARD. That state doesn't make sense for split shade,
// where notifications are always visible and we effectively go to fully expanded
@@ -1327,7 +1336,9 @@
}
updateClockAppearance();
mQsController.updateQsState();
- mNotificationStackScrollLayoutController.updateFooter();
+ if (!mFeatureFlags.isEnabled(Flags.MIGRATE_NSSL)) {
+ mNotificationStackScrollLayoutController.updateFooter();
+ }
}
private View reInflateStub(int viewId, int stubId, int layoutId, boolean enabled) {
@@ -1368,10 +1379,6 @@
attachSplitShadeMediaPlayerContainer(
keyguardStatusView.findViewById(R.id.status_view_media_container));
- } else {
- attachSplitShadeMediaPlayerContainer(
- mKeyguardViewConfigurator.getKeyguardRootView()
- .findViewById(R.id.status_view_media_container));
}
// we need to update KeyguardStatusView constraints after reinflating it
@@ -1400,6 +1407,13 @@
updateViewControllers(userAvatarView, keyguardUserSwitcherView);
+ if (mFeatureFlags.isEnabled(Flags.MIGRATE_KEYGUARD_STATUS_VIEW) && !mFeatureFlags.isEnabled(
+ Flags.LAZY_INFLATE_KEYGUARD)) {
+ attachSplitShadeMediaPlayerContainer(
+ mKeyguardViewConfigurator.getKeyguardRootView()
+ .findViewById(R.id.status_view_media_container));
+ }
+
if (!mFeatureFlags.isEnabled(Flags.MIGRATE_SPLIT_KEYGUARD_BOTTOM_AREA)) {
// Update keyguard bottom area
int index = mView.indexOfChild(mKeyguardBottomArea);
@@ -1481,6 +1495,10 @@
if (SPEW_LOGCAT) Log.d(TAG, "Skipping computeMaxKeyguardNotifications() by request");
}
+ if (mFeatureFlags.isEnabled(Flags.MIGRATE_NSSL)) {
+ return;
+ }
+
if (isKeyguardShowing() && !mKeyguardBypassController.getBypassEnabled()) {
mNotificationStackScrollLayoutController.setMaxDisplayedNotifications(
mMaxAllowedKeyguardNotifications);
@@ -1552,7 +1570,7 @@
private void positionClockAndNotifications(boolean forceClockUpdate) {
boolean animate = mNotificationStackScrollLayoutController.isAddOrRemoveAnimationPending();
int stackScrollerPadding;
- boolean onKeyguard = isOnKeyguard();
+ boolean onKeyguard = isKeyguardShowing();
if (onKeyguard || forceClockUpdate) {
updateClockAppearance();
@@ -1624,8 +1642,10 @@
mKeyguardStatusViewController.getClockBottom(mStatusBarHeaderHeightKeyguard),
mKeyguardStatusViewController.isClockTopAligned());
mClockPositionAlgorithm.run(mClockPositionResult);
- mKeyguardStatusViewController.setLockscreenClockY(
- mClockPositionAlgorithm.getExpandedPreferredClockY());
+ if (!mFeatureFlags.isEnabled(Flags.MIGRATE_NSSL)) {
+ mKeyguardStatusViewController.setLockscreenClockY(
+ mClockPositionAlgorithm.getExpandedPreferredClockY());
+ }
if (mFeatureFlags.isEnabled(Flags.MIGRATE_SPLIT_KEYGUARD_BOTTOM_AREA)) {
mKeyguardInteractor.setClockPosition(
mClockPositionResult.clockX, mClockPositionResult.clockY);
@@ -1635,9 +1655,12 @@
}
boolean animate = mNotificationStackScrollLayoutController.isAddOrRemoveAnimationPending();
boolean animateClock = (animate || mAnimateNextPositionUpdate) && shouldAnimateClockChange;
- mKeyguardStatusViewController.updatePosition(
- mClockPositionResult.clockX, mClockPositionResult.clockY,
- mClockPositionResult.clockScale, animateClock);
+
+ if (!mFeatureFlags.isEnabled(Flags.MIGRATE_NSSL)) {
+ mKeyguardStatusViewController.updatePosition(
+ mClockPositionResult.clockX, mClockPositionResult.clockY,
+ mClockPositionResult.clockScale, animateClock);
+ }
if (mKeyguardQsUserSwitchController != null) {
mKeyguardQsUserSwitchController.updatePosition(
mClockPositionResult.clockX,
@@ -1867,8 +1890,12 @@
}
float alpha = mClockPositionResult.clockAlpha * mKeyguardOnlyContentAlpha;
mKeyguardStatusViewController.setAlpha(alpha);
- mKeyguardStatusViewController
- .setTranslationY(mKeyguardOnlyTransitionTranslationY, /* excludeMedia= */true);
+ if (mFeatureFlags.isEnabled(Flags.MIGRATE_NSSL)) {
+ // TODO (b/296373478) This is for split shade media movement.
+ } else {
+ mKeyguardStatusViewController
+ .setTranslationY(mKeyguardOnlyTransitionTranslationY, /* excludeMedia= */true);
+ }
if (mKeyguardQsUserSwitchController != null) {
mKeyguardQsUserSwitchController.setAlpha(alpha);
@@ -1980,7 +2007,7 @@
mQsController.setExpandImmediate(true);
setShowShelfOnly(true);
}
- if (mSplitShadeEnabled && isOnKeyguard()) {
+ if (mSplitShadeEnabled && isKeyguardShowing()) {
// It's a special case as this method is likely to not be initiated by finger movement
// but rather called from adb shell or accessibility service.
// We're using LockscreenShadeTransitionController because on lockscreen that's the
@@ -2024,7 +2051,7 @@
mQsController.setLastShadeFlingWasExpanding(expand);
mHeadsUpTouchHelper.notifyFling(!expand);
mKeyguardStateController.notifyPanelFlingStart(!expand /* flingingToDismiss */);
- setClosingWithAlphaFadeout(!expand && !isOnKeyguard() && getFadeoutAlpha() == 1.0f);
+ setClosingWithAlphaFadeout(!expand && !isKeyguardShowing() && getFadeoutAlpha() == 1.0f);
mNotificationStackScrollLayoutController.setPanelFlinging(true);
if (target == mExpandedHeight && mOverExpansion == 0.0f) {
// We're at the target and didn't fling and there's no overshoot
@@ -2425,9 +2452,14 @@
}
void requestScrollerTopPaddingUpdate(boolean animate) {
- mNotificationStackScrollLayoutController.updateTopPadding(
- mQsController.calculateNotificationsTopPadding(mIsExpandingOrCollapsing,
- getKeyguardNotificationStaticPadding(), mExpandedFraction), animate);
+ float padding = mQsController.calculateNotificationsTopPadding(mIsExpandingOrCollapsing,
+ getKeyguardNotificationStaticPadding(), mExpandedFraction);
+ if (mFeatureFlags.isEnabled(Flags.MIGRATE_NSSL)) {
+ mSharedNotificationContainerInteractor.setTopPosition(padding);
+ } else {
+ mNotificationStackScrollLayoutController.updateTopPadding(padding, animate);
+ }
+
if (isKeyguardShowing()
&& mKeyguardBypassController.getBypassEnabled()) {
// update the position of the header
@@ -2791,7 +2823,6 @@
}
private void onTrackingStarted() {
- mFalsingCollector.onTrackingStarted(!mKeyguardStateController.canDismissLockScreen());
endClosing();
mTracking = true;
mTrackingStartedListener.onTrackingStarted();
@@ -2807,7 +2838,6 @@
}
private void onTrackingStopped(boolean expand) {
- mFalsingCollector.onTrackingStopped();
mTracking = false;
maybeStopTrackingExpansionFromStatusBar(expand);
@@ -2861,7 +2891,6 @@
@VisibleForTesting
void onUnlockHintStarted() {
- mFalsingCollector.onUnlockHintStarted();
mKeyguardIndicationController.showActionToUnlock();
mScrimController.setExpansionAffectsAlpha(false);
mNotificationStackScrollLayoutController.setUnlockHintRunning(true);
@@ -3022,7 +3051,7 @@
mNotificationStackScrollLayoutController
.setExpandingVelocity(getCurrentExpandVelocity());
}
- if (mKeyguardBypassController.getBypassEnabled() && isOnKeyguard()) {
+ if (mKeyguardBypassController.getBypassEnabled() && isKeyguardShowing()) {
// The expandedHeight is always the full panel Height when bypassing
expandedHeight = getMaxPanelHeight();
}
@@ -3033,7 +3062,7 @@
private void updateStatusBarIcons() {
boolean showIconsWhenExpanded = getExpandedHeight() < getOpeningHeight();
- if (showIconsWhenExpanded && isOnKeyguard()) {
+ if (showIconsWhenExpanded && isKeyguardShowing()) {
showIconsWhenExpanded = false;
}
if (showIconsWhenExpanded != mShowIconsWhenExpanded) {
@@ -3047,10 +3076,6 @@
return mBarState;
}
- private boolean isOnKeyguard() {
- return mBarState == KEYGUARD;
- }
-
/** Called when a HUN is dragged up or down to indicate the starting height for shade motion. */
@VisibleForTesting
void setHeadsUpDraggingStartingHeight(int startHeight) {
@@ -4434,7 +4459,7 @@
@Override
public void onHeadsUpPinned(NotificationEntry entry) {
- if (!isOnKeyguard()) {
+ if (!isKeyguardShowing()) {
mNotificationStackScrollLayoutController.generateHeadsUpAnimation(
entry.getHeadsUpAnimationView(), true);
}
@@ -4447,7 +4472,7 @@
// we need to make sure that an animation happens in this case, otherwise the
// notification
// will stick to the top without any interaction.
- if (isFullyCollapsed() && entry.isRowHeadsUp() && !isOnKeyguard()) {
+ if (isFullyCollapsed() && entry.isRowHeadsUp() && !isKeyguardShowing()) {
mNotificationStackScrollLayoutController.generateHeadsUpAnimation(
entry.getHeadsUpAnimationView(), false);
entry.setHeadsUpIsVisible();
@@ -4748,7 +4773,7 @@
// we need to ignore it on keyguard as this is a false alarm - transition from unlocked
// to locked will trigger this event and we're not actually in the process of opening
// the shade, lockscreen is just always expanded
- if (mSplitShadeEnabled && !isOnKeyguard()) {
+ if (mSplitShadeEnabled && !isKeyguardShowing()) {
mQsController.setExpandImmediate(true);
}
mOpenCloseListener.onOpenStarted();
@@ -4799,8 +4824,11 @@
private Consumer<Float> setTransitionY(
NotificationStackScrollLayoutController stackScroller) {
return (Float translationY) -> {
- mKeyguardStatusViewController.setTranslationY(translationY, /* excludeMedia= */false);
- stackScroller.setTranslationY(translationY);
+ if (!mFeatureFlags.isEnabled(Flags.MIGRATE_NSSL)) {
+ mKeyguardStatusViewController.setTranslationY(translationY,
+ /* excludeMedia= */false);
+ stackScroller.setTranslationY(translationY);
+ }
};
}
diff --git a/packages/SystemUI/src/com/android/systemui/shade/NotificationShadeWindowControllerImpl.java b/packages/SystemUI/src/com/android/systemui/shade/NotificationShadeWindowControllerImpl.java
index ad9df72..4a76dd0 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/NotificationShadeWindowControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/shade/NotificationShadeWindowControllerImpl.java
@@ -21,7 +21,6 @@
import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_BEHAVIOR_CONTROLLED;
import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_OPTIMIZE_MEASURE;
-import static com.android.systemui.DejankUtils.whitelistIpcs;
import static com.android.systemui.statusbar.NotificationRemoteInputManager.ENABLE_REMOTE_INPUT;
import android.app.IActivityManager;
@@ -52,6 +51,7 @@
import com.android.systemui.biometrics.AuthController;
import com.android.systemui.colorextraction.SysuiColorExtractor;
import com.android.systemui.dagger.SysUISingleton;
+import com.android.systemui.dagger.qualifiers.Background;
import com.android.systemui.dump.DumpManager;
import com.android.systemui.dump.DumpsysTableLogger;
import com.android.systemui.keyguard.KeyguardViewMediator;
@@ -76,6 +76,7 @@
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
+import java.util.concurrent.Executor;
import java.util.function.Consumer;
import java.util.stream.Collectors;
@@ -104,6 +105,7 @@
private final float mKeyguardMaxRefreshRate;
private final KeyguardViewMediator mKeyguardViewMediator;
private final KeyguardBypassController mKeyguardBypassController;
+ private final Executor mBackgroundExecutor;
private final AuthController mAuthController;
private ViewGroup mWindowRootView;
private LayoutParams mLp;
@@ -141,6 +143,7 @@
ConfigurationController configurationController,
KeyguardViewMediator keyguardViewMediator,
KeyguardBypassController keyguardBypassController,
+ @Background Executor backgroundExecutor,
SysuiColorExtractor colorExtractor,
DumpManager dumpManager,
KeyguardStateController keyguardStateController,
@@ -159,6 +162,7 @@
mLpChanged = new LayoutParams();
mKeyguardViewMediator = keyguardViewMediator;
mKeyguardBypassController = keyguardBypassController;
+ mBackgroundExecutor = backgroundExecutor;
mColorExtractor = colorExtractor;
mScreenOffAnimationController = screenOffAnimationController;
dumpManager.registerDumpable(this);
@@ -520,13 +524,14 @@
applyWindowLayoutParams();
if (mHasTopUi != mHasTopUiChanged) {
- whitelistIpcs(() -> {
+ mHasTopUi = mHasTopUiChanged;
+ mBackgroundExecutor.execute(() -> {
try {
mActivityManager.setHasTopUi(mHasTopUiChanged);
} catch (RemoteException e) {
Log.e(TAG, "Failed to call setHasTopUi", e);
}
- mHasTopUi = mHasTopUiChanged;
+
});
}
notifyStateChangedCallbacks();
diff --git a/packages/SystemUI/src/com/android/systemui/shade/NotificationShadeWindowViewController.java b/packages/SystemUI/src/com/android/systemui/shade/NotificationShadeWindowViewController.java
index 798f2d5..0f85c76 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/NotificationShadeWindowViewController.java
+++ b/packages/SystemUI/src/com/android/systemui/shade/NotificationShadeWindowViewController.java
@@ -35,6 +35,7 @@
import com.android.keyguard.KeyguardMessageAreaController;
import com.android.keyguard.LockIconViewController;
import com.android.keyguard.dagger.KeyguardBouncerComponent;
+import com.android.systemui.Dumpable;
import com.android.systemui.R;
import com.android.systemui.back.domain.interactor.BackActionInteractor;
import com.android.systemui.bouncer.domain.interactor.BouncerMessageInteractor;
@@ -43,6 +44,7 @@
import com.android.systemui.classifier.FalsingCollector;
import com.android.systemui.dagger.SysUISingleton;
import com.android.systemui.dock.DockManager;
+import com.android.systemui.dump.DumpManager;
import com.android.systemui.flags.FeatureFlags;
import com.android.systemui.flags.Flags;
import com.android.systemui.keyevent.domain.interactor.KeyEventInteractor;
@@ -65,6 +67,8 @@
import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayout;
import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayoutController;
import com.android.systemui.statusbar.phone.CentralSurfaces;
+import com.android.systemui.statusbar.phone.DozeScrimController;
+import com.android.systemui.statusbar.phone.DozeServiceHost;
import com.android.systemui.statusbar.phone.PhoneStatusBarViewController;
import com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager;
import com.android.systemui.statusbar.window.StatusBarWindowStateController;
@@ -81,7 +85,7 @@
* Controller for {@link NotificationShadeWindowView}.
*/
@SysUISingleton
-public class NotificationShadeWindowViewController {
+public class NotificationShadeWindowViewController implements Dumpable {
private static final String TAG = "NotifShadeWindowVC";
private final FalsingCollector mFalsingCollector;
private final SysuiStatusBarStateController mStatusBarStateController;
@@ -118,6 +122,8 @@
private NotificationStackScrollLayout mStackScrollLayout;
private PhoneStatusBarViewController mStatusBarViewController;
private final CentralSurfaces mService;
+ private final DozeServiceHost mDozeServiceHost;
+ private final DozeScrimController mDozeScrimController;
private final BackActionInteractor mBackActionInteractor;
private final PowerInteractor mPowerInteractor;
private final NotificationShadeWindowController mNotificationShadeWindowController;
@@ -152,6 +158,8 @@
StatusBarWindowStateController statusBarWindowStateController,
LockIconViewController lockIconViewController,
CentralSurfaces centralSurfaces,
+ DozeServiceHost dozeServiceHost,
+ DozeScrimController dozeScrimController,
BackActionInteractor backActionInteractor,
PowerInteractor powerInteractor,
NotificationShadeWindowController controller,
@@ -160,6 +168,7 @@
NotificationInsetsController notificationInsetsController,
AmbientState ambientState,
ShadeLogger shadeLogger,
+ DumpManager dumpManager,
PulsingGestureListener pulsingGestureListener,
LockscreenHostedDreamGestureListener lockscreenHostedDreamGestureListener,
KeyguardBouncerViewModel keyguardBouncerViewModel,
@@ -187,8 +196,9 @@
mLockIconViewController = lockIconViewController;
mBackActionInteractor = backActionInteractor;
mShadeLogger = shadeLogger;
- mLockIconViewController.init();
mService = centralSurfaces;
+ mDozeServiceHost = dozeServiceHost;
+ mDozeScrimController = dozeScrimController;
mPowerInteractor = powerInteractor;
mNotificationShadeWindowController = controller;
mKeyguardUnlockAnimationController = keyguardUnlockAnimationController;
@@ -226,6 +236,9 @@
progressProvider -> progressProvider.addCallback(
mDisableSubpixelTextTransitionListener));
}
+
+ lockIconViewController.setLockIconView(mView.findViewById(R.id.lock_icon_view));
+ dumpManager.registerDumpable(this);
}
/**
@@ -288,7 +301,7 @@
}
if (mExpandAnimationRunning) {
if (isDown && mClock.uptimeMillis() > mLaunchAnimationTimeout) {
- mShadeLogger.d("NSWVC: launch animation timed out");
+ Log.wtf(TAG, "NSWVC: launch animation timed out");
setExpandAnimationRunning(false);
} else {
return logDownDispatch(ev, "expand animation running", false);
@@ -332,7 +345,7 @@
}
if (mStatusBarStateController.isDozing()) {
- mService.extendDozePulse();
+ mDozeScrimController.extendPulse();
}
mLockIconViewController.onTouchEvent(
ev,
@@ -391,7 +404,7 @@
@Override
public boolean shouldInterceptTouchEvent(MotionEvent ev) {
- if (mStatusBarStateController.isDozing() && !mService.isPulsing()
+ if (mStatusBarStateController.isDozing() && !mDozeServiceHost.isPulsing()
&& !mDockManager.isDocked()) {
if (ev.getAction() == MotionEvent.ACTION_DOWN) {
mShadeLogger.d("NSWVC: capture all touch events in always-on");
@@ -445,7 +458,7 @@
public boolean handleTouchEvent(MotionEvent ev) {
boolean handled = false;
if (mStatusBarStateController.isDozing()) {
- handled = !mService.isPulsing();
+ handled = !mDozeServiceHost.isPulsing();
}
if (mStatusBarKeyguardViewManager.onTouch(ev)) {
@@ -533,6 +546,7 @@
mAmbientState.setSwipingUp(false);
}
+ @Override
public void dump(PrintWriter pw, String[] args) {
pw.print(" mExpandAnimationRunning=");
pw.println(mExpandAnimationRunning);
@@ -545,6 +559,8 @@
@VisibleForTesting
void setExpandAnimationRunning(boolean running) {
if (mExpandAnimationRunning != running) {
+ // TODO(b/288507023): Remove this log.
+ Log.d(TAG, "Setting mExpandAnimationRunning=" + running);
if (running) {
mLaunchAnimationTimeout = mClock.uptimeMillis() + 5000;
}
diff --git a/packages/SystemUI/src/com/android/systemui/shade/QuickSettingsController.java b/packages/SystemUI/src/com/android/systemui/shade/QuickSettingsController.java
index c9c911b..b2bbffd 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/QuickSettingsController.java
+++ b/packages/SystemUI/src/com/android/systemui/shade/QuickSettingsController.java
@@ -67,7 +67,6 @@
import com.android.systemui.Dumpable;
import com.android.systemui.R;
import com.android.systemui.classifier.Classifier;
-import com.android.systemui.classifier.FalsingCollector;
import com.android.systemui.dagger.SysUISingleton;
import com.android.systemui.dump.DumpManager;
import com.android.systemui.flags.FeatureFlags;
@@ -147,7 +146,6 @@
private final MediaHierarchyManager mMediaHierarchyManager;
private final AmbientState mAmbientState;
private final RecordingController mRecordingController;
- private final FalsingCollector mFalsingCollector;
private final LockscreenGestureLogger mLockscreenGestureLogger;
private final ShadeLogger mShadeLog;
private final KeyguardFaceAuthInteractor mKeyguardFaceAuthInteractor;
@@ -335,7 +333,6 @@
AmbientState ambientState,
RecordingController recordingController,
FalsingManager falsingManager,
- FalsingCollector falsingCollector,
AccessibilityManager accessibilityManager,
LockscreenGestureLogger lockscreenGestureLogger,
MetricsLogger metricsLogger,
@@ -381,7 +378,6 @@
mAmbientState = ambientState;
mRecordingController = recordingController;
mFalsingManager = falsingManager;
- mFalsingCollector = falsingCollector;
mAccessibilityManager = accessibilityManager;
mLockscreenGestureLogger = lockscreenGestureLogger;
@@ -1660,7 +1656,6 @@
}
}
if (shouldQuickSettingsIntercept(event.getX(), event.getY(), -1)) {
- mFalsingCollector.onQsDown();
mShadeLog.logMotionEvent(event,
"handleQsDown: down action, QS tracking enabled");
mTracking = true;
diff --git a/packages/SystemUI/src/com/android/systemui/shade/ShadeControllerImpl.java b/packages/SystemUI/src/com/android/systemui/shade/ShadeControllerImpl.java
index d7a3392..9a3e4e5 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/ShadeControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/shade/ShadeControllerImpl.java
@@ -30,6 +30,7 @@
import com.android.systemui.log.LogBuffer;
import com.android.systemui.log.dagger.ShadeTouchLog;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
+import com.android.systemui.scene.domain.interactor.WindowRootViewVisibilityInteractor;
import com.android.systemui.statusbar.CommandQueue;
import com.android.systemui.statusbar.NotificationPresenter;
import com.android.systemui.statusbar.NotificationShadeWindowController;
@@ -59,6 +60,7 @@
private final CommandQueue mCommandQueue;
private final Executor mMainExecutor;
private final LogBuffer mTouchLog;
+ private final WindowRootViewVisibilityInteractor mWindowRootViewVisibilityInteractor;
private final KeyguardStateController mKeyguardStateController;
private final NotificationShadeWindowController mNotificationShadeWindowController;
private final StatusBarStateController mStatusBarStateController;
@@ -83,6 +85,7 @@
CommandQueue commandQueue,
@Main Executor mainExecutor,
@ShadeTouchLog LogBuffer touchLog,
+ WindowRootViewVisibilityInteractor windowRootViewVisibilityInteractor,
KeyguardStateController keyguardStateController,
StatusBarStateController statusBarStateController,
StatusBarKeyguardViewManager statusBarKeyguardViewManager,
@@ -97,6 +100,7 @@
mCommandQueue = commandQueue;
mMainExecutor = mainExecutor;
mTouchLog = touchLog;
+ mWindowRootViewVisibilityInteractor = windowRootViewVisibilityInteractor;
mShadeViewControllerLazy = shadeViewControllerLazy;
mStatusBarStateController = statusBarStateController;
mStatusBarWindowController = statusBarWindowController;
@@ -391,6 +395,7 @@
private void notifyVisibilityChanged(boolean visible) {
mShadeVisibilityListener.visibilityChanged(visible);
+ mWindowRootViewVisibilityInteractor.setIsLockscreenOrShadeVisible(visible);
}
private void notifyExpandedVisibleChanged(boolean expandedVisible) {
diff --git a/packages/SystemUI/src/com/android/systemui/shade/ShadeSurface.kt b/packages/SystemUI/src/com/android/systemui/shade/ShadeSurface.kt
index 2532bad..b553f0f 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/ShadeSurface.kt
+++ b/packages/SystemUI/src/com/android/systemui/shade/ShadeSurface.kt
@@ -99,9 +99,6 @@
/** Sets the view's alpha to max. */
fun resetAlpha()
- /** Sets progress of the predictive back animation. */
- fun onBackProgressed(progressFraction: Float)
-
/** @see com.android.systemui.keyguard.ScreenLifecycle.Observer.onScreenTurningOn */
fun onScreenTurningOn()
diff --git a/packages/SystemUI/src/com/android/systemui/shade/ShadeViewController.kt b/packages/SystemUI/src/com/android/systemui/shade/ShadeViewController.kt
index 182a676..1121834 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/ShadeViewController.kt
+++ b/packages/SystemUI/src/com/android/systemui/shade/ShadeViewController.kt
@@ -155,6 +155,9 @@
/** Called when Back gesture has been committed (i.e. a back event has definitely occurred) */
fun onBackPressed()
+ /** Sets progress of the predictive back animation. */
+ fun onBackProgressed(progressFraction: Float)
+
/** Sets whether the status bar launch animation is currently running. */
fun setIsLaunchAnimationRunning(running: Boolean)
diff --git a/packages/SystemUI/src/com/android/systemui/shade/ShadeViewControllerEmptyImpl.kt b/packages/SystemUI/src/com/android/systemui/shade/ShadeViewControllerEmptyImpl.kt
index 09b74b2..6a2bef2 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/ShadeViewControllerEmptyImpl.kt
+++ b/packages/SystemUI/src/com/android/systemui/shade/ShadeViewControllerEmptyImpl.kt
@@ -58,6 +58,7 @@
return false
}
override fun onBackPressed() {}
+ override fun onBackProgressed(progressFraction: Float) {}
override fun setIsLaunchAnimationRunning(running: Boolean) {}
override fun setAlpha(alpha: Int, animate: Boolean) {}
override fun setAlphaChangeAnimationEndAction(r: Runnable) {}
diff --git a/packages/SystemUI/src/com/android/systemui/shade/ShadeViewProviderModule.kt b/packages/SystemUI/src/com/android/systemui/shade/ShadeViewProviderModule.kt
index 05b1ac6..6585fcb 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/ShadeViewProviderModule.kt
+++ b/packages/SystemUI/src/com/android/systemui/shade/ShadeViewProviderModule.kt
@@ -22,7 +22,6 @@
import android.view.LayoutInflater
import android.view.ViewStub
import androidx.constraintlayout.motion.widget.MotionLayout
-import com.android.keyguard.LockIconView
import com.android.systemui.R
import com.android.systemui.battery.BatteryMeterView
import com.android.systemui.battery.BatteryMeterViewController
@@ -96,11 +95,11 @@
?: throw IllegalStateException("Window root view could not be properly inflated")
}
- @Provides
- @SysUISingleton
// TODO(b/277762009): Do something similar to
// {@link StatusBarWindowModule.InternalWindowView} so that only
// {@link NotificationShadeWindowViewController} can inject this view.
+ @Provides
+ @SysUISingleton
fun providesNotificationShadeWindowView(
root: WindowRootView,
featureFlags: FeatureFlags,
@@ -206,21 +205,6 @@
// TODO(b/277762009): Only allow this view's controller to inject the view. See above.
@Provides
@SysUISingleton
- fun providesLockIconView(
- keyguardRootView: KeyguardRootView,
- notificationPanelView: NotificationPanelView,
- featureFlags: FeatureFlags
- ): LockIconView {
- if (featureFlags.isEnabled(Flags.MIGRATE_LOCK_ICON)) {
- return keyguardRootView.requireViewById(R.id.lock_icon_view)
- } else {
- return notificationPanelView.requireViewById(R.id.lock_icon_view)
- }
- }
-
- // TODO(b/277762009): Only allow this view's controller to inject the view. See above.
- @Provides
- @SysUISingleton
fun providesTapAgainView(
notificationPanelView: NotificationPanelView,
): TapAgainView {
diff --git a/packages/SystemUI/src/com/android/systemui/shade/data/repository/ShadeRepository.kt b/packages/SystemUI/src/com/android/systemui/shade/data/repository/ShadeRepository.kt
index ebb9935..5a8be1e 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/data/repository/ShadeRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/shade/data/repository/ShadeRepository.kt
@@ -37,11 +37,18 @@
/** Amount qs has expanded. Quick Settings can be expanded without the full shade expansion. */
val qsExpansion: StateFlow<Float>
+ /** The amount the shade has expanded */
+ val shadeExpansion: StateFlow<Float>
+
/** Amount shade has expanded with regard to the UDFPS location */
val udfpsTransitionToFullShadeProgress: StateFlow<Float>
+ /** The amount QS has expanded without notifications */
fun setQsExpansion(qsExpansion: Float)
fun setUdfpsTransitionToFullShadeProgress(progress: Float)
+
+ /** The amount the shade has expanded, [0-1]. 0 means fully collapsed, 1 means fully expanded */
+ fun setShadeExpansion(expansion: Float)
}
/** Business logic for shade interactions */
@@ -69,7 +76,6 @@
val currentState = shadeExpansionStateManager.addExpansionListener(callback)
callback.onPanelExpansionChanged(currentState)
- trySendWithFailureLogging(ShadeModel(), TAG, "initial shade expansion info")
awaitClose { shadeExpansionStateManager.removeExpansionListener(callback) }
}
@@ -78,6 +84,9 @@
private val _qsExpansion = MutableStateFlow(0f)
override val qsExpansion: StateFlow<Float> = _qsExpansion.asStateFlow()
+ private val _shadeExpansion = MutableStateFlow(0f)
+ override val shadeExpansion: StateFlow<Float> = _shadeExpansion.asStateFlow()
+
private var _udfpsTransitionToFullShadeProgress = MutableStateFlow(0f)
override val udfpsTransitionToFullShadeProgress: StateFlow<Float> =
_udfpsTransitionToFullShadeProgress.asStateFlow()
@@ -85,6 +94,10 @@
_qsExpansion.value = qsExpansion
}
+ override fun setShadeExpansion(expansion: Float) {
+ _shadeExpansion.value = expansion
+ }
+
override fun setUdfpsTransitionToFullShadeProgress(progress: Float) {
_udfpsTransitionToFullShadeProgress.value = progress
}
diff --git a/packages/SystemUI/src/com/android/systemui/shade/domain/interactor/ShadeInteractor.kt b/packages/SystemUI/src/com/android/systemui/shade/domain/interactor/ShadeInteractor.kt
index 6fde84a..288d32e 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/domain/interactor/ShadeInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/shade/domain/interactor/ShadeInteractor.kt
@@ -19,6 +19,8 @@
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Application
import com.android.systemui.keyguard.data.repository.KeyguardRepository
+import com.android.systemui.keyguard.shared.model.StatusBarState
+import com.android.systemui.shade.data.repository.ShadeRepository
import com.android.systemui.statusbar.disableflags.data.repository.DisableFlagsRepository
import com.android.systemui.statusbar.pipeline.mobile.data.repository.UserSetupRepository
import com.android.systemui.statusbar.policy.DeviceProvisionedController
@@ -43,6 +45,7 @@
userSetupRepository: UserSetupRepository,
deviceProvisionedController: DeviceProvisionedController,
userInteractor: UserInteractor,
+ repository: ShadeRepository,
) {
/** Emits true if the shade is currently allowed and false otherwise. */
val isShadeEnabled: StateFlow<Boolean> =
@@ -50,6 +53,25 @@
.map { it.isShadeEnabled() }
.stateIn(scope, SharingStarted.Eagerly, initialValue = false)
+ /** The amount [0-1] that the shade has been opened */
+ val shadeExpansion: Flow<Float> =
+ combine(repository.shadeExpansion, keyguardRepository.statusBarState) {
+ shadeExpansion,
+ statusBarState ->
+ // This is required, as shadeExpansion gets reset to 0f even with the shade open
+ if (statusBarState == StatusBarState.SHADE_LOCKED) {
+ 1f
+ } else {
+ shadeExpansion
+ }
+ }
+
+ /**
+ * The amount [0-1] QS has been opened. Normal shade with notifications (QQS) visible will
+ * report 0f.
+ */
+ val qsExpansion: StateFlow<Float> = repository.qsExpansion
+
/** Emits true if the shade can be expanded from QQS to QS and false otherwise. */
val isExpandToQsEnabled: Flow<Boolean> =
combine(
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/LockscreenShadeTransitionController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/LockscreenShadeTransitionController.kt
index 672796a..f004982 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/LockscreenShadeTransitionController.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/LockscreenShadeTransitionController.kt
@@ -81,6 +81,7 @@
private val powerInteractor: PowerInteractor,
) : Dumpable {
private var pulseHeight: Float = 0f
+
@get:VisibleForTesting
var fractionToShade: Float = 0f
private set
@@ -255,17 +256,17 @@
private fun updateResources() {
fullTransitionDistance = context.resources.getDimensionPixelSize(
- R.dimen.lockscreen_shade_full_transition_distance)
+ R.dimen.lockscreen_shade_full_transition_distance)
fullTransitionDistanceByTap = context.resources.getDimensionPixelSize(
R.dimen.lockscreen_shade_transition_by_tap_distance)
notificationShelfTransitionDistance = context.resources.getDimensionPixelSize(
- R.dimen.lockscreen_shade_notif_shelf_transition_distance)
+ R.dimen.lockscreen_shade_notif_shelf_transition_distance)
depthControllerTransitionDistance = context.resources.getDimensionPixelSize(
- R.dimen.lockscreen_shade_depth_controller_transition_distance)
+ R.dimen.lockscreen_shade_depth_controller_transition_distance)
udfpsTransitionDistance = context.resources.getDimensionPixelSize(
- R.dimen.lockscreen_shade_udfps_keyguard_transition_distance)
+ R.dimen.lockscreen_shade_udfps_keyguard_transition_distance)
statusBarTransitionDistance = context.resources.getDimensionPixelSize(
- R.dimen.lockscreen_shade_status_bar_transition_distance)
+ R.dimen.lockscreen_shade_status_bar_transition_distance)
useSplitShade = LargeScreenUtils.shouldUseSplitNotificationShade(context.resources)
}
@@ -292,8 +293,8 @@
*/
internal fun canDragDown(): Boolean {
return (statusBarStateController.state == StatusBarState.KEYGUARD ||
- nsslController.isInLockedDownShade()) &&
- (qS.isFullyCollapsed || useSplitShade)
+ nsslController.isInLockedDownShade()) &&
+ (qS.isFullyCollapsed || useSplitShade)
}
/**
@@ -308,10 +309,15 @@
if (nsslController.isInLockedDownShade()) {
logger.logDraggedDownLockDownShade(startingChild)
statusBarStateController.setLeaveOpenOnKeyguardHide(true)
- activityStarter.dismissKeyguardThenExecute(OnDismissAction {
- nextHideKeyguardNeedsNoAnimation = true
- false
- }, cancelRunnable, false /* afterKeyguardGone */)
+ activityStarter.dismissKeyguardThenExecute(
+ {
+ nextHideKeyguardNeedsNoAnimation = true
+ false
+ },
+ cancelRunnable,
+ /* afterKeyguardGone= */
+ false,
+ )
} else {
logger.logDraggedDown(startingChild, dragLengthY)
if (!ambientState.isDozing() || startingChild != null) {
@@ -320,11 +326,18 @@
val animationHandler = { delay: Long ->
if (startingChild is ExpandableNotificationRow) {
startingChild.onExpandedByGesture(
- true /* drag down is always an open */)
+ true /* drag down is always an open */
+ )
}
shadeViewController.transitionToExpandedShade(delay)
- callbacks.forEach { it.setTransitionToFullShadeAmount(0f,
- true /* animated */, delay) }
+ callbacks.forEach {
+ it.setTransitionToFullShadeAmount(
+ 0f,
+ /* animated= */
+ true,
+ delay
+ )
+ }
// Let's reset ourselves, ready for the next animation
@@ -350,7 +363,12 @@
*/
internal fun onDragDownReset() {
logger.logDragDownAborted()
- nsslController.setDimmed(true /* dimmed */, true /* animated */)
+ nsslController.setDimmed(
+ /* dimmed= */
+ true,
+ /* animate= */
+ true,
+ )
nsslController.resetScrollPosition()
nsslController.resetCheckSnoozeLeavebehind()
setDragDownAmountAnimated(0f)
@@ -361,7 +379,12 @@
* @param above whether they dragged above it
*/
internal fun onCrossedThreshold(above: Boolean) {
- nsslController.setDimmed(!above /* dimmed */, true /* animate */)
+ nsslController.setDimmed(
+ /* dimmed= */
+ !above,
+ /* animate= */
+ true,
+ )
}
/**
@@ -411,8 +434,8 @@
*/
internal val isDragDownAnywhereEnabled: Boolean
get() = (statusBarStateController.getState() == StatusBarState.KEYGUARD &&
- !keyguardBypassController.bypassEnabled &&
- (qS.isFullyCollapsed || useSplitShade))
+ !keyguardBypassController.bypassEnabled &&
+ (qS.isFullyCollapsed || useSplitShade))
/**
* The amount in pixels that the user has dragged down.
@@ -424,12 +447,20 @@
if (!nsslController.isInLockedDownShade() || field == 0f || forceApplyAmount) {
fractionToShade =
MathUtils.saturate(dragDownAmount / notificationShelfTransitionDistance)
+ shadeRepository.setShadeExpansion(fractionToShade)
nsslController.setTransitionToFullShadeAmount(fractionToShade)
qsTransitionController.dragDownAmount = value
- callbacks.forEach { it.setTransitionToFullShadeAmount(field,
- false /* animate */, 0 /* delay */) }
+ callbacks.forEach {
+ it.setTransitionToFullShadeAmount(
+ field,
+ /* animate= */
+ false,
+ /* delay= */
+ 0,
+ )
+ }
mediaHierarchyManager.setTransitionToFullShadeAmount(field)
scrimTransitionController.dragDownAmount = value
@@ -537,8 +568,11 @@
shadeViewController.transitionToExpandedShade(delay)
}
}
- goToLockedShadeInternal(expandedView, animationHandler,
- cancelAction = null)
+ goToLockedShadeInternal(
+ expandedView,
+ animationHandler,
+ cancelAction = null
+ )
}
}
@@ -569,14 +603,19 @@
var entry: NotificationEntry? = null
if (expandView is ExpandableNotificationRow) {
entry = expandView.entry
- entry.setUserExpanded(true /* userExpanded */, true /* allowChildExpansion */)
+ entry.setUserExpanded(
+ /* userExpanded= */
+ true,
+ /* allowChildExpansion= */
+ true,
+ )
// Indicate that the group expansion is changing at this time -- this way the group
// and children backgrounds / divider animations will look correct.
entry.setGroupExpansionChanging(true)
userId = entry.sbn.userId
}
var fullShadeNeedsBouncer = (
- !lockScreenUserManager.shouldShowLockscreenNotifications() ||
+ !lockScreenUserManager.shouldShowLockscreenNotifications() ||
falsingCollector.shouldEnforceBouncer())
if (keyguardBypassController.bypassEnabled) {
fullShadeNeedsBouncer = false
@@ -595,7 +634,10 @@
val cancelHandler = Runnable {
draggedDownEntry?.apply {
setUserLocked(false)
- notifyHeightChanged(false /* needsAnimation */)
+ notifyHeightChanged(
+ /* needsAnimation= */
+ false,
+ )
draggedDownEntry = null
}
cancelAction?.run()
@@ -613,7 +655,10 @@
// This call needs to be after updating the shade state since otherwise
// the scrimstate resets too early
if (animationHandler != null) {
- animationHandler.invoke(0 /* delay */)
+ animationHandler.invoke(
+ /* delay= */
+ 0,
+ )
} else {
performDefaultGoToFullShadeAnimation(0)
}
@@ -720,12 +765,13 @@
it.println("isDragDownAnywhereEnabled: $isDragDownAnywhereEnabled")
it.println("isFalsingCheckNeeded: $isFalsingCheckNeeded")
it.println("isWakingToShadeLocked: $isWakingToShadeLocked")
- it.println("hasPendingHandlerOnKeyguardDismiss: " +
- "${animationHandlerOnKeyguardDismiss != null}")
+ it.println(
+ "hasPendingHandlerOnKeyguardDismiss: " +
+ "${animationHandlerOnKeyguardDismiss != null}"
+ )
}
}
-
fun addCallback(callback: Callback) {
if (!callbacks.contains(callback)) {
callbacks.add(callback)
@@ -790,7 +836,7 @@
fun updateResources(context: Context) {
minDragDistance = context.resources.getDimensionPixelSize(
- R.dimen.keyguard_drag_down_min_distance)
+ R.dimen.keyguard_drag_down_min_distance)
val configuration = ViewConfiguration.get(context)
touchSlop = configuration.scaledTouchSlop.toFloat()
slopMultiplier = configuration.scaledAmbiguousGestureMultiplier
@@ -807,16 +853,17 @@
initialTouchY = y
initialTouchX = x
}
+
MotionEvent.ACTION_MOVE -> {
val h = y - initialTouchY
// Adjust the touch slop if another gesture may be being performed.
val touchSlop = if (event.classification
- == MotionEvent.CLASSIFICATION_AMBIGUOUS_GESTURE)
+ == MotionEvent.CLASSIFICATION_AMBIGUOUS_GESTURE) {
touchSlop * slopMultiplier
- else
+ } else {
touchSlop
+ }
if (h > touchSlop && h > Math.abs(x - initialTouchX)) {
- falsingCollector.onNotificationStartDraggingDown()
isDraggingDown = true
captureStartingChild(initialTouchX, initialTouchY)
initialTouchY = y
@@ -857,8 +904,9 @@
}
return true
}
+
MotionEvent.ACTION_UP -> if (!falsingManager.isUnlockingDisabled && !isFalseTouch &&
- dragDownCallback.canDragDown()) {
+ dragDownCallback.canDragDown()) {
dragDownCallback.onDraggedDown(startingChild, (y - initialTouchY).toInt())
if (startingChild != null) {
expandCallback.setUserLockedChild(startingChild, false)
@@ -869,6 +917,7 @@
stopDragging()
return false
}
+
MotionEvent.ACTION_CANCEL -> {
stopDragging()
return false
@@ -912,8 +961,8 @@
@VisibleForTesting
fun cancelChildExpansion(
- child: ExpandableView,
- animationDuration: Long = SPRING_BACK_ANIMATION_LENGTH_MS
+ child: ExpandableView,
+ animationDuration: Long = SPRING_BACK_ANIMATION_LENGTH_MS
) {
if (child.actualHeight == child.collapsedHeight) {
expandCallback.setUserLockedChild(child, false)
@@ -935,7 +984,6 @@
}
private fun stopDragging() {
- falsingCollector.onNotificationStopDraggingDown()
if (startingChild != null) {
cancelChildExpansion(startingChild!!)
startingChild = null
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationMediaManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationMediaManager.java
index 763400b..5bd40b8 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationMediaManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationMediaManager.java
@@ -65,7 +65,6 @@
import com.android.systemui.statusbar.notification.collection.notifcollection.NotifCollectionListener;
import com.android.systemui.statusbar.notification.collection.render.NotificationVisibilityProvider;
import com.android.systemui.statusbar.phone.BiometricUnlockController;
-import com.android.systemui.statusbar.phone.CentralSurfaces;
import com.android.systemui.statusbar.phone.KeyguardBypassController;
import com.android.systemui.statusbar.phone.LockscreenWallpaper;
import com.android.systemui.statusbar.phone.ScrimController;
@@ -74,6 +73,8 @@
import com.android.systemui.util.Utils;
import com.android.systemui.util.concurrency.DelayableExecutor;
+import dagger.Lazy;
+
import java.io.PrintWriter;
import java.lang.ref.WeakReference;
import java.util.ArrayList;
@@ -87,8 +88,6 @@
import java.util.Set;
import java.util.stream.Collectors;
-import dagger.Lazy;
-
/**
* Handles tasks and state related to media notifications. For example, there is a 'current' media
* notification, which this class keeps track of.
@@ -133,7 +132,6 @@
private final Context mContext;
private final ArrayList<MediaListener> mMediaListeners;
- private final Lazy<Optional<CentralSurfaces>> mCentralSurfacesOptionalLazy;
private final MediaArtworkProcessor mMediaArtworkProcessor;
private final Set<AsyncTask<?, ?, ?>> mProcessArtworkTasks = new ArraySet<>();
@@ -186,7 +184,6 @@
*/
public NotificationMediaManager(
Context context,
- Lazy<Optional<CentralSurfaces>> centralSurfacesOptionalLazy,
Lazy<NotificationShadeWindowController> notificationShadeWindowController,
NotificationVisibilityProvider visibilityProvider,
MediaArtworkProcessor mediaArtworkProcessor,
@@ -205,8 +202,6 @@
mMediaArtworkProcessor = mediaArtworkProcessor;
mKeyguardBypassController = keyguardBypassController;
mMediaListeners = new ArrayList<>();
- // TODO: use KeyguardStateController#isOccluded to remove this dependency
- mCentralSurfacesOptionalLazy = centralSurfacesOptionalLazy;
mNotificationShadeWindowController = notificationShadeWindowController;
mVisibilityProvider = visibilityProvider;
mMainExecutor = mainExecutor;
@@ -619,9 +614,7 @@
NotificationShadeWindowController windowController =
mNotificationShadeWindowController.get();
- boolean hideBecauseOccluded =
- mCentralSurfacesOptionalLazy.get()
- .map(CentralSurfaces::isOccluded).orElse(false);
+ boolean hideBecauseOccluded = mKeyguardStateController.isOccluded();
final boolean hasArtwork = artworkDrawable != null;
mColorExtractor.setHasMediaArtwork(hasMediaArtwork);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/PulseExpansionHandler.kt b/packages/SystemUI/src/com/android/systemui/statusbar/PulseExpansionHandler.kt
index fc6eaa8..2c0741e 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/PulseExpansionHandler.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/PulseExpansionHandler.kt
@@ -28,10 +28,10 @@
import android.view.VelocityTracker
import android.view.ViewConfiguration
import androidx.annotation.VisibleForTesting
+import com.android.app.animation.Interpolators
import com.android.systemui.Dumpable
import com.android.systemui.Gefingerpoken
import com.android.systemui.R
-import com.android.app.animation.Interpolators
import com.android.systemui.classifier.Classifier.NOTIFICATION_DRAG_DOWN
import com.android.systemui.classifier.FalsingCollector
import com.android.systemui.dagger.SysUISingleton
@@ -76,6 +76,7 @@
companion object {
private val SPRING_BACK_ANIMATION_LENGTH_MS = 375
}
+
private val mPowerManager: PowerManager?
private var mInitialTouchX: Float = 0.0f
@@ -94,7 +95,10 @@
pulseExpandAbortListener?.run()
}
}
- headsUpManager.unpinAll(true /* userUnPinned */)
+ headsUpManager.unpinAll(
+ /*userUnPinned= */
+ true,
+ )
}
}
var leavingLockscreen: Boolean = false
@@ -168,7 +172,6 @@
MotionEvent.ACTION_MOVE -> {
val h = y - mInitialTouchY
if (h > touchSlop && h > Math.abs(x - mInitialTouchX)) {
- falsingCollector.onStartExpandingFromPulse()
isExpanding = true
captureStartingChild(mInitialTouchX, mInitialTouchY)
mInitialTouchY = y
@@ -200,14 +203,14 @@
event.action == MotionEvent.ACTION_UP) && isExpanding
val isDraggingNotificationOrCanBypass = mStartingChild?.showingPulsing() == true ||
- bypassController.canBypass()
+ bypassController.canBypass()
if ((!canHandleMotionEvent() || !isDraggingNotificationOrCanBypass) && !finishExpanding) {
// We allow cancellations/finishing to still go through here to clean up the state
return false
}
if (velocityTracker == null || !isExpanding ||
- event.actionMasked == MotionEvent.ACTION_DOWN) {
+ event.actionMasked == MotionEvent.ACTION_DOWN) {
return startExpansion(event)
}
velocityTracker!!.addMovement(event)
@@ -217,9 +220,12 @@
when (event.actionMasked) {
MotionEvent.ACTION_MOVE -> updateExpansionHeight(moveDistance)
MotionEvent.ACTION_UP -> {
- velocityTracker!!.computeCurrentVelocity(1000 /* units */)
+ velocityTracker!!.computeCurrentVelocity(
+ /* units= */
+ 1000,
+ )
val canExpand = moveDistance > 0 && velocityTracker!!.getYVelocity() > -1000 &&
- statusBarStateController.state != StatusBarState.SHADE
+ statusBarStateController.state != StatusBarState.SHADE
if (!falsingManager.isUnlockingDisabled && !isFalseTouch && canExpand) {
finishExpansion()
} else {
@@ -227,6 +233,7 @@
}
recycleVelocityTracker()
}
+
MotionEvent.ACTION_CANCEL -> {
cancelExpansion()
recycleVelocityTracker()
@@ -243,17 +250,25 @@
}
if (statusBarStateController.isDozing) {
wakeUpCoordinator.willWakeUp = true
- mPowerManager!!.wakeUp(SystemClock.uptimeMillis(), PowerManager.WAKE_REASON_GESTURE,
- "com.android.systemui:PULSEDRAG")
+ mPowerManager!!.wakeUp(
+ SystemClock.uptimeMillis(),
+ PowerManager.WAKE_REASON_GESTURE,
+ "com.android.systemui:PULSEDRAG"
+ )
}
- lockscreenShadeTransitionController.goToLockedShade(startingChild,
- needsQSAnimation = false)
+ lockscreenShadeTransitionController.goToLockedShade(
+ startingChild,
+ needsQSAnimation = false
+ )
lockscreenShadeTransitionController.finishPulseAnimation(cancelled = false)
leavingLockscreen = true
isExpanding = false
if (mStartingChild is ExpandableNotificationRow) {
val row = mStartingChild as ExpandableNotificationRow?
- row!!.onExpandedByGesture(true /* userExpanded */)
+ row!!.onExpandedByGesture(
+ /*userExpanded= */
+ true,
+ )
}
}
@@ -261,15 +276,20 @@
var expansionHeight = max(height, 0.0f)
if (mStartingChild != null) {
val child = mStartingChild!!
- val newHeight = Math.min((child.collapsedHeight + expansionHeight).toInt(),
- child.maxContentHeight)
+ val newHeight = Math.min(
+ (child.collapsedHeight + expansionHeight).toInt(),
+ child.maxContentHeight
+ )
child.actualHeight = newHeight
} else {
wakeUpCoordinator.setNotificationsVisibleForExpansion(
height
> lockscreenShadeTransitionController.distanceUntilShowingPulsingNotifications,
- true /* animate */,
- true /* increaseSpeed */)
+ /*animate= */
+ true,
+ /*increaseSpeed= */
+ true
+ )
}
lockscreenShadeTransitionController.setPulseHeight(expansionHeight, animate = false)
}
@@ -285,8 +305,8 @@
@VisibleForTesting
fun reset(
- child: ExpandableView,
- animationDuration: Long = SPRING_BACK_ANIMATION_LENGTH_MS.toLong()
+ child: ExpandableView,
+ animationDuration: Long = SPRING_BACK_ANIMATION_LENGTH_MS.toLong()
) {
if (child.actualHeight == child.collapsedHeight) {
setUserLocked(child, false)
@@ -315,15 +335,19 @@
private fun cancelExpansion() {
isExpanding = false
- falsingCollector.onExpansionFromPulseStopped()
if (mStartingChild != null) {
reset(mStartingChild!!)
mStartingChild = null
}
lockscreenShadeTransitionController.finishPulseAnimation(cancelled = true)
- wakeUpCoordinator.setNotificationsVisibleForExpansion(false /* visible */,
- true /* animate */,
- false /* increaseSpeed */)
+ wakeUpCoordinator.setNotificationsVisibleForExpansion(
+ /*visible= */
+ false,
+ /*animate= */
+ true,
+ /*increaseSpeed= */
+ false
+ )
}
private fun findView(x: Float, y: Float): ExpandableView? {
@@ -335,7 +359,9 @@
val childAtRawPosition = stackScrollerController.getChildAtRawPosition(totalX, totalY)
return if (childAtRawPosition != null && childAtRawPosition.isContentExpandable) {
childAtRawPosition
- } else null
+ } else {
+ null
+ }
}
fun setUp(stackScrollerController: NotificationStackScrollLayoutController) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/connectivity/ConnectivityModule.kt b/packages/SystemUI/src/com/android/systemui/statusbar/connectivity/ConnectivityModule.kt
index 92aa986..b46b525 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/connectivity/ConnectivityModule.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/connectivity/ConnectivityModule.kt
@@ -16,6 +16,8 @@
package com.android.systemui.statusbar.connectivity
+import com.android.systemui.flags.FeatureFlags
+import com.android.systemui.flags.Flags.SIGNAL_CALLBACK_DEPRECATION
import com.android.systemui.qs.tileimpl.QSTileImpl
import com.android.systemui.qs.tiles.AirplaneModeTile
import com.android.systemui.qs.tiles.BluetoothTile
@@ -23,21 +25,17 @@
import com.android.systemui.qs.tiles.DataSaverTile
import com.android.systemui.qs.tiles.HotspotTile
import com.android.systemui.qs.tiles.InternetTile
+import com.android.systemui.qs.tiles.InternetTileNewImpl
import com.android.systemui.qs.tiles.NfcTile
import dagger.Binds
import dagger.Module
+import dagger.Provides
import dagger.multibindings.IntoMap
import dagger.multibindings.StringKey
@Module
interface ConnectivityModule {
- /** Inject InternetTile into tileMap in QSModule */
- @Binds
- @IntoMap
- @StringKey(InternetTile.TILE_SPEC)
- fun bindInternetTile(internetTile: InternetTile): QSTileImpl<*>
-
/** Inject BluetoothTile into tileMap in QSModule */
@Binds
@IntoMap
@@ -70,4 +68,21 @@
/** Inject NfcTile into tileMap in QSModule */
@Binds @IntoMap @StringKey(NfcTile.TILE_SPEC) fun bindNfcTile(nfcTile: NfcTile): QSTileImpl<*>
+
+ companion object {
+ /** Inject InternetTile or InternetTileNewImpl into tileMap in QSModule */
+ @Provides
+ @IntoMap
+ @StringKey(InternetTile.TILE_SPEC)
+ fun bindInternetTile(
+ internetTile: InternetTile,
+ newInternetTile: InternetTileNewImpl,
+ featureFlags: FeatureFlags,
+ ): QSTileImpl<*> =
+ if (featureFlags.isEnabled(SIGNAL_CALLBACK_DEPRECATION)) {
+ newInternetTile
+ } else {
+ internetTile
+ }
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/dagger/CentralSurfacesDependenciesModule.java b/packages/SystemUI/src/com/android/systemui/statusbar/dagger/CentralSurfacesDependenciesModule.java
index f726c4e..3dfe068 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/dagger/CentralSurfacesDependenciesModule.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/dagger/CentralSurfacesDependenciesModule.java
@@ -64,7 +64,6 @@
import com.android.systemui.statusbar.notification.collection.NotifPipeline;
import com.android.systemui.statusbar.notification.collection.notifcollection.CommonNotifCollection;
import com.android.systemui.statusbar.notification.collection.render.NotificationVisibilityProvider;
-import com.android.systemui.statusbar.phone.CentralSurfaces;
import com.android.systemui.statusbar.phone.CentralSurfacesImpl;
import com.android.systemui.statusbar.phone.KeyguardBypassController;
import com.android.systemui.statusbar.phone.ManagedProfileController;
@@ -135,7 +134,6 @@
@Provides
static NotificationMediaManager provideNotificationMediaManager(
Context context,
- Lazy<Optional<CentralSurfaces>> centralSurfacesOptionalLazy,
Lazy<NotificationShadeWindowController> notificationShadeWindowController,
NotificationVisibilityProvider visibilityProvider,
MediaArtworkProcessor mediaArtworkProcessor,
@@ -152,7 +150,6 @@
DisplayManager displayManager) {
return new NotificationMediaManager(
context,
- centralSurfacesOptionalLazy,
notificationShadeWindowController,
visibilityProvider,
mediaArtworkProcessor,
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/events/SystemStatusAnimationSchedulerImpl.kt b/packages/SystemUI/src/com/android/systemui/statusbar/events/SystemStatusAnimationSchedulerImpl.kt
index f40f570..a3bc002 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/events/SystemStatusAnimationSchedulerImpl.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/events/SystemStatusAnimationSchedulerImpl.kt
@@ -132,8 +132,9 @@
override fun onStatusEvent(event: StatusEvent) {
Assert.isMainThread()
- // Ignore any updates until the system is up and running
- if (isTooEarly() || !isImmersiveIndicatorEnabled()) {
+ // Ignore any updates until the system is up and running. However, for important events that
+ // request to be force visible (like privacy), ignore whether it's too early.
+ if ((isTooEarly() && !event.forceVisible) || !isImmersiveIndicatorEnabled()) {
return
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/events/SystemStatusAnimationSchedulerLegacyImpl.kt b/packages/SystemUI/src/com/android/systemui/statusbar/events/SystemStatusAnimationSchedulerLegacyImpl.kt
index 5fa83ef..6b5a548 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/events/SystemStatusAnimationSchedulerLegacyImpl.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/events/SystemStatusAnimationSchedulerLegacyImpl.kt
@@ -93,8 +93,9 @@
@SystemAnimationState override fun getAnimationState() = animationState
override fun onStatusEvent(event: StatusEvent) {
- // Ignore any updates until the system is up and running
- if (isTooEarly() || !isImmersiveIndicatorEnabled()) {
+ // Ignore any updates until the system is up and running. However, for important events that
+ // request to be force visible (like privacy), ignore whether it's too early.
+ if ((isTooEarly() && !event.forceVisible) || !isImmersiveIndicatorEnabled()) {
return
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationLaunchAnimatorController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationLaunchAnimatorController.kt
index 3198029..0aedbf3 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationLaunchAnimatorController.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationLaunchAnimatorController.kt
@@ -16,6 +16,7 @@
package com.android.systemui.statusbar.notification
+import android.util.Log
import android.view.ViewGroup
import com.android.internal.jank.InteractionJankMonitor
import com.android.systemui.animation.ActivityLaunchAnimator
@@ -28,6 +29,8 @@
import kotlin.math.ceil
import kotlin.math.max
+private const val TAG = "NotificationLaunchAnimatorController"
+
/** A provider of [NotificationLaunchAnimatorController]. */
class NotificationLaunchAnimatorControllerProvider(
private val notificationExpansionRepository: NotificationExpansionRepository,
@@ -86,28 +89,33 @@
val clipStartLocation = notificationListContainer.topClippingStartLocation
val roundedTopClipping = (clipStartLocation - location[1]).coerceAtLeast(0)
val windowTop = location[1] + roundedTopClipping
- val topCornerRadius = if (roundedTopClipping > 0) {
- // Because the rounded Rect clipping is complex, we start the top rounding at
- // 0, which is pretty close to matching the real clipping.
- // We'd have to clipOut the overlaid drawable too with the outer rounded rect in case
- // if we'd like to have this perfect, but this is close enough.
- 0f
- } else {
- notification.topCornerRadius
- }
- val params = LaunchAnimationParameters(
- top = windowTop,
- bottom = location[1] + height,
- left = location[0],
- right = location[0] + notification.width,
- topCornerRadius = topCornerRadius,
- bottomCornerRadius = notification.bottomCornerRadius
- )
+ val topCornerRadius =
+ if (roundedTopClipping > 0) {
+ // Because the rounded Rect clipping is complex, we start the top rounding at
+ // 0, which is pretty close to matching the real clipping.
+ // We'd have to clipOut the overlaid drawable too with the outer rounded rect in
+ // case
+ // if we'd like to have this perfect, but this is close enough.
+ 0f
+ } else {
+ notification.topCornerRadius
+ }
+ val params =
+ LaunchAnimationParameters(
+ top = windowTop,
+ bottom = location[1] + height,
+ left = location[0],
+ right = location[0] + notification.width,
+ topCornerRadius = topCornerRadius,
+ bottomCornerRadius = notification.bottomCornerRadius
+ )
params.startTranslationZ = notification.translationZ
params.startNotificationTop = location[1]
- params.notificationParentTop = notificationListContainer
- .getViewParentForNotification(notificationEntry).locationOnScreen[1]
+ params.notificationParentTop =
+ notificationListContainer
+ .getViewParentForNotification(notificationEntry)
+ .locationOnScreen[1]
params.startRoundedTopClipping = roundedTopClipping
params.startClipTopAmount = notification.clipTopAmount
if (notification.isChildInGroup) {
@@ -132,6 +140,8 @@
}
override fun onIntentStarted(willAnimate: Boolean) {
+ // TODO(b/288507023): Remove this log.
+ Log.d(TAG, "onIntentStarted(willAnimate=$willAnimate)")
notificationExpansionRepository.setIsExpandAnimationRunning(willAnimate)
notificationEntry.isExpandAnimationRunning = willAnimate
@@ -141,16 +151,30 @@
}
}
- private fun removeHun(animate: Boolean) {
- if (!headsUpManager.isAlerting(notificationKey)) {
- return
- }
+ private val headsUpNotificationRow: ExpandableNotificationRow? get() {
+ val summaryEntry = notificationEntry.parent?.summary
+ return when {
+ headsUpManager.isAlerting(notificationKey) -> notification
+ summaryEntry == null -> null
+ headsUpManager.isAlerting(summaryEntry.key) -> summaryEntry.row
+ else -> null
+ }
+ }
+
+ private fun removeHun(animate: Boolean) {
+ val row = headsUpNotificationRow ?: return
+
+ // TODO: b/297247841 - Call on the row we're removing, which may differ from notification.
HeadsUpUtil.setNeedsHeadsUpDisappearAnimationAfterClick(notification, animate)
- headsUpManager.removeNotification(notificationKey, true /* releaseImmediately */, animate)
+
+ headsUpManager.removeNotification(row.entry.key, true /* releaseImmediately */, animate)
}
override fun onLaunchAnimationCancelled(newKeyguardOccludedState: Boolean?) {
+ // TODO(b/288507023): Remove this log.
+ Log.d(TAG, "onLaunchAnimationCancelled()")
+
// TODO(b/184121838): Should we call InteractionJankMonitor.cancel if the animation started
// here?
notificationExpansionRepository.setIsExpandAnimationRunning(false)
@@ -163,11 +187,12 @@
notification.isExpandAnimationRunning = true
notificationListContainer.setExpandingNotification(notification)
- jankMonitor.begin(notification,
- InteractionJankMonitor.CUJ_NOTIFICATION_APP_START)
+ jankMonitor.begin(notification, InteractionJankMonitor.CUJ_NOTIFICATION_APP_START)
}
override fun onLaunchAnimationEnd(isExpandingFullyAbove: Boolean) {
+ // TODO(b/288507023): Remove this log.
+ Log.d(TAG, "onLaunchAnimationEnd()")
jankMonitor.end(InteractionJankMonitor.CUJ_NOTIFICATION_APP_START)
notification.isExpandAnimationRunning = false
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/dagger/NotificationsModule.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/dagger/NotificationsModule.java
index 637637d..09be41b 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/dagger/NotificationsModule.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/dagger/NotificationsModule.java
@@ -19,12 +19,13 @@
import android.content.Context;
import com.android.internal.jank.InteractionJankMonitor;
+import com.android.systemui.CoreStartable;
import com.android.systemui.R;
import com.android.systemui.dagger.SysUISingleton;
import com.android.systemui.dagger.qualifiers.UiBackground;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
+import com.android.systemui.scene.domain.interactor.WindowRootViewVisibilityInteractor;
import com.android.systemui.shade.ShadeEventsModule;
-import com.android.systemui.shade.ShadeExpansionStateManager;
import com.android.systemui.statusbar.NotificationListener;
import com.android.systemui.statusbar.notification.NotificationActivityStarter;
import com.android.systemui.statusbar.notification.NotificationLaunchAnimatorControllerProvider;
@@ -76,10 +77,13 @@
import com.android.systemui.statusbar.phone.HeadsUpManagerPhone;
import com.android.systemui.statusbar.phone.KeyguardBypassController;
import com.android.systemui.statusbar.phone.StatusBarNotificationActivityStarter;
+import com.android.systemui.util.kotlin.JavaAdapter;
import dagger.Binds;
import dagger.Module;
import dagger.Provides;
+import dagger.multibindings.ClassKey;
+import dagger.multibindings.IntoMap;
import java.util.concurrent.Executor;
@@ -110,6 +114,13 @@
@Binds
NotifGutsViewManager bindNotifGutsViewManager(NotificationGutsManager notificationGutsManager);
+ /** Binds {@link NotificationGutsManager} as a {@link CoreStartable}. */
+ @Binds
+ @IntoMap
+ @ClassKey(NotificationGutsManager.class)
+ CoreStartable bindsNotificationGutsManager(NotificationGutsManager notificationGutsManager);
+
+
/** Provides an instance of {@link VisibilityLocationProvider} */
@Binds
VisibilityLocationProvider bindVisibilityLocationProvider(
@@ -125,7 +136,8 @@
NotificationVisibilityProvider visibilityProvider,
NotifPipeline notifPipeline,
StatusBarStateController statusBarStateController,
- ShadeExpansionStateManager shadeExpansionStateManager,
+ WindowRootViewVisibilityInteractor windowRootViewVisibilityInteractor,
+ JavaAdapter javaAdapter,
NotificationLogger.ExpansionStateLogger expansionStateLogger,
NotificationPanelLogger notificationPanelLogger) {
return new NotificationLogger(
@@ -135,11 +147,18 @@
visibilityProvider,
notifPipeline,
statusBarStateController,
- shadeExpansionStateManager,
+ windowRootViewVisibilityInteractor,
+ javaAdapter,
expansionStateLogger,
notificationPanelLogger);
}
+ /** Binds {@link NotificationLogger} as a {@link CoreStartable}. */
+ @Binds
+ @IntoMap
+ @ClassKey(NotificationLogger.class)
+ CoreStartable bindsNotificationLogger(NotificationLogger notificationLogger);
+
/** Provides an instance of {@link NotificationPanelLogger} */
@SysUISingleton
@Provides
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/data/repository/NotificationExpansionRepository.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/data/repository/NotificationExpansionRepository.kt
index f605bdf..8754c4a 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/data/repository/NotificationExpansionRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/data/repository/NotificationExpansionRepository.kt
@@ -16,12 +16,15 @@
package com.android.systemui.statusbar.notification.data.repository
+import android.util.Log
import com.android.systemui.dagger.SysUISingleton
import javax.inject.Inject
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.asStateFlow
+private const val TAG = "NotificationExpansionRepository"
+
/** A repository tracking the status of notification expansion animations. */
@SysUISingleton
class NotificationExpansionRepository @Inject constructor() {
@@ -37,6 +40,8 @@
/** Sets whether the notification expansion animation is currently running. */
fun setIsExpandAnimationRunning(running: Boolean) {
+ // TODO(b/288507023): Remove this log.
+ Log.d(TAG, "setIsExpandAnimationRunning(running=$running)")
_isExpandAnimationRunning.value = running
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/logging/NotificationLogger.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/logging/NotificationLogger.java
index 26f97de..8d2a63e 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/logging/NotificationLogger.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/logging/NotificationLogger.java
@@ -33,10 +33,11 @@
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.statusbar.IStatusBarService;
import com.android.internal.statusbar.NotificationVisibility;
+import com.android.systemui.CoreStartable;
import com.android.systemui.dagger.qualifiers.UiBackground;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
import com.android.systemui.plugins.statusbar.StatusBarStateController.StateListener;
-import com.android.systemui.shade.ShadeExpansionStateManager;
+import com.android.systemui.scene.domain.interactor.WindowRootViewVisibilityInteractor;
import com.android.systemui.statusbar.NotificationListener;
import com.android.systemui.statusbar.StatusBarState;
import com.android.systemui.statusbar.notification.collection.NotifLiveDataStore;
@@ -48,6 +49,7 @@
import com.android.systemui.statusbar.notification.stack.ExpandableViewState;
import com.android.systemui.statusbar.notification.stack.NotificationListContainer;
import com.android.systemui.util.Compile;
+import com.android.systemui.util.kotlin.JavaAdapter;
import java.util.Collection;
import java.util.Collections;
@@ -62,7 +64,7 @@
* Handles notification logging, in particular, logging which notifications are visible and which
* are not.
*/
-public class NotificationLogger implements StateListener {
+public class NotificationLogger implements StateListener, CoreStartable {
static final String TAG = "NotificationLogger";
private static final boolean DEBUG = Compile.IS_DEBUG && Log.isLoggable(TAG, Log.DEBUG);
@@ -81,6 +83,8 @@
private final NotifPipeline mNotifPipeline;
private final NotificationPanelLogger mNotificationPanelLogger;
private final ExpansionStateLogger mExpansionStateLogger;
+ private final WindowRootViewVisibilityInteractor mWindowRootViewVisibilityInteractor;
+ private final JavaAdapter mJavaAdapter;
protected Handler mHandler = new Handler();
protected IStatusBarService mBarService;
@@ -88,10 +92,7 @@
private NotificationListContainer mListContainer;
private final Object mDozingLock = new Object();
@GuardedBy("mDozingLock")
- private Boolean mDozing = null; // Use null to indicate state is not yet known
- @GuardedBy("mDozingLock")
private Boolean mLockscreen = null; // Use null to indicate state is not yet known
- private Boolean mPanelExpanded = null; // Use null to indicate state is not yet known
private boolean mLogging = false;
// Tracks notifications currently visible in mNotificationStackScroller and
@@ -202,7 +203,8 @@
NotificationVisibilityProvider visibilityProvider,
NotifPipeline notifPipeline,
StatusBarStateController statusBarStateController,
- ShadeExpansionStateManager shadeExpansionStateManager,
+ WindowRootViewVisibilityInteractor windowRootViewVisibilityInteractor,
+ JavaAdapter javaAdapter,
ExpansionStateLogger expansionStateLogger,
NotificationPanelLogger notificationPanelLogger) {
mNotificationListener = notificationListener;
@@ -214,9 +216,10 @@
ServiceManager.getService(Context.STATUS_BAR_SERVICE));
mExpansionStateLogger = expansionStateLogger;
mNotificationPanelLogger = notificationPanelLogger;
+ mWindowRootViewVisibilityInteractor = windowRootViewVisibilityInteractor;
+ mJavaAdapter = javaAdapter;
// Not expected to be destroyed, don't need to unsubscribe
statusBarStateController.addCallback(this);
- shadeExpansionStateManager.addFullExpansionListener(this::onShadeExpansionFullyChanged);
registerNewPipelineListener();
}
@@ -239,6 +242,22 @@
mListContainer = listContainer;
}
+ @Override
+ public void start() {
+ mJavaAdapter.alwaysCollectFlow(
+ mWindowRootViewVisibilityInteractor.isLockscreenOrShadeVisibleAndInteractive(),
+ this::onLockscreenOrShadeVisibleAndInteractiveChanged);
+ }
+
+ private void onLockscreenOrShadeVisibleAndInteractiveChanged(boolean visible) {
+ if (visible) {
+ startNotificationLogging();
+ } else {
+ // Ensure we stop notification logging when the device isn't interactive.
+ stopNotificationLogging();
+ }
+ }
+
public void stopNotificationLogging() {
if (mLogging) {
mLogging = false;
@@ -263,10 +282,15 @@
if (DEBUG) {
Log.i(TAG, "startNotificationLogging");
}
+ boolean lockscreen;
+ synchronized (mDozingLock) {
+ lockscreen = mLockscreen != null && mLockscreen;
+ }
+ mNotificationPanelLogger.logPanelShown(lockscreen, getVisibleNotifications());
mListContainer.setChildLocationsChangedListener(this::onChildLocationsChanged);
- // Some transitions like mVisibleToUser=false -> mVisibleToUser=true don't
- // cause the scroller to emit child location events. Hence generate
- // one ourselves to guarantee that we're reporting visible
+ // Sometimes, the transition from lockscreenOrShadeVisible=false ->
+ // lockscreenOrShadeVisible=true doesn't cause the scroller to emit child location
+ // events. Hence generate one ourselves to guarantee that we're reporting visible
// notifications.
// (Note that in cases where the scroller does emit events, this
// additional event doesn't break anything.)
@@ -274,13 +298,6 @@
}
}
- private void setDozing(boolean dozing) {
- synchronized (mDozingLock) {
- mDozing = dozing;
- maybeUpdateLoggingStatus();
- }
- }
-
private void logNotificationVisibilityChanges(
Collection<NotificationVisibility> newlyVisible,
Collection<NotificationVisibility> noLongerVisible) {
@@ -362,39 +379,6 @@
}
}
- @Override
- public void onDozingChanged(boolean isDozing) {
- if (DEBUG) {
- Log.i(TAG, "onDozingChanged: new=" + isDozing);
- }
- setDozing(isDozing);
- }
-
- @GuardedBy("mDozingLock")
- private void maybeUpdateLoggingStatus() {
- if (mPanelExpanded == null || mDozing == null) {
- if (DEBUG) {
- Log.i(TAG, "Panel status unclear: panelExpandedKnown="
- + (mPanelExpanded == null) + " dozingKnown=" + (mDozing == null));
- }
- return;
- }
- // Once we know panelExpanded and Dozing, turn logging on & off when appropriate
- boolean lockscreen = mLockscreen == null ? false : mLockscreen;
- if (mPanelExpanded && !mDozing) {
- mNotificationPanelLogger.logPanelShown(lockscreen, getVisibleNotifications());
- if (DEBUG) {
- Log.i(TAG, "Notification panel shown, lockscreen=" + lockscreen);
- }
- startNotificationLogging();
- } else {
- if (DEBUG) {
- Log.i(TAG, "Notification panel hidden, lockscreen=" + lockscreen);
- }
- stopNotificationLogging();
- }
- }
-
/**
* Called when the notification is expanded / collapsed.
*/
@@ -404,20 +388,6 @@
}
@VisibleForTesting
- void onShadeExpansionFullyChanged(Boolean isExpanded) {
- // mPanelExpanded is initialized as null
- if (mPanelExpanded == null || !mPanelExpanded.equals(isExpanded)) {
- if (DEBUG) {
- Log.i(TAG, "onPanelExpandedChanged: new=" + isExpanded);
- }
- mPanelExpanded = isExpanded;
- synchronized (mDozingLock) {
- maybeUpdateLoggingStatus();
- }
- }
- }
-
- @VisibleForTesting
void onChildLocationsChanged() {
if (mHandler.hasCallbacks(mVisibilityReporter)) {
// Visibilities will be reported when the existing
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 d92d11b..c02382d 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
@@ -74,7 +74,6 @@
import com.android.internal.widget.CachingIconView;
import com.android.internal.widget.CallLayout;
import com.android.systemui.R;
-import com.android.systemui.classifier.FalsingCollector;
import com.android.systemui.flags.FeatureFlags;
import com.android.systemui.flags.Flags;
import com.android.systemui.flags.ViewRefactorFlag;
@@ -245,7 +244,6 @@
private NotificationEntry mEntry;
private String mAppName;
private FalsingManager mFalsingManager;
- private FalsingCollector mFalsingCollector;
/**
* Whether or not the notification is using the heads up view and should peek from the top.
@@ -1711,7 +1709,6 @@
OnExpandClickListener onExpandClickListener,
CoordinateOnClickListener onFeedbackClickListener,
FalsingManager falsingManager,
- FalsingCollector falsingCollector,
StatusBarStateController statusBarStateController,
PeopleNotificationIdentifier peopleNotificationIdentifier,
OnUserInteractionCallback onUserInteractionCallback,
@@ -1743,7 +1740,6 @@
mOnExpandClickListener = onExpandClickListener;
setOnFeedbackClickListener(onFeedbackClickListener);
mFalsingManager = falsingManager;
- mFalsingCollector = falsingCollector;
mStatusBarStateController = statusBarStateController;
mPeopleNotificationIdentifier = peopleNotificationIdentifier;
for (NotificationContentView l : mLayouts) {
@@ -2471,7 +2467,6 @@
* @param allowChildExpansion whether a call to this method allows expanding children
*/
public void setUserExpanded(boolean userExpanded, boolean allowChildExpansion) {
- mFalsingCollector.setNotificationExpanded();
if (mIsSummaryWithChildren && !shouldShowPublic() && allowChildExpansion
&& !mChildrenContainer.showingAsLowPriority()) {
final boolean wasExpanded = mGroupExpansionManager.isGroupExpanded(mEntry);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowController.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowController.java
index 80f5d19..af55f44 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowController.java
@@ -34,7 +34,6 @@
import com.android.internal.logging.MetricsLogger;
import com.android.internal.statusbar.IStatusBarService;
-import com.android.systemui.classifier.FalsingCollector;
import com.android.systemui.flags.FeatureFlags;
import com.android.systemui.flags.Flags;
import com.android.systemui.plugins.FalsingManager;
@@ -102,7 +101,6 @@
private final NotificationGutsManager mNotificationGutsManager;
private final OnUserInteractionCallback mOnUserInteractionCallback;
private final FalsingManager mFalsingManager;
- private final FalsingCollector mFalsingCollector;
private final FeatureFlags mFeatureFlags;
private final boolean mAllowLongPress;
private final PeopleNotificationIdentifier mPeopleNotificationIdentifier;
@@ -222,7 +220,6 @@
@Named(ALLOW_NOTIFICATION_LONG_PRESS_NAME) boolean allowLongPress,
OnUserInteractionCallback onUserInteractionCallback,
FalsingManager falsingManager,
- FalsingCollector falsingCollector,
FeatureFlags featureFlags,
PeopleNotificationIdentifier peopleNotificationIdentifier,
Optional<BubblesManager> bubblesManagerOptional,
@@ -251,7 +248,6 @@
mFalsingManager = falsingManager;
mOnFeedbackClickListener = mNotificationGutsManager::openGuts;
mAllowLongPress = allowLongPress;
- mFalsingCollector = falsingCollector;
mFeatureFlags = featureFlags;
mPeopleNotificationIdentifier = peopleNotificationIdentifier;
mBubblesManagerOptional = bubblesManagerOptional;
@@ -285,7 +281,6 @@
mOnExpandClickListener,
mOnFeedbackClickListener,
mFalsingManager,
- mFalsingCollector,
mStatusBarStateController,
mPeopleNotificationIdentifier,
mOnUserInteractionCallback,
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationGutsManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationGutsManager.java
index 6f79ea8..44ead26 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationGutsManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationGutsManager.java
@@ -45,6 +45,7 @@
import com.android.internal.logging.UiEventLogger;
import com.android.internal.logging.nano.MetricsProto;
import com.android.settingslib.notification.ConversationIconFactory;
+import com.android.systemui.CoreStartable;
import com.android.systemui.R;
import com.android.systemui.dagger.SysUISingleton;
import com.android.systemui.dagger.qualifiers.Background;
@@ -53,6 +54,7 @@
import com.android.systemui.plugins.ActivityStarter;
import com.android.systemui.plugins.statusbar.NotificationMenuRowPlugin;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
+import com.android.systemui.scene.domain.interactor.WindowRootViewVisibilityInteractor;
import com.android.systemui.settings.UserContextProvider;
import com.android.systemui.shade.ShadeController;
import com.android.systemui.statusbar.NotificationLockscreenUserManager;
@@ -69,6 +71,7 @@
import com.android.systemui.statusbar.phone.CentralSurfaces;
import com.android.systemui.statusbar.phone.HeadsUpManagerPhone;
import com.android.systemui.statusbar.policy.DeviceProvisionedController;
+import com.android.systemui.util.kotlin.JavaAdapter;
import com.android.systemui.wmshell.BubblesManager;
import java.util.Optional;
@@ -80,7 +83,7 @@
* closing guts, and keeping track of the currently exposed notification guts.
*/
@SysUISingleton
-public class NotificationGutsManager implements NotifGutsViewManager {
+public class NotificationGutsManager implements NotifGutsViewManager, CoreStartable {
private static final String TAG = "NotificationGutsManager";
// Must match constant in Settings. Used to highlight preferences when linking to Settings.
@@ -109,6 +112,7 @@
private final Handler mMainHandler;
private final Handler mBgHandler;
+ private final JavaAdapter mJavaAdapter;
private final Optional<BubblesManager> mBubblesManagerOptional;
private Runnable mOpenRunnable;
private final INotificationManager mNotificationManager;
@@ -121,6 +125,7 @@
private final UserContextProvider mContextTracker;
private final UiEventLogger mUiEventLogger;
private final ShadeController mShadeController;
+ private final WindowRootViewVisibilityInteractor mWindowRootViewVisibilityInteractor;
private NotifGutsViewListener mGutsListener;
private final HeadsUpManagerPhone mHeadsUpManagerPhone;
private final ActivityStarter mActivityStarter;
@@ -129,6 +134,7 @@
public NotificationGutsManager(Context context,
@Main Handler mainHandler,
@Background Handler bgHandler,
+ JavaAdapter javaAdapter,
AccessibilityManager accessibilityManager,
HighPriorityProvider highPriorityProvider,
INotificationManager notificationManager,
@@ -143,6 +149,7 @@
UiEventLogger uiEventLogger,
OnUserInteractionCallback onUserInteractionCallback,
ShadeController shadeController,
+ WindowRootViewVisibilityInteractor windowRootViewVisibilityInteractor,
NotificationLockscreenUserManager notificationLockscreenUserManager,
StatusBarStateController statusBarStateController,
DeviceProvisionedController deviceProvisionedController,
@@ -152,6 +159,7 @@
mContext = context;
mMainHandler = mainHandler;
mBgHandler = bgHandler;
+ mJavaAdapter = javaAdapter;
mAccessibilityManager = accessibilityManager;
mHighPriorityProvider = highPriorityProvider;
mNotificationManager = notificationManager;
@@ -166,6 +174,7 @@
mUiEventLogger = uiEventLogger;
mOnUserInteractionCallback = onUserInteractionCallback;
mShadeController = shadeController;
+ mWindowRootViewVisibilityInteractor = windowRootViewVisibilityInteractor;
mLockscreenUserManager = notificationLockscreenUserManager;
mStatusBarStateController = statusBarStateController;
mDeviceProvisionedController = deviceProvisionedController;
@@ -187,6 +196,25 @@
mNotificationActivityStarter = notificationActivityStarter;
}
+ @Override
+ public void start() {
+ mJavaAdapter.alwaysCollectFlow(
+ mWindowRootViewVisibilityInteractor.isLockscreenOrShadeVisible(),
+ this::onLockscreenShadeVisibilityChanged);
+ }
+
+ private void onLockscreenShadeVisibilityChanged(boolean visible) {
+ if (!visible) {
+ closeAndSaveGuts(
+ /* removeLeavebehind= */ true ,
+ /* force= */ true,
+ /* removeControls= */ true,
+ /* x= */ -1,
+ /* y= */ -1,
+ /* resetMenu= */ true);
+ }
+ }
+
public void onDensityOrFontScaleChanged(NotificationEntry entry) {
setExposedGuts(entry.getGuts());
bindGuts(entry.getRow());
@@ -512,7 +540,7 @@
mNotificationGutsExposed.removeCallbacks(mOpenRunnable);
mNotificationGutsExposed.closeControls(removeLeavebehinds, removeControls, x, y, force);
}
- if (resetMenu) {
+ if (resetMenu && mListContainer != null) {
mListContainer.resetExposedMenuView(false /* animate */, true /* force */);
}
}
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 6db8df9..d8f513c 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
@@ -455,7 +455,6 @@
@Override
public void onDragCancelled(View v) {
- mFalsingCollector.onNotificationStopDismissing();
}
/**
@@ -501,7 +500,6 @@
}
mView.addSwipedOutView(view);
- mFalsingCollector.onNotificationDismissed();
if (mFalsingCollector.shouldEnforceBouncer()) {
mActivityStarter.executeRunnableDismissingKeyguard(
null,
@@ -549,7 +547,6 @@
@Override
public void onBeginDrag(View v) {
- mFalsingCollector.onNotificationStartDismissing();
mView.onSwipeBegin(v);
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/domain/interactor/SharedNotificationContainerInteractor.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/domain/interactor/SharedNotificationContainerInteractor.kt
index 874450b..4ed31c2 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/domain/interactor/SharedNotificationContainerInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/domain/interactor/SharedNotificationContainerInteractor.kt
@@ -20,19 +20,27 @@
import android.content.Context
import com.android.systemui.R
import com.android.systemui.common.ui.data.repository.ConfigurationRepository
+import com.android.systemui.dagger.SysUISingleton
import javax.inject.Inject
import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.MutableStateFlow
+import kotlinx.coroutines.flow.asStateFlow
import kotlinx.coroutines.flow.distinctUntilChanged
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.flow.onStart
/** Encapsulates business-logic specifically related to the shared notification stack container. */
+@SysUISingleton
class SharedNotificationContainerInteractor
@Inject
constructor(
configurationRepository: ConfigurationRepository,
private val context: Context,
) {
+
+ private val _topPosition = MutableStateFlow(0f)
+ val topPosition = _topPosition.asStateFlow()
+
val configurationBasedDimensions: Flow<ConfigurationBasedDimensions> =
configurationRepository.onAnyConfigurationChange
.onStart { emit(Unit) }
@@ -54,6 +62,11 @@
}
.distinctUntilChanged()
+ /** Top position (without translation) of the shared container. */
+ fun setTopPosition(top: Float) {
+ _topPosition.value = top
+ }
+
data class ConfigurationBasedDimensions(
val useSplitShade: Boolean,
val useLargeScreenHeader: Boolean,
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewbinder/SharedNotificationContainerBinder.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewbinder/SharedNotificationContainerBinder.kt
index fb1d55d..2af7181 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewbinder/SharedNotificationContainerBinder.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewbinder/SharedNotificationContainerBinder.kt
@@ -19,6 +19,7 @@
import androidx.lifecycle.Lifecycle
import androidx.lifecycle.repeatOnLifecycle
import com.android.systemui.lifecycle.repeatWhenAttached
+import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayoutController
import com.android.systemui.statusbar.notification.stack.ui.view.SharedNotificationContainer
import com.android.systemui.statusbar.notification.stack.ui.viewmodel.SharedNotificationContainerViewModel
import kotlinx.coroutines.launch
@@ -30,9 +31,10 @@
fun bind(
view: SharedNotificationContainer,
viewModel: SharedNotificationContainerViewModel,
+ controller: NotificationStackScrollLayoutController,
) {
view.repeatWhenAttached {
- repeatOnLifecycle(Lifecycle.State.STARTED) {
+ repeatOnLifecycle(Lifecycle.State.CREATED) {
launch {
viewModel.configurationBasedDimensions.collect {
view.updateConstraints(
@@ -42,8 +44,29 @@
marginEnd = it.marginEnd,
marginBottom = it.marginBottom,
)
+
+ controller.setOverExpansion(0f)
+ controller.setOverScrollAmount(0)
+ controller.updateFooter()
}
}
+
+ launch {
+ viewModel.maxNotifications.collect {
+ controller.setMaxDisplayedNotifications(it)
+ }
+ }
+
+ launch {
+ viewModel.position.collect {
+ controller.updateTopPadding(
+ it.top,
+ controller.isAddOrRemoveAnimationPending()
+ )
+ }
+ }
+
+ launch { viewModel.translationY.collect { controller.setTranslationY(it) } }
}
}
}
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 b2e5ac1..6f4adeb 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
@@ -17,18 +17,35 @@
package com.android.systemui.statusbar.notification.stack.ui.viewmodel
+import com.android.systemui.common.shared.model.SharedNotificationContainerPosition
+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.shade.domain.interactor.ShadeInteractor
+import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayoutController
+import com.android.systemui.statusbar.notification.stack.NotificationStackSizeCalculator
import com.android.systemui.statusbar.notification.stack.domain.interactor.SharedNotificationContainerInteractor
import javax.inject.Inject
import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.combine
import kotlinx.coroutines.flow.distinctUntilChanged
+import kotlinx.coroutines.flow.flatMapLatest
import kotlinx.coroutines.flow.map
-/** View-model for the shared notification container */
+/** View-model for the shared notification container, used by both the shade and keyguard spaces */
class SharedNotificationContainerViewModel
@Inject
constructor(
interactor: SharedNotificationContainerInteractor,
+ keyguardInteractor: KeyguardInteractor,
+ keyguardTransitionInteractor: KeyguardTransitionInteractor,
+ notificationStackSizeCalculator: NotificationStackSizeCalculator,
+ controller: NotificationStackScrollLayoutController,
+ shadeInteractor: ShadeInteractor,
) {
+ private val statesForConstrainedNotifications =
+ setOf(KeyguardState.LOCKSCREEN, KeyguardState.AOD, KeyguardState.DOZING)
+
val configurationBasedDimensions: Flow<ConfigurationBasedDimensions> =
interactor.configurationBasedDimensions
.map {
@@ -43,6 +60,91 @@
}
.distinctUntilChanged()
+ /** If the user is visually on one of the unoccluded lockscreen states. */
+ val isOnLockscreen: Flow<Boolean> =
+ keyguardTransitionInteractor.finishedKeyguardState
+ .map { statesForConstrainedNotifications.contains(it) }
+ .distinctUntilChanged()
+
+ /** Are we purely on the keyguard without the shade/qs? */
+ val isOnLockscreenWithoutShade: Flow<Boolean> =
+ combine(
+ isOnLockscreen,
+ // Shade with notifications
+ shadeInteractor.shadeExpansion.map { it > 0f },
+ // Shade without notifications, quick settings only (pull down from very top on
+ // lockscreen)
+ shadeInteractor.qsExpansion.map { it > 0f },
+ ) { isKeyguard, isShadeVisible, qsExpansion ->
+ isKeyguard && !(isShadeVisible || qsExpansion)
+ }
+ .distinctUntilChanged()
+
+ /**
+ * The container occupies the entire screen, and must be positioned relative to other elements.
+ *
+ * On keyguard, this generally fits below the clock and above the lock icon, or in split shade,
+ * the top of the screen to the lock icon.
+ *
+ * When the shade is expanding, the position is controlled by... the shade.
+ */
+ val position: Flow<SharedNotificationContainerPosition> =
+ isOnLockscreenWithoutShade.flatMapLatest { onLockscreen ->
+ if (onLockscreen) {
+ combine(
+ keyguardInteractor.sharedNotificationContainerPosition,
+ configurationBasedDimensions
+ ) { position, config ->
+ if (config.useSplitShade) {
+ position.copy(top = 0f)
+ } else {
+ position
+ }
+ }
+ } else {
+ interactor.topPosition.map { top ->
+ keyguardInteractor.sharedNotificationContainerPosition.value.copy(top = top)
+ }
+ }
+ }
+
+ /**
+ * Under certain scenarios, such as swiping up on the lockscreen, the container will need to be
+ * translated as the keyguard fades out.
+ */
+ val translationY: Flow<Float> =
+ combine(isOnLockscreen, keyguardInteractor.keyguardTranslationY) {
+ isOnLockscreen,
+ translationY ->
+ if (isOnLockscreen) {
+ translationY
+ } else {
+ 0f
+ }
+ }
+
+ /**
+ * When on keyguard, there is limited space to display notifications so calculate how many could
+ * be shown. Otherwise, there is no limit since the vertical space will be scrollable.
+ * TODO: b/296606746 - Need to rerun logic when notifs change
+ */
+ val maxNotifications: Flow<Int> =
+ combine(isOnLockscreen, shadeInteractor.shadeExpansion, position) {
+ onLockscreen,
+ shadeExpansion,
+ positionInfo ->
+ if (onLockscreen && shadeExpansion < 1f) {
+ notificationStackSizeCalculator.computeMaxKeyguardNotifications(
+ controller.getView(),
+ positionInfo.bottom - positionInfo.top,
+ 0f, // Vertical space for shelf is already accounted for
+ controller.getShelfHeight().toFloat(),
+ )
+ } else {
+ -1 // No limit
+ }
+ }
+
data class ConfigurationBasedDimensions(
val marginStart: Int,
val marginTop: Int,
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 d0a093c..0ff1a95 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfaces.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfaces.java
@@ -200,12 +200,6 @@
void onKeyguardViewManagerStatesUpdated();
- boolean isPulsing();
-
- boolean isOccluded();
-
- boolean isDeviceInVrMode();
-
NotificationPresenter getPresenter();
/**
@@ -247,8 +241,6 @@
@Deprecated
float getDisplayHeight();
- void readyForKeyguardDone();
-
void showKeyguard();
boolean hideKeyguard();
@@ -370,8 +362,6 @@
@Deprecated
float getDisplayDensity();
- void extendDozePulse();
-
public static class KeyboardShortcutsMessage {
final int mDeviceId;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesEmptyImpl.kt b/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesEmptyImpl.kt
new file mode 100644
index 0000000..37038a3
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesEmptyImpl.kt
@@ -0,0 +1,120 @@
+/*
+ * Copyright (C) 2022 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.phone
+
+import android.content.Intent
+import androidx.lifecycle.LifecycleRegistry
+import com.android.keyguard.AuthKeyguardMessageArea
+import com.android.systemui.animation.ActivityLaunchAnimator
+import com.android.systemui.navigationbar.NavigationBarView
+import com.android.systemui.plugins.ActivityStarter.OnDismissAction
+import com.android.systemui.qs.QSPanelController
+import com.android.systemui.statusbar.NotificationPresenter
+import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow
+import java.io.PrintWriter
+
+/**
+ * Empty implementation of [CentralSurfaces] for variants only need to override portions of the
+ * interface.
+ *
+ * **Important**: Prefer binding an Optional<CentralSurfaces> to an empty optional instead of
+ * including this class.
+ */
+abstract class CentralSurfacesEmptyImpl : CentralSurfaces {
+ override val lifecycle = LifecycleRegistry(this)
+ override fun updateIsKeyguard() = false
+ override fun updateIsKeyguard(forceStateChange: Boolean) = false
+ override fun getKeyguardMessageArea(): AuthKeyguardMessageArea? = null
+ override fun isLaunchingActivityOverLockscreen() = false
+ override fun onKeyguardViewManagerStatesUpdated() {}
+ override fun getPresenter(): NotificationPresenter? = null
+ override fun onInputFocusTransfer(start: Boolean, cancel: Boolean, velocity: Float) {}
+ override fun getCommandQueuePanelsEnabled() = false
+ override fun getBiometricUnlockController(): BiometricUnlockController? = null
+ override fun showWirelessChargingAnimation(batteryLevel: Int) {}
+ override fun checkBarModes() {}
+ override fun updateBubblesVisibility() {}
+ override fun setInteracting(barWindow: Int, interacting: Boolean) {}
+ override fun dump(pwOriginal: PrintWriter, args: Array<String>) {}
+ override fun getDisplayWidth() = 0f
+ override fun getDisplayHeight() = 0f
+ override fun showKeyguard() {}
+ override fun hideKeyguard() = false
+ override fun showKeyguardImpl() {}
+ override fun fadeKeyguardAfterLaunchTransition(
+ beforeFading: Runnable?,
+ endRunnable: Runnable?,
+ cancelRunnable: Runnable?,
+ ) {}
+ override fun startLaunchTransitionTimeout() {}
+ override fun hideKeyguardImpl(forceStateChange: Boolean) = false
+ override fun keyguardGoingAway() {}
+ override fun setKeyguardFadingAway(startTime: Long, delay: Long, fadeoutDuration: Long) {}
+ override fun finishKeyguardFadingAway() {}
+ override fun userActivity() {}
+ override fun endAffordanceLaunch() {}
+ override fun shouldKeyguardHideImmediately() = false
+ override fun showBouncerWithDimissAndCancelIfKeyguard(
+ performAction: OnDismissAction?,
+ cancelAction: Runnable?,
+ ) {}
+ override fun getNavigationBarView(): NavigationBarView? = null
+ override fun isOverviewEnabled() = false
+ override fun showPinningEnterExitToast(entering: Boolean) {}
+ override fun showPinningEscapeToast() {}
+ override fun setBouncerShowing(bouncerShowing: Boolean) {}
+ override fun getWakefulnessState() = 0
+ override fun isScreenFullyOff() = false
+ override fun showScreenPinningRequest(taskId: Int, allowCancel: Boolean) {}
+ override fun getEmergencyActionIntent(): Intent? = null
+ override fun isCameraAllowedByAdmin() = false
+ override fun isGoingToSleep() = false
+ override fun notifyBiometricAuthModeChanged() {}
+ override fun setTransitionToFullShadeProgress(transitionToFullShadeProgress: Float) {}
+ override fun setPrimaryBouncerHiddenFraction(expansion: Float) {}
+ override fun updateScrimController() {}
+ override fun isKeyguardShowing() = false
+ override fun shouldIgnoreTouch() = false
+ override fun isDeviceInteractive() = false
+ override fun awakenDreams() {}
+ override fun clearNotificationEffects() {}
+ override fun isBouncerShowing() = false
+ override fun isBouncerShowingScrimmed() = false
+ override fun isBouncerShowingOverDream() = false
+ override fun isKeyguardSecure() = false
+ override fun updateNotificationPanelTouchState() {}
+ override fun getRotation() = 0
+ override fun setBarStateForTest(state: Int) {}
+ override fun showTransientUnchecked() {}
+ override fun clearTransient() {}
+ override fun acquireGestureWakeLock(time: Long) {}
+ override fun setAppearance(appearance: Int) = false
+ override fun getBarMode() = 0
+ override fun resendMessage(msg: Int) {}
+ override fun resendMessage(msg: Any?) {}
+ override fun setLastCameraLaunchSource(source: Int) {}
+ override fun setLaunchCameraOnFinishedGoingToSleep(launch: Boolean) {}
+ override fun setLaunchCameraOnFinishedWaking(launch: Boolean) {}
+ override fun setLaunchEmergencyActionOnFinishedGoingToSleep(launch: Boolean) {}
+ override fun setLaunchEmergencyActionOnFinishedWaking(launch: Boolean) {}
+ override fun getQSPanelController(): QSPanelController? = null
+ override fun getDisplayDensity() = 0f
+ override fun setIsLaunchingActivityOverLockscreen(isLaunchingActivityOverLockscreen: Boolean) {}
+ override fun getAnimatorControllerFromNotification(
+ associatedView: ExpandableNotificationRow?,
+ ): ActivityLaunchAnimator.Controller? = null
+}
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 6a3ebe6..8cdf7d8 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesImpl.java
@@ -92,17 +92,12 @@
import android.view.MotionEvent;
import android.view.ThreadedRenderer;
import android.view.View;
-import android.view.ViewRootImpl;
import android.view.WindowInsets;
import android.view.WindowInsetsController.Appearance;
import android.view.WindowManager;
import android.view.WindowManagerGlobal;
import android.view.accessibility.AccessibilityManager;
import android.widget.DateTimeView;
-import android.window.BackEvent;
-import android.window.OnBackAnimationCallback;
-import android.window.OnBackInvokedCallback;
-import android.window.OnBackInvokedDispatcher;
import androidx.annotation.NonNull;
import androidx.lifecycle.Lifecycle;
@@ -176,6 +171,7 @@
import com.android.systemui.qs.QSFragment;
import com.android.systemui.qs.QSPanelController;
import com.android.systemui.recents.ScreenPinningRequest;
+import com.android.systemui.scene.domain.interactor.WindowRootViewVisibilityInteractor;
import com.android.systemui.scrim.ScrimView;
import com.android.systemui.settings.UserTracker;
import com.android.systemui.settings.brightness.BrightnessSliderController;
@@ -221,7 +217,6 @@
import com.android.systemui.statusbar.notification.data.repository.NotificationExpansionRepository;
import com.android.systemui.statusbar.notification.init.NotificationsController;
import com.android.systemui.statusbar.notification.interruption.NotificationInterruptStateProvider;
-import com.android.systemui.statusbar.notification.logging.NotificationLogger;
import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
import com.android.systemui.statusbar.notification.row.NotificationGutsManager;
import com.android.systemui.statusbar.notification.stack.NotificationListContainer;
@@ -462,6 +457,7 @@
private final CentralSurfacesComponent.Factory mCentralSurfacesComponentFactory;
private final PluginManager mPluginManager;
private final ShadeController mShadeController;
+ private final WindowRootViewVisibilityInteractor mWindowRootViewVisibilityInteractor;
private final InitController mInitController;
private final Lazy<CameraLauncher> mCameraLauncherLazy;
private final AlternateBouncerInteractor mAlternateBouncerInteractor;
@@ -489,13 +485,11 @@
private View mReportRejectedTouch;
private final NotificationGutsManager mGutsManager;
- private final NotificationLogger mNotificationLogger;
private final ShadeExpansionStateManager mShadeExpansionStateManager;
private final KeyguardViewMediator mKeyguardViewMediator;
protected final NotificationInterruptStateProvider mNotificationInterruptStateProvider;
private final BrightnessSliderController.Factory mBrightnessSliderFactory;
private final FeatureFlags mFeatureFlags;
- private final boolean mAnimateBack;
private final FragmentService mFragmentService;
private final ScreenOffAnimationController mScreenOffAnimationController;
private final WallpaperController mWallpaperController;
@@ -508,13 +502,6 @@
private CentralSurfacesComponent mCentralSurfacesComponent;
- /**
- * This keeps track of whether we have (or haven't) registered the predictive back callback.
- * Since we can have visible -> visible transitions, we need to avoid
- * double-registering (or double-unregistering) our callback.
- */
- private boolean mIsBackCallbackRegistered = false;
-
/** @see android.view.WindowInsetsController#setSystemBarsAppearance(int, int) */
private @Appearance int mAppearance;
@@ -640,31 +627,6 @@
private final InteractionJankMonitor mJankMonitor;
- /** Existing callback that handles back gesture invoked for the Shade. */
- private final OnBackInvokedCallback mOnBackInvokedCallback;
-
- /**
- * New callback that handles back gesture invoked, cancel, progress
- * and provides feedback via Shade animation.
- * (enabled via the WM_SHADE_ANIMATE_BACK_GESTURE flag)
- */
- private final OnBackAnimationCallback mOnBackAnimationCallback = new OnBackAnimationCallback() {
- @Override
- public void onBackInvoked() {
- mBackActionInteractor.onBackRequested();
- }
-
- @Override
- public void onBackProgressed(BackEvent event) {
- if (mBackActionInteractor.shouldBackBeHandled()) {
- if (mShadeSurface.canBeCollapsed()) {
- float fraction = event.getProgress();
- mShadeSurface.onBackProgressed(fraction);
- }
- }
- }
- };
-
/**
* Public constructor for CentralSurfaces.
*
@@ -694,7 +656,6 @@
FalsingCollector falsingCollector,
BroadcastDispatcher broadcastDispatcher,
NotificationGutsManager notificationGutsManager,
- NotificationLogger notificationLogger,
NotificationInterruptStateProvider notificationInterruptStateProvider,
ShadeExpansionStateManager shadeExpansionStateManager,
KeyguardViewMediator keyguardViewMediator,
@@ -745,6 +706,7 @@
Lazy<CentralSurfacesCommandQueueCallbacks> commandQueueCallbacksLazy,
PluginManager pluginManager,
ShadeController shadeController,
+ WindowRootViewVisibilityInteractor windowRootViewVisibilityInteractor,
StatusBarKeyguardViewManager statusBarKeyguardViewManager,
ViewMediatorCallback viewMediatorCallback,
InitController initController,
@@ -803,7 +765,6 @@
mFalsingManager = falsingManager;
mBroadcastDispatcher = broadcastDispatcher;
mGutsManager = notificationGutsManager;
- mNotificationLogger = notificationLogger;
mNotificationInterruptStateProvider = notificationInterruptStateProvider;
mShadeExpansionStateManager = shadeExpansionStateManager;
mKeyguardViewMediator = keyguardViewMediator;
@@ -856,6 +817,7 @@
mCommandQueueCallbacksLazy = commandQueueCallbacksLazy;
mPluginManager = pluginManager;
mShadeController = shadeController;
+ mWindowRootViewVisibilityInteractor = windowRootViewVisibilityInteractor;
mStatusBarKeyguardViewManager = statusBarKeyguardViewManager;
mKeyguardViewMediatorCallback = viewMediatorCallback;
mInitController = initController;
@@ -926,14 +888,6 @@
if (mFeatureFlags.isEnabled(Flags.WM_ENABLE_PREDICTIVE_BACK_SYSUI)) {
mContext.getApplicationInfo().setEnableOnBackInvokedCallback(true);
}
- // Based on teamfood flag, enable predictive back animation for the Shade.
- mAnimateBack = mFeatureFlags.isEnabled(Flags.WM_SHADE_ANIMATE_BACK_GESTURE);
- mOnBackInvokedCallback = () -> {
- if (DEBUG) {
- Log.d(TAG, "mOnBackInvokedCallback() called");
- }
- mBackActionInteractor.onBackRequested();
- };
}
private void initBubbles(Bubbles bubbles) {
@@ -1171,7 +1125,10 @@
new FoldStateListener(mContext, this::onFoldedStateChanged));
}
- @VisibleForTesting
+ /**
+ * @deprecated use {@link
+ * WindowRootViewVisibilityInteractor.isLockscreenOrShadeVisible} instead.
+ */ @VisibleForTesting
void initShadeVisibilityListener() {
mShadeController.setVisibilityListener(new ShadeController.ShadeVisibilityListener() {
@Override
@@ -1480,7 +1437,7 @@
// - Shade is in QQS over keyguard - swiping up should take us back to keyguard
if (!isKeyguardShowing()
|| mStatusBarKeyguardViewManager.primaryBouncerIsOrWillBeShowing()
- || isOccluded()
+ || mKeyguardStateController.isOccluded()
|| !mKeyguardStateController.canDismissLockScreen()
|| mKeyguardViewMediator.isAnySimPinSecure()
|| (mQsController.getExpanded() && trackingTouch)
@@ -1563,6 +1520,7 @@
mNotifListContainer,
mStackScrollerController.getNotifStackController(),
mNotificationActivityStarter);
+ mWindowRootViewVisibilityInteractor.setUp(mPresenter, mNotificationsController);
}
/**
@@ -1709,27 +1667,6 @@
}
@Override
- public boolean isPulsing() {
- return mDozeServiceHost.isPulsing();
- }
-
- /**
- * When the keyguard is showing and covered by a "showWhenLocked" activity it
- * is occluded. This is controlled by {@link com.android.server.policy.PhoneWindowManager}
- *
- * @return whether the keyguard is currently occluded
- */
- @Override
- public boolean isOccluded() {
- return mKeyguardStateController.isOccluded();
- }
-
- @Override
- public boolean isDeviceInVrMode() {
- return mPresenter.isDeviceInVrMode();
- }
-
- @Override
public NotificationPresenter getPresenter() {
return mPresenter;
}
@@ -1963,8 +1900,6 @@
pw.print(" mDozing="); pw.println(mDozing);
pw.print(" mWallpaperSupported= "); pw.println(mWallpaperSupported);
- pw.println(" ShadeWindowView: ");
- getNotificationShadeWindowViewController().dump(pw, args);
CentralSurfaces.dumpBarTransitions(
pw, "PhoneStatusBarTransitions", mStatusBarTransitions);
@@ -2077,11 +2012,6 @@
return mDisplay.getRotation();
}
- @Override
- public void readyForKeyguardDone() {
- mStatusBarKeyguardViewManager.readyForKeyguardDone();
- }
-
private final BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
@@ -2170,82 +2100,6 @@
com.android.systemui.R.dimen.physical_power_button_center_screen_location_y));
}
- protected void handleVisibleToUserChanged(boolean visibleToUser) {
- if (visibleToUser) {
- onVisibleToUser();
- mNotificationLogger.startNotificationLogging();
-
- if (!mIsBackCallbackRegistered) {
- ViewRootImpl viewRootImpl = getViewRootImpl();
- if (viewRootImpl != null) {
- viewRootImpl.getOnBackInvokedDispatcher()
- .registerOnBackInvokedCallback(OnBackInvokedDispatcher.PRIORITY_DEFAULT,
- mAnimateBack ? mOnBackAnimationCallback
- : mOnBackInvokedCallback);
- mIsBackCallbackRegistered = true;
- if (DEBUG) Log.d(TAG, "is now VISIBLE to user AND callback registered");
- }
- } else {
- if (DEBUG) Log.d(TAG, "is now VISIBLE to user, BUT callback ALREADY unregistered");
- }
- } else {
- mNotificationLogger.stopNotificationLogging();
- onInvisibleToUser();
-
- if (mIsBackCallbackRegistered) {
- ViewRootImpl viewRootImpl = getViewRootImpl();
- if (viewRootImpl != null) {
- viewRootImpl.getOnBackInvokedDispatcher()
- .unregisterOnBackInvokedCallback(
- mAnimateBack ? mOnBackAnimationCallback
- : mOnBackInvokedCallback);
- mIsBackCallbackRegistered = false;
- if (DEBUG) Log.d(TAG, "is NOT VISIBLE to user, AND callback unregistered");
- }
- } else {
- if (DEBUG) {
- Log.d(TAG,
- "is NOT VISIBLE to user, BUT NO callback (or callback ALREADY "
- + "unregistered)");
- }
- }
- }
- }
-
- void onVisibleToUser() {
- /* The LEDs are turned off when the notification panel is shown, even just a little bit.
- * See also CentralSurfaces.setPanelExpanded for another place where we attempt to do
- * this.
- */
- boolean pinnedHeadsUp = mHeadsUpManager.hasPinnedHeadsUp();
- boolean clearNotificationEffects =
- !mPresenter.isPresenterFullyCollapsed() && (mState == StatusBarState.SHADE
- || mState == StatusBarState.SHADE_LOCKED);
- int notificationLoad = mNotificationsController.getActiveNotificationsCount();
- if (pinnedHeadsUp && mPresenter.isPresenterFullyCollapsed()) {
- notificationLoad = 1;
- }
- final int finalNotificationLoad = notificationLoad;
- mUiBgExecutor.execute(() -> {
- try {
- mBarService.onPanelRevealed(clearNotificationEffects,
- finalNotificationLoad);
- } catch (RemoteException ex) {
- // Won't fail unless the world has ended.
- }
- });
- }
-
- void onInvisibleToUser() {
- mUiBgExecutor.execute(() -> {
- try {
- mBarService.onPanelHidden();
- } catch (RemoteException ex) {
- // Won't fail unless the world has ended.
- }
- });
- }
-
private void logStateToEventlog() {
boolean isShowing = mKeyguardStateController.isShowing();
boolean isOccluded = mKeyguardStateController.isOccluded();
@@ -2325,11 +2179,12 @@
// there's no surface we can show to the user. Note that the device goes fully interactive
// late in the transition, so we also allow the device to start dozing once the screen has
// turned off fully.
+ boolean keyguardShowingUnOccluded =
+ mKeyguardStateController.isShowing() && !mKeyguardStateController.isOccluded();
boolean keyguardForDozing = mDozeServiceHost.getDozingRequested()
&& (!mDeviceInteractive || (isGoingToSleep()
- && (isScreenFullyOff()
- || (mKeyguardStateController.isShowing() && !isOccluded()))));
- boolean isWakingAndOccluded = isOccluded() && isWakingOrAwake();
+ && (isScreenFullyOff() || keyguardShowingUnOccluded)));
+ boolean isWakingAndOccluded = mKeyguardStateController.isOccluded() && isWakingOrAwake();
boolean shouldBeKeyguard = (mStatusBarStateController.isKeyguardRequested()
|| keyguardForDozing) && !wakeAndUnlocking && !isWakingAndOccluded;
if (keyguardForDozing) {
@@ -2728,11 +2583,6 @@
mNavigationBarController.showPinningEscapeToast(mDisplayId);
}
- protected ViewRootImpl getViewRootImpl() {
- View root = mNotificationShadeWindowController.getWindowRootView();
- if (root != null) return root.getViewRootImpl();
- return null;
- }
/**
* Propagation of the bouncer state, indicating that it's fully visible.
*/
@@ -2779,7 +2629,6 @@
releaseGestureWakeLock();
mLaunchCameraWhenFinishedWaking = false;
mDeviceInteractive = false;
- updateVisibleToUser();
updateNotificationPanelTouchState();
getNotificationShadeWindowViewController().cancelCurrentTouch();
@@ -2852,12 +2701,14 @@
// cancelling a sleep), from the power button, on a device with a power button
// FPS, and 'press to unlock' is required.
mShouldDelayWakeUpAnimation =
- !isPulsing()
+ !mDozeServiceHost.isPulsing()
&& mStatusBarStateController.getDozeAmount() == 1f
&& mWakefulnessLifecycle.getLastWakeReason()
== PowerManager.WAKE_REASON_POWER_BUTTON
&& mFingerprintManager.get().isPowerbuttonFps()
- && mFingerprintManager.get().hasEnrolledFingerprints()
+ && mKeyguardUpdateMonitor
+ .getCachedIsUnlockWithFingerprintPossible(
+ mUserTracker.getUserId())
&& !touchToUnlockAnytime;
if (DEBUG_WAKEUP_DELAY) {
Log.d(TAG, "mShouldDelayWakeUpAnimation=" + mShouldDelayWakeUpAnimation);
@@ -2876,7 +2727,6 @@
/* wakingUp= */ true,
mShouldDelayWakeUpAnimation);
- updateVisibleToUser();
updateIsKeyguard();
mShouldDelayLockscreenTransitionFromAod = mDozeParameters.getAlwaysOn()
&& mFeatureFlags.isEnabled(
@@ -3128,7 +2978,7 @@
mScrimController.setExpansionAffectsAlpha(!unlocking);
if (mAlternateBouncerInteractor.isVisibleState()) {
- if ((!isOccluded() || mShadeSurface.isPanelExpanded())
+ if ((!mKeyguardStateController.isOccluded() || mShadeSurface.isPanelExpanded())
&& (mState == StatusBarState.SHADE || mState == StatusBarState.SHADE_LOCKED
|| mTransitionToFullShadeProgress > 0f)) {
mScrimController.transitionTo(ScrimState.AUTH_SCRIMMED_SHADE);
@@ -3164,7 +3014,9 @@
// 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();
- } else if (mKeyguardStateController.isShowing() && !isOccluded() && !unlocking) {
+ } else if (mKeyguardStateController.isShowing()
+ && !mKeyguardStateController.isOccluded()
+ && !unlocking) {
mScrimController.transitionTo(ScrimState.KEYGUARD);
} else if (mKeyguardStateController.isShowing() && mKeyguardUpdateMonitor.isDreaming()
&& !unlocking) {
@@ -3203,9 +3055,6 @@
protected boolean mVisible;
- // mScreenOnFromKeyguard && mVisible.
- private boolean mVisibleToUser;
-
protected DevicePolicyManager mDevicePolicyManager;
private final PowerManager mPowerManager;
protected StatusBarKeyguardViewManager mStatusBarKeyguardViewManager;
@@ -3329,21 +3178,8 @@
if (visible) {
DejankUtils.notifyRendererOfExpensiveFrame(
getNotificationShadeWindowView(), "onShadeVisibilityChanged");
- } else {
- mGutsManager.closeAndSaveGuts(true /* removeLeavebehind */, true /* force */,
- true /* removeControls */, -1 /* x */, -1 /* y */, true /* resetMenu */);
}
}
- updateVisibleToUser();
- }
-
- protected void updateVisibleToUser() {
- boolean oldVisibleToUser = mVisibleToUser;
- mVisibleToUser = mVisible && mDeviceInteractive;
-
- if (oldVisibleToUser != mVisibleToUser) {
- handleVisibleToUserChanged(mVisibleToUser);
- }
}
/**
@@ -3401,11 +3237,6 @@
}
}
- @Override
- public void extendDozePulse(){
- mDozeScrimController.extendPulse();
- }
-
private final KeyguardUpdateMonitorCallback mUpdateCallback =
new KeyguardUpdateMonitorCallback() {
@Override
@@ -3524,7 +3355,7 @@
// If we're visible and switched to SHADE_LOCKED (the user dragged
// down on the lockscreen), clear notification LED, vibration,
// ringing.
- // Other transitions are covered in handleVisibleToUserChanged().
+ // Other transitions are covered in WindowRootViewVisibilityInteractor.
if (mVisible && (newState == StatusBarState.SHADE_LOCKED
|| mStatusBarStateController.goingToFullShade())) {
clearNotificationEffects();
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ComponentSystemUIDialog.kt b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ComponentSystemUIDialog.kt
new file mode 100644
index 0000000..38a6d39
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ComponentSystemUIDialog.kt
@@ -0,0 +1,154 @@
+/*
+ * 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.statusbar.phone
+
+import android.annotation.CallSuper
+import android.content.Context
+import android.os.Bundle
+import android.view.View
+import android.view.ViewGroup
+import androidx.activity.OnBackPressedDispatcher
+import androidx.activity.OnBackPressedDispatcherOwner
+import androidx.activity.setViewTreeOnBackPressedDispatcherOwner
+import androidx.lifecycle.Lifecycle
+import androidx.lifecycle.LifecycleOwner
+import androidx.lifecycle.LifecycleRegistry
+import androidx.lifecycle.setViewTreeLifecycleOwner
+import androidx.savedstate.SavedStateRegistry
+import androidx.savedstate.SavedStateRegistryController
+import androidx.savedstate.SavedStateRegistryOwner
+import androidx.savedstate.setViewTreeSavedStateRegistryOwner
+import com.android.systemui.animation.DialogLaunchAnimator
+import com.android.systemui.broadcast.BroadcastDispatcher
+import com.android.systemui.flags.FeatureFlags
+import com.android.systemui.model.SysUiState
+
+/**
+ * A [SystemUIDialog] that implements [LifecycleOwner], [SavedStateRegistryOwner] and
+ * [OnBackPressedDispatcherOwner].
+ *
+ * This class was forked from [androidx.activity.ComponentDialog] and can be used to easily create
+ * SystemUI dialogs without the need to subclass [SystemUIDialog]. You should call
+ * [SystemUIDialogFactory.create] to easily instantiate this class.
+ *
+ * Important: [ComponentSystemUIDialog] should be created and shown on the main thread.
+ *
+ * @see SystemUIDialogFactory.create
+ */
+class ComponentSystemUIDialog(
+ context: Context,
+ theme: Int,
+ dismissOnDeviceLock: Boolean,
+ featureFlags: FeatureFlags,
+ dialogManager: SystemUIDialogManager,
+ sysUiState: SysUiState,
+ broadcastDispatcher: BroadcastDispatcher,
+ dialogLaunchAnimator: DialogLaunchAnimator,
+) :
+ SystemUIDialog(
+ context,
+ theme,
+ dismissOnDeviceLock,
+ featureFlags,
+ dialogManager,
+ sysUiState,
+ broadcastDispatcher,
+ dialogLaunchAnimator
+ ),
+ LifecycleOwner,
+ SavedStateRegistryOwner,
+ OnBackPressedDispatcherOwner {
+ private var _lifecycleRegistry: LifecycleRegistry? = null
+ private val lifecycleRegistry: LifecycleRegistry
+ get() = _lifecycleRegistry ?: LifecycleRegistry(this).also { _lifecycleRegistry = it }
+
+ private val savedStateRegistryController: SavedStateRegistryController =
+ SavedStateRegistryController.create(this)
+ override val savedStateRegistry: SavedStateRegistry
+ get() = savedStateRegistryController.savedStateRegistry
+
+ override val lifecycle: Lifecycle
+ get() = lifecycleRegistry
+
+ override fun onSaveInstanceState(): Bundle {
+ val bundle = super.onSaveInstanceState()
+ savedStateRegistryController.performSave(bundle)
+ return bundle
+ }
+
+ @CallSuper
+ override fun onCreate(savedInstanceState: Bundle?) {
+ super.onCreate(savedInstanceState)
+ onBackPressedDispatcher.setOnBackInvokedDispatcher(onBackInvokedDispatcher)
+ savedStateRegistryController.performRestore(savedInstanceState)
+ lifecycleRegistry.handleLifecycleEvent(Lifecycle.Event.ON_CREATE)
+ }
+
+ @CallSuper
+ override fun start() {
+ lifecycleRegistry.handleLifecycleEvent(Lifecycle.Event.ON_RESUME)
+ }
+
+ @CallSuper
+ override fun stop() {
+ // This is the closest thing to onDestroy that a Dialog has
+ // TODO(b/296180426): Make SystemUIDialog.onStop() and onStart() open again (annotated with
+ // @CallSuper) and do this *before* calling super.onStop(), like AndroidX ComponentDialog
+ // does.
+ lifecycleRegistry.handleLifecycleEvent(Lifecycle.Event.ON_DESTROY)
+ _lifecycleRegistry = null
+ }
+
+ @Suppress("DEPRECATION")
+ override val onBackPressedDispatcher = OnBackPressedDispatcher { super.onBackPressed() }
+
+ @CallSuper
+ override fun onBackPressed() {
+ onBackPressedDispatcher.onBackPressed()
+ }
+
+ override fun setContentView(layoutResID: Int) {
+ initializeViewTreeOwners()
+ super.setContentView(layoutResID)
+ }
+
+ override fun setContentView(view: View) {
+ initializeViewTreeOwners()
+ super.setContentView(view)
+ }
+
+ override fun setContentView(view: View, params: ViewGroup.LayoutParams?) {
+ initializeViewTreeOwners()
+ super.setContentView(view, params)
+ }
+
+ override fun addContentView(view: View, params: ViewGroup.LayoutParams?) {
+ initializeViewTreeOwners()
+ super.addContentView(view, params)
+ }
+
+ /**
+ * Sets the view tree owners before setting the content view so that the inflation process and
+ * attach listeners will see them already present.
+ */
+ @CallSuper
+ open fun initializeViewTreeOwners() {
+ window!!.decorView.setViewTreeLifecycleOwner(this)
+ window!!.decorView.setViewTreeOnBackPressedDispatcherOwner(this)
+ window!!.decorView.setViewTreeSavedStateRegistryOwner(this)
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/DozeServiceHost.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/DozeServiceHost.java
index 801cdbf..4849f64 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/DozeServiceHost.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/DozeServiceHost.java
@@ -185,7 +185,7 @@
return mDozingRequested;
}
- boolean isPulsing() {
+ public boolean isPulsing() {
return mPulsing;
}
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 62a8cfd..b0f8276 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java
@@ -763,7 +763,7 @@
* Sets the amount of vertical over scroll that should be performed on the notifications scrim.
*/
public void setNotificationsOverScrollAmount(int overScrollAmount) {
- mNotificationsScrim.setTranslationY(overScrollAmount);
+ if (mNotificationsScrim != null) mNotificationsScrim.setTranslationY(overScrollAmount);
}
/**
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarIconController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarIconController.java
index d5cb6b6..4878d45 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarIconController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarIconController.java
@@ -81,11 +81,7 @@
/** Removes an icon that had come from an active tile service. */
void removeIconForTile(String slot);
- /**
- * Adds or updates an icon for the given slot for **internal system icons**.
- *
- * TODO(b/265307726): Re-name this to `setInternalIcon`.
- */
+ /** Adds or updates an icon for the given slot for **internal system icons**. */
void setIcon(String slot, int resourceId, CharSequence contentDescription);
/**
@@ -127,11 +123,6 @@
*/
void removeIcon(String slot, int tag);
- /**
- * TODO(b/265307726): Re-name this to `removeAllIconsForInternalSlot`.
- */
- void removeAllIconsForSlot(String slot);
-
// TODO: See if we can rename this tunable name.
String ICON_HIDE_LIST = "icon_blacklist";
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarIconControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarIconControllerImpl.java
index 553cbc5..0f4d68c 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarIconControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarIconControllerImpl.java
@@ -385,13 +385,7 @@
}
private void removeAllIconsForExternalSlot(String slotName) {
- removeAllIconsForSlot(createExternalSlotName(slotName));
- }
-
- /** */
- @Override
- public void removeAllIconsForSlot(String slotName) {
- removeAllIconsForSlot(slotName, /* fromNewPipeline */ false);
+ removeAllIconsForSlot(createExternalSlotName(slotName), /* fromNewPipeline= */ false);
}
private void removeAllIconsForSlot(String slotName, boolean fromNewPipeline) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/SystemUIDialog.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/SystemUIDialog.java
index baf94fc..69510ce 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/SystemUIDialog.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/SystemUIDialog.java
@@ -68,7 +68,7 @@
// TODO(b/203389579): Remove this once the dialog width on large screens has been agreed on.
private static final String FLAG_TABLET_DIALOG_WIDTH =
"persist.systemui.flag_tablet_dialog_width";
- private static final boolean DEFAULT_DISMISS_ON_DEVICE_LOCK = true;
+ public static final boolean DEFAULT_DISMISS_ON_DEVICE_LOCK = true;
private final Context mContext;
private final FeatureFlags mFeatureFlags;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/SystemUIDialogFactory.kt b/packages/SystemUI/src/com/android/systemui/statusbar/phone/SystemUIDialogFactory.kt
new file mode 100644
index 0000000..3b15065
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/SystemUIDialogFactory.kt
@@ -0,0 +1,66 @@
+/*
+ * 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.statusbar.phone
+
+import android.content.Context
+import com.android.systemui.animation.DialogLaunchAnimator
+import com.android.systemui.broadcast.BroadcastDispatcher
+import com.android.systemui.dagger.qualifiers.Application
+import com.android.systemui.flags.FeatureFlagsClassic
+import com.android.systemui.model.SysUiState
+import com.android.systemui.util.Assert
+import javax.inject.Inject
+
+/** A factory to easily instantiate a [ComponentSystemUIDialog]. */
+class SystemUIDialogFactory
+@Inject
+constructor(
+ @Application val applicationContext: Context,
+ private val featureFlags: FeatureFlagsClassic,
+ private val dialogManager: SystemUIDialogManager,
+ private val sysUiState: SysUiState,
+ private val broadcastDispatcher: BroadcastDispatcher,
+ private val dialogLaunchAnimator: DialogLaunchAnimator,
+) {
+ /**
+ * Create a new [ComponentSystemUIDialog].
+ *
+ * Important: This should be called on the main thread and the returned dialog should be shown
+ * on the main thread.
+ *
+ * @param context the [Context] in which the dialog will be constructed.
+ * @param dismissOnDeviceLock whether the dialog should be automatically dismissed when the
+ * device is locked (true by default).
+ */
+ fun create(
+ context: Context = this.applicationContext,
+ dismissOnDeviceLock: Boolean = SystemUIDialog.DEFAULT_DISMISS_ON_DEVICE_LOCK,
+ ): ComponentSystemUIDialog {
+ Assert.isMainThread()
+
+ return ComponentSystemUIDialog(
+ context,
+ SystemUIDialog.DEFAULT_THEME,
+ dismissOnDeviceLock,
+ featureFlags,
+ dialogManager,
+ sysUiState,
+ broadcastDispatcher,
+ dialogLaunchAnimator,
+ )
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/ethernet/domain/EthernetInteractor.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/ethernet/domain/EthernetInteractor.kt
new file mode 100644
index 0000000..3709e4c
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/ethernet/domain/EthernetInteractor.kt
@@ -0,0 +1,62 @@
+/*
+ * 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.statusbar.pipeline.ethernet.domain
+
+import com.android.settingslib.AccessibilityContentDescriptions
+import com.android.systemui.R
+import com.android.systemui.common.shared.model.ContentDescription
+import com.android.systemui.common.shared.model.Icon
+import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.statusbar.pipeline.shared.data.repository.ConnectivityRepository
+import javax.inject.Inject
+import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.map
+
+/**
+ * Currently we don't do much to interact with ethernet. We simply need a place to map between the
+ * connectivity state of a default ethernet connection, and an icon representing that connection.
+ */
+@SysUISingleton
+class EthernetInteractor
+@Inject
+constructor(
+ connectivityRepository: ConnectivityRepository,
+) {
+ /** Icon representing the current connectivity status of the ethernet connection */
+ val icon: Flow<Icon.Resource?> =
+ connectivityRepository.defaultConnections.map {
+ if (it.ethernet.isDefault) {
+ if (it.isValidated) {
+ Icon.Resource(
+ R.drawable.stat_sys_ethernet_fully,
+ ContentDescription.Resource(
+ AccessibilityContentDescriptions.ETHERNET_CONNECTION_VALUES[1]
+ )
+ )
+ } else {
+ Icon.Resource(
+ R.drawable.stat_sys_ethernet,
+ ContentDescription.Resource(
+ AccessibilityContentDescriptions.ETHERNET_CONNECTION_VALUES[0]
+ )
+ )
+ }
+ } else {
+ null
+ }
+ }
+}
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 051e88f..02473f2 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
@@ -16,9 +16,11 @@
package com.android.systemui.statusbar.pipeline.mobile.data
+import android.content.Intent
import android.telephony.ServiceState
import android.telephony.SignalStrength
import android.telephony.TelephonyDisplayInfo
+import android.telephony.TelephonyManager
import com.android.settingslib.SignalIcon
import com.android.settingslib.mobile.MobileMappings
import com.android.systemui.dagger.SysUISingleton
@@ -162,6 +164,28 @@
fun logOnSubscriptionsChanged() {
buffer.log(TAG, LogLevel.INFO, {}, { "onSubscriptionsChanged" })
}
+
+ fun logServiceProvidersUpdatedBroadcast(intent: Intent) {
+ val showSpn = intent.getBooleanExtra(TelephonyManager.EXTRA_SHOW_SPN, false)
+ val spn = intent.getStringExtra(TelephonyManager.EXTRA_DATA_SPN)
+ val showPlmn = intent.getBooleanExtra(TelephonyManager.EXTRA_SHOW_PLMN, false)
+ val plmn = intent.getStringExtra(TelephonyManager.EXTRA_PLMN)
+
+ buffer.log(
+ TAG,
+ LogLevel.INFO,
+ {
+ bool1 = showSpn
+ str1 = spn
+ bool2 = showPlmn
+ str2 = plmn
+ },
+ {
+ "Intent: ACTION_SERVICE_PROVIDERS_UPDATED." +
+ " showSpn=$bool1 spn=$str1 showPlmn=$bool2 plmn=$str2"
+ }
+ )
+ }
}
private const val TAG = "MobileInputLog"
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/MobileConnectionRepositoryImpl.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/MobileConnectionRepositoryImpl.kt
index 1f1ac92..cd68621 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/MobileConnectionRepositoryImpl.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/MobileConnectionRepositoryImpl.kt
@@ -16,12 +16,16 @@
package com.android.systemui.statusbar.pipeline.mobile.data.repository.prod
+import android.annotation.SuppressLint
+import android.content.BroadcastReceiver
+import android.content.Context
import android.content.Intent
import android.content.IntentFilter
import android.telephony.CellSignalStrength.SIGNAL_STRENGTH_NONE_OR_UNKNOWN
import android.telephony.CellSignalStrengthCdma
import android.telephony.ServiceState
import android.telephony.SignalStrength
+import android.telephony.SubscriptionManager.EXTRA_SUBSCRIPTION_INDEX
import android.telephony.SubscriptionManager.INVALID_SUBSCRIPTION_ID
import android.telephony.TelephonyCallback
import android.telephony.TelephonyDisplayInfo
@@ -34,6 +38,7 @@
import android.telephony.TelephonyManager.UNKNOWN_CARRIER_ID
import com.android.settingslib.Utils
import com.android.systemui.broadcast.BroadcastDispatcher
+import com.android.systemui.common.coroutine.ConflatedCallbackFlow.conflatedCallbackFlow
import com.android.systemui.dagger.qualifiers.Application
import com.android.systemui.dagger.qualifiers.Background
import com.android.systemui.log.table.TableLogBuffer
@@ -81,6 +86,7 @@
@OptIn(ExperimentalCoroutinesApi::class)
class MobileConnectionRepositoryImpl(
override val subId: Int,
+ private val context: Context,
subscriptionModel: StateFlow<SubscriptionModel?>,
defaultNetworkName: NetworkNameModel,
networkNameSeparator: String,
@@ -323,16 +329,35 @@
}
.stateIn(scope, SharingStarted.WhileSubscribed(), telephonyManager.simCarrierId)
+ /** BroadcastDispatcher does not handle sticky broadcasts, so we can't use it here */
+ @SuppressLint("RegisterReceiverViaContext")
override val networkName: StateFlow<NetworkNameModel> =
- broadcastDispatcher
- .broadcastFlow(
- filter = IntentFilter(TelephonyManager.ACTION_SERVICE_PROVIDERS_UPDATED),
- map = { intent, _ -> intent },
- )
- .filter { intent ->
- intent.getIntExtra(EXTRA_SUBSCRIPTION_ID, INVALID_SUBSCRIPTION_ID) == subId
+ conflatedCallbackFlow {
+ val receiver =
+ object : BroadcastReceiver() {
+ override fun onReceive(context: Context, intent: Intent) {
+ if (
+ intent.getIntExtra(
+ EXTRA_SUBSCRIPTION_INDEX,
+ INVALID_SUBSCRIPTION_ID
+ ) == subId
+ ) {
+ logger.logServiceProvidersUpdatedBroadcast(intent)
+ trySend(
+ intent.toNetworkNameModel(networkNameSeparator)
+ ?: defaultNetworkName
+ )
+ }
+ }
+ }
+
+ context.registerReceiver(
+ receiver,
+ IntentFilter(TelephonyManager.ACTION_SERVICE_PROVIDERS_UPDATED)
+ )
+
+ awaitClose { context.unregisterReceiver(receiver) }
}
- .map { intent -> intent.toNetworkNameModel(networkNameSeparator) ?: defaultNetworkName }
.stateIn(scope, SharingStarted.WhileSubscribed(), defaultNetworkName)
override val dataEnabled = run {
@@ -349,6 +374,7 @@
class Factory
@Inject
constructor(
+ private val context: Context,
private val broadcastDispatcher: BroadcastDispatcher,
private val telephonyManager: TelephonyManager,
private val logger: MobileInputLogger,
@@ -366,6 +392,7 @@
): MobileConnectionRepository {
return MobileConnectionRepositoryImpl(
subId,
+ context,
subscriptionModel,
defaultNetworkName,
networkNameSeparator,
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/domain/interactor/MobileIconInteractor.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/domain/interactor/MobileIconInteractor.kt
index 4cfde5b..4bf297c 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/domain/interactor/MobileIconInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/domain/interactor/MobileIconInteractor.kt
@@ -17,8 +17,8 @@
package com.android.systemui.statusbar.pipeline.mobile.domain.interactor
import android.content.Context
-import android.telephony.CarrierConfigManager
import com.android.settingslib.SignalIcon.MobileIconGroup
+import com.android.settingslib.graph.SignalDrawable
import com.android.settingslib.mobile.MobileIconCarrierIdOverrides
import com.android.settingslib.mobile.MobileIconCarrierIdOverridesImpl
import com.android.systemui.dagger.qualifiers.Application
@@ -31,6 +31,7 @@
import com.android.systemui.statusbar.pipeline.mobile.domain.model.NetworkTypeIconModel
import com.android.systemui.statusbar.pipeline.mobile.domain.model.NetworkTypeIconModel.DefaultIcon
import com.android.systemui.statusbar.pipeline.mobile.domain.model.NetworkTypeIconModel.OverriddenIcon
+import com.android.systemui.statusbar.pipeline.mobile.domain.model.SignalIconModel
import com.android.systemui.statusbar.pipeline.shared.data.model.DataActivityModel
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.ExperimentalCoroutinesApi
@@ -60,24 +61,17 @@
*/
val isDataConnected: StateFlow<Boolean>
- /** Only true if mobile is the default transport but is not validated, otherwise false */
- val isDefaultConnectionFailed: StateFlow<Boolean>
-
/** True if we consider this connection to be in service, i.e. can make calls */
val isInService: StateFlow<Boolean>
- // TODO(b/256839546): clarify naming of default vs active
- /** True if we want to consider the data connection enabled */
- val isDefaultDataEnabled: StateFlow<Boolean>
-
/** Observable for the data enabled state of this connection */
val isDataEnabled: StateFlow<Boolean>
/** True if the RAT icon should always be displayed and false otherwise. */
val alwaysShowDataRatIcon: StateFlow<Boolean>
- /** True if the CDMA level should be preferred over the primary level. */
- val alwaysUseCdmaLevel: StateFlow<Boolean>
+ /** Canonical representation of the current mobile signal strength as a triangle. */
+ val signalLevelIcon: StateFlow<SignalIconModel>
/** Observable for RAT type (network type) indicator */
val networkTypeIconGroup: StateFlow<NetworkTypeIconModel>
@@ -108,9 +102,6 @@
/** True if there is only one active subscription. */
val isSingleCarrier: StateFlow<Boolean>
- /** True if this line of service is emergency-only */
- val isEmergencyOnly: StateFlow<Boolean>
-
/**
* True if this connection is considered roaming. The roaming bit can come from [ServiceState],
* or directly from the telephony manager's CDMA ERI number value. Note that we don't consider a
@@ -118,12 +109,6 @@
*/
val isRoaming: StateFlow<Boolean>
- /** Int describing the connection strength. 0-4 OR 1-5. See [numberOfLevels] */
- val level: StateFlow<Int>
-
- /** Based on [CarrierConfigManager.KEY_INFLATE_SIGNAL_STRENGTH_BOOL], either 4 or 5 */
- val numberOfLevels: StateFlow<Int>
-
/** See [MobileIconsInteractor.isForceHidden]. */
val isForceHidden: Flow<Boolean>
@@ -141,12 +126,12 @@
@Application scope: CoroutineScope,
defaultSubscriptionHasDataEnabled: StateFlow<Boolean>,
override val alwaysShowDataRatIcon: StateFlow<Boolean>,
- override val alwaysUseCdmaLevel: StateFlow<Boolean>,
+ alwaysUseCdmaLevel: StateFlow<Boolean>,
override val isSingleCarrier: StateFlow<Boolean>,
override val mobileIsDefault: StateFlow<Boolean>,
defaultMobileIconMapping: StateFlow<Map<String, MobileIconGroup>>,
defaultMobileIconGroup: StateFlow<MobileIconGroup>,
- override val isDefaultConnectionFailed: StateFlow<Boolean>,
+ isDefaultConnectionFailed: StateFlow<Boolean>,
override val isForceHidden: Flow<Boolean>,
connectionRepository: MobileConnectionRepository,
private val context: Context,
@@ -170,8 +155,6 @@
.distinctUntilChanged()
.stateIn(scope, SharingStarted.WhileSubscribed(), false)
- override val isDefaultDataEnabled = defaultSubscriptionHasDataEnabled
-
override val networkName =
combine(connectionRepository.operatorAlphaShort, connectionRepository.networkName) {
operatorAlphaShort,
@@ -255,8 +238,6 @@
DefaultIcon(defaultMobileIconGroup.value),
)
- override val isEmergencyOnly = connectionRepository.isEmergencyOnly
-
override val isRoaming: StateFlow<Boolean> =
combine(
connectionRepository.carrierNetworkChangeActive,
@@ -274,7 +255,7 @@
}
.stateIn(scope, SharingStarted.WhileSubscribed(), false)
- override val level: StateFlow<Int> =
+ private val level: StateFlow<Int> =
combine(
connectionRepository.isGsm,
connectionRepository.primaryLevel,
@@ -290,7 +271,7 @@
}
.stateIn(scope, SharingStarted.WhileSubscribed(), 0)
- override val numberOfLevels: StateFlow<Int> =
+ private val numberOfLevels: StateFlow<Int> =
connectionRepository.numberOfLevels.stateIn(
scope,
SharingStarted.WhileSubscribed(),
@@ -305,4 +286,54 @@
override val isInService = connectionRepository.isInService
override val isAllowedDuringAirplaneMode = connectionRepository.isAllowedDuringAirplaneMode
+
+ /** Whether or not to show the error state of [SignalDrawable] */
+ private val showExclamationMark: StateFlow<Boolean> =
+ combine(
+ defaultSubscriptionHasDataEnabled,
+ isDefaultConnectionFailed,
+ isInService,
+ ) { isDefaultDataEnabled, isDefaultConnectionFailed, isInService ->
+ !isDefaultDataEnabled || isDefaultConnectionFailed || !isInService
+ }
+ .stateIn(scope, SharingStarted.WhileSubscribed(), true)
+
+ private val shownLevel: StateFlow<Int> =
+ combine(
+ level,
+ isInService,
+ ) { level, isInService ->
+ if (isInService) level else 0
+ }
+ .stateIn(scope, SharingStarted.WhileSubscribed(), 0)
+
+ override val signalLevelIcon: StateFlow<SignalIconModel> = run {
+ val initial =
+ SignalIconModel(
+ level = shownLevel.value,
+ numberOfLevels = numberOfLevels.value,
+ showExclamationMark = showExclamationMark.value,
+ carrierNetworkChange = carrierNetworkChangeActive.value,
+ )
+ combine(
+ shownLevel,
+ numberOfLevels,
+ showExclamationMark,
+ carrierNetworkChangeActive,
+ ) { shownLevel, numberOfLevels, showExclamationMark, carrierNetworkChange ->
+ SignalIconModel(
+ shownLevel,
+ numberOfLevels,
+ showExclamationMark,
+ carrierNetworkChange,
+ )
+ }
+ .distinctUntilChanged()
+ .logDiffsForTable(
+ tableLogBuffer,
+ columnPrefix = "icon",
+ initialValue = initial,
+ )
+ .stateIn(scope, SharingStarted.WhileSubscribed(), initial)
+ }
}
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 d08808b..62150e9 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
@@ -33,6 +33,7 @@
import com.android.systemui.statusbar.pipeline.shared.data.model.ConnectivitySlot
import com.android.systemui.statusbar.pipeline.shared.data.repository.ConnectivityRepository
import com.android.systemui.util.CarrierConfigTracker
+import java.lang.ref.WeakReference
import javax.inject.Inject
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.ExperimentalCoroutinesApi
@@ -70,6 +71,12 @@
/** True if the active mobile data subscription has data enabled */
val activeDataConnectionHasDataEnabled: StateFlow<Boolean>
+ /**
+ * Flow providing a reference to the Interactor for the active data subId. This represents the
+ * [MobileConnectionInteractor] responsible for the active data connection, if any.
+ */
+ val activeDataIconInteractor: StateFlow<MobileIconInteractor?>
+
/** True if the RAT icon should always be displayed and false otherwise. */
val alwaysShowDataRatIcon: StateFlow<Boolean>
@@ -96,9 +103,9 @@
/**
* Vends out a [MobileIconInteractor] tracking the [MobileConnectionRepository] for the given
- * subId. Will throw if the ID is invalid
+ * subId.
*/
- fun createMobileConnectionInteractorForSubId(subId: Int): MobileIconInteractor
+ fun getMobileConnectionInteractorForSubId(subId: Int): MobileIconInteractor
}
@Suppress("EXPERIMENTAL_IS_NOT_ENABLED")
@@ -116,6 +123,9 @@
private val context: Context,
) : MobileIconsInteractor {
+ // Weak reference lookup for created interactors
+ private val reuseCache = mutableMapOf<Int, WeakReference<MobileIconInteractor>>()
+
override val mobileIsDefault =
combine(
mobileConnectionsRepo.mobileIsDefault,
@@ -138,6 +148,17 @@
.flatMapLatest { it?.dataEnabled ?: flowOf(false) }
.stateIn(scope, SharingStarted.WhileSubscribed(), false)
+ override val activeDataIconInteractor: StateFlow<MobileIconInteractor?> =
+ mobileConnectionsRepo.activeMobileDataSubscriptionId
+ .mapLatest {
+ if (it != null) {
+ getMobileConnectionInteractorForSubId(it)
+ } else {
+ null
+ }
+ }
+ .stateIn(scope, SharingStarted.WhileSubscribed(), null)
+
private val unfilteredSubscriptions: Flow<List<SubscriptionModel>> =
mobileConnectionsRepo.subscriptions
@@ -306,21 +327,25 @@
.stateIn(scope, SharingStarted.WhileSubscribed(), false)
/** Vends out new [MobileIconInteractor] for a particular subId */
- override fun createMobileConnectionInteractorForSubId(subId: Int): MobileIconInteractor =
+ override fun getMobileConnectionInteractorForSubId(subId: Int): MobileIconInteractor =
+ reuseCache[subId]?.get() ?: createMobileConnectionInteractorForSubId(subId)
+
+ private fun createMobileConnectionInteractorForSubId(subId: Int): MobileIconInteractor =
MobileIconInteractorImpl(
- scope,
- activeDataConnectionHasDataEnabled,
- alwaysShowDataRatIcon,
- alwaysUseCdmaLevel,
- isSingleCarrier,
- mobileIsDefault,
- defaultMobileIconMapping,
- defaultMobileIconGroup,
- isDefaultConnectionFailed,
- isForceHidden,
- mobileConnectionsRepo.getRepoForSubId(subId),
- context,
- )
+ scope,
+ activeDataConnectionHasDataEnabled,
+ alwaysShowDataRatIcon,
+ alwaysUseCdmaLevel,
+ isSingleCarrier,
+ mobileIsDefault,
+ defaultMobileIconMapping,
+ defaultMobileIconGroup,
+ isDefaultConnectionFailed,
+ isForceHidden,
+ mobileConnectionsRepo.getRepoForSubId(subId),
+ context,
+ )
+ .also { reuseCache[subId] = WeakReference(it) }
companion object {
private const val LOGGING_PREFIX = "Intr"
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/ui/model/SignalIconModel.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/domain/model/SignalIconModel.kt
similarity index 97%
rename from packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/ui/model/SignalIconModel.kt
rename to packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/domain/model/SignalIconModel.kt
index 6de3f85..e58f081 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/ui/model/SignalIconModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/domain/model/SignalIconModel.kt
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.systemui.statusbar.pipeline.mobile.ui.model
+package com.android.systemui.statusbar.pipeline.mobile.domain.model
import com.android.settingslib.graph.SignalDrawable
import com.android.systemui.log.table.Diffable
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/ui/VerboseMobileViewLogger.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/ui/VerboseMobileViewLogger.kt
index cffc833..a1a5370 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/ui/VerboseMobileViewLogger.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/ui/VerboseMobileViewLogger.kt
@@ -22,8 +22,8 @@
import com.android.systemui.log.LogBuffer
import com.android.systemui.log.core.LogLevel
import com.android.systemui.statusbar.pipeline.dagger.VerboseMobileViewLog
+import com.android.systemui.statusbar.pipeline.mobile.domain.model.SignalIconModel
import com.android.systemui.statusbar.pipeline.mobile.ui.MobileViewLogger.Companion.getIdForLogging
-import com.android.systemui.statusbar.pipeline.mobile.ui.model.SignalIconModel
import javax.inject.Inject
/**
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/ui/binder/MobileIconBinder.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/ui/binder/MobileIconBinder.kt
index 4b2fb43..249ca35 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/ui/binder/MobileIconBinder.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/ui/binder/MobileIconBinder.kt
@@ -26,6 +26,7 @@
import android.widget.Space
import androidx.core.view.isVisible
import androidx.lifecycle.Lifecycle
+import androidx.lifecycle.lifecycleScope
import androidx.lifecycle.repeatOnLifecycle
import com.android.settingslib.graph.SignalDrawable
import com.android.systemui.R
@@ -64,7 +65,7 @@
val roamingSpace = view.requireViewById<Space>(R.id.mobile_roaming_space)
val dotView = view.requireViewById<StatusBarIconView>(R.id.status_bar_dot)
- view.isVisible = true
+ view.isVisible = viewModel.isVisible.value
iconView.isVisible = true
// TODO(b/238425913): We should log this visibility state.
@@ -77,108 +78,122 @@
var isCollecting = false
view.repeatWhenAttached {
- repeatOnLifecycle(Lifecycle.State.STARTED) {
- logger.logCollectionStarted(view, viewModel)
- isCollecting = true
-
- launch {
- visibilityState.collect { state ->
- when (state) {
- STATE_ICON -> {
- mobileGroupView.visibility = VISIBLE
- dotView.visibility = GONE
- }
- STATE_DOT -> {
- mobileGroupView.visibility = INVISIBLE
- dotView.visibility = VISIBLE
- }
- STATE_HIDDEN -> {
- mobileGroupView.visibility = INVISIBLE
- dotView.visibility = INVISIBLE
- }
+ lifecycleScope.launch {
+ repeatOnLifecycle(Lifecycle.State.CREATED) {
+ // isVisible controls the visibility state of the outer group, and thus it needs
+ // to run in the CREATED lifecycle so it can continue to watch while invisible
+ // See (b/291031862) for details
+ launch {
+ viewModel.isVisible.collect { isVisible ->
+ viewModel.verboseLogger?.logBinderReceivedVisibility(
+ view,
+ viewModel.subscriptionId,
+ isVisible
+ )
+ view.isVisible = isVisible
+ // [StatusIconContainer] can get out of sync sometimes. Make sure to
+ // request another layout when this changes.
+ view.requestLayout()
}
}
}
+ }
- launch {
- viewModel.isVisible.collect { isVisible ->
- viewModel.verboseLogger?.logBinderReceivedVisibility(
- view,
- viewModel.subscriptionId,
- isVisible
- )
- view.isVisible = isVisible
+ lifecycleScope.launch {
+ repeatOnLifecycle(Lifecycle.State.STARTED) {
+ logger.logCollectionStarted(view, viewModel)
+ isCollecting = true
+
+ launch {
+ visibilityState.collect { state ->
+ when (state) {
+ STATE_ICON -> {
+ mobileGroupView.visibility = VISIBLE
+ dotView.visibility = GONE
+ }
+ STATE_DOT -> {
+ mobileGroupView.visibility = INVISIBLE
+ dotView.visibility = VISIBLE
+ }
+ STATE_HIDDEN -> {
+ mobileGroupView.visibility = INVISIBLE
+ dotView.visibility = INVISIBLE
+ }
+ }
+ }
}
- }
- // Set the icon for the triangle
- launch {
- viewModel.icon.distinctUntilChanged().collect { icon ->
- viewModel.verboseLogger?.logBinderReceivedSignalIcon(
- view,
- viewModel.subscriptionId,
- icon,
- )
- mobileDrawable.level = icon.toSignalDrawableState()
+ // Set the icon for the triangle
+ launch {
+ viewModel.icon.distinctUntilChanged().collect { icon ->
+ viewModel.verboseLogger?.logBinderReceivedSignalIcon(
+ view,
+ viewModel.subscriptionId,
+ icon,
+ )
+ mobileDrawable.level = icon.toSignalDrawableState()
+ }
}
- }
- launch {
- viewModel.contentDescription.distinctUntilChanged().collect {
- ContentDescriptionViewBinder.bind(it, view)
+ launch {
+ viewModel.contentDescription.distinctUntilChanged().collect {
+ ContentDescriptionViewBinder.bind(it, view)
+ }
}
- }
- // Set the network type icon
- launch {
- viewModel.networkTypeIcon.distinctUntilChanged().collect { dataTypeId ->
- viewModel.verboseLogger?.logBinderReceivedNetworkTypeIcon(
- view,
- viewModel.subscriptionId,
- dataTypeId,
- )
- dataTypeId?.let { IconViewBinder.bind(dataTypeId, networkTypeView) }
- networkTypeView.visibility = if (dataTypeId != null) VISIBLE else GONE
+ // Set the network type icon
+ launch {
+ viewModel.networkTypeIcon.distinctUntilChanged().collect { dataTypeId ->
+ viewModel.verboseLogger?.logBinderReceivedNetworkTypeIcon(
+ view,
+ viewModel.subscriptionId,
+ dataTypeId,
+ )
+ dataTypeId?.let { IconViewBinder.bind(dataTypeId, networkTypeView) }
+ networkTypeView.visibility = if (dataTypeId != null) VISIBLE else GONE
+ }
}
- }
- // Set the roaming indicator
- launch {
- viewModel.roaming.distinctUntilChanged().collect { isRoaming ->
- roamingView.isVisible = isRoaming
- roamingSpace.isVisible = isRoaming
+ // Set the roaming indicator
+ launch {
+ viewModel.roaming.distinctUntilChanged().collect { isRoaming ->
+ roamingView.isVisible = isRoaming
+ roamingSpace.isVisible = isRoaming
+ }
}
- }
- // Set the activity indicators
- launch { viewModel.activityInVisible.collect { activityIn.isVisible = it } }
+ // Set the activity indicators
+ launch { viewModel.activityInVisible.collect { activityIn.isVisible = it } }
- launch { viewModel.activityOutVisible.collect { activityOut.isVisible = it } }
+ launch { viewModel.activityOutVisible.collect { activityOut.isVisible = it } }
- launch {
- viewModel.activityContainerVisible.collect { activityContainer.isVisible = it }
- }
-
- // Set the tint
- launch {
- iconTint.collect { tint ->
- val tintList = ColorStateList.valueOf(tint)
- iconView.imageTintList = tintList
- networkTypeView.imageTintList = tintList
- roamingView.imageTintList = tintList
- activityIn.imageTintList = tintList
- activityOut.imageTintList = tintList
- dotView.setDecorColor(tint)
+ launch {
+ viewModel.activityContainerVisible.collect {
+ activityContainer.isVisible = it
+ }
}
- }
- launch { decorTint.collect { tint -> dotView.setDecorColor(tint) } }
+ // Set the tint
+ launch {
+ iconTint.collect { tint ->
+ val tintList = ColorStateList.valueOf(tint)
+ iconView.imageTintList = tintList
+ networkTypeView.imageTintList = tintList
+ roamingView.imageTintList = tintList
+ activityIn.imageTintList = tintList
+ activityOut.imageTintList = tintList
+ dotView.setDecorColor(tint)
+ }
+ }
- try {
- awaitCancellation()
- } finally {
- isCollecting = false
- logger.logCollectionStopped(view, viewModel)
+ launch { decorTint.collect { tint -> dotView.setDecorColor(tint) } }
+
+ try {
+ awaitCancellation()
+ } finally {
+ isCollecting = false
+ logger.logCollectionStopped(view, viewModel)
+ }
}
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/ui/viewmodel/MobileIconViewModel.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/ui/viewmodel/MobileIconViewModel.kt
index 275cfc5..dfabeea 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/ui/viewmodel/MobileIconViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/ui/viewmodel/MobileIconViewModel.kt
@@ -17,14 +17,13 @@
package com.android.systemui.statusbar.pipeline.mobile.ui.viewmodel
import com.android.settingslib.AccessibilityContentDescriptions.PHONE_SIGNAL_STRENGTH
-import com.android.settingslib.graph.SignalDrawable
import com.android.systemui.common.shared.model.ContentDescription
import com.android.systemui.common.shared.model.Icon
import com.android.systemui.log.table.logDiffsForTable
import com.android.systemui.statusbar.pipeline.airplane.domain.interactor.AirplaneModeInteractor
import com.android.systemui.statusbar.pipeline.mobile.domain.interactor.MobileIconInteractor
import com.android.systemui.statusbar.pipeline.mobile.domain.interactor.MobileIconsInteractor
-import com.android.systemui.statusbar.pipeline.mobile.ui.model.SignalIconModel
+import com.android.systemui.statusbar.pipeline.mobile.domain.model.SignalIconModel
import com.android.systemui.statusbar.pipeline.shared.ConnectivityConstants
import com.android.systemui.statusbar.pipeline.shared.data.model.DataActivityModel
import kotlinx.coroutines.CoroutineScope
@@ -76,26 +75,6 @@
constants: ConnectivityConstants,
scope: CoroutineScope,
) : MobileIconViewModelCommon {
- /** Whether or not to show the error state of [SignalDrawable] */
- private val showExclamationMark: StateFlow<Boolean> =
- combine(
- iconInteractor.isDefaultDataEnabled,
- iconInteractor.isDefaultConnectionFailed,
- iconInteractor.isInService,
- ) { isDefaultDataEnabled, isDefaultConnectionFailed, isInService ->
- !isDefaultDataEnabled || isDefaultConnectionFailed || !isInService
- }
- .stateIn(scope, SharingStarted.WhileSubscribed(), true)
-
- private val shownLevel: StateFlow<Int> =
- combine(
- iconInteractor.level,
- iconInteractor.isInService,
- ) { level, isInService ->
- if (isInService) level else 0
- }
- .stateIn(scope, SharingStarted.WhileSubscribed(), 0)
-
override val isVisible: StateFlow<Boolean> =
if (!constants.hasDataCapabilities) {
flowOf(false)
@@ -123,40 +102,12 @@
)
.stateIn(scope, SharingStarted.WhileSubscribed(), false)
- override val icon: Flow<SignalIconModel> = run {
- val initial =
- SignalIconModel(
- level = shownLevel.value,
- numberOfLevels = iconInteractor.numberOfLevels.value,
- showExclamationMark = showExclamationMark.value,
- carrierNetworkChange = iconInteractor.carrierNetworkChangeActive.value,
- )
- combine(
- shownLevel,
- iconInteractor.numberOfLevels,
- showExclamationMark,
- iconInteractor.carrierNetworkChangeActive,
- ) { shownLevel, numberOfLevels, showExclamationMark, carrierNetworkChange ->
- SignalIconModel(
- shownLevel,
- numberOfLevels,
- showExclamationMark,
- carrierNetworkChange,
- )
- }
- .distinctUntilChanged()
- .logDiffsForTable(
- iconInteractor.tableLogBuffer,
- columnPrefix = "icon",
- initialValue = initial,
- )
- .stateIn(scope, SharingStarted.WhileSubscribed(), initial)
- }
+ override val icon: Flow<SignalIconModel> = iconInteractor.signalLevelIcon
override val contentDescription: Flow<ContentDescription> = run {
val initial = ContentDescription.Resource(PHONE_SIGNAL_STRENGTH[0])
- shownLevel
- .map { ContentDescription.Resource(PHONE_SIGNAL_STRENGTH[it]) }
+ iconInteractor.signalLevelIcon
+ .map { ContentDescription.Resource(PHONE_SIGNAL_STRENGTH[it.level]) }
.stateIn(scope, SharingStarted.WhileSubscribed(), initial)
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/ui/viewmodel/MobileIconsViewModel.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/ui/viewmodel/MobileIconsViewModel.kt
index 216afb9..a4ec3a3 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/ui/viewmodel/MobileIconsViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/ui/viewmodel/MobileIconsViewModel.kt
@@ -101,7 +101,7 @@
val common = commonViewModelForSub(subId)
return LocationBasedMobileViewModel.viewModelForLocation(
common,
- mobileIconInteractorForSub(subId),
+ interactor.getMobileConnectionInteractorForSubId(subId),
verboseLogger,
location,
scope,
@@ -112,7 +112,7 @@
return mobileIconSubIdCache[subId]
?: MobileIconViewModel(
subId,
- mobileIconInteractorForSub(subId),
+ interactor.getMobileConnectionInteractorForSubId(subId),
airplaneModeInteractor,
constants,
scope,
@@ -120,14 +120,6 @@
.also { mobileIconSubIdCache[subId] = it }
}
- @VisibleForTesting
- fun mobileIconInteractorForSub(subId: Int): MobileIconInteractor {
- return mobileIconInteractorSubIdCache[subId]
- ?: interactor.createMobileConnectionInteractorForSubId(subId).also {
- mobileIconInteractorSubIdCache[subId] = it
- }
- }
-
private fun invalidateCaches(subIds: List<Int>) {
val subIdsToRemove = mobileIconSubIdCache.keys.filter { !subIds.contains(it) }
subIdsToRemove.forEach { mobileIconSubIdCache.remove(it) }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/shared/ui/binder/InternetTileBinder.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/shared/ui/binder/InternetTileBinder.kt
new file mode 100644
index 0000000..189dc40
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/shared/ui/binder/InternetTileBinder.kt
@@ -0,0 +1,42 @@
+/*
+ * 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.statusbar.pipeline.shared.ui.binder
+
+import androidx.lifecycle.Lifecycle
+import androidx.lifecycle.coroutineScope
+import androidx.lifecycle.repeatOnLifecycle
+import com.android.systemui.statusbar.pipeline.shared.ui.model.InternetTileModel
+import java.util.function.Consumer
+import kotlinx.coroutines.flow.StateFlow
+import kotlinx.coroutines.launch
+
+/**
+ * Binds an [InternetTileModel] flow to a consumer for the internet tile to apply to its qs state
+ */
+object InternetTileBinder {
+ fun bind(
+ lifecycle: Lifecycle,
+ tileModelFlow: StateFlow<InternetTileModel>,
+ consumer: Consumer<InternetTileModel>
+ ) {
+ lifecycle.coroutineScope.launch {
+ lifecycle.repeatOnLifecycle(Lifecycle.State.RESUMED) {
+ tileModelFlow.collect { consumer.accept(it) }
+ }
+ }
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/shared/ui/model/InternetTileModel.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/shared/ui/model/InternetTileModel.kt
new file mode 100644
index 0000000..327dd8d
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/shared/ui/model/InternetTileModel.kt
@@ -0,0 +1,91 @@
+/*
+ * 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.statusbar.pipeline.shared.ui.model
+
+import android.content.Context
+import android.graphics.drawable.Drawable
+import android.service.quicksettings.Tile
+import com.android.settingslib.graph.SignalDrawable
+import com.android.systemui.common.shared.model.Text
+import com.android.systemui.common.shared.model.Text.Companion.loadText
+import com.android.systemui.plugins.qs.QSTile
+import com.android.systemui.qs.tileimpl.QSTileImpl
+
+/** Model describing the state that the QS Internet tile should be in. */
+sealed interface InternetTileModel {
+ val secondaryTitle: String?
+ val secondaryLabel: Text?
+ val iconId: Int?
+ val icon: QSTile.Icon?
+
+ fun applyTo(state: QSTile.SignalState, context: Context) {
+ if (secondaryLabel != null) {
+ state.secondaryLabel = secondaryLabel.loadText(context)
+ } else {
+ state.secondaryLabel = secondaryTitle
+ }
+
+ // inout indicators are unused
+ state.activityIn = false
+ state.activityOut = false
+
+ // To support both SignalDrawable and other icons, give priority to icons over IDs
+ if (icon != null) {
+ state.icon = icon
+ } else if (iconId != null) {
+ state.icon = QSTileImpl.ResourceIcon.get(iconId!!)
+ }
+
+ state.state =
+ if (this is Active) {
+ Tile.STATE_ACTIVE
+ } else {
+ Tile.STATE_INACTIVE
+ }
+ }
+
+ data class Active(
+ override val secondaryTitle: String? = null,
+ override val secondaryLabel: Text? = null,
+ override val iconId: Int? = null,
+ override val icon: QSTile.Icon? = null,
+ ) : InternetTileModel
+
+ data class Inactive(
+ override val secondaryTitle: String? = null,
+ override val secondaryLabel: Text? = null,
+ override val iconId: Int? = null,
+ override val icon: QSTile.Icon? = null,
+ ) : InternetTileModel
+}
+
+/**
+ * [QSTile.Icon]-compatible container class for us to marshal the compacted [SignalDrawable] state
+ * across to the internet tile.
+ */
+data class SignalIcon(val state: Int) : QSTile.Icon() {
+
+ override fun getDrawable(context: Context): Drawable {
+ val d = SignalDrawable(context)
+ d.setLevel(state)
+ return d
+ }
+
+ override fun toString(): String {
+ return String.format("SignalIcon[mState=0x%08x]", state)
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/shared/ui/viewmodel/InternetTileViewModel.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/shared/ui/viewmodel/InternetTileViewModel.kt
new file mode 100644
index 0000000..b6f1677
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/shared/ui/viewmodel/InternetTileViewModel.kt
@@ -0,0 +1,219 @@
+/*
+ * 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.statusbar.pipeline.shared.ui.viewmodel
+
+import android.content.Context
+import com.android.systemui.R
+import com.android.systemui.common.shared.model.Text
+import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.dagger.qualifiers.Application
+import com.android.systemui.qs.tileimpl.QSTileImpl
+import com.android.systemui.qs.tileimpl.QSTileImpl.ResourceIcon
+import com.android.systemui.statusbar.pipeline.airplane.data.repository.AirplaneModeRepository
+import com.android.systemui.statusbar.pipeline.ethernet.domain.EthernetInteractor
+import com.android.systemui.statusbar.pipeline.mobile.domain.interactor.MobileIconsInteractor
+import com.android.systemui.statusbar.pipeline.shared.data.repository.ConnectivityRepository
+import com.android.systemui.statusbar.pipeline.shared.ui.model.InternetTileModel
+import com.android.systemui.statusbar.pipeline.shared.ui.model.SignalIcon
+import com.android.systemui.statusbar.pipeline.wifi.domain.interactor.WifiInteractor
+import com.android.systemui.statusbar.pipeline.wifi.shared.model.WifiNetworkModel
+import com.android.systemui.statusbar.pipeline.wifi.ui.model.WifiIcon
+import javax.inject.Inject
+import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.SharingStarted
+import kotlinx.coroutines.flow.StateFlow
+import kotlinx.coroutines.flow.combine
+import kotlinx.coroutines.flow.flatMapLatest
+import kotlinx.coroutines.flow.flowOf
+import kotlinx.coroutines.flow.stateIn
+
+/**
+ * View model for the quick settings [InternetTile]. This model exposes mainly a single flow of
+ * InternetTileModel objects, so that updating the tile is as simple as collecting on this state
+ * flow and then calling [QSTileImpl.refreshState]
+ */
+@OptIn(ExperimentalCoroutinesApi::class)
+@SysUISingleton
+class InternetTileViewModel
+@Inject
+constructor(
+ airplaneModeRepository: AirplaneModeRepository,
+ connectivityRepository: ConnectivityRepository,
+ ethernetInteractor: EthernetInteractor,
+ mobileIconsInteractor: MobileIconsInteractor,
+ wifiInteractor: WifiInteractor,
+ private val context: Context,
+ @Application scope: CoroutineScope,
+) {
+ // Three symmetrical Flows that can be switched upon based on the value of
+ // [DefaultConnectionModel]
+ private val wifiIconFlow: Flow<InternetTileModel> =
+ wifiInteractor.wifiNetwork.flatMapLatest {
+ val wifiIcon = WifiIcon.fromModel(it, context, showHotspotInfo = true)
+ if (it is WifiNetworkModel.Active && wifiIcon is WifiIcon.Visible) {
+ flowOf(
+ InternetTileModel.Active(
+ secondaryTitle = removeDoubleQuotes(it.ssid),
+ icon = ResourceIcon.get(wifiIcon.icon.res)
+ )
+ )
+ } else {
+ notConnectedFlow
+ }
+ }
+
+ private val mobileDataContentName: Flow<CharSequence?> =
+ mobileIconsInteractor.activeDataIconInteractor.flatMapLatest {
+ if (it == null) {
+ flowOf(null)
+ } else {
+ combine(it.isRoaming, it.networkTypeIconGroup) { isRoaming, networkTypeIconGroup ->
+ val cd = loadString(networkTypeIconGroup.contentDescription)
+ if (isRoaming) {
+ val roaming = context.getString(R.string.data_connection_roaming)
+ if (cd != null) {
+ context.getString(R.string.mobile_data_text_format, roaming, cd)
+ } else {
+ roaming
+ }
+ } else {
+ cd
+ }
+ }
+ }
+ }
+
+ private val mobileIconFlow: Flow<InternetTileModel> =
+ mobileIconsInteractor.activeDataIconInteractor.flatMapLatest {
+ if (it == null) {
+ notConnectedFlow
+ } else {
+ combine(
+ it.networkName,
+ it.signalLevelIcon,
+ mobileDataContentName,
+ ) { networkNameModel, signalIcon, dataContentDescription ->
+ InternetTileModel.Active(
+ secondaryTitle =
+ mobileDataContentConcat(networkNameModel.name, dataContentDescription),
+ icon = SignalIcon(signalIcon.toSignalDrawableState()),
+ )
+ }
+ }
+ }
+
+ private fun mobileDataContentConcat(
+ networkName: String?,
+ dataContentDescription: CharSequence?
+ ): String {
+ if (dataContentDescription == null) {
+ return networkName ?: ""
+ }
+ if (networkName == null) {
+ return dataContentDescription.toString()
+ }
+
+ return context.getString(
+ R.string.mobile_carrier_text_format,
+ networkName,
+ dataContentDescription
+ )
+ }
+
+ private fun loadString(resId: Int): String? =
+ if (resId > 0) {
+ context.getString(resId)
+ } else {
+ null
+ }
+
+ private val ethernetIconFlow: Flow<InternetTileModel> =
+ ethernetInteractor.icon.flatMapLatest {
+ if (it == null) {
+ notConnectedFlow
+ } else {
+ flowOf(
+ InternetTileModel.Active(
+ secondaryTitle = it.contentDescription.toString(),
+ iconId = it.res
+ )
+ )
+ }
+ }
+
+ private val notConnectedFlow: StateFlow<InternetTileModel> =
+ combine(
+ wifiInteractor.areNetworksAvailable,
+ airplaneModeRepository.isAirplaneMode,
+ ) { networksAvailable, isAirplaneMode ->
+ when {
+ isAirplaneMode -> {
+ InternetTileModel.Inactive(
+ secondaryTitle = context.getString(R.string.status_bar_airplane),
+ icon = ResourceIcon.get(R.drawable.ic_qs_no_internet_unavailable)
+ )
+ }
+ networksAvailable -> {
+ InternetTileModel.Inactive(
+ secondaryTitle =
+ context.getString(R.string.quick_settings_networks_available),
+ iconId = R.drawable.ic_qs_no_internet_available,
+ )
+ }
+ else -> {
+ NOT_CONNECTED_NETWORKS_UNAVAILABLE
+ }
+ }
+ }
+ .stateIn(scope, SharingStarted.WhileSubscribed(), NOT_CONNECTED_NETWORKS_UNAVAILABLE)
+
+ /**
+ * Strict ordering of which repo is sending its data to the internet tile. Swaps between each of
+ * the interim providers (wifi, mobile, ethernet, or not-connected)
+ */
+ private val activeModelProvider: Flow<InternetTileModel> =
+ connectivityRepository.defaultConnections.flatMapLatest {
+ when {
+ it.ethernet.isDefault -> ethernetIconFlow
+ it.mobile.isDefault || it.carrierMerged.isDefault -> mobileIconFlow
+ it.wifi.isDefault -> wifiIconFlow
+ else -> notConnectedFlow
+ }
+ }
+
+ /** Consumable flow describing the correct state for the InternetTile */
+ val tileModel: StateFlow<InternetTileModel> =
+ activeModelProvider.stateIn(scope, SharingStarted.WhileSubscribed(), notConnectedFlow.value)
+
+ companion object {
+ val NOT_CONNECTED_NETWORKS_UNAVAILABLE =
+ InternetTileModel.Inactive(
+ secondaryLabel = Text.Resource(R.string.quick_settings_networks_unavailable),
+ iconId = R.drawable.ic_qs_no_internet_unavailable,
+ )
+
+ private fun removeDoubleQuotes(string: String?): String? {
+ if (string == null) return null
+ val length = string.length
+ return if (length > 1 && string[0] == '"' && string[length - 1] == '"') {
+ string.substring(1, length - 1)
+ } else string
+ }
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/wifi/data/repository/WifiRepository.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/wifi/data/repository/WifiRepository.kt
index b5b99a7..b22e09e 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/wifi/data/repository/WifiRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/wifi/data/repository/WifiRepository.kt
@@ -19,6 +19,7 @@
import com.android.systemui.CoreStartable
import com.android.systemui.statusbar.pipeline.shared.data.model.DataActivityModel
import com.android.systemui.statusbar.pipeline.wifi.shared.model.WifiNetworkModel
+import com.android.systemui.statusbar.pipeline.wifi.shared.model.WifiScanEntry
import kotlinx.coroutines.flow.StateFlow
/** Provides data related to the wifi state. */
@@ -45,6 +46,12 @@
val wifiActivity: StateFlow<DataActivityModel>
/**
+ * The list of known wifi networks, per [WifiManager.scanResults]. This list is passively
+ * updated and does not trigger a scan.
+ */
+ val wifiScanResults: StateFlow<List<WifiScanEntry>>
+
+ /**
* Returns true if the device is currently connected to a wifi network with a valid SSID and
* false otherwise.
*/
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/wifi/data/repository/WifiRepositorySwitcher.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/wifi/data/repository/WifiRepositorySwitcher.kt
index 80091ac..ca042e2 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/wifi/data/repository/WifiRepositorySwitcher.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/wifi/data/repository/WifiRepositorySwitcher.kt
@@ -27,6 +27,7 @@
import com.android.systemui.statusbar.pipeline.wifi.data.repository.demo.DemoWifiRepository
import com.android.systemui.statusbar.pipeline.wifi.data.repository.prod.WifiRepositoryImpl
import com.android.systemui.statusbar.pipeline.wifi.shared.model.WifiNetworkModel
+import com.android.systemui.statusbar.pipeline.wifi.shared.model.WifiScanEntry
import javax.inject.Inject
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.ExperimentalCoroutinesApi
@@ -124,4 +125,9 @@
activeRepo
.flatMapLatest { it.wifiActivity }
.stateIn(scope, SharingStarted.WhileSubscribed(), realImpl.wifiActivity.value)
+
+ override val wifiScanResults: StateFlow<List<WifiScanEntry>> =
+ activeRepo
+ .flatMapLatest { it.wifiScanResults }
+ .stateIn(scope, SharingStarted.WhileSubscribed(), realImpl.wifiScanResults.value)
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/wifi/data/repository/demo/DemoWifiRepository.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/wifi/data/repository/demo/DemoWifiRepository.kt
index 4b19c3a..152d181 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/wifi/data/repository/demo/DemoWifiRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/wifi/data/repository/demo/DemoWifiRepository.kt
@@ -22,6 +22,7 @@
import com.android.systemui.statusbar.pipeline.wifi.data.repository.WifiRepository
import com.android.systemui.statusbar.pipeline.wifi.data.repository.demo.model.FakeWifiEventModel
import com.android.systemui.statusbar.pipeline.wifi.shared.model.WifiNetworkModel
+import com.android.systemui.statusbar.pipeline.wifi.shared.model.WifiScanEntry
import javax.inject.Inject
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Job
@@ -55,6 +56,10 @@
MutableStateFlow(DataActivityModel(hasActivityIn = false, hasActivityOut = false))
override val wifiActivity: StateFlow<DataActivityModel> = _wifiActivity
+ private val _wifiScanResults: MutableStateFlow<List<WifiScanEntry>> =
+ MutableStateFlow(emptyList())
+ override val wifiScanResults: StateFlow<List<WifiScanEntry>> = _wifiScanResults
+
fun startProcessingCommands() {
demoCommandJob =
scope.launch {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/wifi/data/repository/prod/DisabledWifiRepository.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/wifi/data/repository/prod/DisabledWifiRepository.kt
index 36c46a9..cfdbe4a 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/wifi/data/repository/prod/DisabledWifiRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/wifi/data/repository/prod/DisabledWifiRepository.kt
@@ -21,6 +21,7 @@
import com.android.systemui.statusbar.pipeline.wifi.data.repository.WifiRepositoryDagger
import com.android.systemui.statusbar.pipeline.wifi.data.repository.WifiRepositoryViaTrackerLibDagger
import com.android.systemui.statusbar.pipeline.wifi.shared.model.WifiNetworkModel
+import com.android.systemui.statusbar.pipeline.wifi.shared.model.WifiScanEntry
import javax.inject.Inject
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.StateFlow
@@ -49,6 +50,9 @@
override val wifiActivity: StateFlow<DataActivityModel> =
MutableStateFlow(ACTIVITY).asStateFlow()
+ override val wifiScanResults: StateFlow<List<WifiScanEntry>> =
+ MutableStateFlow<List<WifiScanEntry>>(emptyList()).asStateFlow()
+
companion object {
private val NETWORK = WifiNetworkModel.Unavailable
private val ACTIVITY = DataActivityModel(hasActivityIn = false, hasActivityOut = false)
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/wifi/data/repository/prod/WifiRepositoryHelper.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/wifi/data/repository/prod/WifiRepositoryHelper.kt
index f1b98b3..67dd32f 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/wifi/data/repository/prod/WifiRepositoryHelper.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/wifi/data/repository/prod/WifiRepositoryHelper.kt
@@ -16,15 +16,21 @@
package com.android.systemui.statusbar.pipeline.wifi.data.repository.prod
+import android.annotation.SuppressLint
+import android.net.wifi.ScanResult
import android.net.wifi.WifiManager
import com.android.systemui.common.coroutine.ConflatedCallbackFlow.conflatedCallbackFlow
+import com.android.systemui.dagger.qualifiers.Background
import com.android.systemui.dagger.qualifiers.Main
import com.android.systemui.log.table.TableLogBuffer
import com.android.systemui.log.table.logDiffsForTable
import com.android.systemui.statusbar.pipeline.shared.data.model.DataActivityModel
import com.android.systemui.statusbar.pipeline.shared.data.model.toWifiDataActivityModel
+import com.android.systemui.statusbar.pipeline.wifi.shared.model.WifiScanEntry
import java.util.concurrent.Executor
+import kotlinx.coroutines.CoroutineDispatcher
import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.asExecutor
import kotlinx.coroutines.channels.awaitClose
import kotlinx.coroutines.flow.SharingStarted
import kotlinx.coroutines.flow.StateFlow
@@ -64,6 +70,34 @@
)
}
+ /**
+ * Creates a flow that listens for new [ScanResult]s from [WifiManager]. Does not request a scan
+ */
+ fun createNetworkScanFlow(
+ wifiManager: WifiManager,
+ scope: CoroutineScope,
+ @Background dispatcher: CoroutineDispatcher,
+ inputLogger: () -> Unit,
+ ): StateFlow<List<WifiScanEntry>> {
+ return conflatedCallbackFlow {
+ val callback =
+ object : WifiManager.ScanResultsCallback() {
+ @SuppressLint("MissingPermission")
+ override fun onScanResultsAvailable() {
+ inputLogger.invoke()
+ trySend(wifiManager.scanResults.toModel())
+ }
+ }
+
+ wifiManager.registerScanResultsCallback(dispatcher.asExecutor(), callback)
+
+ awaitClose { wifiManager.unregisterScanResultsCallback(callback) }
+ }
+ .stateIn(scope, SharingStarted.Eagerly, emptyList())
+ }
+
+ private fun List<ScanResult>.toModel(): List<WifiScanEntry> = map { WifiScanEntry(it.SSID) }
+
// TODO(b/292534484): This print should only be done in [MessagePrinter] part of the log buffer.
private fun prettyPrintActivity(activity: Int): String {
return when (activity) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/wifi/data/repository/prod/WifiRepositoryImpl.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/wifi/data/repository/prod/WifiRepositoryImpl.kt
index 7c7b58d..59ef884 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/wifi/data/repository/prod/WifiRepositoryImpl.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/wifi/data/repository/prod/WifiRepositoryImpl.kt
@@ -48,6 +48,7 @@
import com.android.systemui.statusbar.pipeline.wifi.data.repository.WifiRepositoryDagger
import com.android.systemui.statusbar.pipeline.wifi.shared.WifiInputLogger
import com.android.systemui.statusbar.pipeline.wifi.shared.model.WifiNetworkModel
+import com.android.systemui.statusbar.pipeline.wifi.shared.model.WifiScanEntry
import java.util.concurrent.Executor
import javax.inject.Inject
import kotlinx.coroutines.CoroutineDispatcher
@@ -231,6 +232,14 @@
logger::logActivity,
)
+ override val wifiScanResults: StateFlow<List<WifiScanEntry>> =
+ WifiRepositoryHelper.createNetworkScanFlow(
+ wifiManager,
+ scope,
+ bgDispatcher,
+ logger::logScanResults
+ )
+
companion object {
// Start out with no known wifi network.
// Note: [WifiStatusTracker] (the old implementation of connectivity logic) does do an
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/wifi/data/repository/prod/WifiRepositoryViaTrackerLib.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/wifi/data/repository/prod/WifiRepositoryViaTrackerLib.kt
index d4f40dd..9b404f1 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/wifi/data/repository/prod/WifiRepositoryViaTrackerLib.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/wifi/data/repository/prod/WifiRepositoryViaTrackerLib.kt
@@ -23,6 +23,7 @@
import androidx.lifecycle.LifecycleRegistry
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Application
+import com.android.systemui.dagger.qualifiers.Background
import com.android.systemui.dagger.qualifiers.Main
import com.android.systemui.flags.FeatureFlags
import com.android.systemui.flags.Flags
@@ -42,6 +43,7 @@
import com.android.systemui.statusbar.pipeline.wifi.data.repository.prod.WifiRepositoryImpl.Companion.WIFI_STATE_DEFAULT
import com.android.systemui.statusbar.pipeline.wifi.shared.model.WifiNetworkModel
import com.android.systemui.statusbar.pipeline.wifi.shared.model.WifiNetworkModel.Inactive.toHotspotDeviceType
+import com.android.systemui.statusbar.pipeline.wifi.shared.model.WifiScanEntry
import com.android.wifitrackerlib.HotspotNetworkEntry
import com.android.wifitrackerlib.MergedCarrierEntry
import com.android.wifitrackerlib.WifiEntry
@@ -51,6 +53,7 @@
import com.android.wifitrackerlib.WifiPickerTracker
import java.util.concurrent.Executor
import javax.inject.Inject
+import kotlinx.coroutines.CoroutineDispatcher
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.channels.awaitClose
import kotlinx.coroutines.flow.SharingStarted
@@ -73,6 +76,7 @@
featureFlags: FeatureFlags,
@Application private val scope: CoroutineScope,
@Main private val mainExecutor: Executor,
+ @Background private val bgDispatcher: CoroutineDispatcher,
private val wifiPickerTrackerFactory: WifiPickerTrackerFactory,
private val wifiManager: WifiManager,
@WifiTrackerLibInputLog private val inputLogger: LogBuffer,
@@ -300,6 +304,14 @@
this::logActivity,
)
+ override val wifiScanResults: StateFlow<List<WifiScanEntry>> =
+ WifiRepositoryHelper.createNetworkScanFlow(
+ wifiManager,
+ scope,
+ bgDispatcher,
+ this::logScanResults,
+ )
+
private fun logOnWifiEntriesChanged(connectedEntry: WifiEntry?) {
inputLogger.log(
TAG,
@@ -322,6 +334,9 @@
inputLogger.log(TAG, LogLevel.DEBUG, { str1 = activity }, { "onActivityChanged: $str1" })
}
+ private fun logScanResults() =
+ inputLogger.log(TAG, LogLevel.DEBUG, {}, { "onScanResultsAvailable" })
+
/**
* Data class storing all the information fetched from [WifiPickerTracker].
*
@@ -345,6 +360,7 @@
private val featureFlags: FeatureFlags,
@Application private val scope: CoroutineScope,
@Main private val mainExecutor: Executor,
+ @Background private val bgDispatcher: CoroutineDispatcher,
private val wifiPickerTrackerFactory: WifiPickerTrackerFactory,
@WifiTrackerLibInputLog private val inputLogger: LogBuffer,
@WifiTrackerLibTableLog private val wifiTrackerLibTableLogBuffer: TableLogBuffer,
@@ -354,6 +370,7 @@
featureFlags,
scope,
mainExecutor,
+ bgDispatcher,
wifiPickerTrackerFactory,
wifiManager,
inputLogger,
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/wifi/domain/interactor/WifiInteractor.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/wifi/domain/interactor/WifiInteractor.kt
index 1a41abf..110e339 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/wifi/domain/interactor/WifiInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/wifi/domain/interactor/WifiInteractor.kt
@@ -17,15 +17,21 @@
package com.android.systemui.statusbar.pipeline.wifi.domain.interactor
import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.dagger.qualifiers.Application
import com.android.systemui.statusbar.pipeline.shared.data.model.ConnectivitySlot
import com.android.systemui.statusbar.pipeline.shared.data.model.DataActivityModel
import com.android.systemui.statusbar.pipeline.shared.data.repository.ConnectivityRepository
import com.android.systemui.statusbar.pipeline.wifi.data.repository.WifiRepository
import com.android.systemui.statusbar.pipeline.wifi.shared.model.WifiNetworkModel
+import com.android.systemui.statusbar.pipeline.wifi.shared.model.WifiScanEntry
import javax.inject.Inject
+import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.SharingStarted
import kotlinx.coroutines.flow.StateFlow
+import kotlinx.coroutines.flow.combine
import kotlinx.coroutines.flow.map
+import kotlinx.coroutines.flow.stateIn
/**
* The business logic layer for the wifi icon.
@@ -54,6 +60,9 @@
/** True if we're configured to force-hide the wifi icon and false otherwise. */
val isForceHidden: Flow<Boolean>
+
+ /** True if there are networks available other than the currently-connected one */
+ val areNetworksAvailable: StateFlow<Boolean>
}
@SysUISingleton
@@ -62,6 +71,7 @@
constructor(
connectivityRepository: ConnectivityRepository,
wifiRepository: WifiRepository,
+ @Application scope: CoroutineScope,
) : WifiInteractor {
override val ssid: Flow<String?> =
@@ -91,4 +101,26 @@
override val isForceHidden: Flow<Boolean> =
connectivityRepository.forceHiddenSlots.map { it.contains(ConnectivitySlot.WIFI) }
+
+ override val areNetworksAvailable: StateFlow<Boolean> =
+ combine(
+ wifiNetwork,
+ wifiRepository.wifiScanResults,
+ ) { currentNetwork, scanResults ->
+ // We consider networks to be available if the scan results list contains networks
+ // other than the one that is currently connected
+ if (scanResults.isEmpty()) {
+ false
+ } else if (currentNetwork !is WifiNetworkModel.Active) {
+ true
+ } else {
+ anyNonMatchingNetworkExists(currentNetwork, scanResults)
+ }
+ }
+ .stateIn(scope, SharingStarted.WhileSubscribed(), false)
+
+ private fun anyNonMatchingNetworkExists(
+ currentNetwork: WifiNetworkModel.Active,
+ availableNetworks: List<WifiScanEntry>
+ ): Boolean = availableNetworks.firstOrNull { it.ssid != currentNetwork.ssid } != null
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/wifi/shared/WifiInputLogger.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/wifi/shared/WifiInputLogger.kt
index f244376..b76bb51 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/wifi/shared/WifiInputLogger.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/wifi/shared/WifiInputLogger.kt
@@ -59,6 +59,8 @@
fun logActivity(activity: String) {
buffer.log(TAG, LogLevel.DEBUG, { str1 = activity }, { "Activity: $str1" })
}
+
+ fun logScanResults() = buffer.log(TAG, LogLevel.DEBUG, {}, { "onScanResultsAvailable" })
}
private const val TAG = "WifiInputLog"
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/wifi/shared/model/WifiScanEntry.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/wifi/shared/model/WifiScanEntry.kt
new file mode 100644
index 0000000..d4a5a0e
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/wifi/shared/model/WifiScanEntry.kt
@@ -0,0 +1,23 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.statusbar.pipeline.wifi.shared.model
+
+/**
+ * Represents a single entry in the scan results callback. Use the [ssid] field to check against
+ * other networks
+ */
+data class WifiScanEntry(val ssid: String)
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/wifi/ui/model/WifiIcon.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/wifi/ui/model/WifiIcon.kt
index 094bcf9..668c5b3 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/wifi/ui/model/WifiIcon.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/wifi/ui/model/WifiIcon.kt
@@ -17,10 +17,18 @@
package com.android.systemui.statusbar.pipeline.wifi.ui.model
import android.annotation.DrawableRes
+import android.content.Context
+import androidx.annotation.StringRes
+import androidx.annotation.VisibleForTesting
+import com.android.settingslib.AccessibilityContentDescriptions.WIFI_CONNECTION_STRENGTH
+import com.android.settingslib.AccessibilityContentDescriptions.WIFI_NO_CONNECTION
+import com.android.systemui.R
import com.android.systemui.common.shared.model.ContentDescription
import com.android.systemui.common.shared.model.Icon
import com.android.systemui.log.table.Diffable
import com.android.systemui.log.table.TableRowLogger
+import com.android.systemui.statusbar.connectivity.WifiIcons
+import com.android.systemui.statusbar.pipeline.wifi.shared.model.WifiNetworkModel
/** Represents the various states of the wifi icon. */
sealed interface WifiIcon : Diffable<WifiIcon> {
@@ -34,7 +42,7 @@
* description.
*/
class Visible(
- @DrawableRes res: Int,
+ @DrawableRes val res: Int,
val contentDescription: ContentDescription.Loaded,
) : WifiIcon {
val icon = Icon.Resource(res, contentDescription)
@@ -51,6 +59,84 @@
override fun logFull(row: TableRowLogger) {
row.logChange(COL_ICON, toString())
}
+
+ companion object {
+ @StringRes
+ @VisibleForTesting
+ internal val NO_INTERNET = R.string.data_connection_no_internet
+
+ /**
+ * Mapping from a [WifiNetworkModel] to the appropriate [WifiIcon].
+ *
+ * @param showHotspotInfo true if the wifi icon should represent the hotspot device (if it
+ * exists) and false if the wifi icon should only ever show the wifi level and *not* the
+ * hotspot device.
+ */
+ fun fromModel(
+ model: WifiNetworkModel,
+ context: Context,
+ showHotspotInfo: Boolean,
+ ): WifiIcon =
+ when (model) {
+ is WifiNetworkModel.Unavailable -> Hidden
+ is WifiNetworkModel.Invalid -> Hidden
+ is WifiNetworkModel.CarrierMerged -> Hidden
+ is WifiNetworkModel.Inactive ->
+ Visible(
+ res = WifiIcons.WIFI_NO_NETWORK,
+ ContentDescription.Loaded(
+ "${context.getString(WIFI_NO_CONNECTION)},${context.getString(
+ NO_INTERNET
+ )}"
+ )
+ )
+ is WifiNetworkModel.Active -> {
+ val levelDesc = context.getString(WIFI_CONNECTION_STRENGTH[model.level])
+ val contentDescription =
+ ContentDescription.Loaded(
+ if (model.isValidated) {
+ (levelDesc)
+ } else {
+ "$levelDesc,${context.getString(NO_INTERNET)}"
+ }
+ )
+ Visible(model.toIcon(showHotspotInfo), contentDescription)
+ }
+ }
+
+ @DrawableRes
+ private fun WifiNetworkModel.Active.toIcon(showHotspotInfo: Boolean): Int {
+ return if (!showHotspotInfo) {
+ this.toBasicIcon()
+ } else {
+ when (this.hotspotDeviceType) {
+ WifiNetworkModel.HotspotDeviceType.NONE -> this.toBasicIcon()
+ WifiNetworkModel.HotspotDeviceType.TABLET ->
+ com.android.settingslib.R.drawable.ic_hotspot_tablet
+ WifiNetworkModel.HotspotDeviceType.LAPTOP ->
+ com.android.settingslib.R.drawable.ic_hotspot_laptop
+ WifiNetworkModel.HotspotDeviceType.WATCH ->
+ com.android.settingslib.R.drawable.ic_hotspot_watch
+ WifiNetworkModel.HotspotDeviceType.AUTO ->
+ com.android.settingslib.R.drawable.ic_hotspot_auto
+ // Use phone as the default drawable
+ WifiNetworkModel.HotspotDeviceType.PHONE,
+ WifiNetworkModel.HotspotDeviceType.UNKNOWN,
+ WifiNetworkModel.HotspotDeviceType.INVALID ->
+ com.android.settingslib.R.drawable.ic_hotspot_phone
+ }
+ }
+ }
+
+ @DrawableRes
+ private fun WifiNetworkModel.Active.toBasicIcon(): Int {
+ return if (this.isValidated) {
+ WifiIcons.WIFI_FULL_ICONS[this.level]
+ } else {
+ WifiIcons.WIFI_NO_INTERNET_ICONS[this.level]
+ }
+ }
+ }
}
private const val COL_ICON = "icon"
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/wifi/ui/viewmodel/WifiViewModel.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/wifi/ui/viewmodel/WifiViewModel.kt
index d9c2144..d099c8e 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/wifi/ui/viewmodel/WifiViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/wifi/ui/viewmodel/WifiViewModel.kt
@@ -17,19 +17,10 @@
package com.android.systemui.statusbar.pipeline.wifi.ui.viewmodel
import android.content.Context
-import androidx.annotation.StringRes
-import androidx.annotation.VisibleForTesting
-import com.android.settingslib.AccessibilityContentDescriptions.WIFI_CONNECTION_STRENGTH
-import com.android.settingslib.AccessibilityContentDescriptions.WIFI_NO_CONNECTION
-import com.android.systemui.R
-import com.android.systemui.common.shared.model.ContentDescription
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Application
import com.android.systemui.log.table.TableLogBuffer
import com.android.systemui.log.table.logDiffsForTable
-import com.android.systemui.statusbar.connectivity.WifiIcons.WIFI_FULL_ICONS
-import com.android.systemui.statusbar.connectivity.WifiIcons.WIFI_NO_INTERNET_ICONS
-import com.android.systemui.statusbar.connectivity.WifiIcons.WIFI_NO_NETWORK
import com.android.systemui.statusbar.pipeline.airplane.ui.viewmodel.AirplaneModeViewModel
import com.android.systemui.statusbar.pipeline.dagger.StatusBarPipelineModule.Companion.FIRST_MOBILE_SUB_SHOWING_NETWORK_TYPE_ICON
import com.android.systemui.statusbar.pipeline.dagger.WifiTableLog
@@ -75,39 +66,6 @@
@Application private val scope: CoroutineScope,
wifiConstants: WifiConstants,
) : WifiViewModelCommon {
- /** Returns the icon to use based on the given network. */
- private fun WifiNetworkModel.icon(): WifiIcon {
- return when (this) {
- is WifiNetworkModel.Unavailable -> WifiIcon.Hidden
- is WifiNetworkModel.Invalid -> WifiIcon.Hidden
- is WifiNetworkModel.CarrierMerged -> WifiIcon.Hidden
- is WifiNetworkModel.Inactive ->
- WifiIcon.Visible(
- res = WIFI_NO_NETWORK,
- ContentDescription.Loaded(
- "${context.getString(WIFI_NO_CONNECTION)},${context.getString(NO_INTERNET)}"
- )
- )
- is WifiNetworkModel.Active -> {
- val levelDesc = context.getString(WIFI_CONNECTION_STRENGTH[this.level])
- when {
- this.isValidated ->
- WifiIcon.Visible(
- WIFI_FULL_ICONS[this.level],
- ContentDescription.Loaded(levelDesc),
- )
- else ->
- WifiIcon.Visible(
- WIFI_NO_INTERNET_ICONS[this.level],
- ContentDescription.Loaded(
- "$levelDesc,${context.getString(NO_INTERNET)}"
- ),
- )
- }
- }
- }
- }
-
override val wifiIcon: StateFlow<WifiIcon> =
combine(
interactor.isEnabled,
@@ -119,7 +77,8 @@
return@combine WifiIcon.Hidden
}
- val icon = wifiNetwork.icon()
+ // Don't show any hotspot info in the status bar.
+ val icon = WifiIcon.fromModel(wifiNetwork, context, showHotspotInfo = false)
return@combine when {
isDefault -> icon
@@ -186,10 +145,4 @@
airplaneModeViewModel.isAirplaneModeIconVisible
override val isSignalSpacerVisible: Flow<Boolean> = shouldShowSignalSpacerProvider.get()
-
- companion object {
- @StringRes
- @VisibleForTesting
- internal val NO_INTERNET = R.string.data_connection_no_internet
- }
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/HeadsUpUtil.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/HeadsUpUtil.java
index 1212585..feef029 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/HeadsUpUtil.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/HeadsUpUtil.java
@@ -16,9 +16,15 @@
package com.android.systemui.statusbar.policy;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.util.Log;
import android.view.View;
import com.android.systemui.R;
+import com.android.systemui.statusbar.notification.collection.NotificationEntry;
+import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
+import com.android.systemui.util.Compile;
/**
* A class of utility static methods for heads up notifications.
@@ -26,12 +32,18 @@
public final class HeadsUpUtil {
private static final int TAG_CLICKED_NOTIFICATION = R.id.is_clicked_heads_up_tag;
+ private static final String LOG_TAG = "HeadsUpUtil";
+ private static final boolean LOG_DEBUG = Compile.IS_DEBUG && Log.isLoggable(LOG_TAG, Log.DEBUG);
+
/**
* Set the given view as clicked or not-clicked.
* @param view The view to be set the flag to.
* @param clicked True to set as clicked. False to not-clicked.
*/
public static void setNeedsHeadsUpDisappearAnimationAfterClick(View view, boolean clicked) {
+ if (LOG_DEBUG) {
+ logTagClickedNotificationChanged(view, clicked);
+ }
view.setTag(TAG_CLICKED_NOTIFICATION, clicked ? true : null);
}
@@ -44,4 +56,36 @@
Boolean clicked = (Boolean) view.getTag(TAG_CLICKED_NOTIFICATION);
return clicked != null && clicked;
}
+
+ private static void logTagClickedNotificationChanged(@Nullable View view, boolean isClicked) {
+ if (view == null) {
+ return;
+ }
+
+ final boolean wasClicked = isClickedHeadsUpNotification(view);
+ if (isClicked == wasClicked) {
+ return;
+ }
+
+ Log.d(LOG_TAG, getViewKey(view) + ": TAG_CLICKED_NOTIFICATION set to " + isClicked);
+ }
+
+ private static @NonNull String getViewKey(@NonNull View view) {
+ if (!(view instanceof ExpandableNotificationRow)) {
+ return "(not a row)";
+ }
+
+ final ExpandableNotificationRow row = (ExpandableNotificationRow) view;
+ final NotificationEntry entry = row.getEntry();
+ if (entry == null) {
+ return "(null entry)";
+ }
+
+ final String key = entry.getKey();
+ if (key == null) {
+ return "(null key)";
+ }
+
+ return key;
+ }
}
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/ClockEventControllerTest.kt b/packages/SystemUI/tests/src/com/android/keyguard/ClockEventControllerTest.kt
index b67f280..c91313ad 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/ClockEventControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/keyguard/ClockEventControllerTest.kt
@@ -22,13 +22,11 @@
import android.widget.FrameLayout
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
-import com.android.systemui.bouncer.data.repository.FakeKeyguardBouncerRepository
import com.android.systemui.broadcast.BroadcastDispatcher
-import com.android.systemui.common.ui.data.repository.FakeConfigurationRepository
-import com.android.systemui.flags.FeatureFlags
+import com.android.systemui.flags.Flags
import com.android.systemui.keyguard.data.repository.FakeKeyguardRepository
import com.android.systemui.keyguard.data.repository.KeyguardTransitionRepository
-import com.android.systemui.keyguard.domain.interactor.KeyguardInteractor
+import com.android.systemui.keyguard.domain.interactor.KeyguardInteractorFactory
import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractorFactory
import com.android.systemui.log.LogBuffer
import com.android.systemui.plugins.ClockAnimations
@@ -38,7 +36,6 @@
import com.android.systemui.plugins.ClockFaceController
import com.android.systemui.plugins.ClockFaceEvents
import com.android.systemui.plugins.ClockTickRate
-import com.android.systemui.statusbar.CommandQueue
import com.android.systemui.statusbar.policy.BatteryController
import com.android.systemui.statusbar.policy.ConfigurationController
import com.android.systemui.util.concurrency.DelayableExecutor
@@ -47,6 +44,8 @@
import com.android.systemui.util.mockito.capture
import com.android.systemui.util.mockito.eq
import com.android.systemui.util.mockito.mock
+import java.util.TimeZone
+import java.util.concurrent.Executor
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.runBlocking
import kotlinx.coroutines.test.TestScope
@@ -63,10 +62,8 @@
import org.mockito.Mockito.never
import org.mockito.Mockito.times
import org.mockito.Mockito.verify
-import org.mockito.junit.MockitoJUnit
-import java.util.TimeZone
-import java.util.concurrent.Executor
import org.mockito.Mockito.`when` as whenever
+import org.mockito.junit.MockitoJUnit
@RunWith(AndroidTestingRunner::class)
@SmallTest
@@ -82,7 +79,6 @@
@Mock private lateinit var clock: ClockController
@Mock private lateinit var mainExecutor: DelayableExecutor
@Mock private lateinit var bgExecutor: Executor
- @Mock private lateinit var featureFlags: FeatureFlags
@Mock private lateinit var smallClockController: ClockFaceController
@Mock private lateinit var smallClockView: View
@Mock private lateinit var smallClockViewTreeObserver: ViewTreeObserver
@@ -95,9 +91,7 @@
@Mock private lateinit var largeClockEvents: ClockFaceEvents
@Mock private lateinit var parentView: View
@Mock private lateinit var transitionRepository: KeyguardTransitionRepository
- @Mock private lateinit var commandQueue: CommandQueue
private lateinit var repository: FakeKeyguardRepository
- private lateinit var bouncerRepository: FakeKeyguardBouncerRepository
@Mock private lateinit var smallLogBuffer: LogBuffer
@Mock private lateinit var largeLogBuffer: LogBuffer
private lateinit var underTest: ClockEventController
@@ -123,31 +117,35 @@
.thenReturn(ClockFaceConfig(tickRate = ClockTickRate.PER_MINUTE))
repository = FakeKeyguardRepository()
- bouncerRepository = FakeKeyguardBouncerRepository()
- underTest = ClockEventController(
- KeyguardInteractor(
+ val withDeps =
+ KeyguardInteractorFactory.create(
repository = repository,
- commandQueue = commandQueue,
- featureFlags = featureFlags,
- bouncerRepository = bouncerRepository,
- configurationRepository = FakeConfigurationRepository(),
- ),
- KeyguardTransitionInteractorFactory.create(
- scope = TestScope().backgroundScope,
- ).keyguardTransitionInteractor,
- broadcastDispatcher,
- batteryController,
- keyguardUpdateMonitor,
- configurationController,
- context.resources,
- context,
- mainExecutor,
- bgExecutor,
- smallLogBuffer,
- largeLogBuffer,
- featureFlags
- )
+ )
+
+ withDeps.featureFlags.apply {
+ set(Flags.REGION_SAMPLING, false)
+ set(Flags.DOZING_MIGRATION_1, false)
+ }
+ underTest =
+ ClockEventController(
+ withDeps.keyguardInteractor,
+ KeyguardTransitionInteractorFactory.create(
+ scope = TestScope().backgroundScope,
+ )
+ .keyguardTransitionInteractor,
+ broadcastDispatcher,
+ batteryController,
+ keyguardUpdateMonitor,
+ configurationController,
+ context.resources,
+ context,
+ mainExecutor,
+ bgExecutor,
+ smallLogBuffer,
+ largeLogBuffer,
+ withDeps.featureFlags
+ )
underTest.clock = clock
runBlocking(IMMEDIATE) {
@@ -171,38 +169,41 @@
}
@Test
- fun themeChanged_verifyClockPaletteUpdated() = runBlocking(IMMEDIATE) {
- verify(smallClockEvents).onRegionDarknessChanged(anyBoolean())
- verify(largeClockEvents).onRegionDarknessChanged(anyBoolean())
+ fun themeChanged_verifyClockPaletteUpdated() =
+ runBlocking(IMMEDIATE) {
+ verify(smallClockEvents).onRegionDarknessChanged(anyBoolean())
+ verify(largeClockEvents).onRegionDarknessChanged(anyBoolean())
- val captor = argumentCaptor<ConfigurationController.ConfigurationListener>()
- verify(configurationController).addCallback(capture(captor))
- captor.value.onThemeChanged()
+ val captor = argumentCaptor<ConfigurationController.ConfigurationListener>()
+ verify(configurationController).addCallback(capture(captor))
+ captor.value.onThemeChanged()
- verify(events).onColorPaletteChanged(any())
- }
+ verify(events).onColorPaletteChanged(any())
+ }
@Test
- fun fontChanged_verifyFontSizeUpdated() = runBlocking(IMMEDIATE) {
- val captor = argumentCaptor<ConfigurationController.ConfigurationListener>()
- verify(configurationController).addCallback(capture(captor))
- captor.value.onDensityOrFontScaleChanged()
+ fun fontChanged_verifyFontSizeUpdated() =
+ runBlocking(IMMEDIATE) {
+ val captor = argumentCaptor<ConfigurationController.ConfigurationListener>()
+ verify(configurationController).addCallback(capture(captor))
+ captor.value.onDensityOrFontScaleChanged()
- verify(smallClockEvents, times(2)).onFontSettingChanged(anyFloat())
- verify(largeClockEvents, times(2)).onFontSettingChanged(anyFloat())
- }
+ verify(smallClockEvents, times(2)).onFontSettingChanged(anyFloat())
+ verify(largeClockEvents, times(2)).onFontSettingChanged(anyFloat())
+ }
@Test
- fun batteryCallback_keyguardShowingCharging_verifyChargeAnimation() = runBlocking(IMMEDIATE) {
- val batteryCaptor = argumentCaptor<BatteryController.BatteryStateChangeCallback>()
- verify(batteryController).addCallback(capture(batteryCaptor))
- val keyguardCaptor = argumentCaptor<KeyguardUpdateMonitorCallback>()
- verify(keyguardUpdateMonitor).registerCallback(capture(keyguardCaptor))
- keyguardCaptor.value.onKeyguardVisibilityChanged(true)
- batteryCaptor.value.onBatteryLevelChanged(10, false, true)
+ fun batteryCallback_keyguardShowingCharging_verifyChargeAnimation() =
+ runBlocking(IMMEDIATE) {
+ val batteryCaptor = argumentCaptor<BatteryController.BatteryStateChangeCallback>()
+ verify(batteryController).addCallback(capture(batteryCaptor))
+ val keyguardCaptor = argumentCaptor<KeyguardUpdateMonitorCallback>()
+ verify(keyguardUpdateMonitor).registerCallback(capture(keyguardCaptor))
+ keyguardCaptor.value.onKeyguardVisibilityChanged(true)
+ batteryCaptor.value.onBatteryLevelChanged(10, false, true)
- verify(animations, times(2)).charge()
- }
+ verify(animations, times(2)).charge()
+ }
@Test
fun batteryCallback_keyguardShowingCharging_Duplicate_verifyChargeAnimation() =
@@ -219,16 +220,17 @@
}
@Test
- fun batteryCallback_keyguardHiddenCharging_verifyChargeAnimation() = runBlocking(IMMEDIATE) {
- val batteryCaptor = argumentCaptor<BatteryController.BatteryStateChangeCallback>()
- verify(batteryController).addCallback(capture(batteryCaptor))
- val keyguardCaptor = argumentCaptor<KeyguardUpdateMonitorCallback>()
- verify(keyguardUpdateMonitor).registerCallback(capture(keyguardCaptor))
- keyguardCaptor.value.onKeyguardVisibilityChanged(false)
- batteryCaptor.value.onBatteryLevelChanged(10, false, true)
+ fun batteryCallback_keyguardHiddenCharging_verifyChargeAnimation() =
+ runBlocking(IMMEDIATE) {
+ val batteryCaptor = argumentCaptor<BatteryController.BatteryStateChangeCallback>()
+ verify(batteryController).addCallback(capture(batteryCaptor))
+ val keyguardCaptor = argumentCaptor<KeyguardUpdateMonitorCallback>()
+ verify(keyguardUpdateMonitor).registerCallback(capture(keyguardCaptor))
+ keyguardCaptor.value.onKeyguardVisibilityChanged(false)
+ batteryCaptor.value.onBatteryLevelChanged(10, false, true)
- verify(animations, never()).charge()
- }
+ verify(animations, never()).charge()
+ }
@Test
fun batteryCallback_keyguardShowingNotCharging_verifyChargeAnimation() =
@@ -244,102 +246,111 @@
}
@Test
- fun localeCallback_verifyClockNotified() = runBlocking(IMMEDIATE) {
- val captor = argumentCaptor<BroadcastReceiver>()
- verify(broadcastDispatcher).registerReceiver(
- capture(captor), any(), eq(null), eq(null), anyInt(), eq(null)
- )
- captor.value.onReceive(context, mock())
+ fun localeCallback_verifyClockNotified() =
+ runBlocking(IMMEDIATE) {
+ val captor = argumentCaptor<BroadcastReceiver>()
+ verify(broadcastDispatcher)
+ .registerReceiver(capture(captor), any(), eq(null), eq(null), anyInt(), eq(null))
+ captor.value.onReceive(context, mock())
- verify(events).onLocaleChanged(any())
- }
+ verify(events).onLocaleChanged(any())
+ }
@Test
- fun keyguardCallback_visibilityChanged_clockDozeCalled() = runBlocking(IMMEDIATE) {
- val captor = argumentCaptor<KeyguardUpdateMonitorCallback>()
- verify(keyguardUpdateMonitor).registerCallback(capture(captor))
+ fun keyguardCallback_visibilityChanged_clockDozeCalled() =
+ runBlocking(IMMEDIATE) {
+ val captor = argumentCaptor<KeyguardUpdateMonitorCallback>()
+ verify(keyguardUpdateMonitor).registerCallback(capture(captor))
- captor.value.onKeyguardVisibilityChanged(true)
- verify(animations, never()).doze(0f)
+ captor.value.onKeyguardVisibilityChanged(true)
+ verify(animations, never()).doze(0f)
- captor.value.onKeyguardVisibilityChanged(false)
- verify(animations, times(2)).doze(0f)
- }
+ captor.value.onKeyguardVisibilityChanged(false)
+ verify(animations, times(2)).doze(0f)
+ }
@Test
- fun keyguardCallback_timeFormat_clockNotified() = runBlocking(IMMEDIATE) {
- val captor = argumentCaptor<KeyguardUpdateMonitorCallback>()
- verify(keyguardUpdateMonitor).registerCallback(capture(captor))
- captor.value.onTimeFormatChanged("12h")
+ fun keyguardCallback_timeFormat_clockNotified() =
+ runBlocking(IMMEDIATE) {
+ val captor = argumentCaptor<KeyguardUpdateMonitorCallback>()
+ verify(keyguardUpdateMonitor).registerCallback(capture(captor))
+ captor.value.onTimeFormatChanged("12h")
- verify(events).onTimeFormatChanged(false)
- }
+ verify(events).onTimeFormatChanged(false)
+ }
@Test
- fun keyguardCallback_timezoneChanged_clockNotified() = runBlocking(IMMEDIATE) {
- val mockTimeZone = mock<TimeZone>()
- val captor = argumentCaptor<KeyguardUpdateMonitorCallback>()
- verify(keyguardUpdateMonitor).registerCallback(capture(captor))
- captor.value.onTimeZoneChanged(mockTimeZone)
+ fun keyguardCallback_timezoneChanged_clockNotified() =
+ runBlocking(IMMEDIATE) {
+ val mockTimeZone = mock<TimeZone>()
+ val captor = argumentCaptor<KeyguardUpdateMonitorCallback>()
+ verify(keyguardUpdateMonitor).registerCallback(capture(captor))
+ captor.value.onTimeZoneChanged(mockTimeZone)
- verify(events).onTimeZoneChanged(mockTimeZone)
- }
+ verify(events).onTimeZoneChanged(mockTimeZone)
+ }
@Test
- fun keyguardCallback_userSwitched_clockNotified() = runBlocking(IMMEDIATE) {
- val captor = argumentCaptor<KeyguardUpdateMonitorCallback>()
- verify(keyguardUpdateMonitor).registerCallback(capture(captor))
- captor.value.onUserSwitchComplete(10)
+ fun keyguardCallback_userSwitched_clockNotified() =
+ runBlocking(IMMEDIATE) {
+ val captor = argumentCaptor<KeyguardUpdateMonitorCallback>()
+ verify(keyguardUpdateMonitor).registerCallback(capture(captor))
+ captor.value.onUserSwitchComplete(10)
- verify(events).onTimeFormatChanged(false)
- }
+ verify(events).onTimeFormatChanged(false)
+ }
@Test
- fun keyguardCallback_verifyKeyguardChanged() = runBlocking(IMMEDIATE) {
- val job = underTest.listenForDozeAmount(this)
- repository.setDozeAmount(0.4f)
+ fun keyguardCallback_verifyKeyguardChanged() =
+ runBlocking(IMMEDIATE) {
+ val job = underTest.listenForDozeAmount(this)
+ repository.setDozeAmount(0.4f)
- yield()
+ yield()
- verify(animations, times(2)).doze(0.4f)
+ verify(animations, times(2)).doze(0.4f)
- job.cancel()
- }
+ job.cancel()
+ }
@Test
- fun unregisterListeners_validate() = runBlocking(IMMEDIATE) {
- underTest.unregisterListeners()
- verify(broadcastDispatcher).unregisterReceiver(any())
- verify(configurationController).removeCallback(any())
- verify(batteryController).removeCallback(any())
- verify(keyguardUpdateMonitor).removeCallback(any())
- verify(smallClockController.view)
+ fun unregisterListeners_validate() =
+ runBlocking(IMMEDIATE) {
+ underTest.unregisterListeners()
+ verify(broadcastDispatcher).unregisterReceiver(any())
+ verify(configurationController).removeCallback(any())
+ verify(batteryController).removeCallback(any())
+ verify(keyguardUpdateMonitor).removeCallback(any())
+ verify(smallClockController.view)
.removeOnAttachStateChangeListener(underTest.smallClockOnAttachStateChangeListener)
- verify(largeClockController.view)
+ verify(largeClockController.view)
.removeOnAttachStateChangeListener(underTest.largeClockOnAttachStateChangeListener)
- }
+ }
@Test
- fun registerOnAttachStateChangeListener_validate() = runBlocking(IMMEDIATE) {
- verify(smallClockController.view)
- .addOnAttachStateChangeListener(underTest.smallClockOnAttachStateChangeListener)
- verify(largeClockController.view)
- .addOnAttachStateChangeListener(underTest.largeClockOnAttachStateChangeListener)
- }
+ fun registerOnAttachStateChangeListener_validate() =
+ runBlocking(IMMEDIATE) {
+ verify(smallClockController.view)
+ .addOnAttachStateChangeListener(underTest.smallClockOnAttachStateChangeListener)
+ verify(largeClockController.view)
+ .addOnAttachStateChangeListener(underTest.largeClockOnAttachStateChangeListener)
+ }
@Test
- fun registerAndRemoveOnGlobalLayoutListener_correctly() = runBlocking(IMMEDIATE) {
- underTest.smallClockOnAttachStateChangeListener!!.onViewAttachedToWindow(smallClockView)
- verify(smallClockFrame.viewTreeObserver).addOnGlobalLayoutListener(any())
- underTest.smallClockOnAttachStateChangeListener!!.onViewDetachedFromWindow(smallClockView)
- verify(smallClockFrame.viewTreeObserver).removeOnGlobalLayoutListener(any())
- }
+ fun registerAndRemoveOnGlobalLayoutListener_correctly() =
+ runBlocking(IMMEDIATE) {
+ underTest.smallClockOnAttachStateChangeListener!!.onViewAttachedToWindow(smallClockView)
+ verify(smallClockFrame.viewTreeObserver).addOnGlobalLayoutListener(any())
+ underTest.smallClockOnAttachStateChangeListener!!.onViewDetachedFromWindow(
+ smallClockView
+ )
+ verify(smallClockFrame.viewTreeObserver).removeOnGlobalLayoutListener(any())
+ }
@Test
fun registerOnGlobalLayoutListener_RemoveOnAttachStateChangeListener_correctly() =
runBlocking(IMMEDIATE) {
- underTest.smallClockOnAttachStateChangeListener!!
- .onViewAttachedToWindow(smallClockView)
+ underTest.smallClockOnAttachStateChangeListener!!.onViewAttachedToWindow(smallClockView)
verify(smallClockFrame.viewTreeObserver).addOnGlobalLayoutListener(any())
underTest.unregisterListeners()
verify(smallClockFrame.viewTreeObserver).removeOnGlobalLayoutListener(any())
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardPasswordViewControllerTest.kt b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardPasswordViewControllerTest.kt
index 3a94730..e1b608f 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardPasswordViewControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardPasswordViewControllerTest.kt
@@ -16,15 +16,16 @@
package com.android.keyguard
-import android.testing.AndroidTestingRunner
import android.testing.TestableLooper
import android.view.inputmethod.InputMethodManager
import android.widget.EditText
import android.widget.ImageView
+import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.internal.util.LatencyTracker
import com.android.internal.widget.LockPatternUtils
import com.android.systemui.R
+import com.android.systemui.RoboPilotTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.classifier.FalsingCollector
import com.android.systemui.flags.FakeFeatureFlags
@@ -45,7 +46,8 @@
import org.mockito.MockitoAnnotations
@SmallTest
-@RunWith(AndroidTestingRunner::class)
+@RoboPilotTest
+@RunWith(AndroidJUnit4::class)
@TestableLooper.RunWithLooper
class KeyguardPasswordViewControllerTest : SysuiTestCase() {
@Mock private lateinit var keyguardPasswordView: KeyguardPasswordView
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardPatternViewControllerTest.kt b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardPatternViewControllerTest.kt
index 1acd676..93048a5 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardPatternViewControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardPatternViewControllerTest.kt
@@ -16,15 +16,16 @@
package com.android.keyguard
-import android.testing.AndroidTestingRunner
import android.testing.TestableLooper
import android.view.View
import androidx.constraintlayout.widget.ConstraintLayout
import androidx.constraintlayout.widget.ConstraintSet
+import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.internal.util.LatencyTracker
import com.android.internal.widget.LockPatternUtils
import com.android.systemui.R
+import com.android.systemui.RoboPilotTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.classifier.FalsingCollector
import com.android.systemui.classifier.FalsingCollectorFake
@@ -52,7 +53,8 @@
import org.mockito.MockitoAnnotations
@SmallTest
-@RunWith(AndroidTestingRunner::class)
+@RoboPilotTest
+@RunWith(AndroidJUnit4::class)
@TestableLooper.RunWithLooper
class KeyguardPatternViewControllerTest : SysuiTestCase() {
private lateinit var mKeyguardPatternView: KeyguardPatternView
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardPinBasedInputViewControllerTest.java b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardPinBasedInputViewControllerTest.java
index efe1955..2b90e7c 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardPinBasedInputViewControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardPinBasedInputViewControllerTest.java
@@ -22,16 +22,17 @@
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
-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.internal.util.LatencyTracker;
import com.android.internal.widget.LockPatternUtils;
import com.android.keyguard.KeyguardSecurityModel.SecurityMode;
import com.android.systemui.R;
+import com.android.systemui.RoboPilotTest;
import com.android.systemui.SysuiTestCase;
import com.android.systemui.classifier.FalsingCollector;
import com.android.systemui.classifier.FalsingCollectorFake;
@@ -46,7 +47,8 @@
import org.mockito.MockitoAnnotations;
@SmallTest
-@RunWith(AndroidTestingRunner.class)
+@RoboPilotTest
+@RunWith(AndroidJUnit4.class)
@RunWithLooper
public class KeyguardPinBasedInputViewControllerTest extends SysuiTestCase {
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardPinViewControllerTest.kt b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardPinViewControllerTest.kt
index 80fd721..61acacd 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardPinViewControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardPinViewControllerTest.kt
@@ -16,16 +16,17 @@
package com.android.keyguard
-import android.testing.AndroidTestingRunner
import android.testing.TestableLooper
import android.view.View
import androidx.constraintlayout.widget.ConstraintLayout
import androidx.constraintlayout.widget.ConstraintSet
+import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.internal.util.LatencyTracker
import com.android.internal.widget.LockPatternUtils
import com.android.keyguard.KeyguardSecurityModel.SecurityMode
import com.android.systemui.R
+import com.android.systemui.RoboPilotTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.classifier.FalsingCollector
import com.android.systemui.classifier.FalsingCollectorFake
@@ -52,7 +53,8 @@
import org.mockito.MockitoAnnotations
@SmallTest
-@RunWith(AndroidTestingRunner::class)
+@RoboPilotTest
+@RunWith(AndroidJUnit4::class)
@TestableLooper.RunWithLooper
class KeyguardPinViewControllerTest : SysuiTestCase() {
@@ -175,7 +177,8 @@
private fun getPinTopGuideline(): Float {
val cs = ConstraintSet()
- val container = objectKeyguardPINView.requireViewById(R.id.pin_container) as ConstraintLayout
+ val container =
+ objectKeyguardPINView.requireViewById(R.id.pin_container) as ConstraintLayout
cs.clone(container)
return cs.getConstraint(R.id.pin_pad_top_guideline).layout.guidePercent
}
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSecurityContainerControllerTest.kt b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSecurityContainerControllerTest.kt
index 80172a1..6bff4ce 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSecurityContainerControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSecurityContainerControllerTest.kt
@@ -21,7 +21,6 @@
import android.hardware.biometrics.BiometricOverlayConstants
import android.media.AudioManager
import android.telephony.TelephonyManager
-import android.testing.AndroidTestingRunner
import android.testing.TestableLooper.RunWithLooper
import android.testing.TestableResources
import android.view.Gravity
@@ -30,6 +29,7 @@
import android.view.View
import android.view.WindowInsetsController
import android.widget.FrameLayout
+import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.internal.logging.MetricsLogger
import com.android.internal.logging.UiEventLogger
@@ -37,6 +37,7 @@
import com.android.keyguard.KeyguardSecurityContainer.UserSwitcherViewMode.UserSwitcherCallback
import com.android.keyguard.KeyguardSecurityModel.SecurityMode
import com.android.systemui.R
+import com.android.systemui.RoboPilotTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.authentication.domain.interactor.AuthenticationInteractor
import com.android.systemui.biometrics.FaceAuthAccessibilityDelegate
@@ -96,7 +97,8 @@
import org.mockito.MockitoAnnotations
@SmallTest
-@RunWith(AndroidTestingRunner::class)
+@RoboPilotTest
+@RunWith(AndroidJUnit4::class)
@RunWithLooper
class KeyguardSecurityContainerControllerTest : SysuiTestCase() {
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSecurityContainerTest.java b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSecurityContainerTest.java
index f6450a4..3e330d65 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSecurityContainerTest.java
+++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSecurityContainerTest.java
@@ -44,7 +44,6 @@
import android.content.pm.UserInfo;
import android.content.res.Configuration;
import android.graphics.Insets;
-import android.testing.AndroidTestingRunner;
import android.testing.TestableLooper;
import android.view.MotionEvent;
import android.view.View;
@@ -54,9 +53,11 @@
import android.window.OnBackAnimationCallback;
import androidx.constraintlayout.widget.ConstraintSet;
+import androidx.test.ext.junit.runners.AndroidJUnit4;
import androidx.test.filters.SmallTest;
import com.android.systemui.R;
+import com.android.systemui.RoboPilotTest;
import com.android.systemui.SysuiTestCase;
import com.android.systemui.classifier.FalsingA11yDelegate;
import com.android.systemui.plugins.FalsingManager;
@@ -75,7 +76,8 @@
import java.util.ArrayList;
@SmallTest
-@RunWith(AndroidTestingRunner.class)
+@RoboPilotTest
+@RunWith(AndroidJUnit4.class)
@TestableLooper.RunWithLooper()
public class KeyguardSecurityContainerTest extends SysuiTestCase {
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSecurityViewFlipperControllerTest.java b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSecurityViewFlipperControllerTest.java
index 64e1458..68c2f59 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSecurityViewFlipperControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSecurityViewFlipperControllerTest.java
@@ -25,17 +25,18 @@
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
-import android.testing.AndroidTestingRunner;
import android.testing.TestableLooper;
import android.view.LayoutInflater;
import android.view.ViewGroup;
import android.view.WindowInsetsController;
import androidx.asynclayoutinflater.view.AsyncLayoutInflater;
+import androidx.test.ext.junit.runners.AndroidJUnit4;
import androidx.test.filters.SmallTest;
import com.android.keyguard.KeyguardSecurityModel.SecurityMode;
import com.android.systemui.R;
+import com.android.systemui.RoboPilotTest;
import com.android.systemui.SysuiTestCase;
import com.android.systemui.flags.FeatureFlags;
@@ -49,7 +50,8 @@
import org.mockito.junit.MockitoRule;
@SmallTest
-@RunWith(AndroidTestingRunner.class)
+@RoboPilotTest
+@RunWith(AndroidJUnit4.class)
@TestableLooper.RunWithLooper()
public class KeyguardSecurityViewFlipperControllerTest extends SysuiTestCase {
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSimPinViewControllerTest.kt b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSimPinViewControllerTest.kt
index 291dda25..4db5f35 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSimPinViewControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSimPinViewControllerTest.kt
@@ -18,13 +18,14 @@
import android.telephony.PinResult
import android.telephony.TelephonyManager
-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.internal.util.LatencyTracker
import com.android.internal.widget.LockPatternUtils
import com.android.systemui.R
+import com.android.systemui.RoboPilotTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.classifier.FalsingCollector
import com.android.systemui.flags.FakeFeatureFlags
@@ -43,7 +44,8 @@
import org.mockito.MockitoAnnotations
@SmallTest
-@RunWith(AndroidTestingRunner::class)
+@RoboPilotTest
+@RunWith(AndroidJUnit4::class)
@TestableLooper.RunWithLooper
class KeyguardSimPinViewControllerTest : SysuiTestCase() {
private lateinit var simPinView: KeyguardSimPinView
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSimPukViewControllerTest.kt b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSimPukViewControllerTest.kt
index 626faa6..47ff3b9 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSimPukViewControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSimPukViewControllerTest.kt
@@ -18,13 +18,14 @@
import android.telephony.PinResult
import android.telephony.TelephonyManager
-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.internal.util.LatencyTracker
import com.android.internal.widget.LockPatternUtils
import com.android.systemui.R
+import com.android.systemui.RoboPilotTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.classifier.FalsingCollector
import com.android.systemui.flags.FakeFeatureFlags
@@ -39,7 +40,8 @@
import org.mockito.MockitoAnnotations
@SmallTest
-@RunWith(AndroidTestingRunner::class)
+@RoboPilotTest
+@RunWith(AndroidJUnit4::class)
@TestableLooper.RunWithLooper
class KeyguardSimPukViewControllerTest : SysuiTestCase() {
private lateinit var simPukView: KeyguardSimPukView
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/LockIconViewControllerBaseTest.java b/packages/SystemUI/tests/src/com/android/keyguard/LockIconViewControllerBaseTest.java
index b18137c..09ff546 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/LockIconViewControllerBaseTest.java
+++ b/packages/SystemUI/tests/src/com/android/keyguard/LockIconViewControllerBaseTest.java
@@ -24,7 +24,6 @@
import static org.mockito.Mockito.any;
import static org.mockito.Mockito.anyInt;
-import static org.mockito.Mockito.atLeast;
import static org.mockito.Mockito.reset;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
@@ -47,7 +46,6 @@
import com.android.systemui.doze.util.BurnInHelperKt;
import com.android.systemui.dump.DumpManager;
import com.android.systemui.flags.FakeFeatureFlags;
-import com.android.systemui.keyguard.data.repository.KeyguardTransitionRepository;
import com.android.systemui.keyguard.domain.interactor.KeyguardInteractorFactory;
import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractorFactory;
import com.android.systemui.plugins.FalsingManager;
@@ -92,7 +90,6 @@
protected @Mock ConfigurationController mConfigurationController;
protected @Mock VibratorHelper mVibrator;
protected @Mock AuthRippleController mAuthRippleController;
- protected @Mock KeyguardTransitionRepository mTransitionRepository;
protected FakeExecutor mDelayableExecutor = new FakeExecutor(new FakeSystemClock());
protected FakeFeatureFlags mFeatureFlags;
protected @Mock PrimaryBouncerInteractor mPrimaryBouncerInteractor;
@@ -150,7 +147,6 @@
mFeatureFlags.set(MIGRATE_LOCK_ICON, false);
mFeatureFlags.set(LOCKSCREEN_WALLPAPER_DREAM_ENABLED, false);
mUnderTest = new LockIconViewController(
- mLockIconView,
mStatusBarStateController,
mKeyguardUpdateMonitor,
mKeyguardViewController,
@@ -165,11 +161,12 @@
mAuthRippleController,
mResources,
KeyguardTransitionInteractorFactory.create(
- TestScopeProvider.getTestScope().getBackgroundScope(),
- mTransitionRepository).getKeyguardTransitionInteractor(),
+ TestScopeProvider.getTestScope().getBackgroundScope())
+ .getKeyguardTransitionInteractor(),
KeyguardInteractorFactory.create(mFeatureFlags).getKeyguardInteractor(),
mFeatureFlags,
- mPrimaryBouncerInteractor
+ mPrimaryBouncerInteractor,
+ mContext
);
}
@@ -230,9 +227,6 @@
protected void init(boolean useMigrationFlag) {
mFeatureFlags.set(DOZING_MIGRATION_1, useMigrationFlag);
- mUnderTest.init();
-
- verify(mLockIconView, atLeast(1)).addOnAttachStateChangeListener(mAttachCaptor.capture());
- mAttachCaptor.getValue().onViewAttachedToWindow(mLockIconView);
+ mUnderTest.setLockIconView(mLockIconView);
}
}
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/LockIconViewControllerTest.java b/packages/SystemUI/tests/src/com/android/keyguard/LockIconViewControllerTest.java
index 45021ba..979fc83 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/LockIconViewControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/keyguard/LockIconViewControllerTest.java
@@ -52,6 +52,12 @@
@TestableLooper.RunWithLooper
public class LockIconViewControllerTest extends LockIconViewControllerBaseTest {
+ @Override
+ public void setUp() throws Exception {
+ super.setUp();
+ when(mLockIconView.isAttachedToWindow()).thenReturn(true);
+ }
+
@Test
public void testUpdateFingerprintLocationOnInit() {
// GIVEN fp sensor location is available pre-attached
diff --git a/packages/SystemUI/tests/src/com/android/systemui/appops/AppOpsControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/appops/AppOpsControllerTest.java
index b100336..f9830b1 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/appops/AppOpsControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/appops/AppOpsControllerTest.java
@@ -71,6 +71,7 @@
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
+import java.util.Map;
@SmallTest
@RunWith(AndroidTestingRunner.class)
@@ -163,6 +164,204 @@
}
@Test
+ public void startListening_fetchesCurrentActive_none() {
+ when(mAppOpsManager.getPackagesForOps(AppOpsControllerImpl.OPS))
+ .thenReturn(List.of());
+
+ mController.setListening(true);
+
+ assertThat(mController.getActiveAppOps()).isEmpty();
+ }
+
+ /** Regression test for b/294104969. */
+ @Test
+ public void startListening_fetchesCurrentActive_oneActive() {
+ AppOpsManager.PackageOps packageOps = createPackageOp(
+ "package.test",
+ /* packageUid= */ 2,
+ AppOpsManager.OPSTR_FINE_LOCATION,
+ /* isRunning= */ true);
+ when(mAppOpsManager.getPackagesForOps(AppOpsControllerImpl.OPS))
+ .thenReturn(List.of(packageOps));
+
+ // WHEN we start listening
+ mController.setListening(true);
+
+ // THEN the active list has the op
+ List<AppOpItem> list = mController.getActiveAppOps();
+ assertEquals(1, list.size());
+ AppOpItem first = list.get(0);
+ assertThat(first.getPackageName()).isEqualTo("package.test");
+ assertThat(first.getUid()).isEqualTo(2);
+ assertThat(first.getCode()).isEqualTo(AppOpsManager.OP_FINE_LOCATION);
+ }
+
+ @Test
+ public void startListening_fetchesCurrentActive_multiplePackages() {
+ AppOpsManager.PackageOps packageOps1 = createPackageOp(
+ "package.one",
+ /* packageUid= */ 1,
+ AppOpsManager.OPSTR_FINE_LOCATION,
+ /* isRunning= */ true);
+ AppOpsManager.PackageOps packageOps2 = createPackageOp(
+ "package.two",
+ /* packageUid= */ 2,
+ AppOpsManager.OPSTR_FINE_LOCATION,
+ /* isRunning= */ false);
+ AppOpsManager.PackageOps packageOps3 = createPackageOp(
+ "package.three",
+ /* packageUid= */ 3,
+ AppOpsManager.OPSTR_FINE_LOCATION,
+ /* isRunning= */ true);
+ when(mAppOpsManager.getPackagesForOps(AppOpsControllerImpl.OPS))
+ .thenReturn(List.of(packageOps1, packageOps2, packageOps3));
+
+ // WHEN we start listening
+ mController.setListening(true);
+
+ // THEN the active list has the ops
+ List<AppOpItem> list = mController.getActiveAppOps();
+ assertEquals(2, list.size());
+
+ AppOpItem item0 = list.get(0);
+ assertThat(item0.getPackageName()).isEqualTo("package.one");
+ assertThat(item0.getUid()).isEqualTo(1);
+ assertThat(item0.getCode()).isEqualTo(AppOpsManager.OP_FINE_LOCATION);
+
+ AppOpItem item1 = list.get(1);
+ assertThat(item1.getPackageName()).isEqualTo("package.three");
+ assertThat(item1.getUid()).isEqualTo(3);
+ assertThat(item1.getCode()).isEqualTo(AppOpsManager.OP_FINE_LOCATION);
+ }
+
+ @Test
+ public void startListening_fetchesCurrentActive_multipleEntries() {
+ AppOpsManager.PackageOps packageOps = mock(AppOpsManager.PackageOps.class);
+ when(packageOps.getUid()).thenReturn(1);
+ when(packageOps.getPackageName()).thenReturn("package.one");
+
+ // Entry 1
+ AppOpsManager.OpEntry entry1 = mock(AppOpsManager.OpEntry.class);
+ when(entry1.getOpStr()).thenReturn(AppOpsManager.OPSTR_PHONE_CALL_MICROPHONE);
+ AppOpsManager.AttributedOpEntry attributed1 = mock(AppOpsManager.AttributedOpEntry.class);
+ when(attributed1.isRunning()).thenReturn(true);
+ when(entry1.getAttributedOpEntries()).thenReturn(Map.of("tag", attributed1));
+ // Entry 2
+ AppOpsManager.OpEntry entry2 = mock(AppOpsManager.OpEntry.class);
+ when(entry2.getOpStr()).thenReturn(AppOpsManager.OPSTR_CAMERA);
+ AppOpsManager.AttributedOpEntry attributed2 = mock(AppOpsManager.AttributedOpEntry.class);
+ when(attributed2.isRunning()).thenReturn(true);
+ when(entry2.getAttributedOpEntries()).thenReturn(Map.of("tag", attributed2));
+ // Entry 3
+ AppOpsManager.OpEntry entry3 = mock(AppOpsManager.OpEntry.class);
+ when(entry3.getOpStr()).thenReturn(AppOpsManager.OPSTR_FINE_LOCATION);
+ AppOpsManager.AttributedOpEntry attributed3 = mock(AppOpsManager.AttributedOpEntry.class);
+ when(attributed3.isRunning()).thenReturn(false);
+ when(entry3.getAttributedOpEntries()).thenReturn(Map.of("tag", attributed3));
+
+ when(packageOps.getOps()).thenReturn(List.of(entry1, entry2, entry3));
+ when(mAppOpsManager.getPackagesForOps(AppOpsControllerImpl.OPS))
+ .thenReturn(List.of(packageOps));
+
+ // WHEN we start listening
+ mController.setListening(true);
+
+ // THEN the active list has the ops
+ List<AppOpItem> list = mController.getActiveAppOps();
+ assertEquals(2, list.size());
+
+ AppOpItem first = list.get(0);
+ assertThat(first.getPackageName()).isEqualTo("package.one");
+ assertThat(first.getUid()).isEqualTo(1);
+ assertThat(first.getCode()).isEqualTo(AppOpsManager.OP_PHONE_CALL_MICROPHONE);
+
+ AppOpItem second = list.get(1);
+ assertThat(second.getPackageName()).isEqualTo("package.one");
+ assertThat(second.getUid()).isEqualTo(1);
+ assertThat(second.getCode()).isEqualTo(AppOpsManager.OP_CAMERA);
+ }
+
+ @Test
+ public void startListening_fetchesCurrentActive_multipleAttributes() {
+ AppOpsManager.PackageOps packageOps = mock(AppOpsManager.PackageOps.class);
+ when(packageOps.getUid()).thenReturn(1);
+ when(packageOps.getPackageName()).thenReturn("package.one");
+ AppOpsManager.OpEntry entry = mock(AppOpsManager.OpEntry.class);
+ when(entry.getOpStr()).thenReturn(AppOpsManager.OPSTR_RECORD_AUDIO);
+
+ AppOpsManager.AttributedOpEntry attributed1 = mock(AppOpsManager.AttributedOpEntry.class);
+ when(attributed1.isRunning()).thenReturn(false);
+ AppOpsManager.AttributedOpEntry attributed2 = mock(AppOpsManager.AttributedOpEntry.class);
+ when(attributed2.isRunning()).thenReturn(true);
+ AppOpsManager.AttributedOpEntry attributed3 = mock(AppOpsManager.AttributedOpEntry.class);
+ when(attributed3.isRunning()).thenReturn(true);
+ when(entry.getAttributedOpEntries()).thenReturn(
+ Map.of("attr1", attributed1, "attr2", attributed2, "attr3", attributed3));
+
+ when(packageOps.getOps()).thenReturn(List.of(entry));
+ when(mAppOpsManager.getPackagesForOps(AppOpsControllerImpl.OPS))
+ .thenReturn(List.of(packageOps));
+
+ // WHEN we start listening
+ mController.setListening(true);
+
+ // THEN the active list has the ops
+ List<AppOpItem> list = mController.getActiveAppOps();
+ // Multiple attributes get merged into one entry in the active ops
+ assertEquals(1, list.size());
+
+ AppOpItem first = list.get(0);
+ assertThat(first.getPackageName()).isEqualTo("package.one");
+ assertThat(first.getUid()).isEqualTo(1);
+ assertThat(first.getCode()).isEqualTo(AppOpsManager.OP_RECORD_AUDIO);
+ }
+
+ /** Regression test for b/294104969. */
+ @Test
+ public void addCallback_existingCallbacksNotifiedOfCurrentActive() {
+ AppOpsManager.PackageOps packageOps1 = createPackageOp(
+ "package.one",
+ /* packageUid= */ 1,
+ AppOpsManager.OPSTR_FINE_LOCATION,
+ /* isRunning= */ true);
+ AppOpsManager.PackageOps packageOps2 = createPackageOp(
+ "package.two",
+ /* packageUid= */ 2,
+ AppOpsManager.OPSTR_RECORD_AUDIO,
+ /* isRunning= */ true);
+ AppOpsManager.PackageOps packageOps3 = createPackageOp(
+ "package.three",
+ /* packageUid= */ 3,
+ AppOpsManager.OPSTR_PHONE_CALL_MICROPHONE,
+ /* isRunning= */ true);
+ when(mAppOpsManager.getPackagesForOps(AppOpsControllerImpl.OPS))
+ .thenReturn(List.of(packageOps1, packageOps2, packageOps3));
+
+ // WHEN we start listening
+ mController.addCallback(
+ new int[]{AppOpsManager.OP_RECORD_AUDIO, AppOpsManager.OP_FINE_LOCATION},
+ mCallback);
+ mTestableLooper.processAllMessages();
+
+ // THEN the callback is notified of the current active ops it cares about
+ verify(mCallback).onActiveStateChanged(
+ AppOpsManager.OP_FINE_LOCATION,
+ /* uid= */ 1,
+ "package.one",
+ true);
+ verify(mCallback).onActiveStateChanged(
+ AppOpsManager.OP_RECORD_AUDIO,
+ /* uid= */ 2,
+ "package.two",
+ true);
+ verify(mCallback, never()).onActiveStateChanged(
+ AppOpsManager.OP_PHONE_CALL_MICROPHONE,
+ /* uid= */ 3,
+ "package.three",
+ true);
+ }
+
+ @Test
public void addCallback_includedCode() {
mController.addCallback(
new int[]{AppOpsManager.OP_RECORD_AUDIO, AppOpsManager.OP_FINE_LOCATION},
@@ -772,6 +971,22 @@
assertFalse(list.get(1).isDisabled());
}
+ private AppOpsManager.PackageOps createPackageOp(
+ String packageName, int packageUid, String opStr, boolean isRunning) {
+ AppOpsManager.PackageOps packageOps = mock(AppOpsManager.PackageOps.class);
+ when(packageOps.getPackageName()).thenReturn(packageName);
+ when(packageOps.getUid()).thenReturn(packageUid);
+ AppOpsManager.OpEntry entry = mock(AppOpsManager.OpEntry.class);
+ when(entry.getOpStr()).thenReturn(opStr);
+ AppOpsManager.AttributedOpEntry attributed = mock(AppOpsManager.AttributedOpEntry.class);
+ when(attributed.isRunning()).thenReturn(isRunning);
+
+ when(packageOps.getOps()).thenReturn(Collections.singletonList(entry));
+ when(entry.getAttributedOpEntries()).thenReturn(Map.of("tag", attributed));
+
+ return packageOps;
+ }
+
private class TestHandler extends AppOpsControllerImpl.H {
TestHandler(Looper looper) {
mController.super(looper);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/back/domain/interactor/BackActionInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/back/domain/interactor/BackActionInteractorTest.kt
index 88c710a..ddb482f 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/back/domain/interactor/BackActionInteractorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/back/domain/interactor/BackActionInteractorTest.kt
@@ -16,18 +16,46 @@
package com.android.systemui.back.domain.interactor
+import android.view.ViewRootImpl
+import android.window.BackEvent
+import android.window.BackEvent.EDGE_LEFT
+import android.window.OnBackAnimationCallback
+import android.window.OnBackInvokedCallback
+import android.window.OnBackInvokedDispatcher
+import android.window.WindowOnBackInvokedDispatcher
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
+import com.android.internal.statusbar.IStatusBarService
import com.android.systemui.SysuiTestCase
+import com.android.systemui.flags.FakeFeatureFlags
+import com.android.systemui.flags.Flags
+import com.android.systemui.keyguard.data.repository.FakeKeyguardRepository
+import com.android.systemui.keyguard.shared.model.WakeSleepReason
+import com.android.systemui.keyguard.shared.model.WakefulnessModel
+import com.android.systemui.keyguard.shared.model.WakefulnessState
import com.android.systemui.plugins.statusbar.StatusBarStateController
+import com.android.systemui.scene.data.repository.WindowRootViewVisibilityRepository
+import com.android.systemui.scene.domain.interactor.WindowRootViewVisibilityInteractor
+import com.android.systemui.scene.ui.view.WindowRootView
import com.android.systemui.shade.QuickSettingsController
import com.android.systemui.shade.ShadeController
import com.android.systemui.shade.ShadeViewController
+import com.android.systemui.statusbar.NotificationShadeWindowController
import com.android.systemui.statusbar.StatusBarState
import com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager
+import com.android.systemui.statusbar.policy.HeadsUpManager
+import com.android.systemui.util.concurrency.FakeExecutor
+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.whenever
+import com.android.systemui.util.time.FakeSystemClock
+import com.google.common.truth.Truth.assertThat
import junit.framework.Assert.assertFalse
import junit.framework.Assert.assertTrue
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.test.TestScope
+import kotlinx.coroutines.test.runCurrent
import org.junit.Before
import org.junit.Rule
import org.junit.Test
@@ -41,7 +69,12 @@
@SmallTest
@RunWith(AndroidJUnit4::class)
+@OptIn(ExperimentalCoroutinesApi::class)
class BackActionInteractorTest : SysuiTestCase() {
+ private val testScope = TestScope()
+ private val featureFlags = FakeFeatureFlags()
+ private val executor = FakeExecutor(FakeSystemClock())
+
@JvmField @Rule var mockitoRule = MockitoJUnit.rule()
@Mock private lateinit var statusBarStateController: StatusBarStateController
@@ -49,18 +82,42 @@
@Mock private lateinit var shadeController: ShadeController
@Mock private lateinit var qsController: QuickSettingsController
@Mock private lateinit var shadeViewController: ShadeViewController
+ @Mock private lateinit var notificationShadeWindowController: NotificationShadeWindowController
+ @Mock private lateinit var windowRootView: WindowRootView
+ @Mock private lateinit var viewRootImpl: ViewRootImpl
+ @Mock private lateinit var onBackInvokedDispatcher: WindowOnBackInvokedDispatcher
+ @Mock private lateinit var iStatusBarService: IStatusBarService
+ @Mock private lateinit var headsUpManager: HeadsUpManager
- private lateinit var backActionInteractor: BackActionInteractor
+ private val keyguardRepository = FakeKeyguardRepository()
+ private val windowRootViewVisibilityInteractor: WindowRootViewVisibilityInteractor by lazy {
+ WindowRootViewVisibilityInteractor(
+ testScope.backgroundScope,
+ WindowRootViewVisibilityRepository(iStatusBarService, executor),
+ keyguardRepository,
+ headsUpManager,
+ )
+ }
- @Before
- fun setup() {
- backActionInteractor =
- BackActionInteractor(
+ private val backActionInteractor: BackActionInteractor by lazy {
+ BackActionInteractor(
+ testScope.backgroundScope,
statusBarStateController,
statusBarKeyguardViewManager,
shadeController,
+ notificationShadeWindowController,
+ windowRootViewVisibilityInteractor,
+ featureFlags,
)
- backActionInteractor.setup(qsController, shadeViewController)
+ .apply { this.setup(qsController, shadeViewController) }
+ }
+
+ @Before
+ fun setUp() {
+ featureFlags.set(Flags.WM_SHADE_ANIMATE_BACK_GESTURE, false)
+ whenever(notificationShadeWindowController.windowRootView).thenReturn(windowRootView)
+ whenever(windowRootView.viewRootImpl).thenReturn(viewRootImpl)
+ whenever(viewRootImpl.onBackInvokedDispatcher).thenReturn(onBackInvokedDispatcher)
}
@Test
@@ -117,4 +174,139 @@
verify(statusBarKeyguardViewManager, never()).onBackPressed()
verify(shadeViewController, never()).animateCollapseQs(anyBoolean())
}
+
+ @Test
+ fun shadeVisibleAndDeviceAwake_callbackRegistered() {
+ backActionInteractor.start()
+ windowRootViewVisibilityInteractor.setIsLockscreenOrShadeVisible(true)
+ setWakefulness(WakefulnessState.AWAKE)
+
+ testScope.runCurrent()
+
+ verify(onBackInvokedDispatcher)
+ .registerOnBackInvokedCallback(eq(OnBackInvokedDispatcher.PRIORITY_DEFAULT), any())
+ }
+
+ @Test
+ fun noWindowRootView_noCrashAttemptingCallbackRegistration() {
+ whenever(notificationShadeWindowController.windowRootView).thenReturn(null)
+
+ backActionInteractor.start()
+ windowRootViewVisibilityInteractor.setIsLockscreenOrShadeVisible(true)
+ setWakefulness(WakefulnessState.AWAKE)
+
+ testScope.runCurrent()
+ // No assert necessary, just testing no crash
+ }
+
+ @Test
+ fun shadeNotVisible_callbackUnregistered() {
+ backActionInteractor.start()
+ windowRootViewVisibilityInteractor.setIsLockscreenOrShadeVisible(true)
+ setWakefulness(WakefulnessState.AWAKE)
+ val callback = getBackInvokedCallback()
+
+ windowRootViewVisibilityInteractor.setIsLockscreenOrShadeVisible(false)
+ testScope.runCurrent()
+
+ verify(onBackInvokedDispatcher).unregisterOnBackInvokedCallback(callback)
+ }
+
+ @Test
+ fun deviceAsleep_callbackUnregistered() {
+ backActionInteractor.start()
+ windowRootViewVisibilityInteractor.setIsLockscreenOrShadeVisible(true)
+ setWakefulness(WakefulnessState.AWAKE)
+ val callback = getBackInvokedCallback()
+
+ setWakefulness(WakefulnessState.ASLEEP)
+ testScope.runCurrent()
+
+ verify(onBackInvokedDispatcher).unregisterOnBackInvokedCallback(callback)
+ }
+
+ @Test
+ fun animationFlagOff_onBackInvoked_keyguardNotified() {
+ backActionInteractor.start()
+ featureFlags.set(Flags.WM_SHADE_ANIMATE_BACK_GESTURE, false)
+ windowRootViewVisibilityInteractor.setIsLockscreenOrShadeVisible(true)
+ setWakefulness(WakefulnessState.AWAKE)
+ val callback = getBackInvokedCallback()
+ whenever(statusBarKeyguardViewManager.canHandleBackPressed()).thenReturn(true)
+
+ callback.onBackInvoked()
+
+ verify(statusBarKeyguardViewManager).onBackPressed()
+ }
+
+ @Test
+ fun animationFlagOn_onBackInvoked_keyguardNotified() {
+ featureFlags.set(Flags.WM_SHADE_ANIMATE_BACK_GESTURE, true)
+ backActionInteractor.start()
+ windowRootViewVisibilityInteractor.setIsLockscreenOrShadeVisible(true)
+ setWakefulness(WakefulnessState.AWAKE)
+ val callback = getBackInvokedCallback()
+ whenever(statusBarKeyguardViewManager.canHandleBackPressed()).thenReturn(true)
+
+ callback.onBackInvoked()
+
+ verify(statusBarKeyguardViewManager).onBackPressed()
+ }
+
+ @Test
+ fun animationFlagOn_callbackIsAnimationCallback() {
+ featureFlags.set(Flags.WM_SHADE_ANIMATE_BACK_GESTURE, true)
+ backActionInteractor.start()
+ windowRootViewVisibilityInteractor.setIsLockscreenOrShadeVisible(true)
+ setWakefulness(WakefulnessState.AWAKE)
+
+ val callback = getBackInvokedCallback()
+
+ assertThat(callback).isInstanceOf(OnBackAnimationCallback::class.java)
+ }
+
+ @Test
+ fun onBackProgressed_shadeCannotBeCollapsed_shadeViewControllerNotNotified() {
+ featureFlags.set(Flags.WM_SHADE_ANIMATE_BACK_GESTURE, true)
+ backActionInteractor.start()
+ windowRootViewVisibilityInteractor.setIsLockscreenOrShadeVisible(true)
+ setWakefulness(WakefulnessState.AWAKE)
+ val callback = getBackInvokedCallback() as OnBackAnimationCallback
+
+ whenever(shadeViewController.canBeCollapsed()).thenReturn(false)
+
+ callback.onBackProgressed(createBackEvent(0.3f))
+
+ verify(shadeViewController, never()).onBackProgressed(0.3f)
+ }
+
+ @Test
+ fun onBackProgressed_shadeCanBeCollapsed_shadeViewControllerNotified() {
+ featureFlags.set(Flags.WM_SHADE_ANIMATE_BACK_GESTURE, true)
+ backActionInteractor.start()
+ windowRootViewVisibilityInteractor.setIsLockscreenOrShadeVisible(true)
+ setWakefulness(WakefulnessState.AWAKE)
+ val callback = getBackInvokedCallback() as OnBackAnimationCallback
+
+ whenever(shadeViewController.canBeCollapsed()).thenReturn(true)
+
+ callback.onBackProgressed(createBackEvent(0.4f))
+
+ verify(shadeViewController).onBackProgressed(0.4f)
+ }
+
+ private fun getBackInvokedCallback(): OnBackInvokedCallback {
+ testScope.runCurrent()
+ val captor = argumentCaptor<OnBackInvokedCallback>()
+ verify(onBackInvokedDispatcher).registerOnBackInvokedCallback(any(), captor.capture())
+ return captor.value!!
+ }
+
+ private fun createBackEvent(progress: Float): BackEvent =
+ BackEvent(/* touchX= */ 0f, /* touchY= */ 0f, progress, /* swipeEdge= */ EDGE_LEFT)
+
+ private fun setWakefulness(state: WakefulnessState) {
+ val model = WakefulnessModel(state, WakeSleepReason.OTHER, WakeSleepReason.OTHER)
+ keyguardRepository.setWakefulnessModel(model)
+ }
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsControllerTest.java
index 7ab8e8b..e56b5c7 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsControllerTest.java
@@ -1616,4 +1616,43 @@
// THEN vibrate is used
verify(mVibrator).performHapticFeedback(any(), eq(UdfpsController.LONG_PRESS));
}
+
+ @Test
+ public void aodInterrupt_withNewTouchDetection() throws RemoteException {
+ mUdfpsController.cancelAodSendFingerUpAction();
+ final NormalizedTouchData touchData = new NormalizedTouchData(0, 0f, 0f, 0f, 0f, 0f, 0L,
+ 0L);
+ final TouchProcessorResult processorResultDown =
+ new TouchProcessorResult.ProcessedTouch(InteractionEvent.DOWN,
+ 1 /* pointerId */, touchData);
+
+ // Enable new touch detection.
+ when(mFeatureFlags.isEnabled(Flags.UDFPS_NEW_TOUCH_DETECTION)).thenReturn(true);
+
+ // Configure UdfpsController to use FingerprintManager as opposed to AlternateTouchProvider.
+ initUdfpsController(mOpticalProps, false /* hasAlternateTouchProvider */);
+
+ // GIVEN that the overlay is showing and screen is on and fp is running
+ mOverlayController.showUdfpsOverlay(TEST_REQUEST_ID, 0,
+ BiometricOverlayConstants.REASON_AUTH_KEYGUARD, mUdfpsOverlayControllerCallback);
+ mScreenObserver.onScreenTurnedOn();
+ mFgExecutor.runAllReady();
+
+ // WHEN fingerprint is requested because of AOD interrupt
+ mUdfpsController.onAodInterrupt(0, 0, 2f, 3f);
+
+ // Check case where touch driver sends touch to UdfpsView as well
+ verify(mUdfpsView).setOnTouchListener(mTouchListenerCaptor.capture());
+ when(mUdfpsView.isWithinSensorArea(anyFloat(), anyFloat())).thenReturn(true);
+ when(mSinglePointerTouchProcessor.processTouch(any(), anyInt(), any())).thenReturn(
+ processorResultDown);
+ MotionEvent downEvent = MotionEvent.obtain(0, 0, ACTION_DOWN, 0, 0, 0);
+ mTouchListenerCaptor.getValue().onTouch(mUdfpsView, downEvent);
+
+ mBiometricExecutor.runAllReady();
+
+ // THEN only one onPointerDown is sent
+ verify(mFingerprintManager).onPointerDown(anyLong(), anyInt(), anyInt(), anyFloat(),
+ anyFloat(), anyFloat(), anyFloat(), anyFloat(), anyLong(), anyLong(), anyBoolean());
+ }
}
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 47084c0..0ed46da 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
@@ -20,6 +20,7 @@
import android.hardware.face.FaceSensorPropertiesInternal
import android.hardware.fingerprint.FingerprintSensorPropertiesInternal
import android.view.HapticFeedbackConstants
+import android.view.MotionEvent
import androidx.test.filters.SmallTest
import com.android.internal.widget.LockPatternUtils
import com.android.systemui.SysuiTestCase
@@ -500,6 +501,81 @@
}
@Test
+ fun auto_confirm_authentication_when_finger_down() = runGenericTest {
+ val expectConfirmation = testCase.expectConfirmation(atLeastOneFailure = false)
+
+ // No icon button when face only, can't confirm before auth
+ if (!testCase.isFaceOnly) {
+ viewModel.onOverlayTouch(obtainMotionEvent(MotionEvent.ACTION_DOWN))
+ }
+ viewModel.showAuthenticated(testCase.authenticatedModality, 0)
+
+ val authenticating by collectLastValue(viewModel.isAuthenticating)
+ val authenticated by collectLastValue(viewModel.isAuthenticated)
+ val message by collectLastValue(viewModel.message)
+ val size by collectLastValue(viewModel.size)
+ val legacyState by collectLastValue(viewModel.legacyState)
+ val canTryAgain by collectLastValue(viewModel.canTryAgainNow)
+
+ assertThat(authenticating).isFalse()
+ assertThat(canTryAgain).isFalse()
+ assertThat(authenticated?.isAuthenticated).isTrue()
+
+ if (testCase.isFaceOnly && expectConfirmation) {
+ assertThat(legacyState).isEqualTo(AuthBiometricView.STATE_PENDING_CONFIRMATION)
+
+ assertThat(size).isEqualTo(PromptSize.MEDIUM)
+ assertButtonsVisible(
+ cancel = true,
+ confirm = true,
+ )
+
+ viewModel.confirmAuthenticated()
+ assertThat(message).isEqualTo(PromptMessage.Empty)
+ assertButtonsVisible()
+ } else {
+ assertThat(legacyState).isEqualTo(AuthBiometricView.STATE_AUTHENTICATED)
+ }
+ }
+
+ @Test
+ fun cannot_auto_confirm_authentication_when_finger_up() = runGenericTest {
+ val expectConfirmation = testCase.expectConfirmation(atLeastOneFailure = false)
+
+ // No icon button when face only, can't confirm before auth
+ if (!testCase.isFaceOnly) {
+ viewModel.onOverlayTouch(obtainMotionEvent(MotionEvent.ACTION_DOWN))
+ viewModel.onOverlayTouch(obtainMotionEvent(MotionEvent.ACTION_UP))
+ }
+ viewModel.showAuthenticated(testCase.authenticatedModality, 0)
+
+ val authenticating by collectLastValue(viewModel.isAuthenticating)
+ val authenticated by collectLastValue(viewModel.isAuthenticated)
+ val message by collectLastValue(viewModel.message)
+ val size by collectLastValue(viewModel.size)
+ val legacyState by collectLastValue(viewModel.legacyState)
+ val canTryAgain by collectLastValue(viewModel.canTryAgainNow)
+
+ assertThat(authenticated?.needsUserConfirmation).isEqualTo(expectConfirmation)
+ if (expectConfirmation) {
+ assertThat(size).isEqualTo(PromptSize.MEDIUM)
+ assertButtonsVisible(
+ cancel = true,
+ confirm = true,
+ )
+
+ viewModel.confirmAuthenticated()
+ assertThat(message).isEqualTo(PromptMessage.Empty)
+ assertButtonsVisible()
+ }
+
+ assertThat(authenticating).isFalse()
+ assertThat(authenticated?.isAuthenticated).isTrue()
+ assertThat(legacyState).isEqualTo(AuthBiometricView.STATE_AUTHENTICATED)
+ assertThat(canTryAgain).isFalse()
+ }
+
+ @Test
fun cannot_confirm_unless_authenticated() = runGenericTest {
val authenticating by collectLastValue(viewModel.isAuthenticating)
val authenticated by collectLastValue(viewModel.isAuthenticated)
@@ -679,6 +755,10 @@
testScope.runTest { block() }
}
+ /** Obtain a MotionEvent with the specified MotionEvent action constant */
+ private fun obtainMotionEvent(action: Int): MotionEvent =
+ MotionEvent.obtain(0, 0, action, 0f, 0f, 0)
+
companion object {
@JvmStatic
@Parameterized.Parameters(name = "{0}")
diff --git a/packages/SystemUI/tests/src/com/android/systemui/camera/CameraGestureHelperTest.kt b/packages/SystemUI/tests/src/com/android/systemui/camera/CameraGestureHelperTest.kt
index 937a7a9..037c1ba 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/camera/CameraGestureHelperTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/camera/CameraGestureHelperTest.kt
@@ -31,6 +31,7 @@
import com.android.systemui.settings.UserTracker
import com.android.systemui.statusbar.StatusBarState
import com.android.systemui.statusbar.phone.CentralSurfaces
+import com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager
import com.android.systemui.statusbar.policy.KeyguardStateController
import com.android.systemui.util.mockito.KotlinArgumentCaptor
import com.android.systemui.util.mockito.eq
@@ -55,6 +56,8 @@
@Mock
lateinit var centralSurfaces: CentralSurfaces
@Mock
+ lateinit var statusBarKeyguardViewManager: StatusBarKeyguardViewManager
+ @Mock
lateinit var keyguardStateController: KeyguardStateController
@Mock
lateinit var packageManager: PackageManager
@@ -91,6 +94,7 @@
context = mock(),
centralSurfaces = centralSurfaces,
keyguardStateController = keyguardStateController,
+ statusBarKeyguardViewManager = statusBarKeyguardViewManager,
packageManager = packageManager,
activityManager = activityManager,
activityStarter = activityStarter,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/communal/ui/view/layout/blueprints/DefaultCommunalBlueprintTest.kt b/packages/SystemUI/tests/src/com/android/systemui/communal/ui/view/layout/blueprints/DefaultCommunalBlueprintTest.kt
index e3a75f1..4ad9549 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/communal/ui/view/layout/blueprints/DefaultCommunalBlueprintTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/communal/ui/view/layout/blueprints/DefaultCommunalBlueprintTest.kt
@@ -2,6 +2,7 @@
import android.testing.AndroidTestingRunner
import android.testing.TestableLooper
+import androidx.constraintlayout.widget.ConstraintLayout
import androidx.constraintlayout.widget.ConstraintSet
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
@@ -28,9 +29,16 @@
}
@Test
- fun apply() {
+ fun addView() {
+ val constraintLayout = ConstraintLayout(context, null)
+ blueprint.addViews(constraintLayout)
+ verify(widgetSection).addViews(constraintLayout)
+ }
+
+ @Test
+ fun applyConstraints() {
val cs = ConstraintSet()
- blueprint.apply(cs)
- verify(widgetSection).apply(cs)
+ blueprint.applyConstraints(cs)
+ verify(widgetSection).applyConstraints(cs)
}
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/controls/dagger/ControlsComponentTest.kt b/packages/SystemUI/tests/src/com/android/systemui/controls/dagger/ControlsComponentTest.kt
index 0b27bc9..54f66dc 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/controls/dagger/ControlsComponentTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/controls/dagger/ControlsComponentTest.kt
@@ -29,7 +29,6 @@
import com.android.systemui.controls.ui.ControlsUiController
import com.android.systemui.settings.UserTracker
import com.android.systemui.statusbar.policy.KeyguardStateController
-import dagger.Lazy
import java.util.Optional
import org.junit.Assert.assertEquals
import org.junit.Assert.assertFalse
@@ -173,10 +172,9 @@
private fun setupComponent(enabled: Boolean): ControlsComponent {
return ControlsComponent(
enabled,
- mContext,
- Lazy { controller },
- Lazy { uiController },
- Lazy { listingController },
+ { controller },
+ { uiController },
+ { listingController },
lockPatternUtils,
keyguardStateController,
userTracker,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/controls/management/ControlsListingControllerImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/controls/management/ControlsListingControllerImplTest.kt
index b1061ba..74d0d21 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/controls/management/ControlsListingControllerImplTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/controls/management/ControlsListingControllerImplTest.kt
@@ -36,7 +36,6 @@
import com.android.systemui.controls.ControlsServiceInfo
import com.android.systemui.dump.DumpManager
import com.android.systemui.flags.FeatureFlags
-import com.android.systemui.flags.Flags.APP_PANELS_ALL_APPS_ALLOWED
import com.android.systemui.settings.UserTracker
import com.android.systemui.util.ActivityTaskManagerProxy
import com.android.systemui.util.concurrency.FakeExecutor
@@ -123,9 +122,6 @@
arrayOf(componentName.packageName)
)
- // Return false by default, we'll test the true path
- `when`(featureFlags.isEnabled(APP_PANELS_ALL_APPS_ALLOWED)).thenReturn(false)
-
val wrapper = object : ContextWrapper(mContext) {
override fun createContextAsUser(user: UserHandle, flags: Int): Context {
return baseContext
@@ -469,38 +465,7 @@
}
@Test
- fun testPackageNotPreferred_nullPanel() {
- mContext.orCreateTestableResources
- .addOverride(R.array.config_controlsPreferredPackages, arrayOf<String>())
-
- val serviceInfo = ServiceInfo(
- componentName,
- activityName
- )
-
- `when`(packageManager.getComponentEnabledSetting(eq(activityName)))
- .thenReturn(PackageManager.COMPONENT_ENABLED_STATE_ENABLED)
-
- setUpQueryResult(listOf(
- ActivityInfo(
- activityName,
- exported = true,
- permission = Manifest.permission.BIND_CONTROLS
- )
- ))
-
- val list = listOf(serviceInfo)
- serviceListingCallbackCaptor.value.onServicesReloaded(list)
-
- executor.runAllReady()
-
- assertNull(controller.getCurrentServices()[0].panelActivity)
- }
-
- @Test
- fun testPackageNotPreferred_allowAllApps_correctPanel() {
- `when`(featureFlags.isEnabled(APP_PANELS_ALL_APPS_ALLOWED)).thenReturn(true)
-
+ fun testPackageNotPreferred_correctPanel() {
mContext.orCreateTestableResources
.addOverride(R.array.config_controlsPreferredPackages, arrayOf<String>())
diff --git a/packages/SystemUI/tests/src/com/android/systemui/display/data/repository/DisplayRepositoryTest.kt b/packages/SystemUI/tests/src/com/android/systemui/display/data/repository/DisplayRepositoryTest.kt
index 9be54fb..db7c003 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/display/data/repository/DisplayRepositoryTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/display/data/repository/DisplayRepositoryTest.kt
@@ -165,13 +165,78 @@
assertThat(value?.ids()).containsExactly(1, 2, 3, 4)
}
+ @Test
+ fun onDisplayConnected_pendingDisplayReceived() =
+ testScope.runTest {
+ val pendingDisplay by latestPendingDisplayFlowValue()
+
+ displayListener.value.onDisplayConnected(1)
+
+ assertThat(pendingDisplay).isEqualTo(1)
+ }
+
+ @Test
+ fun onDisplayDisconnected_pendingDisplayNull() =
+ testScope.runTest {
+ val pendingDisplay by latestPendingDisplayFlowValue()
+ displayListener.value.onDisplayConnected(1)
+
+ assertThat(pendingDisplay).isNotNull()
+
+ displayListener.value.onDisplayDisconnected(1)
+
+ assertThat(pendingDisplay).isNull()
+ }
+
+ @Test
+ fun onDisplayDisconnected_unknownDisplay_doesNotSendNull() =
+ testScope.runTest {
+ val pendingDisplay by latestPendingDisplayFlowValue()
+ displayListener.value.onDisplayConnected(1)
+
+ assertThat(pendingDisplay).isNotNull()
+
+ displayListener.value.onDisplayDisconnected(2)
+
+ assertThat(pendingDisplay).isNotNull()
+ }
+
+ @Test
+ fun onDisplayConnected_multipleTimes_sendsOnlyTheLastOne() =
+ testScope.runTest {
+ val pendingDisplay by latestPendingDisplayFlowValue()
+ displayListener.value.onDisplayConnected(1)
+ displayListener.value.onDisplayConnected(2)
+
+ assertThat(pendingDisplay).isEqualTo(2)
+ }
+
private fun Iterable<Display>.ids(): List<Int> = map { it.displayId }
// Wrapper to capture the displayListener.
private fun TestScope.latestDisplayFlowValue(): FlowValue<Set<Display>?> {
val flowValue = collectLastValue(displayRepository.displays)
verify(displayManager)
- .registerDisplayListener(displayListener.capture(), eq(testHandler), anyLong())
+ .registerDisplayListener(
+ displayListener.capture(),
+ eq(testHandler),
+ eq(
+ DisplayManager.EVENT_FLAG_DISPLAY_ADDED or
+ DisplayManager.EVENT_FLAG_DISPLAY_CHANGED or
+ DisplayManager.EVENT_FLAG_DISPLAY_REMOVED
+ )
+ )
+ return flowValue
+ }
+
+ private fun TestScope.latestPendingDisplayFlowValue(): FlowValue<Int?> {
+ val flowValue = collectLastValue(displayRepository.pendingDisplayId)
+ verify(displayManager)
+ .registerDisplayListener(
+ displayListener.capture(),
+ eq(testHandler),
+ eq(DisplayManager.EVENT_FLAG_DISPLAY_CONNECTION_CHANGED)
+ )
return flowValue
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/display/domain/interactor/ConnectedDisplayInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/display/domain/interactor/ConnectedDisplayInteractorTest.kt
index eb0ad69..50617a1 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/display/domain/interactor/ConnectedDisplayInteractorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/display/domain/interactor/ConnectedDisplayInteractorTest.kt
@@ -16,6 +16,7 @@
package com.android.systemui.display.domain.interactor
+import android.hardware.display.DisplayManager
import android.testing.AndroidTestingRunner
import android.testing.TestableLooper
import android.view.Display
@@ -27,14 +28,20 @@
import com.android.systemui.coroutines.collectLastValue
import com.android.systemui.display.data.repository.FakeDisplayRepository
import com.android.systemui.display.data.repository.display
+import com.android.systemui.display.domain.interactor.ConnectedDisplayInteractor.PendingDisplay
import com.android.systemui.display.domain.interactor.ConnectedDisplayInteractor.State
+import com.android.systemui.keyguard.data.repository.FakeKeyguardRepository
+import com.android.systemui.util.mockito.eq
+import com.android.systemui.util.mockito.mock
import com.google.common.truth.Truth.assertThat
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.test.TestScope
import kotlinx.coroutines.test.UnconfinedTestDispatcher
import kotlinx.coroutines.test.runTest
+import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
+import org.mockito.Mockito
@RunWith(AndroidTestingRunner::class)
@TestableLooper.RunWithLooper
@@ -42,11 +49,22 @@
@SmallTest
class ConnectedDisplayInteractorTest : SysuiTestCase() {
+ private val displayManager = mock<DisplayManager>()
private val fakeDisplayRepository = FakeDisplayRepository()
+ private val fakeKeyguardRepository = FakeKeyguardRepository()
private val connectedDisplayStateProvider: ConnectedDisplayInteractor =
- ConnectedDisplayInteractorImpl(fakeDisplayRepository)
+ ConnectedDisplayInteractorImpl(
+ displayManager,
+ fakeKeyguardRepository,
+ fakeDisplayRepository
+ )
private val testScope = TestScope(UnconfinedTestDispatcher())
+ @Before
+ fun setup() {
+ fakeKeyguardRepository.setKeyguardUnlocked(true)
+ }
+
@Test
fun displayState_nullDisplays_disconnected() =
testScope.runTest {
@@ -126,6 +144,70 @@
assertThat(value).isEqualTo(State.CONNECTED_SECURE)
}
+ @Test
+ fun pendingDisplay_propagated() =
+ testScope.runTest {
+ val value by lastPendingDisplay()
+ val pendingDisplayId = 4
+
+ fakeDisplayRepository.emit(pendingDisplayId)
+
+ assertThat(value).isNotNull()
+ }
+
+ @Test
+ fun onPendingDisplay_enable_displayEnabled() =
+ testScope.runTest {
+ val pendingDisplay by lastPendingDisplay()
+
+ fakeDisplayRepository.emit(1)
+ pendingDisplay!!.enable()
+
+ Mockito.verify(displayManager).enableConnectedDisplay(eq(1))
+ }
+
+ @Test
+ fun onPendingDisplay_disable_displayDisabled() =
+ testScope.runTest {
+ val pendingDisplay by lastPendingDisplay()
+
+ fakeDisplayRepository.emit(1)
+ pendingDisplay!!.disable()
+
+ Mockito.verify(displayManager).disableConnectedDisplay(eq(1))
+ }
+
+ @Test
+ fun onPendingDisplay_keyguardUnlocked_returnsPendingDisplay() =
+ testScope.runTest {
+ fakeKeyguardRepository.setKeyguardUnlocked(false)
+ val pendingDisplay by lastPendingDisplay()
+
+ fakeDisplayRepository.emit(1)
+ assertThat(pendingDisplay).isNull()
+
+ fakeKeyguardRepository.setKeyguardUnlocked(true)
+
+ assertThat(pendingDisplay).isNotNull()
+ }
+
+ @Test
+ fun onPendingDisplay_keyguardLocked_returnsNull() =
+ testScope.runTest {
+ fakeKeyguardRepository.setKeyguardUnlocked(true)
+ val pendingDisplay by lastPendingDisplay()
+
+ fakeDisplayRepository.emit(1)
+ assertThat(pendingDisplay).isNotNull()
+
+ fakeKeyguardRepository.setKeyguardUnlocked(false)
+
+ assertThat(pendingDisplay).isNull()
+ }
+
private fun TestScope.lastValue(): FlowValue<State?> =
collectLastValue(connectedDisplayStateProvider.connectedDisplayState)
+
+ private fun TestScope.lastPendingDisplay(): FlowValue<PendingDisplay?> =
+ collectLastValue(connectedDisplayStateProvider.pendingDisplay)
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/display/ui/view/MirroringConfirmationDialogTest.kt b/packages/SystemUI/tests/src/com/android/systemui/display/ui/view/MirroringConfirmationDialogTest.kt
new file mode 100644
index 0000000..7059647
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/display/ui/view/MirroringConfirmationDialogTest.kt
@@ -0,0 +1,77 @@
+/*
+ * 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.display.ui.view
+
+import android.testing.AndroidTestingRunner
+import android.testing.TestableLooper
+import android.view.View
+import androidx.test.filters.SmallTest
+import com.android.systemui.R
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.util.mockito.any
+import com.android.systemui.util.mockito.mock
+import org.junit.After
+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.MockitoAnnotations
+
+@SmallTest
+@RunWith(AndroidTestingRunner::class)
+@TestableLooper.RunWithLooper(setAsMainLooper = true)
+class MirroringConfirmationDialogTest : SysuiTestCase() {
+
+ private lateinit var dialog: MirroringConfirmationDialog
+
+ private val onStartMirroringCallback = mock<View.OnClickListener>()
+ private val onCancelCallback = mock<View.OnClickListener>()
+ @Before
+ fun setUp() {
+ MockitoAnnotations.initMocks(this)
+
+ dialog = MirroringConfirmationDialog(context, onStartMirroringCallback, onCancelCallback)
+ }
+
+ @Test
+ fun startMirroringButton_clicked_callsCorrectCallback() {
+ dialog.show()
+
+ dialog.requireViewById<View>(R.id.enable_display).callOnClick()
+
+ verify(onStartMirroringCallback).onClick(any())
+ verify(onCancelCallback, never()).onClick(any())
+ }
+
+ @Test
+ fun cancelButton_clicked_callsCorrectCallback() {
+ dialog.show()
+
+ dialog.requireViewById<View>(R.id.cancel).callOnClick()
+
+ verify(onCancelCallback).onClick(any())
+ verify(onStartMirroringCallback, never()).onClick(any())
+ }
+
+ @After
+ fun teardown() {
+ if (::dialog.isInitialized) {
+ dialog.dismiss()
+ }
+ }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/graphics/ImageLoaderTest.kt b/packages/SystemUI/tests/src/com/android/systemui/graphics/ImageLoaderTest.kt
index ccd631e..8f66344 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/graphics/ImageLoaderTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/graphics/ImageLoaderTest.kt
@@ -9,6 +9,7 @@
import android.graphics.drawable.Icon
import android.graphics.drawable.VectorDrawable
import android.net.Uri
+import android.util.Size
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.systemui.R
@@ -78,12 +79,19 @@
}
@Test
- fun invalidIcon_returnsNull() =
+ fun invalidIcon_loadDrawable_returnsNull() =
testScope.runTest {
assertThat(imageLoader.loadDrawable(Icon.createWithFilePath("this is broken"))).isNull()
}
@Test
+ fun invalidIcon_loadSize_returnsNull() =
+ testScope.runTest {
+ assertThat(imageLoader.loadSize(Icon.createWithFilePath("this is broken"), context))
+ .isNull()
+ }
+
+ @Test
fun invalidIS_returnsNull() =
testScope.runTest {
assertThat(
@@ -172,6 +180,17 @@
}
@Test
+ fun validBitmapIcon_loadSize_returnsNull() =
+ testScope.runTest {
+ val bitmap =
+ BitmapFactory.decodeResource(
+ context.resources,
+ R.drawable.dessert_zombiegingerbread
+ )
+ assertThat(imageLoader.loadSize(Icon.createWithBitmap(bitmap), context)).isNull()
+ }
+
+ @Test
fun validUriIcon_returnsBitmapDrawable() =
testScope.runTest {
val bitmap =
@@ -186,6 +205,17 @@
}
@Test
+ fun validUriIcon_returnsSize() =
+ testScope.runTest {
+ val drawable = context.resources.getDrawable(R.drawable.dessert_zombiegingerbread)
+ val uri =
+ "android.resource://${context.packageName}/${R.drawable.dessert_zombiegingerbread}"
+ val loadedSize =
+ imageLoader.loadSize(Icon.createWithContentUri(Uri.parse(uri)), context)
+ assertSizeEqualToDrawableSize(loadedSize, drawable)
+ }
+
+ @Test
fun validDataIcon_returnsBitmapDrawable() =
testScope.runTest {
val bitmap =
@@ -205,6 +235,54 @@
}
@Test
+ fun validDataIcon_loadSize_returnsNull() =
+ testScope.runTest {
+ val bitmap =
+ BitmapFactory.decodeResource(
+ context.resources,
+ R.drawable.dessert_zombiegingerbread
+ )
+ val bos =
+ ByteArrayOutputStream(
+ bitmap.byteCount * 2
+ ) // Compressed bitmap should be smaller than its source.
+ bitmap.compress(Bitmap.CompressFormat.PNG, 100, bos)
+
+ val array = bos.toByteArray()
+ assertThat(imageLoader.loadSize(Icon.createWithData(array, 0, array.size), context))
+ .isNull()
+ }
+
+ @Test
+ fun validResourceIcon_returnsBitmapDrawable() =
+ testScope.runTest {
+ val bitmap = context.resources.getDrawable(R.drawable.dessert_zombiegingerbread)
+ val loadedDrawable =
+ imageLoader.loadDrawable(
+ Icon.createWithResource(
+ "com.android.systemui.tests",
+ R.drawable.dessert_zombiegingerbread
+ )
+ )
+ assertBitmapEqualToDrawable(loadedDrawable, (bitmap as BitmapDrawable).bitmap)
+ }
+
+ @Test
+ fun validResourceIcon_loadSize_returnsNull() =
+ testScope.runTest {
+ assertThat(
+ imageLoader.loadSize(
+ Icon.createWithResource(
+ "com.android.systemui.tests",
+ R.drawable.dessert_zombiegingerbread
+ ),
+ context
+ )
+ )
+ .isNull()
+ }
+
+ @Test
fun validSystemResourceIcon_returnsBitmapDrawable() =
testScope.runTest {
val bitmap =
@@ -217,6 +295,18 @@
}
@Test
+ fun validSystemResourceIcon_loadSize_returnsNull() =
+ testScope.runTest {
+ assertThat(
+ imageLoader.loadSize(
+ Icon.createWithResource("android", android.R.drawable.ic_dialog_alert),
+ context
+ )
+ )
+ .isNull()
+ }
+
+ @Test
fun invalidDifferentPackageResourceIcon_returnsNull() =
testScope.runTest {
val loadedDrawable =
@@ -230,6 +320,20 @@
}
@Test
+ fun invalidDifferentPackageResourceIcon_loadSize_returnsNull() =
+ testScope.runTest {
+ assertThat(
+ imageLoader.loadDrawable(
+ Icon.createWithResource(
+ "noooope.wrong.package",
+ R.drawable.dessert_zombiegingerbread
+ )
+ )
+ )
+ .isNull()
+ }
+
+ @Test
fun validBitmapResource_widthMoreRestricted_downsizesKeepingAspectRatio() =
testScope.runTest {
val loadedDrawable =
@@ -343,4 +447,10 @@
assertThat(actual?.width).isEqualTo(expected.width)
assertThat(actual?.height).isEqualTo(expected.height)
}
+
+ private fun assertSizeEqualToDrawableSize(actual: Size?, expected: Drawable) {
+ assertThat(actual).isNotNull()
+ assertThat(actual?.width).isEqualTo(expected.intrinsicWidth)
+ assertThat(actual?.height).isEqualTo(expected.intrinsicHeight)
+ }
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/haptics/slider/SeekableSliderEventProducerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/haptics/slider/SeekableSliderEventProducerTest.kt
new file mode 100644
index 0000000..71a56cd
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/haptics/slider/SeekableSliderEventProducerTest.kt
@@ -0,0 +1,126 @@
+/*
+ * 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.haptics.slider
+
+import android.widget.SeekBar
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.SmallTest
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.coroutines.collectLastValue
+import junit.framework.Assert.assertEquals
+import kotlinx.coroutines.test.runTest
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@SmallTest
+@RunWith(AndroidJUnit4::class)
+class SeekableSliderEventProducerTest : SysuiTestCase() {
+
+ private val seekBar = SeekBar(mContext)
+ private val eventProducer = SeekableSliderEventProducer()
+ private val eventFlow = eventProducer.produceEvents()
+
+ @Test
+ fun onStartTrackingTouch_noProgress_trackingTouchEventProduced() = runTest {
+ val latest by collectLastValue(eventFlow)
+
+ eventProducer.onStartTrackingTouch(seekBar)
+
+ assertEquals(SliderEvent(SliderEventType.STARTED_TRACKING_TOUCH, 0F), latest)
+ }
+
+ @Test
+ fun onStopTrackingTouch_noProgress_StoppedTrackingTouchEventProduced() = runTest {
+ val latest by collectLastValue(eventFlow)
+
+ eventProducer.onStopTrackingTouch(seekBar)
+
+ assertEquals(SliderEvent(SliderEventType.STOPPED_TRACKING_TOUCH, 0F), latest)
+ }
+
+ @Test
+ fun onProgressChangeByUser_changeByUserEventProduced_withNormalizedProgress() = runTest {
+ val progress = 50
+ val latest by collectLastValue(eventFlow)
+
+ eventProducer.onProgressChanged(seekBar, progress, true)
+
+ assertEquals(SliderEvent(SliderEventType.PROGRESS_CHANGE_BY_USER, 0.5F), latest)
+ }
+
+ @Test
+ fun onProgressChangeByUser_zeroWidthSlider_changeByUserEventProduced_withMaxProgress() =
+ runTest {
+ // No-width slider where the min and max values are the same
+ seekBar.min = 100
+ seekBar.max = 100
+ val progress = 50
+ val latest by collectLastValue(eventFlow)
+
+ eventProducer.onProgressChanged(seekBar, progress, true)
+
+ assertEquals(SliderEvent(SliderEventType.PROGRESS_CHANGE_BY_USER, 1.0F), latest)
+ }
+
+ @Test
+ fun onProgressChangeByProgram_changeByProgramEventProduced_withNormalizedProgress() = runTest {
+ val progress = 50
+ val latest by collectLastValue(eventFlow)
+
+ eventProducer.onProgressChanged(seekBar, progress, false)
+
+ assertEquals(SliderEvent(SliderEventType.PROGRESS_CHANGE_BY_PROGRAM, 0.5F), latest)
+ }
+
+ @Test
+ fun onProgressChangeByProgram_zeroWidthSlider_changeByProgramEventProduced_withMaxProgress() =
+ runTest {
+ // No-width slider where the min and max values are the same
+ seekBar.min = 100
+ seekBar.max = 100
+ val progress = 50
+ val latest by collectLastValue(eventFlow)
+
+ eventProducer.onProgressChanged(seekBar, progress, false)
+
+ assertEquals(SliderEvent(SliderEventType.PROGRESS_CHANGE_BY_PROGRAM, 1.0F), latest)
+ }
+
+ @Test
+ fun onStartTrackingTouch_afterProgress_trackingTouchEventProduced_withNormalizedProgress() =
+ runTest {
+ val progress = 50
+ val latest by collectLastValue(eventFlow)
+
+ eventProducer.onProgressChanged(seekBar, progress, true)
+ eventProducer.onStartTrackingTouch(seekBar)
+
+ assertEquals(SliderEvent(SliderEventType.STARTED_TRACKING_TOUCH, 0.5F), latest)
+ }
+
+ @Test
+ fun onStopTrackingTouch_afterProgress_stopTrackingTouchEventProduced_withNormalizedProgress() =
+ runTest {
+ val progress = 50
+ val latest by collectLastValue(eventFlow)
+
+ eventProducer.onProgressChanged(seekBar, progress, true)
+ eventProducer.onStopTrackingTouch(seekBar)
+
+ assertEquals(SliderEvent(SliderEventType.STOPPED_TRACKING_TOUCH, 0.5F), latest)
+ }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/CustomizationProviderTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/CustomizationProviderTest.kt
index e73d580..36822e6 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/CustomizationProviderTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/CustomizationProviderTest.kt
@@ -34,8 +34,6 @@
import com.android.systemui.SystemUIAppComponentFactoryBase
import com.android.systemui.SysuiTestCase
import com.android.systemui.animation.DialogLaunchAnimator
-import com.android.systemui.bouncer.data.repository.FakeKeyguardBouncerRepository
-import com.android.systemui.common.ui.data.repository.FakeConfigurationRepository
import com.android.systemui.dock.DockManagerFake
import com.android.systemui.flags.FakeFeatureFlags
import com.android.systemui.flags.Flags
@@ -45,9 +43,8 @@
import com.android.systemui.keyguard.data.quickaffordance.KeyguardQuickAffordanceLocalUserSelectionManager
import com.android.systemui.keyguard.data.quickaffordance.KeyguardQuickAffordanceRemoteUserSelectionManager
import com.android.systemui.keyguard.data.repository.FakeBiometricSettingsRepository
-import com.android.systemui.keyguard.data.repository.FakeKeyguardRepository
import com.android.systemui.keyguard.data.repository.KeyguardQuickAffordanceRepository
-import com.android.systemui.keyguard.domain.interactor.KeyguardInteractor
+import com.android.systemui.keyguard.domain.interactor.KeyguardInteractorFactory
import com.android.systemui.keyguard.domain.interactor.KeyguardQuickAffordanceInteractor
import com.android.systemui.keyguard.shared.quickaffordance.KeyguardQuickAffordancesMetricsLogger
import com.android.systemui.keyguard.ui.preview.KeyguardPreviewRenderer
@@ -58,7 +55,6 @@
import com.android.systemui.settings.UserTracker
import com.android.systemui.shared.customization.data.content.CustomizationProviderContract as Contract
import com.android.systemui.shared.keyguard.shared.model.KeyguardQuickAffordanceSlots
-import com.android.systemui.statusbar.CommandQueue
import com.android.systemui.statusbar.policy.KeyguardStateController
import com.android.systemui.util.FakeSharedPreferences
import com.android.systemui.util.mockito.any
@@ -95,7 +91,6 @@
@Mock private lateinit var backgroundHandler: Handler
@Mock private lateinit var previewSurfacePackage: SurfaceControlViewHost.SurfacePackage
@Mock private lateinit var launchAnimator: DialogLaunchAnimator
- @Mock private lateinit var commandQueue: CommandQueue
@Mock private lateinit var devicePolicyManager: DevicePolicyManager
@Mock private lateinit var logger: KeyguardQuickAffordancesMetricsLogger
@@ -183,13 +178,10 @@
underTest.interactor =
KeyguardQuickAffordanceInteractor(
keyguardInteractor =
- KeyguardInteractor(
- repository = FakeKeyguardRepository(),
- commandQueue = commandQueue,
- featureFlags = featureFlags,
- bouncerRepository = FakeKeyguardBouncerRepository(),
- configurationRepository = FakeConfigurationRepository(),
- ),
+ KeyguardInteractorFactory.create(
+ featureFlags = featureFlags,
+ )
+ .keyguardInteractor,
lockPatternUtils = lockPatternUtils,
keyguardStateController = keyguardStateController,
userTracker = userTracker,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/KeyguardViewMediatorTest.java b/packages/SystemUI/tests/src/com/android/systemui/keyguard/KeyguardViewMediatorTest.java
index daafba2..f78d051 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/KeyguardViewMediatorTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/KeyguardViewMediatorTest.java
@@ -221,6 +221,7 @@
mSystemClock = new FakeSystemClock();
when(mLockPatternUtils.getDevicePolicyManager()).thenReturn(mDevicePolicyManager);
when(mPowerManager.newWakeLock(anyInt(), any())).thenReturn(mock(WakeLock.class));
+ when(mPowerManager.isInteractive()).thenReturn(true);
when(mInteractionJankMonitor.begin(any(), anyInt())).thenReturn(true);
when(mInteractionJankMonitor.end(anyInt())).thenReturn(true);
mContext.addMockSystemService(Context.ALARM_SERVICE, mAlarmManager);
@@ -241,6 +242,7 @@
mConfigurationController,
mViewMediator,
mKeyguardBypassController,
+ mUiBgExecutor,
mColorExtractor,
mDumpManager,
mKeyguardStateController,
@@ -868,8 +870,6 @@
when(mKeyguardStateController.isShowing()).thenReturn(true);
TestableLooper.get(this).processAllMessages();
- when(mPowerManager.isInteractive()).thenReturn(true);
-
mViewMediator.onSystemReady();
TestableLooper.get(this).processAllMessages();
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/KeyguardRepositoryImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/KeyguardRepositoryImplTest.kt
index 5ead16b..2691860 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/KeyguardRepositoryImplTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/KeyguardRepositoryImplTest.kt
@@ -46,6 +46,7 @@
import com.android.systemui.statusbar.phone.DozeParameters
import com.android.systemui.statusbar.phone.KeyguardBypassController
import com.android.systemui.statusbar.policy.KeyguardStateController
+import com.android.systemui.util.mockito.any
import com.android.systemui.util.mockito.argumentCaptor
import com.android.systemui.util.mockito.whenever
import com.android.systemui.util.mockito.withArgCaptor
@@ -322,20 +323,20 @@
val captor = argumentCaptor<StatusBarStateController.StateListener>()
runCurrent()
- verify(statusBarStateController).addCallback(captor.capture())
+ verify(statusBarStateController, atLeastOnce()).addCallback(captor.capture())
- captor.value.onDozeAmountChanged(0.433f, 0.4f)
+ captor.allValues.forEach { it.onDozeAmountChanged(0.433f, 0.4f) }
runCurrent()
- captor.value.onDozeAmountChanged(0.498f, 0.5f)
+ captor.allValues.forEach { it.onDozeAmountChanged(0.498f, 0.5f) }
runCurrent()
- captor.value.onDozeAmountChanged(0.661f, 0.65f)
+ captor.allValues.forEach { it.onDozeAmountChanged(0.661f, 0.65f) }
runCurrent()
assertThat(values).isEqualTo(listOf(0f, 0.433f, 0.498f, 0.661f))
job.cancel()
runCurrent()
- verify(statusBarStateController).removeCallback(captor.value)
+ verify(statusBarStateController).removeCallback(any())
}
@Test
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardInteractorTest.kt
index 4b09468..972af4a 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardInteractorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardInteractorTest.kt
@@ -30,6 +30,7 @@
import com.android.systemui.keyguard.data.repository.FakeCommandQueue
import com.android.systemui.keyguard.data.repository.FakeKeyguardRepository
import com.android.systemui.keyguard.shared.model.CameraLaunchSourceModel
+import com.android.systemui.shade.data.repository.FakeShadeRepository
import com.google.common.truth.Truth.assertThat
import kotlinx.coroutines.flow.onCompletion
import kotlinx.coroutines.test.TestScope
@@ -52,6 +53,7 @@
private lateinit var repository: FakeKeyguardRepository
private lateinit var bouncerRepository: FakeKeyguardBouncerRepository
private lateinit var configurationRepository: FakeConfigurationRepository
+ private lateinit var shadeRepository: FakeShadeRepository
@Before
fun setUp() {
@@ -62,6 +64,7 @@
repository = FakeKeyguardRepository()
bouncerRepository = FakeKeyguardBouncerRepository()
configurationRepository = FakeConfigurationRepository()
+ shadeRepository = FakeShadeRepository()
underTest =
KeyguardInteractor(
repository,
@@ -69,6 +72,7 @@
featureFlags,
bouncerRepository,
configurationRepository,
+ shadeRepository,
)
}
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 ca93246..d457605 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
@@ -1049,7 +1049,6 @@
@Test
fun occludedToAlternateBouncer() =
testScope.runTest {
-
// GIVEN a prior transition has run to OCCLUDED
runTransition(KeyguardState.LOCKSCREEN, KeyguardState.OCCLUDED)
keyguardRepository.setKeyguardOccluded(true)
@@ -1073,6 +1072,31 @@
}
@Test
+ fun occludedToPrimaryBouncer() =
+ testScope.runTest {
+ // GIVEN a prior transition has run to OCCLUDED
+ runTransition(KeyguardState.LOCKSCREEN, KeyguardState.OCCLUDED)
+ keyguardRepository.setKeyguardOccluded(true)
+ runCurrent()
+
+ // WHEN primary bouncer shows
+ bouncerRepository.setPrimaryShow(true) // beverlyt
+ runCurrent()
+
+ val info =
+ withArgCaptor<TransitionInfo> {
+ verify(transitionRepository).startTransition(capture(), anyBoolean())
+ }
+ // THEN a transition to AlternateBouncer should occur
+ assertThat(info.ownerName).isEqualTo("FromOccludedTransitionInteractor")
+ assertThat(info.from).isEqualTo(KeyguardState.OCCLUDED)
+ assertThat(info.to).isEqualTo(KeyguardState.PRIMARY_BOUNCER)
+ assertThat(info.animator).isNotNull()
+
+ coroutineContext.cancelChildren()
+ }
+
+ @Test
fun primaryBouncerToOccluded() =
testScope.runTest {
// GIVEN device not sleeping
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/OccludingAppDeviceEntryInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/OccludingAppDeviceEntryInteractorTest.kt
index 1dcb55d..f24ea6c 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/OccludingAppDeviceEntryInteractorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/OccludingAppDeviceEntryInteractorTest.kt
@@ -116,13 +116,13 @@
keyguardUpdateMonitor,
),
fingerprintAuthRepository,
- KeyguardInteractor(
- keyguardRepository,
- commandQueue = mock(),
- featureFlags,
- bouncerRepository,
- configurationRepository,
- ),
+ KeyguardInteractorFactory.create(
+ featureFlags = featureFlags,
+ repository = keyguardRepository,
+ bouncerRepository = bouncerRepository,
+ configurationRepository = configurationRepository,
+ )
+ .keyguardInteractor,
PrimaryBouncerInteractor(
bouncerRepository,
primaryBouncerView = mock(),
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/view/layout/blueprints/DefaultKeyguardBlueprintTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/view/layout/blueprints/DefaultKeyguardBlueprintTest.kt
index addb181..3b4eab2 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/view/layout/blueprints/DefaultKeyguardBlueprintTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/view/layout/blueprints/DefaultKeyguardBlueprintTest.kt
@@ -19,13 +19,17 @@
import android.testing.AndroidTestingRunner
import android.testing.TestableLooper.RunWithLooper
+import androidx.constraintlayout.widget.ConstraintLayout
import androidx.constraintlayout.widget.ConstraintSet
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
+import com.android.systemui.flags.FakeFeatureFlags
+import com.android.systemui.flags.Flags
import com.android.systemui.keyguard.ui.view.KeyguardRootView
import com.android.systemui.keyguard.ui.view.layout.sections.DefaultAmbientIndicationAreaSection
import com.android.systemui.keyguard.ui.view.layout.sections.DefaultIndicationAreaSection
import com.android.systemui.keyguard.ui.view.layout.sections.DefaultLockIconSection
+import com.android.systemui.keyguard.ui.view.layout.sections.DefaultNotificationStackScrollLayoutSection
import com.android.systemui.keyguard.ui.view.layout.sections.DefaultSettingsPopupMenuSection
import com.android.systemui.keyguard.ui.view.layout.sections.DefaultShortcutsSection
import com.android.systemui.keyguard.ui.view.layout.sections.DefaultStatusViewSection
@@ -34,6 +38,7 @@
import org.junit.Test
import org.junit.runner.RunWith
import org.mockito.Mock
+import org.mockito.Mockito.never
import org.mockito.Mockito.verify
import org.mockito.MockitoAnnotations
@@ -50,7 +55,9 @@
private lateinit var defaultAmbientIndicationAreaSection: DefaultAmbientIndicationAreaSection
@Mock private lateinit var defaultSettingsPopupMenuSection: DefaultSettingsPopupMenuSection
@Mock private lateinit var defaultStatusViewSection: DefaultStatusViewSection
+ @Mock private lateinit var defaultNSSLSection: DefaultNotificationStackScrollLayoutSection
@Mock private lateinit var splitShadeGuidelines: SplitShadeGuidelines
+ private val featureFlags = FakeFeatureFlags()
@Before
fun setup() {
@@ -64,20 +71,32 @@
defaultAmbientIndicationAreaSection,
defaultSettingsPopupMenuSection,
defaultStatusViewSection,
+ defaultNSSLSection,
splitShadeGuidelines,
+ featureFlags,
)
+ featureFlags.set(Flags.LAZY_INFLATE_KEYGUARD, false)
}
@Test
- fun apply() {
+ fun addViews() {
+ val constraintLayout = ConstraintLayout(context, null)
+ underTest.addViews(constraintLayout)
+ underTest.sections.forEach { verify(it, never()).addViews(constraintLayout) }
+ }
+
+ @Test
+ fun addViews_lazyInflateFlagOn() {
+ featureFlags.set(Flags.LAZY_INFLATE_KEYGUARD, true)
+ val constraintLayout = ConstraintLayout(context, null)
+ underTest.addViews(constraintLayout)
+ underTest.sections.forEach { verify(it).addViews(constraintLayout) }
+ }
+
+ @Test
+ fun applyConstraints() {
val cs = ConstraintSet()
- underTest.apply(cs)
- verify(defaultIndicationAreaSection).apply(cs)
- verify(defaultLockIconSection).apply(cs)
- verify(defaultShortcutsSection).apply(cs)
- verify(defaultAmbientIndicationAreaSection).apply(cs)
- verify(defaultSettingsPopupMenuSection).apply(cs)
- verify(defaultStatusViewSection).apply(cs)
- verify(splitShadeGuidelines).apply(cs)
+ underTest.applyConstraints(cs)
+ underTest.sections.forEach { verify(it).applyConstraints(cs) }
}
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/view/layout/sections/DefaultIndicationAreaSectionTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/view/layout/sections/DefaultIndicationAreaSectionTest.kt
index 3dcc03d..798b23e 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/view/layout/sections/DefaultIndicationAreaSectionTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/view/layout/sections/DefaultIndicationAreaSectionTest.kt
@@ -22,20 +22,45 @@
import androidx.test.filters.SmallTest
import com.android.systemui.R
import com.android.systemui.SysuiTestCase
+import com.android.systemui.flags.FeatureFlags
+import com.android.systemui.keyguard.ui.viewmodel.KeyguardIndicationAreaViewModel
+import com.android.systemui.keyguard.ui.viewmodel.KeyguardRootViewModel
+import com.android.systemui.statusbar.KeyguardIndicationController
import com.google.common.truth.Truth.assertThat
+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.MockitoAnnotations
@RunWith(JUnit4::class)
@SmallTest
class DefaultIndicationAreaSectionTest : SysuiTestCase() {
- private val underTest = DefaultIndicationAreaSection(context)
+ @Mock private lateinit var keyguardIndicationAreaViewModel: KeyguardIndicationAreaViewModel
+ @Mock private lateinit var keyguardRootViewModel: KeyguardRootViewModel
+ @Mock private lateinit var indicationController: KeyguardIndicationController
+ @Mock private lateinit var featureFlags: FeatureFlags
+
+ private lateinit var underTest: DefaultIndicationAreaSection
+
+ @Before
+ fun setup() {
+ MockitoAnnotations.initMocks(this)
+ underTest =
+ DefaultIndicationAreaSection(
+ context,
+ keyguardIndicationAreaViewModel,
+ keyguardRootViewModel,
+ indicationController,
+ featureFlags,
+ )
+ }
@Test
fun apply() {
val cs = ConstraintSet()
- underTest.apply(cs)
+ underTest.applyConstraints(cs)
val constraint = cs.getConstraint(R.id.keyguard_indication_area)
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/view/layout/sections/DefaultLockIconSectionTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/view/layout/sections/DefaultLockIconSectionTest.kt
index 379c03c..1192a80 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/view/layout/sections/DefaultLockIconSectionTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/view/layout/sections/DefaultLockIconSectionTest.kt
@@ -22,10 +22,12 @@
import androidx.constraintlayout.widget.ConstraintSet
import androidx.test.filters.SmallTest
import com.android.keyguard.KeyguardUpdateMonitor
+import com.android.keyguard.LockIconViewController
import com.android.systemui.R
import com.android.systemui.SysuiTestCase
import com.android.systemui.biometrics.AuthController
-import com.android.systemui.keyguard.ui.view.KeyguardRootView
+import com.android.systemui.flags.FeatureFlags
+import com.android.systemui.shade.NotificationPanelView
import com.google.common.truth.Truth.assertThat
import org.junit.Before
import org.junit.Test
@@ -41,19 +43,30 @@
@Mock private lateinit var keyguardUpdateMonitor: KeyguardUpdateMonitor
@Mock private lateinit var authController: AuthController
@Mock(answer = Answers.RETURNS_DEEP_STUBS) private lateinit var windowManager: WindowManager
+ @Mock private lateinit var notificationPanelView: NotificationPanelView
+ @Mock private lateinit var featureFlags: FeatureFlags
+ @Mock private lateinit var lockIconViewController: LockIconViewController
private lateinit var underTest: DefaultLockIconSection
@Before
fun setup() {
MockitoAnnotations.initMocks(this)
underTest =
- DefaultLockIconSection(keyguardUpdateMonitor, authController, windowManager, context)
+ DefaultLockIconSection(
+ keyguardUpdateMonitor,
+ authController,
+ windowManager,
+ context,
+ notificationPanelView,
+ featureFlags,
+ lockIconViewController
+ )
}
@Test
fun apply() {
val cs = ConstraintSet()
- underTest.apply(cs)
+ underTest.applyConstraints(cs)
val constraint = cs.getConstraint(R.id.lock_icon_view)
@@ -64,7 +77,7 @@
@Test
fun testCenterLockIcon() {
val cs = ConstraintSet()
- underTest.centerLockIcon(Point(5, 6), 1F, 5, cs)
+ underTest.centerLockIcon(Point(5, 6), 1F, cs)
val constraint = cs.getConstraint(R.id.lock_icon_view)
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenSceneViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenSceneViewModelTest.kt
index a9f288d..b30dc9c 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenSceneViewModelTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenSceneViewModelTest.kt
@@ -23,15 +23,13 @@
import com.android.systemui.scene.SceneTestUtils
import com.android.systemui.scene.shared.model.SceneKey
import com.android.systemui.scene.shared.model.SceneModel
+import com.android.systemui.util.mockito.mock
import com.google.common.truth.Truth.assertThat
-import kotlinx.coroutines.ExperimentalCoroutinesApi
-import kotlinx.coroutines.test.runCurrent
import kotlinx.coroutines.test.runTest
import org.junit.Test
import org.junit.runner.RunWith
import org.junit.runners.JUnit4
-@OptIn(ExperimentalCoroutinesApi::class)
@SmallTest
@RunWith(JUnit4::class)
class LockscreenSceneViewModelTest : SysuiTestCase() {
@@ -47,10 +45,9 @@
private val underTest =
LockscreenSceneViewModel(
authenticationInteractor = authenticationInteractor,
- bouncerInteractor =
- utils.bouncerInteractor(
- authenticationInteractor = authenticationInteractor,
- sceneInteractor = sceneInteractor,
+ longPress =
+ KeyguardLongPressViewModel(
+ interactor = mock(),
),
)
@@ -76,30 +73,4 @@
assertThat(upTransitionSceneKey).isEqualTo(SceneKey.Bouncer)
}
-
- @Test
- fun onLockButtonClicked_deviceLockedSecurely_switchesToBouncer() =
- testScope.runTest {
- val currentScene by collectLastValue(sceneInteractor.desiredScene)
- utils.authenticationRepository.setAuthenticationMethod(AuthenticationMethodModel.Pin)
- utils.authenticationRepository.setUnlocked(false)
- runCurrent()
-
- underTest.onLockButtonClicked()
-
- assertThat(currentScene).isEqualTo(SceneModel(SceneKey.Bouncer))
- }
-
- @Test
- fun onLockButtonClicked_deviceUnlocked_switchesToGone() =
- testScope.runTest {
- val currentScene by collectLastValue(sceneInteractor.desiredScene)
- utils.authenticationRepository.setAuthenticationMethod(AuthenticationMethodModel.Pin)
- utils.authenticationRepository.setUnlocked(true)
- runCurrent()
-
- underTest.onLockButtonClicked()
-
- assertThat(currentScene).isEqualTo(SceneModel(SceneKey.Gone))
- }
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/controls/ui/MediaCarouselControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/controls/ui/MediaCarouselControllerTest.kt
index ef51e47..3961a94 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/controls/ui/MediaCarouselControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/controls/ui/MediaCarouselControllerTest.kt
@@ -33,7 +33,6 @@
import com.android.keyguard.TestScopeProvider
import com.android.systemui.R
import com.android.systemui.SysuiTestCase
-import com.android.systemui.classifier.FalsingCollector
import com.android.systemui.dagger.qualifiers.Main
import com.android.systemui.dump.DumpManager
import com.android.systemui.keyguard.data.repository.FakeKeyguardTransitionRepository
@@ -105,7 +104,6 @@
@Mock @Main private lateinit var executor: DelayableExecutor
@Mock lateinit var mediaDataManager: MediaDataManager
@Mock lateinit var configurationController: ConfigurationController
- @Mock lateinit var falsingCollector: FalsingCollector
@Mock lateinit var falsingManager: FalsingManager
@Mock lateinit var dumpManager: DumpManager
@Mock lateinit var logger: MediaUiEventLogger
@@ -146,7 +144,6 @@
executor,
mediaDataManager,
configurationController,
- falsingCollector,
falsingManager,
dumpManager,
logger,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/power/PowerUITest.java b/packages/SystemUI/tests/src/com/android/systemui/power/PowerUITest.java
index 338182a..b9ee19b 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/power/PowerUITest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/power/PowerUITest.java
@@ -36,6 +36,8 @@
import android.os.PowerManager;
import android.os.Temperature;
import android.provider.Settings;
+import android.service.vr.IVrManager;
+import android.service.vr.IVrStateCallbacks;
import android.test.suitebuilder.annotation.SmallTest;
import android.testing.AndroidTestingRunner;
import android.testing.TestableLooper;
@@ -50,20 +52,17 @@
import com.android.systemui.power.PowerUI.WarningsUI;
import com.android.systemui.settings.UserTracker;
import com.android.systemui.statusbar.CommandQueue;
-import com.android.systemui.statusbar.phone.CentralSurfaces;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
+import org.mockito.ArgumentCaptor;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
import java.time.Duration;
-import java.util.Optional;
import java.util.concurrent.TimeUnit;
-import dagger.Lazy;
-
@RunWith(AndroidTestingRunner.class)
@RunWithLooper
@SmallTest
@@ -93,15 +92,12 @@
private IThermalEventListener mSkinThermalEventListener;
@Mock private BroadcastDispatcher mBroadcastDispatcher;
@Mock private CommandQueue mCommandQueue;
- @Mock private Lazy<Optional<CentralSurfaces>> mCentralSurfacesOptionalLazy;
- @Mock private CentralSurfaces mCentralSurfaces;
+ @Mock private IVrManager mVrManager;
@Before
public void setup() {
MockitoAnnotations.initMocks(this);
- when(mCentralSurfacesOptionalLazy.get()).thenReturn(Optional.of(mCentralSurfaces));
-
createPowerUi();
mSkinThermalEventListener = mPowerUI.new SkinThermalEventListener();
mUsbThermalEventListener = mPowerUI.new UsbThermalEventListener();
@@ -143,6 +139,23 @@
}
@Test
+ public void testSkinWarning_throttlingEmergency_butVrMode() throws Exception {
+ mPowerUI.start();
+
+ ArgumentCaptor<IVrStateCallbacks> vrCallback =
+ ArgumentCaptor.forClass(IVrStateCallbacks.class);
+ verify(mVrManager).registerListener(vrCallback.capture());
+
+ vrCallback.getValue().onVrStateChanged(true);
+ final Temperature temp = getEmergencyStatusTemp(Temperature.TYPE_SKIN, "skin2");
+ mSkinThermalEventListener.notifyThrottling(temp);
+
+ TestableLooper.get(this).processAllMessages();
+ // don't show skin high temperature warning when in VR mode
+ verify(mMockWarnings, never()).showHighTemperatureWarning();
+ }
+
+ @Test
public void testUsbAlarm_throttlingCritical() throws Exception {
mPowerUI.start();
@@ -683,8 +696,14 @@
private void createPowerUi() {
mPowerUI = new PowerUI(
- mContext, mBroadcastDispatcher, mCommandQueue, mCentralSurfacesOptionalLazy,
- mMockWarnings, mEnhancedEstimates, mWakefulnessLifecycle, mPowerManager,
+ mContext,
+ mBroadcastDispatcher,
+ mCommandQueue,
+ mVrManager,
+ mMockWarnings,
+ mEnhancedEstimates,
+ mWakefulnessLifecycle,
+ mPowerManager,
mUserTracker);
mPowerUI.mThermalService = mThermalServiceMock;
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/power/data/repository/PowerRepositoryImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/power/data/repository/PowerRepositoryImplTest.kt
index a01394f..f566efe6 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/power/data/repository/PowerRepositoryImplTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/power/data/repository/PowerRepositoryImplTest.kt
@@ -193,6 +193,23 @@
assertThat(reasonCaptor.value).contains(context.applicationContext.packageName)
}
+ @Test
+ fun userActivity_notifiesPowerManager() {
+ systemClock.setUptimeMillis(345000)
+
+ underTest.userTouch()
+
+ val flagsCaptor = argumentCaptor<Int>()
+ verify(manager)
+ .userActivity(
+ eq(345000L),
+ eq(PowerManager.USER_ACTIVITY_EVENT_TOUCH),
+ capture(flagsCaptor)
+ )
+ assertThat(flagsCaptor.value).isNotEqualTo(PowerManager.USER_ACTIVITY_FLAG_NO_CHANGE_LIGHTS)
+ assertThat(flagsCaptor.value).isNotEqualTo(PowerManager.USER_ACTIVITY_FLAG_INDIRECT)
+ }
+
private fun verifyRegistered() {
// We must verify with all arguments, even those that are optional because they have default
// values because Mockito is forcing us to. Once we can use mockito-kotlin, we should be
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/CastTileTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/CastTileTest.java
index 93ed994..e537131 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/CastTileTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/CastTileTest.java
@@ -14,6 +14,8 @@
package com.android.systemui.qs.tiles;
+import static com.android.systemui.flags.Flags.SIGNAL_CALLBACK_DEPRECATION;
+
import static junit.framework.Assert.assertTrue;
import static junit.framework.TestCase.assertEquals;
@@ -37,9 +39,11 @@
import androidx.test.filters.SmallTest;
import com.android.internal.logging.MetricsLogger;
+import com.android.keyguard.TestScopeProvider;
import com.android.systemui.SysuiTestCase;
import com.android.systemui.animation.DialogLaunchAnimator;
import com.android.systemui.classifier.FalsingManagerFake;
+import com.android.systemui.flags.FakeFeatureFlags;
import com.android.systemui.plugins.ActivityStarter;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
import com.android.systemui.qs.QSHost;
@@ -49,6 +53,12 @@
import com.android.systemui.statusbar.connectivity.NetworkController;
import com.android.systemui.statusbar.connectivity.SignalCallback;
import com.android.systemui.statusbar.connectivity.WifiIndicators;
+import com.android.systemui.statusbar.pipeline.shared.data.repository.FakeConnectivityRepository;
+import com.android.systemui.statusbar.pipeline.wifi.data.repository.FakeWifiRepository;
+import com.android.systemui.statusbar.pipeline.wifi.domain.interactor.WifiInteractor;
+import com.android.systemui.statusbar.pipeline.wifi.domain.interactor.WifiInteractorImpl;
+import com.android.systemui.statusbar.pipeline.wifi.shared.model.WifiNetworkModel;
+import com.android.systemui.statusbar.pipeline.wifi.shared.model.WifiNetworkModel.Inactive;
import com.android.systemui.statusbar.policy.CastController;
import com.android.systemui.statusbar.policy.CastController.CastDevice;
import com.android.systemui.statusbar.policy.HotspotController;
@@ -65,6 +75,7 @@
import java.util.ArrayList;
import java.util.List;
+import kotlinx.coroutines.test.TestScope;
@RunWith(AndroidTestingRunner.class)
@TestableLooper.RunWithLooper(setAsMainLooper = true)
@@ -98,6 +109,12 @@
@Mock
private QsEventLogger mUiEventLogger;
+ private WifiInteractor mWifiInteractor;
+ private final TileJavaAdapter mJavaAdapter = new TileJavaAdapter();
+ private final FakeWifiRepository mWifiRepository = new FakeWifiRepository();
+ private final FakeFeatureFlags mFeatureFlags = new FakeFeatureFlags();
+ private final TestScope mTestScope = TestScopeProvider.getTestScope();
+
private TestableLooper mTestableLooper;
private CastTile mCastTile;
@@ -108,42 +125,11 @@
when(mHost.getContext()).thenReturn(mContext);
- mCastTile = new CastTile(
- mHost,
- mUiEventLogger,
- mTestableLooper.getLooper(),
- new Handler(mTestableLooper.getLooper()),
- new FalsingManagerFake(),
- mMetricsLogger,
- mStatusBarStateController,
- mActivityStarter,
- mQSLogger,
- mController,
- mKeyguard,
- mNetworkController,
- mHotspotController,
- mDialogLaunchAnimator
+ mWifiInteractor = new WifiInteractorImpl(
+ new FakeConnectivityRepository(),
+ mWifiRepository,
+ mTestScope
);
- mCastTile.initialize();
-
- // We are not setting the mocks to listening, so we trigger a first refresh state to
- // set the initial state
- mCastTile.refreshState();
-
- mTestableLooper.processAllMessages();
-
- mCastTile.handleSetListening(true);
- ArgumentCaptor<SignalCallback> signalCallbackArgumentCaptor =
- ArgumentCaptor.forClass(SignalCallback.class);
- verify(mNetworkController).observe(any(LifecycleOwner.class),
- signalCallbackArgumentCaptor.capture());
- mSignalCallback = signalCallbackArgumentCaptor.getValue();
-
- ArgumentCaptor<HotspotController.Callback> hotspotCallbackArgumentCaptor =
- ArgumentCaptor.forClass(HotspotController.Callback.class);
- verify(mHotspotController).observe(any(LifecycleOwner.class),
- hotspotCallbackArgumentCaptor.capture());
- mHotspotCallback = hotspotCallbackArgumentCaptor.getValue();
}
@After
@@ -156,10 +142,11 @@
// All these tests for enabled/disabled wifi have hotspot not enabled
@Test
public void testStateUnavailable_wifiDisabled() {
+ createAndStartTileOldImpl();
IconState qsIcon = new IconState(false, 0, "");
WifiIndicators indicators = new WifiIndicators(
false, mock(IconState.class),
- qsIcon, false,false, "",
+ qsIcon, false, false, "",
false, "");
mSignalCallback.setWifiIndicators(indicators);
mTestableLooper.processAllMessages();
@@ -169,10 +156,11 @@
@Test
public void testStateUnavailable_wifiNotConnected() {
+ createAndStartTileOldImpl();
IconState qsIcon = new IconState(false, 0, "");
WifiIndicators indicators = new WifiIndicators(
true, mock(IconState.class),
- qsIcon, false,false, "",
+ qsIcon, false, false, "",
false, "");
mSignalCallback.setWifiIndicators(indicators);
mTestableLooper.processAllMessages();
@@ -184,7 +172,7 @@
IconState qsIcon = new IconState(true, 0, "");
WifiIndicators indicators = new WifiIndicators(
true, mock(IconState.class),
- qsIcon, false,false, "",
+ qsIcon, false, false, "",
false, "");
mSignalCallback.setWifiIndicators(indicators);
mTestableLooper.processAllMessages();
@@ -192,6 +180,7 @@
@Test
public void testStateActive_wifiEnabledAndCasting() {
+ createAndStartTileOldImpl();
CastController.CastDevice device = new CastController.CastDevice();
device.state = CastController.CastDevice.STATE_CONNECTED;
List<CastDevice> devices = new ArrayList<>();
@@ -204,15 +193,87 @@
@Test
public void testStateInactive_wifiEnabledNotCasting() {
+ createAndStartTileOldImpl();
enableWifiAndProcessMessages();
assertEquals(Tile.STATE_INACTIVE, mCastTile.getState().state);
}
// -------------------------------------------------
// -------------------------------------------------
+ // All these tests for enabled/disabled wifi have hotspot not enabled, and have the
+ // SIGNAL_CALLBACK_DEPRECATION flag set to true
+
+ @Test
+ public void stateUnavailable_wifiDisabled_newPipeline() {
+ createAndStartTileNewImpl();
+ mWifiRepository.setIsWifiEnabled(false);
+ mTestableLooper.processAllMessages();
+
+ assertEquals(Tile.STATE_UNAVAILABLE, mCastTile.getState().state);
+ }
+
+ @Test
+ public void stateUnavailable_wifiEnabled_notConnected_newPipeline() {
+ createAndStartTileNewImpl();
+ mWifiRepository.setIsWifiEnabled(true);
+ mWifiRepository.setWifiNetwork(Inactive.INSTANCE);
+ mTestableLooper.processAllMessages();
+
+ assertEquals(Tile.STATE_UNAVAILABLE, mCastTile.getState().state);
+ }
+
+ @Test
+ public void stateActive_wifiConnectedAndCasting_newPipeline() {
+ createAndStartTileNewImpl();
+ CastController.CastDevice device = new CastController.CastDevice();
+ device.state = CastDevice.STATE_CONNECTED;
+ List<CastDevice> devices = new ArrayList<>();
+ devices.add(device);
+ when(mController.getCastDevices()).thenReturn(devices);
+
+ mWifiRepository.setWifiNetwork(
+ new WifiNetworkModel.Active(
+ 1 /* networkId */,
+ true /* isValidated */,
+ 3 /* level */,
+ "test" /* ssid */,
+ WifiNetworkModel.HotspotDeviceType.NONE,
+ false /* isPasspointAccessPoint */,
+ false /* isOnlineSignUpforPasspointAccessPoint */,
+ null /* passpointProviderFriendlyName */
+ ));
+ mTestableLooper.processAllMessages();
+
+ assertEquals(Tile.STATE_ACTIVE, mCastTile.getState().state);
+ }
+
+ @Test
+ public void stateInactive_wifiConnectedNotCasting_newPipeline() {
+ createAndStartTileNewImpl();
+
+ mWifiRepository.setWifiNetwork(
+ new WifiNetworkModel.Active(
+ 1 /* networkId */,
+ true /* isValidated */,
+ 3 /* level */,
+ "test" /* ssid */,
+ WifiNetworkModel.HotspotDeviceType.NONE,
+ false /* isPasspointAccessPoint */,
+ false /* isOnlineSignUpforPasspointAccessPoint */,
+ null /* passpointProviderFriendlyName */
+ ));
+ mTestableLooper.processAllMessages();
+
+ assertEquals(Tile.STATE_INACTIVE, mCastTile.getState().state);
+ }
+
+ // -------------------------------------------------
+
+ // -------------------------------------------------
// All these tests for enabled/disabled hotspot have wifi not enabled
@Test
public void testStateUnavailable_hotspotDisabled() {
+ createAndStartTileOldImpl();
mHotspotCallback.onHotspotChanged(false, 0);
mTestableLooper.processAllMessages();
@@ -221,6 +282,7 @@
@Test
public void testStateUnavailable_hotspotEnabledNotConnected() {
+ createAndStartTileOldImpl();
mHotspotCallback.onHotspotChanged(true, 0);
mTestableLooper.processAllMessages();
@@ -229,6 +291,7 @@
@Test
public void testStateActive_hotspotEnabledAndConnectedAndCasting() {
+ createAndStartTileOldImpl();
CastController.CastDevice device = new CastController.CastDevice();
device.state = CastController.CastDevice.STATE_CONNECTED;
List<CastDevice> devices = new ArrayList<>();
@@ -242,6 +305,7 @@
@Test
public void testStateInactive_hotspotEnabledAndConnectedAndNotCasting() {
+ createAndStartTileOldImpl();
mHotspotCallback.onHotspotChanged(true, 1);
mTestableLooper.processAllMessages();
assertEquals(Tile.STATE_INACTIVE, mCastTile.getState().state);
@@ -250,6 +314,7 @@
@Test
public void testHandleClick_castDevicePresent() {
+ createAndStartTileOldImpl();
CastController.CastDevice device = new CastController.CastDevice();
device.state = CastDevice.STATE_CONNECTED;
device.tag = mock(MediaRouter.RouteInfo.class);
@@ -267,6 +332,7 @@
@Test
public void testHandleClick_projectionOnly() {
+ createAndStartTileOldImpl();
CastController.CastDevice device = new CastController.CastDevice();
device.state = CastDevice.STATE_CONNECTED;
device.tag = mock(MediaProjectionInfo.class);
@@ -283,6 +349,7 @@
@Test
public void testUpdateState_projectionOnly() {
+ createAndStartTileOldImpl();
CastController.CastDevice device = new CastController.CastDevice();
device.state = CastDevice.STATE_CONNECTED;
device.tag = mock(MediaProjectionInfo.class);
@@ -298,6 +365,7 @@
@Test
public void testUpdateState_castingAndProjection() {
+ createAndStartTileOldImpl();
CastController.CastDevice casting = new CastController.CastDevice();
casting.state = CastDevice.STATE_CONNECTED;
casting.tag = mock(RouteInfo.class);
@@ -322,6 +390,7 @@
@Test
public void testUpdateState_connectedAndConnecting() {
+ createAndStartTileOldImpl();
CastController.CastDevice connecting = new CastController.CastDevice();
connecting.state = CastDevice.STATE_CONNECTING;
connecting.tag = mock(RouteInfo.class);
@@ -346,6 +415,7 @@
@Test
public void testExpandView_wifiNotConnected() {
+ createAndStartTileOldImpl();
mCastTile.refreshState();
mTestableLooper.processAllMessages();
@@ -354,6 +424,7 @@
@Test
public void testExpandView_wifiEnabledNotCasting() {
+ createAndStartTileOldImpl();
enableWifiAndProcessMessages();
assertTrue(mCastTile.getState().forceExpandIcon);
@@ -361,6 +432,7 @@
@Test
public void testExpandView_casting_projection() {
+ createAndStartTileOldImpl();
CastController.CastDevice device = new CastController.CastDevice();
device.state = CastController.CastDevice.STATE_CONNECTED;
List<CastDevice> devices = new ArrayList<>();
@@ -374,6 +446,7 @@
@Test
public void testExpandView_connecting_projection() {
+ createAndStartTileOldImpl();
CastController.CastDevice connecting = new CastController.CastDevice();
connecting.state = CastDevice.STATE_CONNECTING;
connecting.name = "Test Casting Device";
@@ -389,6 +462,7 @@
@Test
public void testExpandView_casting_mediaRoute() {
+ createAndStartTileOldImpl();
CastController.CastDevice device = new CastController.CastDevice();
device.state = CastDevice.STATE_CONNECTED;
device.tag = mock(MediaRouter.RouteInfo.class);
@@ -403,6 +477,7 @@
@Test
public void testExpandView_connecting_mediaRoute() {
+ createAndStartTileOldImpl();
CastController.CastDevice connecting = new CastController.CastDevice();
connecting.state = CastDevice.STATE_CONNECTING;
connecting.tag = mock(RouteInfo.class);
@@ -416,4 +491,86 @@
assertTrue(mCastTile.getState().forceExpandIcon);
}
+
+ /**
+ * For simplicity, let this method still set the field even though that's kind of gross
+ */
+ private void createAndStartTileOldImpl() {
+ mFeatureFlags.set(SIGNAL_CALLBACK_DEPRECATION, false);
+ mCastTile = new CastTile(
+ mHost,
+ mUiEventLogger,
+ mTestableLooper.getLooper(),
+ new Handler(mTestableLooper.getLooper()),
+ new FalsingManagerFake(),
+ mMetricsLogger,
+ mStatusBarStateController,
+ mActivityStarter,
+ mQSLogger,
+ mController,
+ mKeyguard,
+ mNetworkController,
+ mHotspotController,
+ mDialogLaunchAnimator,
+ mWifiInteractor,
+ mJavaAdapter,
+ mFeatureFlags
+ );
+ mCastTile.initialize();
+
+ // We are not setting the mocks to listening, so we trigger a first refresh state to
+ // set the initial state
+ mCastTile.refreshState();
+
+ mTestableLooper.processAllMessages();
+
+ mCastTile.handleSetListening(true);
+ ArgumentCaptor<SignalCallback> signalCallbackArgumentCaptor =
+ ArgumentCaptor.forClass(SignalCallback.class);
+ verify(mNetworkController).observe(any(LifecycleOwner.class),
+ signalCallbackArgumentCaptor.capture());
+ mSignalCallback = signalCallbackArgumentCaptor.getValue();
+
+ ArgumentCaptor<HotspotController.Callback> hotspotCallbackArgumentCaptor =
+ ArgumentCaptor.forClass(HotspotController.Callback.class);
+ verify(mHotspotController).observe(any(LifecycleOwner.class),
+ hotspotCallbackArgumentCaptor.capture());
+ mHotspotCallback = hotspotCallbackArgumentCaptor.getValue();
+ }
+
+ private void createAndStartTileNewImpl() {
+ mFeatureFlags.set(SIGNAL_CALLBACK_DEPRECATION, true);
+ mCastTile = new CastTile(
+ mHost,
+ mUiEventLogger,
+ mTestableLooper.getLooper(),
+ new Handler(mTestableLooper.getLooper()),
+ new FalsingManagerFake(),
+ mMetricsLogger,
+ mStatusBarStateController,
+ mActivityStarter,
+ mQSLogger,
+ mController,
+ mKeyguard,
+ mNetworkController,
+ mHotspotController,
+ mDialogLaunchAnimator,
+ mWifiInteractor,
+ mJavaAdapter,
+ mFeatureFlags
+ );
+ mCastTile.initialize();
+
+ // Since we do not capture the callbacks like in the old impl, set the state to RESUMED
+ // So that TileJavaAdapter is collecting on flows
+ mCastTile.setListening(new Object(), true);
+
+ mTestableLooper.processAllMessages();
+
+ ArgumentCaptor<HotspotController.Callback> hotspotCallbackArgumentCaptor =
+ ArgumentCaptor.forClass(HotspotController.Callback.class);
+ verify(mHotspotController).observe(any(LifecycleOwner.class),
+ hotspotCallbackArgumentCaptor.capture());
+ mHotspotCallback = hotspotCallbackArgumentCaptor.getValue();
+ }
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/InternetTileNewImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/InternetTileNewImplTest.kt
new file mode 100644
index 0000000..b6cf459
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/InternetTileNewImplTest.kt
@@ -0,0 +1,241 @@
+/*
+ * 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.qs.tiles
+
+import android.os.Handler
+import android.service.quicksettings.Tile
+import android.testing.TestableLooper
+import android.testing.TestableLooper.RunWithLooper
+import androidx.test.filters.SmallTest
+import com.android.internal.logging.MetricsLogger
+import com.android.systemui.R
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.classifier.FalsingManagerFake
+import com.android.systemui.plugins.ActivityStarter
+import com.android.systemui.plugins.statusbar.StatusBarStateController
+import com.android.systemui.qs.QSHost
+import com.android.systemui.qs.QsEventLogger
+import com.android.systemui.qs.logging.QSLogger
+import com.android.systemui.qs.tiles.dialog.InternetDialogFactory
+import com.android.systemui.statusbar.connectivity.AccessPointController
+import com.android.systemui.statusbar.pipeline.airplane.data.repository.FakeAirplaneModeRepository
+import com.android.systemui.statusbar.pipeline.ethernet.domain.EthernetInteractor
+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.shared.data.model.DefaultConnectionModel
+import com.android.systemui.statusbar.pipeline.shared.data.model.DefaultConnectionModel.Wifi
+import com.android.systemui.statusbar.pipeline.shared.data.repository.FakeConnectivityRepository
+import com.android.systemui.statusbar.pipeline.shared.ui.viewmodel.InternetTileViewModel
+import com.android.systemui.statusbar.pipeline.wifi.data.repository.FakeWifiRepository
+import com.android.systemui.statusbar.pipeline.wifi.domain.interactor.WifiInteractorImpl
+import com.android.systemui.statusbar.pipeline.wifi.shared.model.WifiNetworkModel
+import com.android.systemui.statusbar.pipeline.wifi.shared.model.WifiScanEntry
+import com.android.systemui.util.mockito.mock
+import com.android.systemui.util.mockito.whenever
+import com.google.common.truth.Truth.assertThat
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.test.StandardTestDispatcher
+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.mockito.Mock
+import org.mockito.MockitoAnnotations
+
+@OptIn(ExperimentalCoroutinesApi::class)
+@SmallTest
+@RunWithLooper(setAsMainLooper = true)
+class InternetTileNewImplTest : SysuiTestCase() {
+ lateinit var underTest: InternetTileNewImpl
+
+ private val testDispatcher = StandardTestDispatcher()
+ private val testScope = TestScope(testDispatcher)
+
+ private var airplaneModeRepository = FakeAirplaneModeRepository()
+ private var connectivityRepository = FakeConnectivityRepository()
+ private var ethernetInteractor = EthernetInteractor(connectivityRepository)
+ private var mobileIconsInteractor = FakeMobileIconsInteractor(FakeMobileMappingsProxy(), mock())
+ private var wifiRepository = FakeWifiRepository()
+ private var wifiInteractor =
+ WifiInteractorImpl(connectivityRepository, wifiRepository, testScope.backgroundScope)
+ private lateinit var viewModel: InternetTileViewModel
+
+ private lateinit var looper: TestableLooper
+
+ @Mock private lateinit var host: QSHost
+ @Mock private lateinit var eventLogger: QsEventLogger
+ @Mock private lateinit var metricsLogger: MetricsLogger
+ @Mock private lateinit var sbStateController: StatusBarStateController
+ @Mock private lateinit var activityStarter: ActivityStarter
+ @Mock private lateinit var logger: QSLogger
+ @Mock private lateinit var dialogFactory: InternetDialogFactory
+ @Mock private lateinit var accessPointController: AccessPointController
+
+ @Before
+ fun setUp() {
+ MockitoAnnotations.initMocks(this)
+ looper = TestableLooper.get(this)
+
+ // Allow the tile to load resources
+ whenever(host.context).thenReturn(context)
+ whenever(host.userContext).thenReturn(context)
+
+ viewModel =
+ InternetTileViewModel(
+ airplaneModeRepository,
+ connectivityRepository,
+ ethernetInteractor,
+ mobileIconsInteractor,
+ wifiInteractor,
+ context,
+ testScope.backgroundScope,
+ )
+
+ underTest =
+ InternetTileNewImpl(
+ host,
+ eventLogger,
+ looper.looper,
+ Handler(looper.looper),
+ FalsingManagerFake(),
+ metricsLogger,
+ sbStateController,
+ activityStarter,
+ logger,
+ viewModel,
+ dialogFactory,
+ accessPointController
+ )
+
+ underTest.initialize()
+ underTest.setListening(Object(), true)
+
+ looper.processAllMessages()
+ }
+
+ @Test
+ fun noDefaultConnection_noNetworkAvailable() =
+ testScope.runTest {
+ connectivityRepository.defaultConnections.value = DefaultConnectionModel()
+ wifiRepository.wifiScanResults.value = listOf()
+
+ runCurrent()
+ looper.processAllMessages()
+
+ assertThat(underTest.state.secondaryLabel.toString())
+ .isEqualTo(context.getString(R.string.quick_settings_networks_unavailable))
+ assertThat(underTest.state.state).isEqualTo(Tile.STATE_INACTIVE)
+ }
+
+ @Test
+ fun noDefaultConnection_networksAvailable() =
+ testScope.runTest {
+ connectivityRepository.defaultConnections.value = DefaultConnectionModel()
+ wifiRepository.wifiScanResults.value =
+ listOf(
+ WifiScanEntry(ssid = "ssid 1"),
+ WifiScanEntry(ssid = "ssid 2"),
+ )
+
+ runCurrent()
+ looper.processAllMessages()
+
+ assertThat(underTest.state.secondaryLabel.toString())
+ .isEqualTo(context.getString(R.string.quick_settings_networks_available))
+ assertThat(underTest.state.state).isEqualTo(1)
+ }
+
+ @Test
+ fun airplaneMode_enabled_wifiDisabled() =
+ testScope.runTest {
+ airplaneModeRepository.setIsAirplaneMode(true)
+ connectivityRepository.defaultConnections.value = DefaultConnectionModel()
+ wifiRepository.setIsWifiEnabled(false)
+
+ runCurrent()
+ looper.processAllMessages()
+
+ assertThat(underTest.state.state).isEqualTo(Tile.STATE_INACTIVE)
+ assertThat(underTest.state.secondaryLabel)
+ .isEqualTo(context.getString(R.string.status_bar_airplane))
+ }
+
+ @Test
+ fun airplaneMode_enabled_wifiEnabledButNotConnected() =
+ testScope.runTest {
+ airplaneModeRepository.setIsAirplaneMode(true)
+ connectivityRepository.defaultConnections.value = DefaultConnectionModel()
+ wifiRepository.setIsWifiEnabled(true)
+
+ runCurrent()
+ looper.processAllMessages()
+
+ assertThat(underTest.state.state).isEqualTo(Tile.STATE_INACTIVE)
+ assertThat(underTest.state.secondaryLabel)
+ .isEqualTo(context.getString(R.string.status_bar_airplane))
+ }
+
+ @Test
+ fun airplaneMode_enabled_wifiEnabledAndConnected() =
+ testScope.runTest {
+ airplaneModeRepository.setIsAirplaneMode(true)
+ connectivityRepository.defaultConnections.value =
+ DefaultConnectionModel(
+ wifi = Wifi(true),
+ isValidated = true,
+ )
+ wifiRepository.setIsWifiEnabled(true)
+ wifiRepository.setWifiNetwork(ACTIVE_WIFI)
+
+ runCurrent()
+ looper.processAllMessages()
+
+ assertThat(underTest.state.state).isEqualTo(Tile.STATE_ACTIVE)
+ assertThat(underTest.state.secondaryLabel).isEqualTo(WIFI_SSID)
+ }
+
+ @Test
+ fun wifiConnected() =
+ testScope.runTest {
+ connectivityRepository.defaultConnections.value =
+ DefaultConnectionModel(
+ wifi = Wifi(true),
+ isValidated = true,
+ )
+
+ wifiRepository.setIsWifiEnabled(true)
+ wifiRepository.setWifiNetwork(ACTIVE_WIFI)
+
+ runCurrent()
+ looper.processAllMessages()
+
+ assertThat(underTest.state.state).isEqualTo(Tile.STATE_ACTIVE)
+ assertThat(underTest.state.secondaryLabel).isEqualTo(WIFI_SSID)
+ }
+
+ companion object {
+ const val WIFI_SSID = "test ssid"
+ val ACTIVE_WIFI =
+ WifiNetworkModel.Active(
+ networkId = 1,
+ isValidated = true,
+ level = 4,
+ ssid = WIFI_SSID,
+ )
+ }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/scene/SceneFrameworkIntegrationTest.kt b/packages/SystemUI/tests/src/com/android/systemui/scene/SceneFrameworkIntegrationTest.kt
index 46cbfac..6006cd4 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/scene/SceneFrameworkIntegrationTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/scene/SceneFrameworkIntegrationTest.kt
@@ -28,6 +28,7 @@
import com.android.systemui.flags.FakeFeatureFlags
import com.android.systemui.flags.Flags
import com.android.systemui.keyguard.shared.model.WakefulnessState
+import com.android.systemui.keyguard.ui.viewmodel.KeyguardLongPressViewModel
import com.android.systemui.keyguard.ui.viewmodel.LockscreenSceneViewModel
import com.android.systemui.model.SysUiState
import com.android.systemui.scene.SceneTestUtils.Companion.toDataLayer
@@ -99,7 +100,8 @@
)
private val sceneContainerViewModel =
SceneContainerViewModel(
- interactor = sceneInteractor,
+ sceneInteractor = sceneInteractor,
+ falsingInteractor = utils.falsingInteractor(),
)
.apply { setTransitionState(transitionState) }
@@ -117,7 +119,10 @@
private val lockscreenSceneViewModel =
LockscreenSceneViewModel(
authenticationInteractor = authenticationInteractor,
- bouncerInteractor = bouncerInteractor,
+ longPress =
+ KeyguardLongPressViewModel(
+ interactor = mock(),
+ ),
)
private val shadeSceneViewModel =
@@ -127,7 +132,7 @@
bouncerInteractor = bouncerInteractor,
)
- private val keyguardRepository = utils.keyguardRepository()
+ private val keyguardRepository = utils.keyguardRepository
private val keyguardInteractor =
utils.keyguardInteractor(
repository = keyguardRepository,
@@ -151,6 +156,7 @@
sysUiState = sysUiState,
displayId = displayTracker.defaultDisplayId,
sceneLogger = mock(),
+ falsingCollector = utils.falsingCollector(),
)
startable.start()
@@ -165,9 +171,7 @@
@Test
fun clickLockButtonAndEnterCorrectPin_unlocksDevice() =
testScope.runTest {
- lockscreenSceneViewModel.onLockButtonClicked()
- assertCurrentScene(SceneKey.Bouncer)
- emulateUiSceneTransition()
+ emulateUserDrivenTransition(SceneKey.Bouncer)
enterPin()
assertCurrentScene(SceneKey.Gone)
@@ -460,10 +464,7 @@
.that(authenticationInteractor.isUnlocked.value)
.isFalse()
- lockscreenSceneViewModel.onLockButtonClicked()
- runCurrent()
- emulateUiSceneTransition()
-
+ emulateUserDrivenTransition(SceneKey.Bouncer)
enterPin()
emulateUiSceneTransition(
expectedVisible = false,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/scene/data/repository/WindowRootViewVisibilityRepositoryTest.kt b/packages/SystemUI/tests/src/com/android/systemui/scene/data/repository/WindowRootViewVisibilityRepositoryTest.kt
new file mode 100644
index 0000000..2187f36
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/scene/data/repository/WindowRootViewVisibilityRepositoryTest.kt
@@ -0,0 +1,67 @@
+/*
+ * 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.scene.data.repository
+
+import androidx.test.filters.SmallTest
+import com.android.internal.statusbar.IStatusBarService
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.util.concurrency.FakeExecutor
+import com.android.systemui.util.mockito.mock
+import com.android.systemui.util.time.FakeSystemClock
+import com.google.common.truth.Truth.assertThat
+import org.junit.Test
+import org.mockito.Mockito.verify
+
+@SmallTest
+class WindowRootViewVisibilityRepositoryTest : SysuiTestCase() {
+ private val iStatusBarService = mock<IStatusBarService>()
+ private val executor = FakeExecutor(FakeSystemClock())
+ private val underTest = WindowRootViewVisibilityRepository(iStatusBarService, executor)
+
+ @Test
+ fun isLockscreenOrShadeVisible_true() {
+ underTest.setIsLockscreenOrShadeVisible(true)
+
+ assertThat(underTest.isLockscreenOrShadeVisible.value).isTrue()
+ }
+
+ @Test
+ fun isLockscreenOrShadeVisible_false() {
+ underTest.setIsLockscreenOrShadeVisible(false)
+
+ assertThat(underTest.isLockscreenOrShadeVisible.value).isFalse()
+ }
+
+ @Test
+ fun onLockscreenOrShadeInteractive_statusBarServiceNotified() {
+ underTest.onLockscreenOrShadeInteractive(
+ shouldClearNotificationEffects = true,
+ notificationCount = 3,
+ )
+ executor.runAllReady()
+
+ verify(iStatusBarService).onPanelRevealed(true, 3)
+ }
+
+ @Test
+ fun onLockscreenOrShadeNotInteractive_statusBarServiceNotified() {
+ underTest.onLockscreenOrShadeNotInteractive()
+ executor.runAllReady()
+
+ verify(iStatusBarService).onPanelHidden()
+ }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/scene/domain/interactor/SceneInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/scene/domain/interactor/SceneInteractorTest.kt
index 713c602..8620f61 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/scene/domain/interactor/SceneInteractorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/scene/domain/interactor/SceneInteractorTest.kt
@@ -147,4 +147,11 @@
underTest.setVisible(true, "reason")
assertThat(isVisible).isTrue()
}
+
+ @Test
+ fun userInput() =
+ testScope.runTest {
+ underTest.onUserInput()
+ assertThat(utils.powerRepository.userTouchRegistered).isTrue()
+ }
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/scene/domain/interactor/WindowRootViewVisibilityInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/scene/domain/interactor/WindowRootViewVisibilityInteractorTest.kt
new file mode 100644
index 0000000..f304435
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/scene/domain/interactor/WindowRootViewVisibilityInteractorTest.kt
@@ -0,0 +1,346 @@
+/*
+ * 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.scene.domain.interactor
+
+import androidx.test.filters.SmallTest
+import com.android.internal.statusbar.IStatusBarService
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.coroutines.collectLastValue
+import com.android.systemui.keyguard.data.repository.FakeKeyguardRepository
+import com.android.systemui.keyguard.shared.model.StatusBarState
+import com.android.systemui.keyguard.shared.model.WakeSleepReason
+import com.android.systemui.keyguard.shared.model.WakefulnessModel
+import com.android.systemui.keyguard.shared.model.WakefulnessState
+import com.android.systemui.scene.data.repository.WindowRootViewVisibilityRepository
+import com.android.systemui.statusbar.NotificationPresenter
+import com.android.systemui.statusbar.notification.init.NotificationsController
+import com.android.systemui.statusbar.policy.HeadsUpManager
+import com.android.systemui.util.concurrency.FakeExecutor
+import com.android.systemui.util.mockito.any
+import com.android.systemui.util.mockito.argumentCaptor
+import com.android.systemui.util.mockito.mock
+import com.android.systemui.util.mockito.whenever
+import com.android.systemui.util.time.FakeSystemClock
+import com.google.common.truth.Truth.assertThat
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.test.TestScope
+import kotlinx.coroutines.test.runCurrent
+import kotlinx.coroutines.test.runTest
+import org.junit.Test
+import org.mockito.Mockito.reset
+import org.mockito.Mockito.verify
+
+@SmallTest
+@OptIn(ExperimentalCoroutinesApi::class)
+class WindowRootViewVisibilityInteractorTest : SysuiTestCase() {
+
+ private val testScope = TestScope()
+ private val iStatusBarService = mock<IStatusBarService>()
+ private val executor = FakeExecutor(FakeSystemClock())
+ private val windowRootViewVisibilityRepository =
+ WindowRootViewVisibilityRepository(iStatusBarService, executor)
+ private val keyguardRepository = FakeKeyguardRepository()
+ private val headsUpManager = mock<HeadsUpManager>()
+ private val notificationPresenter = mock<NotificationPresenter>()
+ private val notificationsController = mock<NotificationsController>()
+
+ private val underTest =
+ WindowRootViewVisibilityInteractor(
+ testScope.backgroundScope,
+ windowRootViewVisibilityRepository,
+ keyguardRepository,
+ headsUpManager,
+ )
+ .apply { setUp(notificationPresenter, notificationsController) }
+
+ @Test
+ fun isLockscreenOrShadeVisible_true() {
+ underTest.setIsLockscreenOrShadeVisible(true)
+
+ assertThat(underTest.isLockscreenOrShadeVisible.value).isTrue()
+ }
+
+ @Test
+ fun isLockscreenOrShadeVisible_false() {
+ underTest.setIsLockscreenOrShadeVisible(false)
+
+ assertThat(underTest.isLockscreenOrShadeVisible.value).isFalse()
+ }
+
+ @Test
+ fun isLockscreenOrShadeVisible_matchesRepo() {
+ windowRootViewVisibilityRepository.setIsLockscreenOrShadeVisible(true)
+
+ assertThat(underTest.isLockscreenOrShadeVisible.value).isTrue()
+
+ windowRootViewVisibilityRepository.setIsLockscreenOrShadeVisible(false)
+
+ assertThat(underTest.isLockscreenOrShadeVisible.value).isFalse()
+ }
+
+ @Test
+ fun isLockscreenOrShadeVisibleAndInteractive_notVisible_false() =
+ testScope.runTest {
+ val actual by collectLastValue(underTest.isLockscreenOrShadeVisibleAndInteractive)
+ setWakefulness(WakefulnessState.AWAKE)
+
+ underTest.setIsLockscreenOrShadeVisible(false)
+
+ assertThat(actual).isFalse()
+ }
+
+ @Test
+ fun isLockscreenOrShadeVisibleAndInteractive_deviceAsleep_false() =
+ testScope.runTest {
+ val actual by collectLastValue(underTest.isLockscreenOrShadeVisibleAndInteractive)
+ underTest.setIsLockscreenOrShadeVisible(true)
+
+ setWakefulness(WakefulnessState.ASLEEP)
+
+ assertThat(actual).isFalse()
+ }
+
+ @Test
+ fun isLockscreenOrShadeVisibleAndInteractive_visibleAndAwake_true() =
+ testScope.runTest {
+ val actual by collectLastValue(underTest.isLockscreenOrShadeVisibleAndInteractive)
+
+ underTest.setIsLockscreenOrShadeVisible(true)
+ setWakefulness(WakefulnessState.AWAKE)
+
+ assertThat(actual).isTrue()
+ }
+
+ @Test
+ fun isLockscreenOrShadeVisibleAndInteractive_visibleAndStartingToWake_true() =
+ testScope.runTest {
+ val actual by collectLastValue(underTest.isLockscreenOrShadeVisibleAndInteractive)
+
+ underTest.setIsLockscreenOrShadeVisible(true)
+ setWakefulness(WakefulnessState.STARTING_TO_WAKE)
+
+ assertThat(actual).isTrue()
+ }
+
+ @Test
+ fun isLockscreenOrShadeVisibleAndInteractive_visibleAndStartingToSleep_true() =
+ testScope.runTest {
+ val actual by collectLastValue(underTest.isLockscreenOrShadeVisibleAndInteractive)
+
+ underTest.setIsLockscreenOrShadeVisible(true)
+ setWakefulness(WakefulnessState.STARTING_TO_SLEEP)
+
+ assertThat(actual).isTrue()
+ }
+
+ @Test
+ fun lockscreenShadeInteractive_statusBarServiceNotified() =
+ testScope.runTest {
+ underTest.start()
+
+ makeLockscreenShadeVisible()
+ testScope.runCurrent()
+ executor.runAllReady()
+
+ verify(iStatusBarService).onPanelRevealed(any(), any())
+ }
+
+ @Test
+ fun lockscreenShadeNotInteractive_statusBarServiceNotified() =
+ testScope.runTest {
+ underTest.start()
+
+ // First, make the shade visible
+ makeLockscreenShadeVisible()
+ testScope.runCurrent()
+ reset(iStatusBarService)
+
+ // WHEN lockscreen or shade is no longer visible
+ underTest.setIsLockscreenOrShadeVisible(false)
+ testScope.runCurrent()
+ executor.runAllReady()
+
+ // THEN status bar service is notified
+ verify(iStatusBarService).onPanelHidden()
+ }
+
+ @Test
+ fun lockscreenShadeInteractive_presenterCollapsed_notifEffectsNotCleared() =
+ testScope.runTest {
+ underTest.start()
+ keyguardRepository.setStatusBarState(StatusBarState.SHADE)
+
+ whenever(notificationPresenter.isPresenterFullyCollapsed).thenReturn(true)
+
+ makeLockscreenShadeVisible()
+
+ val shouldClearNotifEffects = argumentCaptor<Boolean>()
+ verify(iStatusBarService).onPanelRevealed(shouldClearNotifEffects.capture(), any())
+ assertThat(shouldClearNotifEffects.value).isFalse()
+ }
+
+ @Test
+ fun lockscreenShadeInteractive_nullPresenter_notifEffectsNotCleared() =
+ testScope.runTest {
+ underTest.start()
+ keyguardRepository.setStatusBarState(StatusBarState.SHADE)
+
+ underTest.setUp(presenter = null, notificationsController)
+
+ makeLockscreenShadeVisible()
+
+ val shouldClearNotifEffects = argumentCaptor<Boolean>()
+ verify(iStatusBarService).onPanelRevealed(shouldClearNotifEffects.capture(), any())
+ assertThat(shouldClearNotifEffects.value).isFalse()
+ }
+
+ @Test
+ fun lockscreenShadeInteractive_stateKeyguard_notifEffectsNotCleared() =
+ testScope.runTest {
+ underTest.start()
+ whenever(notificationPresenter.isPresenterFullyCollapsed).thenReturn(false)
+
+ keyguardRepository.setStatusBarState(StatusBarState.KEYGUARD)
+
+ makeLockscreenShadeVisible()
+
+ val shouldClearNotifEffects = argumentCaptor<Boolean>()
+ verify(iStatusBarService).onPanelRevealed(shouldClearNotifEffects.capture(), any())
+ assertThat(shouldClearNotifEffects.value).isFalse()
+ }
+
+ @Test
+ fun lockscreenShadeInteractive_stateShade_presenterNotCollapsed_notifEffectsCleared() =
+ testScope.runTest {
+ underTest.start()
+ whenever(notificationPresenter.isPresenterFullyCollapsed).thenReturn(false)
+
+ keyguardRepository.setStatusBarState(StatusBarState.SHADE)
+
+ makeLockscreenShadeVisible()
+
+ val shouldClearNotifEffects = argumentCaptor<Boolean>()
+ verify(iStatusBarService).onPanelRevealed(shouldClearNotifEffects.capture(), any())
+ assertThat(shouldClearNotifEffects.value).isTrue()
+ }
+
+ @Test
+ fun lockscreenShadeInteractive_stateShadeLocked_presenterNotCollapsed_notifEffectsCleared() =
+ testScope.runTest {
+ underTest.start()
+ whenever(notificationPresenter.isPresenterFullyCollapsed).thenReturn(false)
+
+ keyguardRepository.setStatusBarState(StatusBarState.SHADE_LOCKED)
+
+ makeLockscreenShadeVisible()
+
+ val shouldClearNotifEffects = argumentCaptor<Boolean>()
+ verify(iStatusBarService).onPanelRevealed(shouldClearNotifEffects.capture(), any())
+ assertThat(shouldClearNotifEffects.value).isTrue()
+ }
+
+ @Test
+ fun lockscreenShadeInteractive_hasHeadsUpAndNotifPresenterCollapsed_notifCountOne() =
+ testScope.runTest {
+ underTest.start()
+
+ whenever(headsUpManager.hasPinnedHeadsUp()).thenReturn(true)
+ whenever(notificationPresenter.isPresenterFullyCollapsed).thenReturn(true)
+ whenever(notificationsController.getActiveNotificationsCount()).thenReturn(4)
+
+ makeLockscreenShadeVisible()
+
+ val notifCount = argumentCaptor<Int>()
+ verify(iStatusBarService).onPanelRevealed(any(), notifCount.capture())
+ assertThat(notifCount.value).isEqualTo(1)
+ }
+
+ @Test
+ fun lockscreenShadeInteractive_hasHeadsUpAndNullPresenter_notifCountOne() =
+ testScope.runTest {
+ underTest.start()
+
+ whenever(headsUpManager.hasPinnedHeadsUp()).thenReturn(true)
+ underTest.setUp(presenter = null, notificationsController)
+
+ makeLockscreenShadeVisible()
+
+ val notifCount = argumentCaptor<Int>()
+ verify(iStatusBarService).onPanelRevealed(any(), notifCount.capture())
+ assertThat(notifCount.value).isEqualTo(1)
+ }
+
+ @Test
+ fun lockscreenShadeInteractive_noHeadsUp_notifCountMatchesNotifController() =
+ testScope.runTest {
+ underTest.start()
+ whenever(notificationPresenter.isPresenterFullyCollapsed).thenReturn(true)
+
+ whenever(headsUpManager.hasPinnedHeadsUp()).thenReturn(false)
+ whenever(notificationsController.getActiveNotificationsCount()).thenReturn(9)
+
+ makeLockscreenShadeVisible()
+
+ val notifCount = argumentCaptor<Int>()
+ verify(iStatusBarService).onPanelRevealed(any(), notifCount.capture())
+ assertThat(notifCount.value).isEqualTo(9)
+ }
+
+ @Test
+ fun lockscreenShadeInteractive_notifPresenterNotCollapsed_notifCountMatchesNotifController() =
+ testScope.runTest {
+ underTest.start()
+ whenever(headsUpManager.hasPinnedHeadsUp()).thenReturn(true)
+
+ whenever(notificationPresenter.isPresenterFullyCollapsed).thenReturn(false)
+ whenever(notificationsController.getActiveNotificationsCount()).thenReturn(8)
+
+ makeLockscreenShadeVisible()
+
+ val notifCount = argumentCaptor<Int>()
+ verify(iStatusBarService).onPanelRevealed(any(), notifCount.capture())
+ assertThat(notifCount.value).isEqualTo(8)
+ }
+
+ @Test
+ fun lockscreenShadeInteractive_noHeadsUp_noNotifController_notifCountZero() =
+ testScope.runTest {
+ underTest.start()
+ whenever(notificationPresenter.isPresenterFullyCollapsed).thenReturn(true)
+
+ whenever(headsUpManager.hasPinnedHeadsUp()).thenReturn(false)
+ underTest.setUp(notificationPresenter, notificationsController = null)
+
+ makeLockscreenShadeVisible()
+
+ val notifCount = argumentCaptor<Int>()
+ verify(iStatusBarService).onPanelRevealed(any(), notifCount.capture())
+ assertThat(notifCount.value).isEqualTo(0)
+ }
+
+ private fun makeLockscreenShadeVisible() {
+ underTest.setIsLockscreenOrShadeVisible(true)
+ setWakefulness(WakefulnessState.AWAKE)
+ testScope.runCurrent()
+ executor.runAllReady()
+ }
+
+ private fun setWakefulness(state: WakefulnessState) {
+ val model = WakefulnessModel(state, WakeSleepReason.OTHER, WakeSleepReason.OTHER)
+ keyguardRepository.setWakefulnessModel(model)
+ }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/scene/domain/startable/SceneContainerStartableTest.kt b/packages/SystemUI/tests/src/com/android/systemui/scene/domain/startable/SceneContainerStartableTest.kt
index 951cadd..771c3e3 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/scene/domain/startable/SceneContainerStartableTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/scene/domain/startable/SceneContainerStartableTest.kt
@@ -22,6 +22,7 @@
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.authentication.domain.model.AuthenticationMethodModel
+import com.android.systemui.classifier.FalsingCollector
import com.android.systemui.coroutines.collectLastValue
import com.android.systemui.flags.Flags
import com.android.systemui.keyguard.shared.model.WakeSleepReason
@@ -45,6 +46,7 @@
import org.junit.runner.RunWith
import org.junit.runners.JUnit4
import org.mockito.Mockito.clearInvocations
+import org.mockito.Mockito.never
import org.mockito.Mockito.times
import org.mockito.Mockito.verify
@@ -60,13 +62,15 @@
private val authenticationInteractor =
utils.authenticationInteractor(
repository = authenticationRepository,
+ sceneInteractor = sceneInteractor,
)
- private val keyguardRepository = utils.keyguardRepository()
+ private val keyguardRepository = utils.keyguardRepository
private val keyguardInteractor =
utils.keyguardInteractor(
repository = keyguardRepository,
)
private val sysUiState: SysUiState = mock()
+ private val falsingCollector: FalsingCollector = mock()
private val underTest =
SceneContainerStartable(
@@ -78,6 +82,7 @@
sysUiState = sysUiState,
displayId = Display.DEFAULT_DISPLAY,
sceneLogger = mock(),
+ falsingCollector = falsingCollector,
)
@Test
@@ -243,7 +248,7 @@
assertThat(currentSceneKey).isEqualTo(SceneKey.Lockscreen)
underTest.start()
- keyguardRepository.setWakefulnessModel(STARTING_TO_WAKE)
+ keyguardRepository.setWakefulnessModel(STARTING_TO_WAKE_FROM_POWER_BUTTON)
assertThat(currentSceneKey).isEqualTo(SceneKey.Gone)
}
@@ -259,7 +264,7 @@
assertThat(currentSceneKey).isEqualTo(SceneKey.Lockscreen)
underTest.start()
- keyguardRepository.setWakefulnessModel(STARTING_TO_WAKE)
+ keyguardRepository.setWakefulnessModel(STARTING_TO_WAKE_FROM_POWER_BUTTON)
assertThat(currentSceneKey).isEqualTo(SceneKey.Lockscreen)
}
@@ -275,7 +280,7 @@
assertThat(currentSceneKey).isEqualTo(SceneKey.Lockscreen)
underTest.start()
- keyguardRepository.setWakefulnessModel(STARTING_TO_WAKE)
+ keyguardRepository.setWakefulnessModel(STARTING_TO_WAKE_FROM_POWER_BUTTON)
assertThat(currentSceneKey).isEqualTo(SceneKey.Lockscreen)
}
@@ -294,11 +299,217 @@
authenticationRepository.setUnlocked(true)
runCurrent()
- keyguardRepository.setWakefulnessModel(STARTING_TO_WAKE)
+ keyguardRepository.setWakefulnessModel(STARTING_TO_WAKE_FROM_POWER_BUTTON)
assertThat(currentSceneKey).isEqualTo(SceneKey.Gone)
}
+ @Test
+ fun collectFalsingSignals_onSuccessfulUnlock() =
+ testScope.runTest {
+ prepareState(
+ initialSceneKey = SceneKey.Lockscreen,
+ authenticationMethod = AuthenticationMethodModel.Pin,
+ isDeviceUnlocked = false,
+ )
+ underTest.start()
+ runCurrent()
+ verify(falsingCollector, never()).onSuccessfulUnlock()
+
+ // Move around scenes without unlocking.
+ listOf(
+ SceneKey.Shade,
+ SceneKey.QuickSettings,
+ SceneKey.Shade,
+ SceneKey.Lockscreen,
+ SceneKey.Bouncer,
+ )
+ .forEach { sceneKey ->
+ sceneInteractor.changeScene(SceneModel(sceneKey), "reason")
+ runCurrent()
+ verify(falsingCollector, never()).onSuccessfulUnlock()
+ }
+
+ // Changing to the Gone scene should report a successful unlock.
+ sceneInteractor.changeScene(SceneModel(SceneKey.Gone), "reason")
+ runCurrent()
+ verify(falsingCollector).onSuccessfulUnlock()
+
+ // Move around scenes without changing back to Lockscreen, shouldn't report another
+ // unlock.
+ listOf(
+ SceneKey.Shade,
+ SceneKey.QuickSettings,
+ SceneKey.Shade,
+ SceneKey.Gone,
+ )
+ .forEach { sceneKey ->
+ sceneInteractor.changeScene(SceneModel(sceneKey), "reason")
+ runCurrent()
+ verify(falsingCollector, times(1)).onSuccessfulUnlock()
+ }
+
+ // Changing to the Lockscreen scene shouldn't report a successful unlock.
+ sceneInteractor.changeScene(SceneModel(SceneKey.Lockscreen), "reason")
+ runCurrent()
+ verify(falsingCollector, times(1)).onSuccessfulUnlock()
+
+ // Move around scenes without unlocking.
+ listOf(
+ SceneKey.Shade,
+ SceneKey.QuickSettings,
+ SceneKey.Shade,
+ SceneKey.Lockscreen,
+ SceneKey.Bouncer,
+ )
+ .forEach { sceneKey ->
+ sceneInteractor.changeScene(SceneModel(sceneKey), "reason")
+ runCurrent()
+ verify(falsingCollector, times(1)).onSuccessfulUnlock()
+ }
+
+ // Changing to the Gone scene should report a second successful unlock.
+ sceneInteractor.changeScene(SceneModel(SceneKey.Gone), "reason")
+ runCurrent()
+ verify(falsingCollector, times(2)).onSuccessfulUnlock()
+ }
+
+ @Test
+ fun collectFalsingSignals_setShowingAod() =
+ testScope.runTest {
+ prepareState(
+ initialSceneKey = SceneKey.Lockscreen,
+ authenticationMethod = AuthenticationMethodModel.Pin,
+ isDeviceUnlocked = false,
+ )
+ underTest.start()
+ runCurrent()
+ verify(falsingCollector).setShowingAod(false)
+
+ keyguardRepository.setIsDozing(true)
+ runCurrent()
+ verify(falsingCollector).setShowingAod(true)
+
+ keyguardRepository.setIsDozing(false)
+ runCurrent()
+ verify(falsingCollector, times(2)).setShowingAod(false)
+ }
+
+ @Test
+ fun collectFalsingSignals_screenOnAndOff_aodUnavailable() =
+ testScope.runTest {
+ keyguardRepository.setAodAvailable(false)
+ runCurrent()
+ prepareState(
+ initialSceneKey = SceneKey.Lockscreen,
+ authenticationMethod = AuthenticationMethodModel.Pin,
+ isDeviceUnlocked = false,
+ )
+ underTest.start()
+ runCurrent()
+ verify(falsingCollector, never()).onScreenTurningOn()
+ verify(falsingCollector, never()).onScreenOnFromTouch()
+ verify(falsingCollector, never()).onScreenOff()
+
+ keyguardRepository.setWakefulnessModel(STARTING_TO_WAKE_FROM_POWER_BUTTON)
+ runCurrent()
+ verify(falsingCollector, times(1)).onScreenTurningOn()
+ verify(falsingCollector, never()).onScreenOnFromTouch()
+ verify(falsingCollector, never()).onScreenOff()
+
+ keyguardRepository.setWakefulnessModel(ASLEEP)
+ runCurrent()
+ verify(falsingCollector, times(1)).onScreenTurningOn()
+ verify(falsingCollector, never()).onScreenOnFromTouch()
+ verify(falsingCollector, times(1)).onScreenOff()
+
+ keyguardRepository.setWakefulnessModel(STARTING_TO_WAKE_FROM_TAP)
+ runCurrent()
+ verify(falsingCollector, times(1)).onScreenTurningOn()
+ verify(falsingCollector, times(1)).onScreenOnFromTouch()
+ verify(falsingCollector, times(1)).onScreenOff()
+
+ keyguardRepository.setWakefulnessModel(ASLEEP)
+ runCurrent()
+ verify(falsingCollector, times(1)).onScreenTurningOn()
+ verify(falsingCollector, times(1)).onScreenOnFromTouch()
+ verify(falsingCollector, times(2)).onScreenOff()
+
+ keyguardRepository.setWakefulnessModel(STARTING_TO_WAKE_FROM_POWER_BUTTON)
+ runCurrent()
+ verify(falsingCollector, times(2)).onScreenTurningOn()
+ verify(falsingCollector, times(1)).onScreenOnFromTouch()
+ verify(falsingCollector, times(2)).onScreenOff()
+ }
+
+ @Test
+ fun collectFalsingSignals_screenOnAndOff_aodAvailable() =
+ testScope.runTest {
+ keyguardRepository.setAodAvailable(true)
+ runCurrent()
+ prepareState(
+ initialSceneKey = SceneKey.Lockscreen,
+ authenticationMethod = AuthenticationMethodModel.Pin,
+ isDeviceUnlocked = false,
+ )
+ underTest.start()
+ runCurrent()
+ verify(falsingCollector, never()).onScreenTurningOn()
+ verify(falsingCollector, never()).onScreenOnFromTouch()
+ verify(falsingCollector, never()).onScreenOff()
+
+ keyguardRepository.setWakefulnessModel(STARTING_TO_WAKE_FROM_POWER_BUTTON)
+ runCurrent()
+ verify(falsingCollector, never()).onScreenTurningOn()
+ verify(falsingCollector, never()).onScreenOnFromTouch()
+ verify(falsingCollector, never()).onScreenOff()
+
+ keyguardRepository.setWakefulnessModel(ASLEEP)
+ runCurrent()
+ verify(falsingCollector, never()).onScreenTurningOn()
+ verify(falsingCollector, never()).onScreenOnFromTouch()
+ verify(falsingCollector, never()).onScreenOff()
+
+ keyguardRepository.setWakefulnessModel(STARTING_TO_WAKE_FROM_TAP)
+ runCurrent()
+ verify(falsingCollector, never()).onScreenTurningOn()
+ verify(falsingCollector, never()).onScreenOnFromTouch()
+ verify(falsingCollector, never()).onScreenOff()
+
+ keyguardRepository.setWakefulnessModel(ASLEEP)
+ runCurrent()
+ verify(falsingCollector, never()).onScreenTurningOn()
+ verify(falsingCollector, never()).onScreenOnFromTouch()
+ verify(falsingCollector, never()).onScreenOff()
+
+ keyguardRepository.setWakefulnessModel(STARTING_TO_WAKE_FROM_POWER_BUTTON)
+ runCurrent()
+ verify(falsingCollector, never()).onScreenTurningOn()
+ verify(falsingCollector, never()).onScreenOnFromTouch()
+ verify(falsingCollector, never()).onScreenOff()
+ }
+
+ @Test
+ fun collectFalsingSignals_bouncerVisibility() =
+ testScope.runTest {
+ prepareState(
+ initialSceneKey = SceneKey.Lockscreen,
+ authenticationMethod = AuthenticationMethodModel.Pin,
+ isDeviceUnlocked = false,
+ )
+ underTest.start()
+ runCurrent()
+ verify(falsingCollector).onBouncerHidden()
+
+ sceneInteractor.changeScene(SceneModel(SceneKey.Bouncer), "reason")
+ runCurrent()
+ verify(falsingCollector).onBouncerShown()
+
+ sceneInteractor.changeScene(SceneModel(SceneKey.Gone), "reason")
+ runCurrent()
+ verify(falsingCollector, times(2)).onBouncerHidden()
+ }
+
private fun prepareState(
isDeviceUnlocked: Boolean = false,
isBypassEnabled: Boolean = false,
@@ -334,11 +545,23 @@
lastWakeReason = WakeSleepReason.POWER_BUTTON,
lastSleepReason = WakeSleepReason.POWER_BUTTON
)
- private val STARTING_TO_WAKE =
+ private val ASLEEP =
+ WakefulnessModel(
+ state = WakefulnessState.ASLEEP,
+ lastWakeReason = WakeSleepReason.POWER_BUTTON,
+ lastSleepReason = WakeSleepReason.POWER_BUTTON
+ )
+ private val STARTING_TO_WAKE_FROM_POWER_BUTTON =
WakefulnessModel(
state = WakefulnessState.STARTING_TO_WAKE,
lastWakeReason = WakeSleepReason.POWER_BUTTON,
lastSleepReason = WakeSleepReason.POWER_BUTTON
)
+ private val STARTING_TO_WAKE_FROM_TAP =
+ WakefulnessModel(
+ state = WakefulnessState.STARTING_TO_WAKE,
+ lastWakeReason = WakeSleepReason.TAP,
+ lastSleepReason = WakeSleepReason.POWER_BUTTON
+ )
}
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/scene/ui/viewmodel/SceneContainerViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/scene/ui/viewmodel/SceneContainerViewModelTest.kt
index 88abb642..0b56a59 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/scene/ui/viewmodel/SceneContainerViewModelTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/scene/ui/viewmodel/SceneContainerViewModelTest.kt
@@ -39,7 +39,8 @@
private val interactor = utils.sceneInteractor()
private val underTest =
SceneContainerViewModel(
- interactor = interactor,
+ sceneInteractor = interactor,
+ falsingInteractor = utils.falsingInteractor(),
)
@Test
diff --git a/packages/SystemUI/tests/src/com/android/systemui/screenshot/ScreenshotDataTest.kt b/packages/SystemUI/tests/src/com/android/systemui/screenshot/ScreenshotDataTest.kt
index 43e9939..f8a8a68 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/screenshot/ScreenshotDataTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/screenshot/ScreenshotDataTest.kt
@@ -20,6 +20,7 @@
import android.graphics.Insets
import android.graphics.Rect
import android.os.UserHandle
+import android.view.Display
import android.view.WindowManager
import com.android.internal.util.ScreenshotRequest
import com.google.common.truth.Truth.assertThat
@@ -54,6 +55,16 @@
assertThat(data.taskId).isEqualTo(taskId)
assertThat(data.userHandle).isEqualTo(UserHandle.of(userId))
assertThat(data.topComponent).isEqualTo(component)
+ assertThat(data.displayId).isEqualTo(Display.DEFAULT_DISPLAY)
+ }
+
+ @Test
+ fun testConstruction_notDefaultDisplayId() {
+ val request = ScreenshotRequest.Builder(type, source).build()
+
+ val data = ScreenshotData.fromRequest(request, displayId = 42)
+
+ assertThat(data.displayId).isEqualTo(42)
}
@Test
diff --git a/packages/SystemUI/tests/src/com/android/systemui/screenshot/ScreenshotDetectionControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/screenshot/ScreenshotDetectionControllerTest.kt
index 08b5d2b..16091b2 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/screenshot/ScreenshotDetectionControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/screenshot/ScreenshotDetectionControllerTest.kt
@@ -170,8 +170,8 @@
private class ComponentInfoFlagMatcher(
@PackageManager.ComponentInfoFlagsBits val mask: Int, val value: Int
): ArgumentMatcher<PackageManager.ComponentInfoFlags> {
- override fun matches(flags: PackageManager.ComponentInfoFlags): Boolean {
- return (mask.toLong() and flags.value) == value.toLong()
+ override fun matches(flags: PackageManager.ComponentInfoFlags?): Boolean {
+ return flags != null && (mask.toLong() and flags.value) == value.toLong()
}
override fun toString(): String{
diff --git a/packages/SystemUI/tests/src/com/android/systemui/screenshot/TakeScreenshotExecutorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/screenshot/TakeScreenshotExecutorTest.kt
new file mode 100644
index 0000000..97c2ed4
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/screenshot/TakeScreenshotExecutorTest.kt
@@ -0,0 +1,303 @@
+package com.android.systemui.screenshot
+
+import android.content.ComponentName
+import android.net.Uri
+import android.testing.AndroidTestingRunner
+import android.view.Display
+import android.view.Display.TYPE_EXTERNAL
+import android.view.Display.TYPE_INTERNAL
+import android.view.Display.TYPE_OVERLAY
+import android.view.Display.TYPE_VIRTUAL
+import android.view.Display.TYPE_WIFI
+import android.view.WindowManager
+import androidx.test.filters.SmallTest
+import com.android.internal.logging.testing.UiEventLoggerFake
+import com.android.internal.util.ScreenshotRequest
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.display.data.repository.FakeDisplayRepository
+import com.android.systemui.display.data.repository.display
+import com.android.systemui.util.mockito.any
+import com.android.systemui.util.mockito.eq
+import com.android.systemui.util.mockito.kotlinArgumentCaptor as ArgumentCaptor
+import com.android.systemui.util.mockito.mock
+import com.android.systemui.util.mockito.nullable
+import com.android.systemui.util.mockito.whenever
+import com.google.common.truth.Truth.assertThat
+import kotlinx.coroutines.test.TestScope
+import kotlinx.coroutines.test.UnconfinedTestDispatcher
+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.never
+import org.mockito.Mockito.times
+import org.mockito.Mockito.verify
+import org.mockito.Mockito.verifyNoMoreInteractions
+
+@RunWith(AndroidTestingRunner::class)
+@SmallTest
+class TakeScreenshotExecutorTest : SysuiTestCase() {
+
+ private val controller0 = mock<ScreenshotController>()
+ private val controller1 = mock<ScreenshotController>()
+ private val controllerFactory = mock<ScreenshotController.Factory>()
+ private val callback = mock<TakeScreenshotService.RequestCallback>()
+
+ private val fakeDisplayRepository = FakeDisplayRepository()
+ private val requestProcessor = FakeRequestProcessor()
+ private val topComponent = ComponentName(mContext, TakeScreenshotExecutorTest::class.java)
+ private val testScope = TestScope(UnconfinedTestDispatcher())
+ private val eventLogger = UiEventLoggerFake()
+
+ private val screenshotExecutor =
+ TakeScreenshotExecutor(
+ controllerFactory,
+ fakeDisplayRepository,
+ testScope,
+ requestProcessor,
+ eventLogger,
+ )
+
+ @Before
+ fun setUp() {
+ whenever(controllerFactory.create(eq(0))).thenReturn(controller0)
+ whenever(controllerFactory.create(eq(1))).thenReturn(controller1)
+ }
+
+ @Test
+ fun executeScreenshots_severalDisplays_callsControllerForEachOne() =
+ testScope.runTest {
+ setDisplays(display(TYPE_INTERNAL, id = 0), display(TYPE_EXTERNAL, id = 1))
+ val onSaved = { _: Uri -> }
+ screenshotExecutor.executeScreenshots(createScreenshotRequest(), onSaved, callback)
+
+ verify(controllerFactory).create(eq(0))
+ verify(controllerFactory).create(eq(1))
+
+ val capturer = ArgumentCaptor<ScreenshotData>()
+
+ verify(controller0).handleScreenshot(capturer.capture(), any(), any())
+ assertThat(capturer.value.displayId).isEqualTo(0)
+ // OnSaved callback should be different.
+ verify(controller1).handleScreenshot(capturer.capture(), any(), any())
+ assertThat(capturer.value.displayId).isEqualTo(1)
+
+ assertThat(eventLogger.numLogs()).isEqualTo(2)
+ assertThat(eventLogger.get(0).eventId)
+ .isEqualTo(ScreenshotEvent.SCREENSHOT_REQUESTED_KEY_OTHER.id)
+ assertThat(eventLogger.get(0).packageName).isEqualTo(topComponent.packageName)
+ assertThat(eventLogger.get(1).eventId)
+ .isEqualTo(ScreenshotEvent.SCREENSHOT_REQUESTED_KEY_OTHER.id)
+ assertThat(eventLogger.get(1).packageName).isEqualTo(topComponent.packageName)
+
+ screenshotExecutor.onDestroy()
+ }
+
+ @Test
+ fun executeScreenshots_onlyVirtualDisplays_noInteractionsWithControllers() =
+ testScope.runTest {
+ setDisplays(display(TYPE_VIRTUAL, id = 0), display(TYPE_VIRTUAL, id = 1))
+ val onSaved = { _: Uri -> }
+ screenshotExecutor.executeScreenshots(createScreenshotRequest(), onSaved, callback)
+
+ verifyNoMoreInteractions(controllerFactory)
+ screenshotExecutor.onDestroy()
+ }
+
+ @Test
+ fun executeScreenshots_allowedTypes_allCaptured() =
+ testScope.runTest {
+ whenever(controllerFactory.create(any())).thenReturn(controller0)
+
+ setDisplays(
+ display(TYPE_INTERNAL, id = 0),
+ display(TYPE_EXTERNAL, id = 1),
+ display(TYPE_OVERLAY, id = 2),
+ display(TYPE_WIFI, id = 3)
+ )
+ val onSaved = { _: Uri -> }
+ screenshotExecutor.executeScreenshots(createScreenshotRequest(), onSaved, callback)
+
+ verify(controller0, times(4)).handleScreenshot(any(), any(), any())
+ screenshotExecutor.onDestroy()
+ }
+
+ @Test
+ fun executeScreenshots_reportsOnFinishedOnlyWhenBothFinished() =
+ testScope.runTest {
+ setDisplays(display(TYPE_INTERNAL, id = 0), display(TYPE_EXTERNAL, id = 1))
+ val onSaved = { _: Uri -> }
+ screenshotExecutor.executeScreenshots(createScreenshotRequest(), onSaved, callback)
+
+ val capturer0 = ArgumentCaptor<TakeScreenshotService.RequestCallback>()
+ val capturer1 = ArgumentCaptor<TakeScreenshotService.RequestCallback>()
+
+ verify(controller0).handleScreenshot(any(), any(), capturer0.capture())
+ verify(controller1).handleScreenshot(any(), any(), capturer1.capture())
+
+ verify(callback, never()).onFinish()
+
+ capturer0.value.onFinish()
+
+ verify(callback, never()).onFinish()
+
+ capturer1.value.onFinish()
+
+ verify(callback).onFinish()
+ screenshotExecutor.onDestroy()
+ }
+
+ @Test
+ fun executeScreenshots_doesNotReportFinishedIfOneFinishesOtherFails() =
+ testScope.runTest {
+ setDisplays(display(TYPE_INTERNAL, id = 0), display(TYPE_EXTERNAL, id = 1))
+ val onSaved = { _: Uri -> }
+ screenshotExecutor.executeScreenshots(createScreenshotRequest(), onSaved, callback)
+
+ val capturer0 = ArgumentCaptor<TakeScreenshotService.RequestCallback>()
+ val capturer1 = ArgumentCaptor<TakeScreenshotService.RequestCallback>()
+
+ verify(controller0).handleScreenshot(any(), any(), capturer0.capture())
+ verify(controller1).handleScreenshot(any(), nullable(), capturer1.capture())
+
+ verify(callback, never()).onFinish()
+
+ capturer0.value.onFinish()
+
+ verify(callback, never()).onFinish()
+
+ capturer1.value.reportError()
+
+ verify(callback, never()).onFinish()
+ verify(callback).reportError()
+
+ screenshotExecutor.onDestroy()
+ }
+
+ @Test
+ fun executeScreenshots_doesNotReportFinishedAfterOneFails() =
+ testScope.runTest {
+ setDisplays(display(TYPE_INTERNAL, id = 0), display(TYPE_EXTERNAL, id = 1))
+ val onSaved = { _: Uri -> }
+ screenshotExecutor.executeScreenshots(createScreenshotRequest(), onSaved, callback)
+
+ val capturer0 = ArgumentCaptor<TakeScreenshotService.RequestCallback>()
+ val capturer1 = ArgumentCaptor<TakeScreenshotService.RequestCallback>()
+
+ verify(controller0).handleScreenshot(any(), any(), capturer0.capture())
+ verify(controller1).handleScreenshot(any(), any(), capturer1.capture())
+
+ verify(callback, never()).onFinish()
+
+ capturer0.value.reportError()
+
+ verify(callback, never()).onFinish()
+ verify(callback).reportError()
+
+ capturer1.value.onFinish()
+
+ verify(callback, never()).onFinish()
+ screenshotExecutor.onDestroy()
+ }
+
+ @Test
+ fun onDestroy_propagatedToControllers() =
+ testScope.runTest {
+ setDisplays(display(TYPE_INTERNAL, id = 0), display(TYPE_EXTERNAL, id = 1))
+ val onSaved = { _: Uri -> }
+ screenshotExecutor.executeScreenshots(createScreenshotRequest(), onSaved, callback)
+
+ screenshotExecutor.onDestroy()
+ verify(controller0).onDestroy()
+ verify(controller1).onDestroy()
+ }
+
+ @Test
+ fun removeWindows_propagatedToControllers() =
+ testScope.runTest {
+ setDisplays(display(TYPE_INTERNAL, id = 0), display(TYPE_EXTERNAL, id = 1))
+ val onSaved = { _: Uri -> }
+ screenshotExecutor.executeScreenshots(createScreenshotRequest(), onSaved, callback)
+
+ screenshotExecutor.removeWindows()
+ verify(controller0).removeWindow()
+ verify(controller1).removeWindow()
+
+ screenshotExecutor.onDestroy()
+ }
+
+ @Test
+ fun onCloseSystemDialogsReceived_propagatedToControllers() =
+ testScope.runTest {
+ setDisplays(display(TYPE_INTERNAL, id = 0), display(TYPE_EXTERNAL, id = 1))
+ val onSaved = { _: Uri -> }
+ screenshotExecutor.executeScreenshots(createScreenshotRequest(), onSaved, callback)
+
+ screenshotExecutor.onCloseSystemDialogsReceived()
+ verify(controller0).dismissScreenshot(any())
+ verify(controller1).dismissScreenshot(any())
+
+ screenshotExecutor.onDestroy()
+ }
+
+ @Test
+ fun onCloseSystemDialogsReceived_someControllerHavePendingTransitions() =
+ testScope.runTest {
+ setDisplays(display(TYPE_INTERNAL, id = 0), display(TYPE_EXTERNAL, id = 1))
+ whenever(controller0.isPendingSharedTransition).thenReturn(true)
+ whenever(controller1.isPendingSharedTransition).thenReturn(false)
+ val onSaved = { _: Uri -> }
+ screenshotExecutor.executeScreenshots(createScreenshotRequest(), onSaved, callback)
+
+ screenshotExecutor.onCloseSystemDialogsReceived()
+ verify(controller0, never()).dismissScreenshot(any())
+ verify(controller1).dismissScreenshot(any())
+
+ screenshotExecutor.onDestroy()
+ }
+
+ @Test
+ fun executeScreenshots_controllerCalledWithRequestProcessorReturnValue() =
+ testScope.runTest {
+ setDisplays(display(TYPE_INTERNAL, id = 0))
+ val screenshotRequest = createScreenshotRequest()
+ val toBeReturnedByProcessor = ScreenshotData.forTesting()
+ requestProcessor.toReturn = toBeReturnedByProcessor
+
+ val onSaved = { _: Uri -> }
+ screenshotExecutor.executeScreenshots(screenshotRequest, onSaved, callback)
+
+ assertThat(requestProcessor.processed)
+ .isEqualTo(ScreenshotData.fromRequest(screenshotRequest))
+
+ val capturer = ArgumentCaptor<ScreenshotData>()
+ verify(controller0).handleScreenshot(capturer.capture(), any(), any())
+ assertThat(capturer.value).isEqualTo(toBeReturnedByProcessor)
+
+ screenshotExecutor.onDestroy()
+ }
+
+ private suspend fun TestScope.setDisplays(vararg displays: Display) {
+ fakeDisplayRepository.emit(displays.toSet())
+ runCurrent()
+ }
+
+ private fun createScreenshotRequest() =
+ ScreenshotRequest.Builder(
+ WindowManager.TAKE_SCREENSHOT_FULLSCREEN,
+ WindowManager.ScreenshotSource.SCREENSHOT_KEY_OTHER
+ )
+ .setTopComponent(topComponent)
+ .build()
+
+ private class FakeRequestProcessor : ScreenshotRequestProcessor {
+ var processed: ScreenshotData? = null
+ var toReturn: ScreenshotData? = null
+
+ override suspend fun process(screenshot: ScreenshotData): ScreenshotData {
+ processed = screenshot
+ return toReturn ?: screenshot
+ }
+ }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/screenshot/TakeScreenshotServiceTest.kt b/packages/SystemUI/tests/src/com/android/systemui/screenshot/TakeScreenshotServiceTest.kt
index 77f7426..a08cda6 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/screenshot/TakeScreenshotServiceTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/screenshot/TakeScreenshotServiceTest.kt
@@ -27,6 +27,7 @@
import android.os.UserHandle
import android.os.UserManager
import android.testing.AndroidTestingRunner
+import android.view.Display
import android.view.WindowManager.ScreenshotSource.SCREENSHOT_KEY_OTHER
import android.view.WindowManager.TAKE_SCREENSHOT_FULLSCREEN
import androidx.test.filters.SmallTest
@@ -34,6 +35,7 @@
import com.android.internal.util.ScreenshotRequest
import com.android.systemui.SysuiTestCase
import com.android.systemui.flags.FakeFeatureFlags
+import com.android.systemui.flags.Flags.MULTI_DISPLAY_SCREENSHOT
import com.android.systemui.screenshot.ScreenshotEvent.SCREENSHOT_CAPTURE_FAILED
import com.android.systemui.screenshot.ScreenshotEvent.SCREENSHOT_REQUESTED_KEY_OTHER
import com.android.systemui.screenshot.TakeScreenshotService.RequestCallback
@@ -48,6 +50,7 @@
import org.junit.runner.RunWith
import org.mockito.ArgumentMatchers.anyInt
import org.mockito.ArgumentMatchers.isNull
+import org.mockito.Mockito.clearInvocations
import org.mockito.Mockito.doAnswer
import org.mockito.Mockito.doThrow
import org.mockito.Mockito.times
@@ -60,6 +63,8 @@
private val application = mock<Application>()
private val controller = mock<ScreenshotController>()
+ private val controllerFactory = mock<ScreenshotController.Factory>()
+ private val takeScreenshotExecutor = mock<TakeScreenshotExecutor>()
private val userManager = mock<UserManager>()
private val requestProcessor = mock<RequestProcessor>()
private val devicePolicyManager = mock<DevicePolicyManager>()
@@ -71,21 +76,11 @@
private val flags = FakeFeatureFlags()
private val topComponent = ComponentName(mContext, TakeScreenshotServiceTest::class.java)
- private val service =
- TakeScreenshotService(
- controller,
- userManager,
- devicePolicyManager,
- eventLogger,
- notificationsController,
- mContext,
- Runnable::run,
- flags,
- requestProcessor
- )
+ private lateinit var service: TakeScreenshotService
@Before
fun setUp() {
+ flags.set(MULTI_DISPLAY_SCREENSHOT, false)
whenever(devicePolicyManager.resources).thenReturn(devicePolicyResourcesManager)
whenever(
devicePolicyManager.getScreenCaptureDisabled(
@@ -95,6 +90,7 @@
)
.thenReturn(false)
whenever(userManager.isUserUnlocked).thenReturn(true)
+ whenever(controllerFactory.create(any())).thenReturn(controller)
// Stub request processor as a synchronous no-op for tests with the flag enabled
doAnswer {
@@ -113,14 +109,7 @@
.whenever(requestProcessor)
.processAsync(/* screenshot= */ any(ScreenshotData::class.java), /* callback= */ any())
- service.attach(
- mContext,
- /* thread = */ null,
- /* className = */ null,
- /* token = */ null,
- application,
- /* activityManager = */ null
- )
+ service = createService()
}
@Test
@@ -146,7 +135,7 @@
verify(controller, times(1))
.handleScreenshot(
- eq(ScreenshotData.fromRequest(request)),
+ eq(ScreenshotData.fromRequest(request, Display.DEFAULT_DISPLAY)),
/* onSavedListener = */ any(),
/* requestCallback = */ any()
)
@@ -295,6 +284,74 @@
failureEvent.packageName
)
}
+
+ @Test
+ fun takeScreenshotFullScreen_multiDisplayFlagEnabled_takeScreenshotExecutor() {
+ flags.set(MULTI_DISPLAY_SCREENSHOT, true)
+ service = createService()
+
+ val request =
+ ScreenshotRequest.Builder(TAKE_SCREENSHOT_FULLSCREEN, SCREENSHOT_KEY_OTHER)
+ .setTopComponent(topComponent)
+ .build()
+
+ service.handleRequest(request, { /* onSaved */}, callback)
+
+ verifyZeroInteractions(controller)
+ verify(takeScreenshotExecutor, times(1)).executeScreenshotsAsync(any(), any(), any())
+
+ assertEquals("Expected one UiEvent", 0, eventLogger.numLogs())
+ }
+
+ @Test
+ fun testServiceLifecycle_multiDisplayScreenshotFlagEnabled() {
+ flags.set(MULTI_DISPLAY_SCREENSHOT, true)
+ service = createService()
+
+ service.onCreate()
+ service.onBind(null /* unused: Intent */)
+
+ service.onUnbind(null /* unused: Intent */)
+ verify(takeScreenshotExecutor, times(1)).removeWindows()
+
+ service.onDestroy()
+ verify(takeScreenshotExecutor, times(1)).onDestroy()
+ }
+
+ @Test
+ fun constructor_MultiDisplayFlagOn_screenshotControllerNotCreated() {
+ flags.set(MULTI_DISPLAY_SCREENSHOT, true)
+ clearInvocations(controllerFactory)
+
+ service = createService()
+
+ verifyZeroInteractions(controllerFactory)
+ }
+
+ private fun createService(): TakeScreenshotService {
+ val service =
+ TakeScreenshotService(
+ controllerFactory,
+ userManager,
+ devicePolicyManager,
+ eventLogger,
+ notificationsController,
+ mContext,
+ Runnable::run,
+ flags,
+ requestProcessor,
+ { takeScreenshotExecutor },
+ )
+ service.attach(
+ mContext,
+ /* thread = */ null,
+ /* className = */ null,
+ /* token = */ null,
+ application,
+ /* activityManager = */ null
+ )
+ return service
+ }
}
private fun Bitmap.equalsHardwareBitmap(other: Bitmap): Boolean {
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 981e44b..c573ac63 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationPanelViewControllerBaseTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationPanelViewControllerBaseTest.java
@@ -145,6 +145,7 @@
import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayout;
import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayoutController;
import com.android.systemui.statusbar.notification.stack.NotificationStackSizeCalculator;
+import com.android.systemui.statusbar.notification.stack.domain.interactor.SharedNotificationContainerInteractor;
import com.android.systemui.statusbar.phone.CentralSurfaces;
import com.android.systemui.statusbar.phone.ConfigurationControllerImpl;
import com.android.systemui.statusbar.phone.DozeParameters;
@@ -322,6 +323,7 @@
@Mock private JavaAdapter mJavaAdapter;
@Mock private CastController mCastController;
@Mock private KeyguardRootView mKeyguardRootView;
+ @Mock private SharedNotificationContainerInteractor mSharedNotificationContainerInteractor;
protected final int mMaxUdfpsBurnInOffsetY = 5;
protected KeyguardBottomAreaInteractor mKeyguardBottomAreaInteractor;
@@ -650,6 +652,7 @@
mKeyuardLongPressViewModel,
mKeyguardInteractor,
mActivityStarter,
+ mSharedNotificationContainerInteractor,
mKeyguardViewConfigurator,
mKeyguardFaceAuthInteractor,
mKeyguardRootView);
@@ -713,7 +716,6 @@
mAmbientState,
mRecordingController,
mFalsingManager,
- new FalsingCollectorFake(),
mAccessibilityManager,
mLockscreenGestureLogger,
mMetricsLogger,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationShadeWindowControllerImplTest.java b/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationShadeWindowControllerImplTest.java
index 1738b06..dfd782b 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationShadeWindowControllerImplTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationShadeWindowControllerImplTest.java
@@ -63,6 +63,8 @@
import com.android.systemui.statusbar.policy.ConfigurationController;
import com.android.systemui.statusbar.policy.KeyguardStateController;
+import com.google.common.util.concurrent.MoreExecutors;
+
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -73,6 +75,7 @@
import org.mockito.Spy;
import java.util.List;
+import java.util.concurrent.Executor;
@RunWith(AndroidTestingRunner.class)
@RunWithLooper
@@ -98,6 +101,7 @@
@Mock private ShadeWindowLogger mShadeWindowLogger;
@Captor private ArgumentCaptor<WindowManager.LayoutParams> mLayoutParameters;
@Captor private ArgumentCaptor<StatusBarStateController.StateListener> mStateListener;
+ private final Executor mBackgroundExecutor = MoreExecutors.directExecutor();
private NotificationShadeWindowControllerImpl mNotificationShadeWindowController;
private float mPreferredRefreshRate = -1;
@@ -125,6 +129,7 @@
mConfigurationController,
mKeyguardViewMediator,
mKeyguardBypassController,
+ mBackgroundExecutor,
mColorExtractor,
mDumpManager,
mKeyguardStateController,
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 3cce423..39fe498 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationShadeWindowViewControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationShadeWindowViewControllerTest.kt
@@ -36,6 +36,7 @@
import com.android.systemui.bouncer.ui.viewmodel.KeyguardBouncerViewModel
import com.android.systemui.classifier.FalsingCollectorFake
import com.android.systemui.dock.DockManager
+import com.android.systemui.dump.DumpManager
import com.android.systemui.dump.logcatLogBuffer
import com.android.systemui.flags.FakeFeatureFlags
import com.android.systemui.flags.Flags
@@ -56,6 +57,8 @@
import com.android.systemui.statusbar.notification.stack.AmbientState
import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayoutController
import com.android.systemui.statusbar.phone.CentralSurfaces
+import com.android.systemui.statusbar.phone.DozeScrimController
+import com.android.systemui.statusbar.phone.DozeServiceHost
import com.android.systemui.statusbar.phone.PhoneStatusBarViewController
import com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager
import com.android.systemui.statusbar.window.StatusBarWindowStateController
@@ -90,6 +93,8 @@
@Mock private lateinit var view: NotificationShadeWindowView
@Mock private lateinit var sysuiStatusBarStateController: SysuiStatusBarStateController
@Mock private lateinit var centralSurfaces: CentralSurfaces
+ @Mock private lateinit var dozeServiceHost: DozeServiceHost
+ @Mock private lateinit var dozeScrimController: DozeScrimController
@Mock private lateinit var backActionInteractor: BackActionInteractor
@Mock private lateinit var powerInteractor: PowerInteractor
@Mock private lateinit var dockManager: DockManager
@@ -98,6 +103,7 @@
@Mock private lateinit var notificationShadeWindowController: NotificationShadeWindowController
@Mock private lateinit var keyguardUnlockAnimationController: KeyguardUnlockAnimationController
@Mock private lateinit var shadeLogger: ShadeLogger
+ @Mock private lateinit var dumpManager: DumpManager
@Mock private lateinit var ambientState: AmbientState
@Mock private lateinit var keyguardBouncerViewModel: KeyguardBouncerViewModel
@Mock private lateinit var stackScrollLayoutController: NotificationStackScrollLayoutController
@@ -155,46 +161,49 @@
fakeClock = FakeSystemClock()
underTest =
NotificationShadeWindowViewController(
- lockscreenShadeTransitionController,
- FalsingCollectorFake(),
- sysuiStatusBarStateController,
- dockManager,
- notificationShadeDepthController,
- view,
- notificationPanelViewController,
- ShadeExpansionStateManager(),
- stackScrollLayoutController,
- statusBarKeyguardViewManager,
- statusBarWindowStateController,
- lockIconViewController,
- centralSurfaces,
- backActionInteractor,
- powerInteractor,
- notificationShadeWindowController,
- unfoldTransitionProgressProvider,
- keyguardUnlockAnimationController,
- notificationInsetsController,
- ambientState,
- shadeLogger,
- pulsingGestureListener,
- mLockscreenHostedDreamGestureListener,
- keyguardBouncerViewModel,
- keyguardBouncerComponentFactory,
- mock(KeyguardMessageAreaController.Factory::class.java),
- keyguardTransitionInteractor,
- primaryBouncerToGoneTransitionViewModel,
- notificationExpansionRepository,
- featureFlags,
- fakeClock,
- BouncerMessageInteractor(
- FakeBouncerMessageRepository(),
- mock(BouncerMessageFactory::class.java),
- FakeUserRepository(),
- CountDownTimerUtil(),
- featureFlags
- ),
- BouncerLogger(logcatLogBuffer("BouncerLog")),
- keyEventInteractor,
+ lockscreenShadeTransitionController,
+ FalsingCollectorFake(),
+ sysuiStatusBarStateController,
+ dockManager,
+ notificationShadeDepthController,
+ view,
+ notificationPanelViewController,
+ ShadeExpansionStateManager(),
+ stackScrollLayoutController,
+ statusBarKeyguardViewManager,
+ statusBarWindowStateController,
+ lockIconViewController,
+ centralSurfaces,
+ dozeServiceHost,
+ dozeScrimController,
+ backActionInteractor,
+ powerInteractor,
+ notificationShadeWindowController,
+ unfoldTransitionProgressProvider,
+ keyguardUnlockAnimationController,
+ notificationInsetsController,
+ ambientState,
+ shadeLogger,
+ dumpManager,
+ pulsingGestureListener,
+ mLockscreenHostedDreamGestureListener,
+ keyguardBouncerViewModel,
+ keyguardBouncerComponentFactory,
+ mock(KeyguardMessageAreaController.Factory::class.java),
+ keyguardTransitionInteractor,
+ primaryBouncerToGoneTransitionViewModel,
+ notificationExpansionRepository,
+ featureFlags,
+ fakeClock,
+ BouncerMessageInteractor(
+ FakeBouncerMessageRepository(),
+ mock(BouncerMessageFactory::class.java),
+ FakeUserRepository(),
+ CountDownTimerUtil(),
+ featureFlags
+ ),
+ BouncerLogger(logcatLogBuffer("BouncerLog")),
+ keyEventInteractor,
)
underTest.setupExpandedStatusBar()
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 66d48d6..3031658 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationShadeWindowViewTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationShadeWindowViewTest.kt
@@ -35,6 +35,7 @@
import com.android.systemui.bouncer.ui.viewmodel.KeyguardBouncerViewModel
import com.android.systemui.classifier.FalsingCollectorFake
import com.android.systemui.dock.DockManager
+import com.android.systemui.dump.DumpManager
import com.android.systemui.dump.logcatLogBuffer
import com.android.systemui.flags.FakeFeatureFlags
import com.android.systemui.flags.Flags
@@ -56,6 +57,8 @@
import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayout
import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayoutController
import com.android.systemui.statusbar.phone.CentralSurfaces
+import com.android.systemui.statusbar.phone.DozeScrimController
+import com.android.systemui.statusbar.phone.DozeServiceHost
import com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager
import com.android.systemui.statusbar.window.StatusBarWindowStateController
import com.android.systemui.unfold.UnfoldTransitionProgressProvider
@@ -89,6 +92,8 @@
@Mock private lateinit var statusBarStateController: SysuiStatusBarStateController
@Mock private lateinit var shadeController: ShadeController
@Mock private lateinit var centralSurfaces: CentralSurfaces
+ @Mock private lateinit var dozeServiceHost: DozeServiceHost
+ @Mock private lateinit var dozeScrimController: DozeScrimController
@Mock private lateinit var backActionInteractor: BackActionInteractor
@Mock private lateinit var powerInteractor: PowerInteractor
@Mock private lateinit var dockManager: DockManager
@@ -107,6 +112,7 @@
@Mock private lateinit var keyguardUnlockAnimationController: KeyguardUnlockAnimationController
@Mock private lateinit var ambientState: AmbientState
@Mock private lateinit var shadeLogger: ShadeLogger
+ @Mock private lateinit var dumpManager: DumpManager
@Mock private lateinit var pulsingGestureListener: PulsingGestureListener
@Mock
private lateinit var mLockscreenHostedDreamGestureListener: LockscreenHostedDreamGestureListener
@@ -174,6 +180,8 @@
statusBarWindowStateController,
lockIconViewController,
centralSurfaces,
+ dozeServiceHost,
+ dozeScrimController,
backActionInteractor,
powerInteractor,
notificationShadeWindowController,
@@ -182,6 +190,7 @@
notificationInsetsController,
ambientState,
shadeLogger,
+ dumpManager,
pulsingGestureListener,
mLockscreenHostedDreamGestureListener,
keyguardBouncerViewModel,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/shade/QuickSettingsControllerBaseTest.java b/packages/SystemUI/tests/src/com/android/systemui/shade/QuickSettingsControllerBaseTest.java
index a2c2912..ab0ae05 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/shade/QuickSettingsControllerBaseTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/shade/QuickSettingsControllerBaseTest.java
@@ -37,7 +37,6 @@
import com.android.keyguard.TestScopeProvider;
import com.android.systemui.R;
import com.android.systemui.SysuiTestCase;
-import com.android.systemui.classifier.FalsingCollector;
import com.android.systemui.dump.DumpManager;
import com.android.systemui.flags.FeatureFlags;
import com.android.systemui.fragments.FragmentHostManager;
@@ -49,7 +48,7 @@
import com.android.systemui.plugins.qs.QS;
import com.android.systemui.qs.QSFragment;
import com.android.systemui.screenrecord.RecordingController;
-import com.android.systemui.shade.data.repository.ShadeRepository;
+import com.android.systemui.shade.data.repository.FakeShadeRepository;
import com.android.systemui.shade.domain.interactor.ShadeInteractor;
import com.android.systemui.shade.transition.ShadeTransitionController;
import com.android.systemui.statusbar.LockscreenShadeTransitionController;
@@ -132,7 +131,6 @@
@Mock protected AmbientState mAmbientState;
@Mock protected RecordingController mRecordingController;
@Mock protected FalsingManager mFalsingManager;
- @Mock protected FalsingCollector mFalsingCollector;
@Mock protected AccessibilityManager mAccessibilityManager;
@Mock protected LockscreenGestureLogger mLockscreenGestureLogger;
@Mock protected MetricsLogger mMetricsLogger;
@@ -147,6 +145,7 @@
protected FakeDisableFlagsRepository mDisableFlagsRepository =
new FakeDisableFlagsRepository();
protected FakeKeyguardRepository mKeyguardRepository = new FakeKeyguardRepository();
+ protected FakeShadeRepository mShadeRepository = new FakeShadeRepository();
protected SysuiStatusBarStateController mStatusBarStateController;
protected ShadeInteractor mShadeInteractor;
@@ -174,7 +173,8 @@
mKeyguardRepository,
new FakeUserSetupRepository(),
mDeviceProvisionedController,
- mUserInteractor
+ mUserInteractor,
+ mShadeRepository
);
KeyguardStatusView keyguardStatusView = new KeyguardStatusView(mContext);
@@ -240,7 +240,6 @@
mAmbientState,
mRecordingController,
mFalsingManager,
- mFalsingCollector,
mAccessibilityManager,
mLockscreenGestureLogger,
mMetricsLogger,
@@ -249,7 +248,7 @@
mShadeLogger,
mDumpManager,
mock(KeyguardFaceAuthInteractor.class),
- mock(ShadeRepository.class),
+ mShadeRepository,
mShadeInteractor,
new JavaAdapter(mTestScope.getBackgroundScope()),
mCastController
diff --git a/packages/SystemUI/tests/src/com/android/systemui/shade/ShadeControllerImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/shade/ShadeControllerImplTest.kt
index 6a14a00..bf2d6a6 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/shade/ShadeControllerImplTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/shade/ShadeControllerImplTest.kt
@@ -20,20 +20,28 @@
import android.view.Display
import android.view.WindowManager
import androidx.test.filters.SmallTest
+import com.android.internal.statusbar.IStatusBarService
+import com.android.keyguard.TestScopeProvider
import com.android.systemui.SysuiTestCase
import com.android.systemui.assist.AssistManager
+import com.android.systemui.keyguard.data.repository.FakeKeyguardRepository
import com.android.systemui.log.LogBuffer
import com.android.systemui.plugins.statusbar.StatusBarStateController
+import com.android.systemui.scene.data.repository.WindowRootViewVisibilityRepository
+import com.android.systemui.scene.domain.interactor.WindowRootViewVisibilityInteractor
import com.android.systemui.statusbar.CommandQueue
import com.android.systemui.statusbar.NotificationShadeWindowController
import com.android.systemui.statusbar.notification.row.NotificationGutsManager
import com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager
import com.android.systemui.statusbar.policy.DeviceProvisionedController
+import com.android.systemui.statusbar.policy.HeadsUpManager
import com.android.systemui.statusbar.policy.KeyguardStateController
import com.android.systemui.statusbar.window.StatusBarWindowController
import com.android.systemui.util.concurrency.FakeExecutor
+import com.android.systemui.util.mockito.mock
import com.android.systemui.util.mockito.whenever
import com.android.systemui.util.time.FakeSystemClock
+import com.google.common.truth.Truth.assertThat
import dagger.Lazy
import org.junit.Before
import org.junit.Test
@@ -47,6 +55,8 @@
@RunWith(AndroidTestingRunner::class)
@SmallTest
class ShadeControllerImplTest : SysuiTestCase() {
+ private val executor = FakeExecutor(FakeSystemClock())
+
@Mock private lateinit var commandQueue: CommandQueue
@Mock private lateinit var keyguardStateController: KeyguardStateController
@Mock private lateinit var statusBarStateController: StatusBarStateController
@@ -61,6 +71,17 @@
@Mock private lateinit var nswvc: NotificationShadeWindowViewController
@Mock private lateinit var display: Display
@Mock private lateinit var touchLog: LogBuffer
+ @Mock private lateinit var iStatusBarService: IStatusBarService
+ @Mock private lateinit var headsUpManager: HeadsUpManager
+
+ private val windowRootViewVisibilityInteractor: WindowRootViewVisibilityInteractor by lazy {
+ WindowRootViewVisibilityInteractor(
+ TestScopeProvider.getTestScope(),
+ WindowRootViewVisibilityRepository(iStatusBarService, executor),
+ FakeKeyguardRepository(),
+ headsUpManager,
+ )
+ }
private lateinit var shadeController: ShadeControllerImpl
@@ -74,6 +95,7 @@
commandQueue,
FakeExecutor(FakeSystemClock()),
touchLog,
+ windowRootViewVisibilityInteractor,
keyguardStateController,
statusBarStateController,
statusBarKeyguardViewManager,
@@ -86,6 +108,7 @@
Lazy { gutsManager },
)
shadeController.setNotificationShadeWindowViewController(nswvc)
+ shadeController.setVisibilityListener(mock())
}
@Test
@@ -133,4 +156,24 @@
// VERIFY that cancelCurrentTouch is NOT called
verify(nswvc, never()).cancelCurrentTouch()
}
+
+ @Test
+ fun visible_changesToTrue_windowInteractorUpdated() {
+ shadeController.makeExpandedVisible(true)
+
+ assertThat(windowRootViewVisibilityInteractor.isLockscreenOrShadeVisible.value).isTrue()
+ }
+
+ @Test
+ fun visible_changesToFalse_windowInteractorUpdated() {
+ // GIVEN the shade is currently expanded
+ shadeController.makeExpandedVisible(true)
+ assertThat(windowRootViewVisibilityInteractor.isLockscreenOrShadeVisible.value).isTrue()
+
+ // WHEN the shade is collapsed
+ shadeController.collapseShade()
+
+ // THEN the interactor is notified
+ assertThat(windowRootViewVisibilityInteractor.isLockscreenOrShadeVisible.value).isFalse()
+ }
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/shade/carrier/ShadeCarrierGroupControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/shade/carrier/ShadeCarrierGroupControllerTest.java
index 5fa6b3a..e7056c7 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/shade/carrier/ShadeCarrierGroupControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/shade/carrier/ShadeCarrierGroupControllerTest.java
@@ -58,6 +58,7 @@
import com.android.systemui.statusbar.pipeline.mobile.ui.viewmodel.MobileIconsViewModel;
import com.android.systemui.statusbar.pipeline.mobile.ui.viewmodel.ShadeCarrierGroupMobileIconViewModel;
import com.android.systemui.util.CarrierConfigTracker;
+import com.android.systemui.util.kotlin.FlowProviderKt;
import com.android.systemui.utils.leaks.LeakCheckedTest;
import com.android.systemui.utils.os.FakeHandler;
@@ -72,6 +73,8 @@
import java.util.Arrays;
import java.util.List;
+import kotlinx.coroutines.flow.MutableStateFlow;
+
@RunWith(AndroidTestingRunner.class)
@TestableLooper.RunWithLooper
@SmallTest
@@ -105,7 +108,8 @@
private ShadeCarrier mShadeCarrier3;
private TestableLooper mTestableLooper;
@Mock
- private ShadeCarrierGroupController.OnSingleCarrierChangedListener mOnSingleCarrierChangedListener;
+ private ShadeCarrierGroupController.OnSingleCarrierChangedListener
+ mOnSingleCarrierChangedListener;
@Mock
private MobileUiAdapter mMobileUiAdapter;
@Mock
@@ -119,6 +123,9 @@
@Mock
private StatusBarPipelineFlags mStatusBarPipelineFlags;
+ private final MutableStateFlow<Boolean> mIsVisibleFlow =
+ FlowProviderKt.getMutableStateFlow(true);
+
private FakeSlotIndexResolver mSlotIndexResolver;
private ClickListenerTextView mNoCarrierTextView;
@@ -170,7 +177,7 @@
mMobileUiAdapter,
mMobileContextProvider,
mStatusBarPipelineFlags
- )
+ )
.setShadeCarrierGroup(mShadeCarrierGroup)
.build();
@@ -181,6 +188,7 @@
when(mStatusBarPipelineFlags.useNewShadeCarrierGroupMobileIcons()).thenReturn(true);
when(mMobileContextProvider.getMobileContextForSub(anyInt(), any())).thenReturn(mContext);
when(mMobileIconsViewModel.getLogger()).thenReturn(mMobileViewLogger);
+ when(mShadeCarrierGroupMobileIconViewModel.isVisible()).thenReturn(mIsVisibleFlow);
when(mMobileIconsViewModel.viewModelForSub(anyInt(), any()))
.thenReturn(mShadeCarrierGroupMobileIconViewModel);
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/shade/data/repository/ShadeInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/shade/data/repository/ShadeInteractorTest.kt
index cdcd1a2..e6e7482 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/shade/data/repository/ShadeInteractorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/shade/data/repository/ShadeInteractorTest.kt
@@ -31,6 +31,7 @@
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.shared.model.StatusBarState
import com.android.systemui.plugins.ActivityStarter
import com.android.systemui.shade.domain.interactor.ShadeInteractor
import com.android.systemui.statusbar.disableflags.data.model.DisableFlagsModel
@@ -69,6 +70,7 @@
private val userRepository = FakeUserRepository()
private val disableFlagsRepository = FakeDisableFlagsRepository()
private val keyguardRepository = FakeKeyguardRepository()
+ private val shadeRepository = FakeShadeRepository()
@Mock private lateinit var manager: UserManager
@Mock private lateinit var headlessSystemUserMode: HeadlessSystemUserMode
@@ -143,6 +145,7 @@
userSetupRepository,
deviceProvisionedController,
userInteractor,
+ shadeRepository,
)
}
@@ -353,4 +356,29 @@
// THEN expand is enabled
assertThat(actual).isTrue()
}
+
+ @Test
+ fun fullShadeExpansionWhenShadeLocked() =
+ testScope.runTest {
+ val actual by collectLastValue(underTest.shadeExpansion)
+
+ keyguardRepository.setStatusBarState(StatusBarState.SHADE_LOCKED)
+ shadeRepository.setShadeExpansion(0.5f)
+
+ assertThat(actual).isEqualTo(1f)
+ }
+
+ @Test
+ fun fullShadeExpansionWhenStatusBarStateIsNotShadeLocked() =
+ testScope.runTest {
+ val actual by collectLastValue(underTest.shadeExpansion)
+
+ keyguardRepository.setStatusBarState(StatusBarState.KEYGUARD)
+
+ shadeRepository.setShadeExpansion(0.5f)
+ assertThat(actual).isEqualTo(0.5f)
+
+ shadeRepository.setShadeExpansion(0.8f)
+ assertThat(actual).isEqualTo(0.8f)
+ }
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/shade/data/repository/ShadeRepositoryImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/shade/data/repository/ShadeRepositoryImplTest.kt
index e26a8bd..fdaea22 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/shade/data/repository/ShadeRepositoryImplTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/shade/data/repository/ShadeRepositoryImplTest.kt
@@ -58,9 +58,8 @@
MockitoAnnotations.initMocks(this)
underTest = ShadeRepositoryImpl(shadeExpansionStateManager)
- `when`(shadeExpansionStateManager.addExpansionListener(any())).thenReturn(
- ShadeExpansionChangeEvent(0f, false, false, 0f)
- )
+ `when`(shadeExpansionStateManager.addExpansionListener(any()))
+ .thenReturn(ShadeExpansionChangeEvent(0f, false, false, 0f))
}
@Test
@@ -122,6 +121,21 @@
}
@Test
+ fun updateShadeExpansion() =
+ testScope.runTest {
+ assertThat(underTest.shadeExpansion.value).isEqualTo(0f)
+
+ underTest.setShadeExpansion(.5f)
+ assertThat(underTest.shadeExpansion.value).isEqualTo(.5f)
+
+ underTest.setShadeExpansion(.82f)
+ assertThat(underTest.shadeExpansion.value).isEqualTo(.82f)
+
+ underTest.setShadeExpansion(1f)
+ assertThat(underTest.shadeExpansion.value).isEqualTo(1f)
+ }
+
+ @Test
fun updateUdfpsTransitionToFullShadeProgress() =
testScope.runTest {
assertThat(underTest.udfpsTransitionToFullShadeProgress.value).isEqualTo(0f)
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 4a2518a..51e72c6 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/LockscreenShadeTransitionControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/LockscreenShadeTransitionControllerTest.kt
@@ -60,8 +60,8 @@
import org.mockito.Mockito.never
import org.mockito.Mockito.verify
import org.mockito.Mockito.verifyZeroInteractions
-import org.mockito.junit.MockitoJUnit
import org.mockito.Mockito.`when` as whenever
+import org.mockito.junit.MockitoJUnit
private fun <T> anyObject(): T {
return Mockito.anyObject<T>()
@@ -102,21 +102,24 @@
@Mock lateinit var transitionControllerCallback: LockscreenShadeTransitionController.Callback
private val disableFlagsRepository = FakeDisableFlagsRepository()
private val keyguardRepository = FakeKeyguardRepository()
- private val shadeInteractor = ShadeInteractor(
- testScope.backgroundScope,
- disableFlagsRepository,
- keyguardRepository,
- userSetupRepository = FakeUserSetupRepository(),
- deviceProvisionedController = mock(),
- userInteractor = mock(),
- )
- private val powerInteractor = PowerInteractor(
- FakePowerRepository(),
- keyguardRepository,
- FalsingCollectorFake(),
- screenOffAnimationController = mock(),
- statusBarStateController = mock(),
- )
+ private val shadeInteractor =
+ ShadeInteractor(
+ testScope.backgroundScope,
+ disableFlagsRepository,
+ keyguardRepository,
+ userSetupRepository = FakeUserSetupRepository(),
+ deviceProvisionedController = mock(),
+ userInteractor = mock(),
+ repository = FakeShadeRepository(),
+ )
+ private val powerInteractor =
+ PowerInteractor(
+ FakePowerRepository(),
+ keyguardRepository,
+ FalsingCollectorFake(),
+ screenOffAnimationController = mock(),
+ statusBarStateController = mock(),
+ )
@JvmField @Rule val mockito = MockitoJUnit.rule()
private val configurationController = FakeConfigurationController()
@@ -127,14 +130,13 @@
disableFlagsRepository.disableFlags.value = DisableFlagsModel()
testScope.runCurrent()
- val helper = NotificationTestHelper(
- mContext,
- mDependency,
- TestableLooper.get(this))
+ val helper = NotificationTestHelper(mContext, mDependency, TestableLooper.get(this))
row = helper.createRow()
- context.getOrCreateTestableResources()
- .addOverride(R.bool.config_use_split_notification_shade, false)
- context.getOrCreateTestableResources()
+ context
+ .getOrCreateTestableResources()
+ .addOverride(R.bool.config_use_split_notification_shade, false)
+ context
+ .getOrCreateTestableResources()
.addOverride(R.dimen.lockscreen_shade_depth_controller_transition_distance, 100)
transitionController =
LockscreenShadeTransitionController(
@@ -154,15 +156,20 @@
splitShadeOverScrollerFactory = { _, _ -> splitShadeOverScroller },
singleShadeOverScrollerFactory = { singleShadeOverScroller },
scrimTransitionController =
- LockscreenShadeScrimTransitionController(
- scrimController, context, configurationController, dumpManager),
+ LockscreenShadeScrimTransitionController(
+ scrimController,
+ context,
+ configurationController,
+ dumpManager
+ ),
keyguardTransitionControllerFactory = { notificationPanelController ->
LockscreenShadeKeyguardTransitionController(
mediaHierarchyManager,
notificationPanelController,
context,
configurationController,
- dumpManager)
+ dumpManager
+ )
},
qsTransitionControllerFactory = { qsTransitionController },
activityStarter = activityStarter,
@@ -180,8 +187,8 @@
whenever(statusbarStateController.state).thenReturn(StatusBarState.KEYGUARD)
whenever(nsslController.isInLockedDownShade).thenReturn(false)
whenever(qS.isFullyCollapsed).thenReturn(true)
- whenever(lockScreenUserManager.userAllowsPrivateNotificationsInPublic(anyInt())).thenReturn(
- true)
+ whenever(lockScreenUserManager.userAllowsPrivateNotificationsInPublic(anyInt()))
+ .thenReturn(true)
whenever(lockScreenUserManager.shouldShowLockscreenNotifications()).thenReturn(true)
whenever(lockScreenUserManager.isLockscreenPublicMode(anyInt())).thenReturn(true)
whenever(falsingCollector.shouldEnforceBouncer()).thenReturn(false)
@@ -228,8 +235,10 @@
whenever(statusbarStateController.isDozing).thenReturn(false)
transitionController.goToLockedShade(null)
verify(statusbarStateController).setState(StatusBarState.SHADE_LOCKED)
- assertFalse("Waking to shade locked when not dozing",
- transitionController.isWakingToShadeLocked)
+ assertFalse(
+ "Waking to shade locked when not dozing",
+ transitionController.isWakingToShadeLocked
+ )
}
@Test
@@ -243,9 +252,10 @@
@Test
fun testDontGoWhenShadeDisabled() {
- disableFlagsRepository.disableFlags.value = DisableFlagsModel(
- disable2 = DISABLE2_NOTIFICATION_SHADE,
- )
+ disableFlagsRepository.disableFlags.value =
+ DisableFlagsModel(
+ disable2 = DISABLE2_NOTIFICATION_SHADE,
+ )
testScope.runCurrent()
transitionController.goToLockedShade(null)
verify(statusbarStateController, never()).setState(anyInt())
@@ -298,8 +308,8 @@
verify(nsslController, never()).setTransitionToFullShadeAmount(anyFloat())
verify(mediaHierarchyManager, never()).setTransitionToFullShadeAmount(anyFloat())
verify(scrimController, never()).setTransitionToFullShadeProgress(anyFloat(), anyFloat())
- verify(transitionControllerCallback, never()).setTransitionToFullShadeAmount(anyFloat(),
- anyBoolean(), anyLong())
+ verify(transitionControllerCallback, never())
+ .setTransitionToFullShadeAmount(anyFloat(), anyBoolean(), anyLong())
verify(qsTransitionController, never()).dragDownAmount = anyFloat()
}
@@ -309,15 +319,16 @@
verify(nsslController).setTransitionToFullShadeAmount(anyFloat())
verify(mediaHierarchyManager).setTransitionToFullShadeAmount(anyFloat())
verify(scrimController).setTransitionToFullShadeProgress(anyFloat(), anyFloat())
- verify(transitionControllerCallback).setTransitionToFullShadeAmount(anyFloat(),
- anyBoolean(), anyLong())
+ verify(transitionControllerCallback)
+ .setTransitionToFullShadeAmount(anyFloat(), anyBoolean(), anyLong())
verify(qsTransitionController).dragDownAmount = 10f
verify(depthController).transitionToFullShadeProgress = anyFloat()
}
@Test
fun testDragDownAmount_depthDistanceIsZero_setsProgressToZero() {
- context.getOrCreateTestableResources()
+ context
+ .getOrCreateTestableResources()
.addOverride(R.dimen.lockscreen_shade_depth_controller_transition_distance, 0)
configurationController.notifyConfigurationChanged()
@@ -328,7 +339,8 @@
@Test
fun testDragDownAmount_depthDistanceNonZero_setsProgressBasedOnDistance() {
- context.getOrCreateTestableResources()
+ context
+ .getOrCreateTestableResources()
.addOverride(R.dimen.lockscreen_shade_depth_controller_transition_distance, 100)
configurationController.notifyConfigurationChanged()
@@ -346,13 +358,14 @@
@Test
fun setDragAmount_setsKeyguardAlphaBasedOnDistance() {
- val alphaDistance = context.resources.getDimensionPixelSize(
- R.dimen.lockscreen_shade_npvc_keyguard_content_alpha_transition_distance)
+ val alphaDistance =
+ context.resources.getDimensionPixelSize(
+ R.dimen.lockscreen_shade_npvc_keyguard_content_alpha_transition_distance
+ )
transitionController.dragDownAmount = 10f
val expectedAlpha = 1 - 10f / alphaDistance
- verify(shadeViewController)
- .setKeyguardTransitionProgress(eq(expectedAlpha), anyInt())
+ verify(shadeViewController).setKeyguardTransitionProgress(eq(expectedAlpha), anyInt())
}
@Test
@@ -378,8 +391,7 @@
transitionController.dragDownAmount = 10f
- verify(shadeViewController)
- .setKeyguardTransitionProgress(anyFloat(), eq(mediaTranslationY))
+ verify(shadeViewController).setKeyguardTransitionProgress(anyFloat(), eq(mediaTranslationY))
}
@Test
@@ -392,10 +404,12 @@
val distance =
context.resources.getDimensionPixelSize(
- R.dimen.lockscreen_shade_keyguard_transition_distance)
+ R.dimen.lockscreen_shade_keyguard_transition_distance
+ )
val offset =
context.resources.getDimensionPixelSize(
- R.dimen.lockscreen_shade_keyguard_transition_vertical_offset)
+ R.dimen.lockscreen_shade_keyguard_transition_vertical_offset
+ )
val expectedTranslation = 10f / distance * offset
verify(shadeViewController)
.setKeyguardTransitionProgress(anyFloat(), eq(expectedTranslation.toInt()))
@@ -411,16 +425,19 @@
@Test
fun setDragAmount_setsScrimProgressBasedOnScrimDistance() {
val distance = 10
- context.orCreateTestableResources
- .addOverride(R.dimen.lockscreen_shade_scrim_transition_distance, distance)
+ context.orCreateTestableResources.addOverride(
+ R.dimen.lockscreen_shade_scrim_transition_distance,
+ distance
+ )
configurationController.notifyConfigurationChanged()
transitionController.dragDownAmount = 5f
- verify(scrimController).transitionToFullShadeProgress(
+ verify(scrimController)
+ .transitionToFullShadeProgress(
progress = eq(0.5f),
lockScreenNotificationsProgress = anyFloat()
- )
+ )
}
@Test
@@ -428,17 +445,22 @@
val distance = 100
val delay = 10
context.orCreateTestableResources.addOverride(
- R.dimen.lockscreen_shade_notifications_scrim_transition_distance, distance)
+ R.dimen.lockscreen_shade_notifications_scrim_transition_distance,
+ distance
+ )
context.orCreateTestableResources.addOverride(
- R.dimen.lockscreen_shade_notifications_scrim_transition_delay, delay)
+ R.dimen.lockscreen_shade_notifications_scrim_transition_delay,
+ delay
+ )
configurationController.notifyConfigurationChanged()
transitionController.dragDownAmount = 20f
- verify(scrimController).transitionToFullShadeProgress(
+ verify(scrimController)
+ .transitionToFullShadeProgress(
progress = anyFloat(),
lockScreenNotificationsProgress = eq(0.1f)
- )
+ )
}
@Test
@@ -446,17 +468,22 @@
val distance = 100
val delay = 50
context.orCreateTestableResources.addOverride(
- R.dimen.lockscreen_shade_notifications_scrim_transition_distance, distance)
+ R.dimen.lockscreen_shade_notifications_scrim_transition_distance,
+ distance
+ )
context.orCreateTestableResources.addOverride(
- R.dimen.lockscreen_shade_notifications_scrim_transition_delay, delay)
+ R.dimen.lockscreen_shade_notifications_scrim_transition_delay,
+ delay
+ )
configurationController.notifyConfigurationChanged()
transitionController.dragDownAmount = 20f
- verify(scrimController).transitionToFullShadeProgress(
+ verify(scrimController)
+ .transitionToFullShadeProgress(
progress = anyFloat(),
lockScreenNotificationsProgress = eq(0f)
- )
+ )
}
@Test
@@ -464,17 +491,22 @@
val distance = 100
val delay = 50
context.orCreateTestableResources.addOverride(
- R.dimen.lockscreen_shade_notifications_scrim_transition_distance, distance)
+ R.dimen.lockscreen_shade_notifications_scrim_transition_distance,
+ distance
+ )
context.orCreateTestableResources.addOverride(
- R.dimen.lockscreen_shade_notifications_scrim_transition_delay, delay)
+ R.dimen.lockscreen_shade_notifications_scrim_transition_delay,
+ delay
+ )
configurationController.notifyConfigurationChanged()
transitionController.dragDownAmount = 999999f
- verify(scrimController).transitionToFullShadeProgress(
+ verify(scrimController)
+ .transitionToFullShadeProgress(
progress = anyFloat(),
lockScreenNotificationsProgress = eq(1f)
- )
+ )
}
@Test
@@ -508,8 +540,10 @@
@Test
fun setDragDownAmount_inSplitShade_setsKeyguardStatusBarAlphaBasedOnDistance() {
- val alphaDistance = context.resources.getDimensionPixelSize(
- R.dimen.lockscreen_shade_npvc_keyguard_content_alpha_transition_distance)
+ val alphaDistance =
+ context.resources.getDimensionPixelSize(
+ R.dimen.lockscreen_shade_npvc_keyguard_content_alpha_transition_distance
+ )
val dragDownAmount = 10f
enableSplitShade()
@@ -549,9 +583,6 @@
progress: Float,
lockScreenNotificationsProgress: Float
) {
- scrimController.setTransitionToFullShadeProgress(
- progress,
- lockScreenNotificationsProgress
- )
+ scrimController.setTransitionToFullShadeProgress(progress, lockScreenNotificationsProgress)
}
}
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 786856b..66d2465 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
@@ -20,6 +20,7 @@
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.display.domain.interactor.ConnectedDisplayInteractor
+import com.android.systemui.display.domain.interactor.ConnectedDisplayInteractor.PendingDisplay
import com.android.systemui.display.domain.interactor.ConnectedDisplayInteractor.State.CONNECTED
import com.android.systemui.flags.FakeFeatureFlags
import com.android.systemui.privacy.PrivacyItemController
@@ -105,5 +106,7 @@
suspend fun emit(value: ConnectedDisplayInteractor.State) = flow.emit(value)
override val connectedDisplayState: Flow<ConnectedDisplayInteractor.State>
get() = flow
+ override val pendingDisplay: Flow<PendingDisplay?>
+ get() = MutableSharedFlow<PendingDisplay>()
}
}
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 6be2fa5..4fcccf8 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
@@ -93,9 +93,6 @@
fakeFeatureFlags
)
- // ensure that isTooEarly() check in SystemStatusAnimationScheduler does not return true
- systemClock.advanceTime(Process.getStartUptimeMillis() + MIN_UPTIME)
-
// StatusBarContentInsetProvider is mocked. Ensure that it returns some mocked values.
whenever(statusBarContentInsetProvider.getStatusBarContentInsetsForCurrentRotation())
.thenReturn(android.util.Pair(10, 10))
@@ -156,6 +153,21 @@
assertEquals(0f, batteryChip.view.alpha)
}
+ /** Regression test for b/294104969. */
+ @Test
+ fun testPrivacyStatusEvent_beforeSystemUptime_stillDisplayed() = runTest {
+ initializeSystemStatusAnimationScheduler(testScope = this, advancePastMinUptime = false)
+
+ // WHEN the uptime hasn't quite passed the minimum required uptime...
+ systemClock.setUptimeMillis(Process.getStartUptimeMillis() + MIN_UPTIME / 2)
+
+ // BUT the event is a privacy event
+ createAndScheduleFakePrivacyEvent()
+
+ // THEN the privacy event still happens
+ assertEquals(ANIMATION_QUEUED, systemStatusAnimationScheduler.getAnimationState())
+ }
+
@Test
fun testPrivacyStatusEvent_standardAnimationLifecycle() = runTest {
// Instantiate class under test with TestScope from runTest
@@ -568,7 +580,10 @@
return batteryChip
}
- private fun initializeSystemStatusAnimationScheduler(testScope: TestScope) {
+ private fun initializeSystemStatusAnimationScheduler(
+ testScope: TestScope,
+ advancePastMinUptime: Boolean = true,
+ ) {
systemStatusAnimationScheduler =
SystemStatusAnimationSchedulerImpl(
systemEventCoordinator,
@@ -581,5 +596,10 @@
)
// add a mock listener
systemStatusAnimationScheduler.addCallback(listener)
+
+ if (advancePastMinUptime) {
+ // ensure that isTooEarly() check in SystemStatusAnimationScheduler does not return true
+ systemClock.advanceTime(Process.getStartUptimeMillis() + MIN_UPTIME)
+ }
}
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationLaunchAnimatorControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationLaunchAnimatorControllerTest.kt
index e66eb70..470d340 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationLaunchAnimatorControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationLaunchAnimatorControllerTest.kt
@@ -1,5 +1,6 @@
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
@@ -7,20 +8,26 @@
import com.android.internal.jank.InteractionJankMonitor
import com.android.systemui.SysuiTestCase
import com.android.systemui.coroutines.collectLastValue
+import com.android.systemui.statusbar.notification.collection.GroupEntryBuilder
+import com.android.systemui.statusbar.notification.collection.NotificationEntryBuilder
import com.android.systemui.statusbar.notification.data.repository.NotificationExpansionRepository
import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow
import com.android.systemui.statusbar.notification.row.NotificationTestHelper
import com.android.systemui.statusbar.notification.stack.NotificationListContainer
import com.android.systemui.statusbar.phone.HeadsUpManagerPhone
import com.android.systemui.statusbar.policy.HeadsUpUtil
+import com.android.systemui.tests.R
import junit.framework.Assert.assertFalse
import junit.framework.Assert.assertTrue
import kotlinx.coroutines.test.TestScope
+import org.junit.Assert.assertNotSame
+import org.junit.Assert.assertSame
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.never
import org.mockito.Mockito.verify
import org.mockito.Mockito.`when`
import org.mockito.junit.MockitoJUnit
@@ -118,6 +125,35 @@
}
@Test
+ fun testAlertingSummaryHunRemovedOnNonAlertingChildLaunch() {
+ val GROUP_KEY = "test_group_key"
+
+ val summary = NotificationEntryBuilder().setGroup(mContext, GROUP_KEY).setId(0).apply {
+ modifyNotification(mContext).setSmallIcon(R.drawable.ic_person)
+ }.build()
+ assertNotSame(summary.key, notification.entry.key)
+
+ notificationTestHelper.createRow(summary)
+
+ GroupEntryBuilder().setKey(GROUP_KEY).setSummary(summary).addChild(notification.entry)
+ .build()
+ assertSame(summary, notification.entry.parent?.summary)
+
+ `when`(headsUpManager.isAlerting(notificationKey)).thenReturn(false)
+ `when`(headsUpManager.isAlerting(summary.key)).thenReturn(true)
+
+ assertNotSame(GROUP_ALERT_SUMMARY, summary.sbn.notification.groupAlertBehavior)
+ assertNotSame(GROUP_ALERT_SUMMARY, notification.entry.sbn.notification.groupAlertBehavior)
+
+ controller.onLaunchAnimationEnd(isExpandingFullyAbove = true)
+
+ verify(headsUpManager).removeNotification(
+ summary.key, true /* releaseImmediately */, false /* animate */)
+ verify(headsUpManager, never()).removeNotification(
+ notification.entry.key, true /* releaseImmediately */, false /* animate */)
+ }
+
+ @Test
fun testNotificationIsExpandingDuringAnimation() {
controller.onIntentStarted(willAnimate = true)
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 7117c23..bbf0151 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
@@ -40,8 +40,14 @@
import com.android.internal.logging.InstanceId;
import com.android.internal.statusbar.IStatusBarService;
import com.android.internal.statusbar.NotificationVisibility;
+import com.android.keyguard.TestScopeProvider;
import com.android.systemui.SysuiTestCase;
-import com.android.systemui.shade.ShadeExpansionStateManager;
+import com.android.systemui.keyguard.data.repository.FakeKeyguardRepository;
+import com.android.systemui.keyguard.shared.model.WakeSleepReason;
+import com.android.systemui.keyguard.shared.model.WakefulnessModel;
+import com.android.systemui.keyguard.shared.model.WakefulnessState;
+import com.android.systemui.scene.data.repository.WindowRootViewVisibilityRepository;
+import com.android.systemui.scene.domain.interactor.WindowRootViewVisibilityInteractor;
import com.android.systemui.statusbar.NotificationListener;
import com.android.systemui.statusbar.StatusBarState;
import com.android.systemui.statusbar.StatusBarStateControllerImpl;
@@ -54,7 +60,9 @@
import com.android.systemui.statusbar.notification.logging.nano.Notifications;
import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
import com.android.systemui.statusbar.notification.stack.NotificationListContainer;
+import com.android.systemui.statusbar.policy.HeadsUpManager;
import com.android.systemui.util.concurrency.FakeExecutor;
+import com.android.systemui.util.kotlin.JavaAdapter;
import com.android.systemui.util.time.FakeSystemClock;
import com.google.android.collect.Lists;
@@ -71,6 +79,8 @@
import java.util.concurrent.ConcurrentLinkedQueue;
import java.util.concurrent.Executor;
+import kotlinx.coroutines.test.TestScope;
+
@SmallTest
@RunWith(AndroidTestingRunner.class)
@TestableLooper.RunWithLooper
@@ -89,7 +99,7 @@
@Mock private NotificationVisibilityProvider mVisibilityProvider;
@Mock private NotifPipeline mNotifPipeline;
@Mock private NotificationListener mListener;
- @Mock private ShadeExpansionStateManager mShadeExpansionStateManager;
+ @Mock private HeadsUpManager mHeadsUpManager;
private NotificationEntry mEntry;
private TestableNotificationLogger mLogger;
@@ -97,12 +107,23 @@
private FakeExecutor mUiBgExecutor = new FakeExecutor(new FakeSystemClock());
private NotificationPanelLoggerFake mNotificationPanelLoggerFake =
new NotificationPanelLoggerFake();
+ private final TestScope mTestScope = TestScopeProvider.getTestScope();
+ private final FakeKeyguardRepository mKeyguardRepository = new FakeKeyguardRepository();
+ private WindowRootViewVisibilityInteractor mWindowRootViewVisibilityInteractor;
+ private final JavaAdapter mJavaAdapter = new JavaAdapter(mTestScope.getBackgroundScope());
@Before
public void setUp() {
MockitoAnnotations.initMocks(this);
when(mNotifLiveDataStore.getActiveNotifList()).thenReturn(mActiveNotifEntries);
+ mWindowRootViewVisibilityInteractor = new WindowRootViewVisibilityInteractor(
+ mTestScope.getBackgroundScope(),
+ new WindowRootViewVisibilityRepository(mBarService, mUiBgExecutor),
+ mKeyguardRepository,
+ mHeadsUpManager);
+ mWindowRootViewVisibilityInteractor.setIsLockscreenOrShadeVisible(true);
+
mEntry = new NotificationEntryBuilder()
.setPkg(TEST_PACKAGE_NAME)
.setOpPkg(TEST_PACKAGE_NAME)
@@ -120,10 +141,12 @@
mVisibilityProvider,
mNotifPipeline,
mock(StatusBarStateControllerImpl.class),
- mShadeExpansionStateManager,
+ mWindowRootViewVisibilityInteractor,
+ mJavaAdapter,
mBarService,
mExpansionStateLogger
);
+ mLogger.start();
mLogger.setUpWithContainer(mListContainer);
verify(mNotifPipeline).addCollectionListener(any());
}
@@ -183,31 +206,26 @@
Mockito.reset(mBarService);
setStateAsleep();
- mLogger.onDozingChanged(false); // Wake to lockscreen
- mLogger.onDozingChanged(true); // And go back to sleep, turning off logging
+
+ setStateAwake(); // Wake to lockscreen
+
+ setStateAsleep(); // And go back to sleep, turning off logging
mUiBgExecutor.runAllReady();
+
// The visibility objects are recycled by NotificationLogger, so we can't use specific
// matchers here.
verify(mBarService, times(1)).onNotificationVisibilityChanged(any(), any());
}
- private void setStateAsleep() {
- mLogger.onShadeExpansionFullyChanged(true);
- mLogger.onDozingChanged(true);
- mLogger.onStateChanged(StatusBarState.KEYGUARD);
- }
-
- private void setStateAwake() {
- mLogger.onShadeExpansionFullyChanged(false);
- mLogger.onDozingChanged(false);
- mLogger.onStateChanged(StatusBarState.SHADE);
- }
-
@Test
- public void testLogPanelShownOnWake() {
+ public void testLogPanelShownOnWakeToLockscreen() {
when(mActiveNotifEntries.getValue()).thenReturn(Lists.newArrayList(mEntry));
setStateAsleep();
- mLogger.onDozingChanged(false); // Wake to lockscreen
+
+ // Wake to lockscreen
+ mLogger.onStateChanged(StatusBarState.KEYGUARD);
+ setStateAwake();
+
assertEquals(1, mNotificationPanelLoggerFake.getCalls().size());
assertTrue(mNotificationPanelLoggerFake.get(0).isLockscreen);
assertEquals(1, mNotificationPanelLoggerFake.get(0).list.notifications.length);
@@ -222,9 +240,14 @@
@Test
public void testLogPanelShownOnShadePull() {
when(mActiveNotifEntries.getValue()).thenReturn(Lists.newArrayList(mEntry));
+ // Start as awake, but with the panel not visible
setStateAwake();
+ mWindowRootViewVisibilityInteractor.setIsLockscreenOrShadeVisible(false);
+
// Now expand panel
- mLogger.onShadeExpansionFullyChanged(true);
+ mLogger.onStateChanged(StatusBarState.SHADE);
+ mWindowRootViewVisibilityInteractor.setIsLockscreenOrShadeVisible(true);
+
assertEquals(1, mNotificationPanelLoggerFake.getCalls().size());
assertFalse(mNotificationPanelLoggerFake.get(0).isLockscreen);
assertEquals(1, mNotificationPanelLoggerFake.get(0).list.notifications.length);
@@ -251,13 +274,34 @@
when(mActiveNotifEntries.getValue()).thenReturn(Lists.newArrayList(entry));
setStateAsleep();
- mLogger.onDozingChanged(false); // Wake to lockscreen
+
+ // Wake to lockscreen
+ setStateAwake();
+
assertEquals(1, mNotificationPanelLoggerFake.getCalls().size());
assertEquals(1, mNotificationPanelLoggerFake.get(0).list.notifications.length);
Notifications.Notification n = mNotificationPanelLoggerFake.get(0).list.notifications[0];
assertEquals(0, n.instanceId);
}
+ private void setStateAsleep() {
+ mKeyguardRepository.setWakefulnessModel(
+ new WakefulnessModel(
+ WakefulnessState.ASLEEP,
+ WakeSleepReason.OTHER,
+ WakeSleepReason.OTHER));
+ mTestScope.getTestScheduler().runCurrent();
+ }
+
+ private void setStateAwake() {
+ mKeyguardRepository.setWakefulnessModel(
+ new WakefulnessModel(
+ WakefulnessState.AWAKE,
+ WakeSleepReason.OTHER,
+ WakeSleepReason.OTHER));
+ mTestScope.getTestScheduler().runCurrent();
+ }
+
private class TestableNotificationLogger extends NotificationLogger {
TestableNotificationLogger(NotificationListener notificationListener,
@@ -266,7 +310,8 @@
NotificationVisibilityProvider visibilityProvider,
NotifPipeline notifPipeline,
StatusBarStateControllerImpl statusBarStateController,
- ShadeExpansionStateManager shadeExpansionStateManager,
+ WindowRootViewVisibilityInteractor windowRootViewVisibilityInteractor,
+ JavaAdapter javaAdapter,
IStatusBarService barService,
ExpansionStateLogger expansionStateLogger) {
super(
@@ -276,7 +321,8 @@
visibilityProvider,
notifPipeline,
statusBarStateController,
- shadeExpansionStateManager,
+ windowRootViewVisibilityInteractor,
+ javaAdapter,
expansionStateLogger,
mNotificationPanelLoggerFake
);
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 0cc0b98..7c8199e 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
@@ -27,7 +27,6 @@
import com.android.internal.logging.MetricsLogger
import com.android.internal.statusbar.IStatusBarService
import com.android.systemui.SysuiTestCase
-import com.android.systemui.classifier.FalsingCollector
import com.android.systemui.dump.logcatLogBuffer
import com.android.systemui.flags.FeatureFlags
import com.android.systemui.plugins.FalsingManager
@@ -57,6 +56,7 @@
import com.android.systemui.util.mockito.withArgCaptor
import com.android.systemui.util.time.SystemClock
import com.android.systemui.wmshell.BubblesManager
+import java.util.Optional
import junit.framework.Assert
import org.junit.After
import org.junit.Before
@@ -68,7 +68,6 @@
import org.mockito.Mockito.never
import org.mockito.Mockito.verify
import org.mockito.Mockito.`when` as whenever
-import java.util.Optional
@SmallTest
@RunWith(AndroidTestingRunner::class)
@@ -100,7 +99,6 @@
private val gutsManager: NotificationGutsManager = mock()
private val onUserInteractionCallback: OnUserInteractionCallback = mock()
private val falsingManager: FalsingManager = mock()
- private val falsingCollector: FalsingCollector = mock()
private val featureFlags: FeatureFlags = mock()
private val peopleNotificationIdentifier: PeopleNotificationIdentifier = mock()
private val bubblesManager: BubblesManager = mock()
@@ -140,7 +138,6 @@
/*allowLongPress=*/ false,
onUserInteractionCallback,
falsingManager,
- falsingCollector,
featureFlags,
peopleNotificationIdentifier,
Optional.of(bubblesManager),
@@ -226,20 +223,20 @@
fun registerSettingsListener_forBubbles() {
controller.init(mock(NotificationEntry::class.java))
val viewStateObserver = withArgCaptor {
- verify(view).addOnAttachStateChangeListener(capture());
+ verify(view).addOnAttachStateChangeListener(capture())
}
- viewStateObserver.onViewAttachedToWindow(view);
- verify(settingsController).addCallback(any(), any());
+ viewStateObserver.onViewAttachedToWindow(view)
+ verify(settingsController).addCallback(any(), any())
}
@Test
fun unregisterSettingsListener_forBubbles() {
controller.init(mock(NotificationEntry::class.java))
val viewStateObserver = withArgCaptor {
- verify(view).addOnAttachStateChangeListener(capture());
+ verify(view).addOnAttachStateChangeListener(capture())
}
- viewStateObserver.onViewDetachedFromWindow(view);
- verify(settingsController).removeCallback(any(), any());
+ viewStateObserver.onViewDetachedFromWindow(view)
+ verify(settingsController).removeCallback(any(), any())
}
@Test
@@ -263,11 +260,17 @@
whenever(view.privateLayout).thenReturn(childView)
controller.mSettingsListener.onSettingChanged(
- BUBBLES_SETTING_URI, view.entry.sbn.userId, "1")
+ BUBBLES_SETTING_URI,
+ view.entry.sbn.userId,
+ "1"
+ )
verify(childView).setBubblesEnabledForUser(true)
controller.mSettingsListener.onSettingChanged(
- BUBBLES_SETTING_URI, view.entry.sbn.userId, "9")
+ BUBBLES_SETTING_URI,
+ view.entry.sbn.userId,
+ "9"
+ )
verify(childView).setBubblesEnabledForUser(false)
}
@@ -277,13 +280,12 @@
whenever(view.privateLayout).thenReturn(childView)
val notification = Notification.Builder(mContext).build()
- val sbn = SbnBuilder().setNotification(notification)
- .setUser(UserHandle.of(USER_ALL))
- .build()
- whenever(view.entry).thenReturn(NotificationEntryBuilder()
- .setSbn(sbn)
- .setUser(UserHandle.of(USER_ALL))
- .build())
+ val sbn =
+ SbnBuilder().setNotification(notification).setUser(UserHandle.of(USER_ALL)).build()
+ whenever(view.entry)
+ .thenReturn(
+ NotificationEntryBuilder().setSbn(sbn).setUser(UserHandle.of(USER_ALL)).build()
+ )
controller.mSettingsListener.onSettingChanged(BUBBLES_SETTING_URI, 9, "1")
verify(childView).setBubblesEnabledForUser(true)
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 705d52b..9e0f83c 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
@@ -37,6 +37,7 @@
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.doNothing;
import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.never;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
@@ -66,11 +67,16 @@
import com.android.internal.logging.MetricsLogger;
import com.android.internal.logging.UiEventLogger;
import com.android.internal.logging.testing.UiEventLoggerFake;
+import com.android.internal.statusbar.IStatusBarService;
+import com.android.keyguard.TestScopeProvider;
import com.android.systemui.SysuiTestCase;
+import com.android.systemui.keyguard.data.repository.FakeKeyguardRepository;
import com.android.systemui.people.widget.PeopleSpaceWidgetManager;
import com.android.systemui.plugins.ActivityStarter;
import com.android.systemui.plugins.statusbar.NotificationMenuRowPlugin;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
+import com.android.systemui.scene.data.repository.WindowRootViewVisibilityRepository;
+import com.android.systemui.scene.domain.interactor.WindowRootViewVisibilityInteractor;
import com.android.systemui.settings.UserContextProvider;
import com.android.systemui.shade.ShadeController;
import com.android.systemui.statusbar.NotificationLockscreenUserManager;
@@ -84,6 +90,9 @@
import com.android.systemui.statusbar.notification.stack.NotificationListContainer;
import com.android.systemui.statusbar.phone.HeadsUpManagerPhone;
import com.android.systemui.statusbar.policy.DeviceProvisionedController;
+import com.android.systemui.util.concurrency.FakeExecutor;
+import com.android.systemui.util.kotlin.JavaAdapter;
+import com.android.systemui.util.time.FakeSystemClock;
import com.android.systemui.wmshell.BubblesManager;
import org.junit.Before;
@@ -97,6 +106,8 @@
import java.util.Optional;
+import kotlinx.coroutines.test.TestScope;
+
/**
* Tests for {@link NotificationGutsManager}.
*/
@@ -108,6 +119,10 @@
private NotificationChannel mTestNotificationChannel = new NotificationChannel(
TEST_CHANNEL_ID, TEST_CHANNEL_ID, IMPORTANCE_DEFAULT);
+
+ private TestScope mTestScope = TestScopeProvider.getTestScope();
+ private JavaAdapter mJavaAdapter = new JavaAdapter(mTestScope.getBackgroundScope());
+ private FakeExecutor mExecutor = new FakeExecutor(new FakeSystemClock());
private TestableLooper mTestableLooper;
private Handler mHandler;
private NotificationTestHelper mHelper;
@@ -124,6 +139,7 @@
@Mock private AccessibilityManager mAccessibilityManager;
@Mock private HighPriorityProvider mHighPriorityProvider;
@Mock private INotificationManager mINotificationManager;
+ @Mock private IStatusBarService mBarService;
@Mock private LauncherApps mLauncherApps;
@Mock private ShortcutManager mShortcutManager;
@Mock private ChannelEditorDialogController mChannelEditorDialogController;
@@ -140,6 +156,8 @@
@Mock private UserManager mUserManager;
+ private WindowRootViewVisibilityInteractor mWindowRootViewVisibilityInteractor;
+
@Before
public void setUp() {
mTestableLooper = TestableLooper.get(this);
@@ -148,21 +166,42 @@
mHelper = new NotificationTestHelper(mContext, mDependency, TestableLooper.get(this));
when(mAccessibilityManager.isTouchExplorationEnabled()).thenReturn(false);
- mGutsManager = new NotificationGutsManager(mContext, mHandler, mHandler,
+ mWindowRootViewVisibilityInteractor = new WindowRootViewVisibilityInteractor(
+ mTestScope.getBackgroundScope(),
+ new WindowRootViewVisibilityRepository(mBarService, mExecutor),
+ new FakeKeyguardRepository(),
+ mHeadsUpManagerPhone);
+
+ mGutsManager = new NotificationGutsManager(
+ mContext,
+ mHandler,
+ mHandler,
+ mJavaAdapter,
mAccessibilityManager,
- mHighPriorityProvider, mINotificationManager, mUserManager,
- mPeopleSpaceWidgetManager, mLauncherApps, mShortcutManager,
- mChannelEditorDialogController, mContextTracker, mAssistantFeedbackController,
- Optional.of(mBubblesManager), new UiEventLoggerFake(), mOnUserInteractionCallback,
+ mHighPriorityProvider,
+ mINotificationManager,
+ mUserManager,
+ mPeopleSpaceWidgetManager,
+ mLauncherApps,
+ mShortcutManager,
+ mChannelEditorDialogController,
+ mContextTracker,
+ mAssistantFeedbackController,
+ Optional.of(mBubblesManager),
+ new UiEventLoggerFake(),
+ mOnUserInteractionCallback,
mShadeController,
+ mWindowRootViewVisibilityInteractor,
mNotificationLockscreenUserManager,
mStatusBarStateController,
mDeviceProvisionedController,
mMetricsLogger,
- mHeadsUpManagerPhone, mActivityStarter);
+ mHeadsUpManagerPhone,
+ mActivityStarter);
mGutsManager.setUpWithPresenter(mPresenter, mNotificationListContainer,
mOnSettingsClickListener);
mGutsManager.setNotificationActivityStarter(mNotificationActivityStarter);
+ mGutsManager.start();
}
////////////////////////////////////////////////////////////////////////////////////////////////
@@ -210,6 +249,62 @@
}
@Test
+ public void testLockscreenShadeVisible_visible_gutsNotClosed() {
+ // First, start out lockscreen or shade as not visible
+ mWindowRootViewVisibilityInteractor.setIsLockscreenOrShadeVisible(false);
+ mTestScope.getTestScheduler().runCurrent();
+
+ NotificationGuts guts = mock(NotificationGuts.class);
+ mGutsManager.setExposedGuts(guts);
+
+ // WHEN the lockscreen or shade becomes visible
+ mWindowRootViewVisibilityInteractor.setIsLockscreenOrShadeVisible(true);
+ mTestScope.getTestScheduler().runCurrent();
+
+ // THEN the guts are not closed
+ verify(guts, never()).removeCallbacks(any());
+ verify(guts, never()).closeControls(
+ anyBoolean(), anyBoolean(), anyInt(), anyInt(), anyBoolean());
+ }
+
+ @Test
+ public void testLockscreenShadeVisible_notVisible_gutsClosed() {
+ // First, start out lockscreen or shade as visible
+ mWindowRootViewVisibilityInteractor.setIsLockscreenOrShadeVisible(true);
+ mTestScope.getTestScheduler().runCurrent();
+
+ NotificationGuts guts = mock(NotificationGuts.class);
+ mGutsManager.setExposedGuts(guts);
+
+ // WHEN the lockscreen or shade is no longer visible
+ mWindowRootViewVisibilityInteractor.setIsLockscreenOrShadeVisible(false);
+ mTestScope.getTestScheduler().runCurrent();
+
+ // THEN the guts are closed
+ verify(guts).removeCallbacks(any());
+ verify(guts).closeControls(
+ /* leavebehinds= */ eq(true),
+ /* controls= */ eq(true),
+ /* x= */ anyInt(),
+ /* y= */ anyInt(),
+ /* force= */ eq(true));
+ }
+
+ @Test
+ public void testLockscreenShadeVisible_notVisible_listContainerReset() {
+ // First, start out lockscreen or shade as visible
+ mWindowRootViewVisibilityInteractor.setIsLockscreenOrShadeVisible(true);
+ mTestScope.getTestScheduler().runCurrent();
+
+ // WHEN the lockscreen or shade is no longer visible
+ mWindowRootViewVisibilityInteractor.setIsLockscreenOrShadeVisible(false);
+ mTestScope.getTestScheduler().runCurrent();
+
+ // THEN the list container is reset
+ verify(mNotificationListContainer).resetExposedMenuView(anyBoolean(), anyBoolean());
+ }
+
+ @Test
public void testChangeDensityOrFontScale() {
NotificationGuts guts = spy(new NotificationGuts(mContext));
when(guts.post(any())).thenAnswer(invocation -> {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationTestHelper.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationTestHelper.java
index 3657bdf..9dfcb3f 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationTestHelper.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationTestHelper.java
@@ -54,7 +54,6 @@
import com.android.internal.logging.MetricsLogger;
import com.android.internal.statusbar.IStatusBarService;
import com.android.systemui.TestableDependency;
-import com.android.systemui.classifier.FalsingCollectorFake;
import com.android.systemui.classifier.FalsingManagerFake;
import com.android.systemui.flags.FakeFeatureFlags;
import com.android.systemui.flags.FeatureFlags;
@@ -269,6 +268,10 @@
return generateRow(notification, PKG, UID, USER_HANDLE, mDefaultInflationFlags);
}
+ public ExpandableNotificationRow createRow(NotificationEntry entry) throws Exception {
+ return generateRow(entry, mDefaultInflationFlags);
+ }
+
/**
* Create a row with the specified content views inflated in addition to the default.
*
@@ -538,18 +541,6 @@
@InflationFlag int extraInflationFlags,
int importance)
throws Exception {
- // NOTE: This flag is read when the ExpandableNotificationRow is inflated, so it needs to be
- // set, but we do not want to override an existing value that is needed by a specific test.
- mFeatureFlags.setDefault(Flags.IMPROVED_HUN_ANIMATIONS);
-
- LayoutInflater inflater = (LayoutInflater) mContext.getSystemService(
- mContext.LAYOUT_INFLATER_SERVICE);
- mRow = (ExpandableNotificationRow) inflater.inflate(
- R.layout.status_bar_notification_row,
- null /* root */,
- false /* attachToRoot */);
- ExpandableNotificationRow row = mRow;
-
final NotificationChannel channel =
new NotificationChannel(
notification.getChannelId(),
@@ -569,6 +560,25 @@
.setChannel(channel)
.build();
+ return generateRow(entry, extraInflationFlags);
+ }
+
+ private ExpandableNotificationRow generateRow(
+ NotificationEntry entry,
+ @InflationFlag int extraInflationFlags)
+ throws Exception {
+ // NOTE: This flag is read when the ExpandableNotificationRow is inflated, so it needs to be
+ // set, but we do not want to override an existing value that is needed by a specific test.
+ mFeatureFlags.setDefault(Flags.IMPROVED_HUN_ANIMATIONS);
+
+ LayoutInflater inflater = (LayoutInflater) mContext.getSystemService(
+ mContext.LAYOUT_INFLATER_SERVICE);
+ mRow = (ExpandableNotificationRow) inflater.inflate(
+ R.layout.status_bar_notification_row,
+ null /* root */,
+ false /* attachToRoot */);
+ ExpandableNotificationRow row = mRow;
+
entry.setRow(row);
mIconManager.createIcons(entry);
@@ -589,7 +599,6 @@
mock(OnExpandClickListener.class),
mock(ExpandableNotificationRow.CoordinateOnClickListener.class),
new FalsingManagerFake(),
- new FalsingCollectorFake(),
mStatusBarStateController,
mPeopleNotificationIdentifier,
mOnUserInteractionCallback,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/SharedNotificationContainerViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/SharedNotificationContainerViewModelTest.kt
index afd9954..55b52dc 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/SharedNotificationContainerViewModelTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/SharedNotificationContainerViewModelTest.kt
@@ -21,122 +21,369 @@
import androidx.test.filters.SmallTest
import com.android.systemui.R
import com.android.systemui.SysuiTestCase
+import com.android.systemui.common.shared.model.SharedNotificationContainerPosition
import com.android.systemui.common.ui.data.repository.FakeConfigurationRepository
import com.android.systemui.coroutines.collectLastValue
+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.domain.interactor.KeyguardTransitionInteractorFactory
+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.shade.data.repository.FakeShadeRepository
+import com.android.systemui.shade.domain.interactor.ShadeInteractor
+import com.android.systemui.statusbar.disableflags.data.repository.FakeDisableFlagsRepository
+import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayoutController
+import com.android.systemui.statusbar.notification.stack.NotificationStackSizeCalculator
import com.android.systemui.statusbar.notification.stack.domain.interactor.SharedNotificationContainerInteractor
+import com.android.systemui.statusbar.pipeline.mobile.data.repository.FakeUserSetupRepository
+import com.android.systemui.statusbar.policy.DeviceProvisionedController
+import com.android.systemui.user.domain.interactor.UserInteractor
+import com.android.systemui.util.mockito.any
+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.StandardTestDispatcher
+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.Mock
+import org.mockito.MockitoAnnotations
@SmallTest
@RunWith(AndroidJUnit4::class)
class SharedNotificationContainerViewModelTest : SysuiTestCase() {
+ private val testScope = TestScope(StandardTestDispatcher())
+
+ private val disableFlagsRepository = FakeDisableFlagsRepository()
+ private val userSetupRepository = FakeUserSetupRepository()
+ private val shadeRepository = FakeShadeRepository()
+ private val keyguardRepository = FakeKeyguardRepository()
+
private lateinit var configurationRepository: FakeConfigurationRepository
private lateinit var sharedNotificationContainerInteractor:
SharedNotificationContainerInteractor
private lateinit var underTest: SharedNotificationContainerViewModel
+ private lateinit var keyguardInteractor: KeyguardInteractor
+ private lateinit var keyguardTransitionInteractor: KeyguardTransitionInteractor
+ private lateinit var keyguardTransitionRepository: FakeKeyguardTransitionRepository
+ private lateinit var shadeInteractor: ShadeInteractor
+
+ @Mock private lateinit var notificationStackSizeCalculator: NotificationStackSizeCalculator
+ @Mock private lateinit var deviceProvisionedController: DeviceProvisionedController
+ @Mock private lateinit var userInteractor: UserInteractor
+ @Mock
+ private lateinit var notificationStackScrollLayoutController:
+ NotificationStackScrollLayoutController
@Before
fun setUp() {
+ MockitoAnnotations.initMocks(this)
+
+ whenever(notificationStackScrollLayoutController.getView()).thenReturn(mock())
+ whenever(notificationStackScrollLayoutController.getShelfHeight()).thenReturn(0)
+
configurationRepository = FakeConfigurationRepository()
+ KeyguardTransitionInteractorFactory.create(
+ scope = testScope.backgroundScope,
+ )
+ .also {
+ keyguardInteractor = it.keyguardInteractor
+ keyguardTransitionInteractor = it.keyguardTransitionInteractor
+ keyguardTransitionRepository = it.repository
+ }
+
+ shadeInteractor =
+ ShadeInteractor(
+ testScope.backgroundScope,
+ disableFlagsRepository,
+ keyguardRepository,
+ userSetupRepository,
+ deviceProvisionedController,
+ userInteractor,
+ shadeRepository,
+ )
+
sharedNotificationContainerInteractor =
SharedNotificationContainerInteractor(
configurationRepository,
mContext,
)
- underTest = SharedNotificationContainerViewModel(sharedNotificationContainerInteractor)
+ underTest =
+ SharedNotificationContainerViewModel(
+ sharedNotificationContainerInteractor,
+ keyguardInteractor,
+ keyguardTransitionInteractor,
+ notificationStackSizeCalculator,
+ notificationStackScrollLayoutController,
+ shadeInteractor
+ )
}
@Test
- fun validateMarginStartInSplitShade() = runTest {
- overrideResource(R.bool.config_use_split_notification_shade, true)
- overrideResource(R.dimen.notification_panel_margin_horizontal, 20)
+ fun validateMarginStartInSplitShade() =
+ testScope.runTest {
+ overrideResource(R.bool.config_use_split_notification_shade, true)
+ overrideResource(R.dimen.notification_panel_margin_horizontal, 20)
- val dimens = collectLastValue(underTest.configurationBasedDimensions)
+ val dimens by collectLastValue(underTest.configurationBasedDimensions)
- configurationRepository.onAnyConfigurationChange()
- runCurrent()
+ configurationRepository.onAnyConfigurationChange()
- val lastDimens = dimens()!!
-
- assertThat(lastDimens.marginStart).isEqualTo(0)
- }
+ assertThat(dimens!!.marginStart).isEqualTo(0)
+ }
@Test
- fun validateMarginStart() = runTest {
- overrideResource(R.bool.config_use_split_notification_shade, false)
- overrideResource(R.dimen.notification_panel_margin_horizontal, 20)
+ fun validateMarginStart() =
+ testScope.runTest {
+ overrideResource(R.bool.config_use_split_notification_shade, false)
+ overrideResource(R.dimen.notification_panel_margin_horizontal, 20)
- val dimens = collectLastValue(underTest.configurationBasedDimensions)
+ val dimens by collectLastValue(underTest.configurationBasedDimensions)
- configurationRepository.onAnyConfigurationChange()
- runCurrent()
+ configurationRepository.onAnyConfigurationChange()
- val lastDimens = dimens()!!
-
- assertThat(lastDimens.marginStart).isEqualTo(20)
- }
+ assertThat(dimens!!.marginStart).isEqualTo(20)
+ }
@Test
- fun validateMarginEnd() = runTest {
- overrideResource(R.dimen.notification_panel_margin_horizontal, 50)
+ fun validateMarginEnd() =
+ testScope.runTest {
+ overrideResource(R.dimen.notification_panel_margin_horizontal, 50)
- val dimens = collectLastValue(underTest.configurationBasedDimensions)
+ val dimens by collectLastValue(underTest.configurationBasedDimensions)
- configurationRepository.onAnyConfigurationChange()
- runCurrent()
+ configurationRepository.onAnyConfigurationChange()
- val lastDimens = dimens()!!
-
- assertThat(lastDimens.marginEnd).isEqualTo(50)
- }
+ assertThat(dimens!!.marginEnd).isEqualTo(50)
+ }
@Test
- fun validateMarginBottom() = runTest {
- overrideResource(R.dimen.notification_panel_margin_bottom, 50)
+ fun validateMarginBottom() =
+ testScope.runTest {
+ overrideResource(R.dimen.notification_panel_margin_bottom, 50)
- val dimens = collectLastValue(underTest.configurationBasedDimensions)
+ val dimens by collectLastValue(underTest.configurationBasedDimensions)
- configurationRepository.onAnyConfigurationChange()
- runCurrent()
+ configurationRepository.onAnyConfigurationChange()
- val lastDimens = dimens()!!
-
- assertThat(lastDimens.marginBottom).isEqualTo(50)
- }
+ assertThat(dimens!!.marginBottom).isEqualTo(50)
+ }
@Test
- fun validateMarginTopWithLargeScreenHeader() = runTest {
- overrideResource(R.bool.config_use_large_screen_shade_header, true)
- overrideResource(R.dimen.large_screen_shade_header_height, 50)
- overrideResource(R.dimen.notification_panel_margin_top, 0)
+ fun validateMarginTopWithLargeScreenHeader() =
+ testScope.runTest {
+ overrideResource(R.bool.config_use_large_screen_shade_header, true)
+ overrideResource(R.dimen.large_screen_shade_header_height, 50)
+ overrideResource(R.dimen.notification_panel_margin_top, 0)
- val dimens = collectLastValue(underTest.configurationBasedDimensions)
+ val dimens by collectLastValue(underTest.configurationBasedDimensions)
- configurationRepository.onAnyConfigurationChange()
- runCurrent()
+ configurationRepository.onAnyConfigurationChange()
- val lastDimens = dimens()!!
-
- assertThat(lastDimens.marginTop).isEqualTo(50)
- }
+ assertThat(dimens!!.marginTop).isEqualTo(50)
+ }
@Test
- fun validateMarginTop() = runTest {
- overrideResource(R.bool.config_use_large_screen_shade_header, false)
- overrideResource(R.dimen.large_screen_shade_header_height, 50)
- overrideResource(R.dimen.notification_panel_margin_top, 0)
+ fun validateMarginTop() =
+ testScope.runTest {
+ overrideResource(R.bool.config_use_large_screen_shade_header, false)
+ overrideResource(R.dimen.large_screen_shade_header_height, 50)
+ overrideResource(R.dimen.notification_panel_margin_top, 0)
- val dimens = collectLastValue(underTest.configurationBasedDimensions)
+ val dimens by collectLastValue(underTest.configurationBasedDimensions)
- configurationRepository.onAnyConfigurationChange()
- runCurrent()
+ configurationRepository.onAnyConfigurationChange()
- val lastDimens = dimens()!!
+ assertThat(dimens!!.marginTop).isEqualTo(0)
+ }
- assertThat(lastDimens.marginTop).isEqualTo(0)
+ @Test
+ fun isOnLockscreen() =
+ testScope.runTest {
+ val isOnLockscreen by collectLastValue(underTest.isOnLockscreen)
+
+ keyguardTransitionRepository.sendTransitionStep(
+ TransitionStep(to = KeyguardState.GONE, transitionState = TransitionState.FINISHED)
+ )
+ assertThat(isOnLockscreen).isFalse()
+
+ keyguardTransitionRepository.sendTransitionStep(
+ TransitionStep(
+ to = KeyguardState.LOCKSCREEN,
+ transitionState = TransitionState.FINISHED
+ )
+ )
+ assertThat(isOnLockscreen).isTrue()
+ }
+
+ @Test
+ fun isOnLockscreenWithoutShade() =
+ testScope.runTest {
+ val isOnLockscreenWithoutShade by collectLastValue(underTest.isOnLockscreenWithoutShade)
+
+ // First on AOD
+ shadeRepository.setShadeExpansion(0f)
+ shadeRepository.setQsExpansion(0f)
+ keyguardTransitionRepository.sendTransitionStep(
+ TransitionStep(
+ to = KeyguardState.OCCLUDED,
+ transitionState = TransitionState.FINISHED
+ )
+ )
+ assertThat(isOnLockscreenWithoutShade).isFalse()
+
+ // Now move to lockscreen
+ showLockscreen()
+
+ // While state is LOCKSCREEN, validate variations of both shade and qs expansion
+ shadeRepository.setShadeExpansion(0.1f)
+ shadeRepository.setQsExpansion(0f)
+ assertThat(isOnLockscreenWithoutShade).isFalse()
+
+ shadeRepository.setShadeExpansion(0.1f)
+ shadeRepository.setQsExpansion(0.1f)
+ assertThat(isOnLockscreenWithoutShade).isFalse()
+
+ shadeRepository.setShadeExpansion(0f)
+ shadeRepository.setQsExpansion(0.1f)
+ assertThat(isOnLockscreenWithoutShade).isFalse()
+
+ shadeRepository.setShadeExpansion(0f)
+ shadeRepository.setQsExpansion(0f)
+ assertThat(isOnLockscreenWithoutShade).isTrue()
+ }
+
+ @Test
+ fun positionOnLockscreenNotInSplitShade() =
+ testScope.runTest {
+ val position by collectLastValue(underTest.position)
+
+ // Start on lockscreen
+ showLockscreen()
+
+ // When not in split shade
+ overrideResource(R.bool.config_use_split_notification_shade, false)
+ configurationRepository.onAnyConfigurationChange()
+
+ keyguardInteractor.sharedNotificationContainerPosition.value =
+ SharedNotificationContainerPosition(top = 1f, bottom = 2f)
+
+ assertThat(position)
+ .isEqualTo(SharedNotificationContainerPosition(top = 1f, bottom = 2f))
+ }
+
+ @Test
+ fun positionOnLockscreenInSplitShade() =
+ testScope.runTest {
+ val position by collectLastValue(underTest.position)
+
+ // Start on lockscreen
+ showLockscreen()
+
+ // When in split shade
+ overrideResource(R.bool.config_use_split_notification_shade, true)
+ configurationRepository.onAnyConfigurationChange()
+
+ keyguardInteractor.sharedNotificationContainerPosition.value =
+ SharedNotificationContainerPosition(top = 1f, bottom = 2f)
+ runCurrent()
+
+ // Top should be overridden to 0f
+ assertThat(position)
+ .isEqualTo(SharedNotificationContainerPosition(top = 0f, bottom = 2f))
+ }
+
+ @Test
+ fun positionOnShade() =
+ testScope.runTest {
+ val position by collectLastValue(underTest.position)
+
+ // Start on lockscreen with shade expanded
+ showLockscreenWithShadeExpanded()
+
+ // When not in split shade
+ sharedNotificationContainerInteractor.setTopPosition(10f)
+
+ assertThat(position)
+ .isEqualTo(SharedNotificationContainerPosition(top = 10f, bottom = 0f))
+ }
+
+ @Test
+ fun maxNotificationsOnLockscreen() =
+ testScope.runTest {
+ whenever(
+ notificationStackSizeCalculator.computeMaxKeyguardNotifications(
+ any(),
+ any(),
+ any(),
+ any()
+ )
+ )
+ .thenReturn(10)
+
+ val maxNotifications by collectLastValue(underTest.maxNotifications)
+
+ showLockscreen()
+
+ overrideResource(R.bool.config_use_split_notification_shade, false)
+ configurationRepository.onAnyConfigurationChange()
+ keyguardInteractor.sharedNotificationContainerPosition.value =
+ SharedNotificationContainerPosition(top = 1f, bottom = 2f)
+
+ assertThat(maxNotifications).isEqualTo(10)
+ }
+
+ @Test
+ fun maxNotificationsOnShade() =
+ testScope.runTest {
+ whenever(
+ notificationStackSizeCalculator.computeMaxKeyguardNotifications(
+ any(),
+ any(),
+ any(),
+ any()
+ )
+ )
+ .thenReturn(10)
+ val maxNotifications by collectLastValue(underTest.maxNotifications)
+
+ // Show lockscreen with shade expanded
+ showLockscreenWithShadeExpanded()
+
+ overrideResource(R.bool.config_use_split_notification_shade, false)
+ configurationRepository.onAnyConfigurationChange()
+ keyguardInteractor.sharedNotificationContainerPosition.value =
+ SharedNotificationContainerPosition(top = 1f, bottom = 2f)
+
+ // -1 means No Limit
+ assertThat(maxNotifications).isEqualTo(-1)
+ }
+
+ private suspend fun showLockscreen() {
+ shadeRepository.setShadeExpansion(0f)
+ shadeRepository.setQsExpansion(0f)
+ keyguardTransitionRepository.sendTransitionStep(
+ TransitionStep(
+ to = KeyguardState.LOCKSCREEN,
+ transitionState = TransitionState.FINISHED
+ )
+ )
+ }
+
+ private suspend fun showLockscreenWithShadeExpanded() {
+ shadeRepository.setShadeExpansion(1f)
+ shadeRepository.setQsExpansion(0f)
+ keyguardTransitionRepository.sendTransitionStep(
+ TransitionStep(
+ to = KeyguardState.LOCKSCREEN,
+ transitionState = TransitionState.FINISHED
+ )
+ )
}
}
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 f47efe3..5a1450f 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
@@ -24,7 +24,6 @@
import static junit.framework.Assert.assertFalse;
import static junit.framework.Assert.assertTrue;
-import static junit.framework.TestCase.fail;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyBoolean;
@@ -62,7 +61,6 @@
import android.os.IThermalService;
import android.os.Looper;
import android.os.PowerManager;
-import android.os.RemoteException;
import android.os.UserHandle;
import android.service.dreams.IDreamManager;
import android.support.test.metricshelper.MetricsAsserts;
@@ -73,13 +71,7 @@
import android.util.SparseArray;
import android.view.ViewGroup;
import android.view.ViewGroup.LayoutParams;
-import android.view.ViewRootImpl;
import android.view.WindowManager;
-import android.window.BackEvent;
-import android.window.OnBackAnimationCallback;
-import android.window.OnBackInvokedCallback;
-import android.window.OnBackInvokedDispatcher;
-import android.window.WindowOnBackInvokedDispatcher;
import androidx.test.filters.SmallTest;
@@ -125,6 +117,7 @@
import com.android.systemui.plugins.statusbar.StatusBarStateController;
import com.android.systemui.power.domain.interactor.PowerInteractor;
import com.android.systemui.recents.ScreenPinningRequest;
+import com.android.systemui.scene.domain.interactor.WindowRootViewVisibilityInteractor;
import com.android.systemui.settings.UserTracker;
import com.android.systemui.settings.brightness.BrightnessSliderController;
import com.android.systemui.shade.CameraLauncher;
@@ -158,7 +151,6 @@
import com.android.systemui.statusbar.notification.NotificationLaunchAnimatorControllerProvider;
import com.android.systemui.statusbar.notification.NotificationWakeUpCoordinator;
import com.android.systemui.statusbar.notification.collection.NotifLiveDataStore;
-import com.android.systemui.statusbar.notification.collection.NotifPipeline;
import com.android.systemui.statusbar.notification.collection.NotificationEntry;
import com.android.systemui.statusbar.notification.collection.NotificationEntryBuilder;
import com.android.systemui.statusbar.notification.collection.render.NotificationVisibilityProvider;
@@ -167,10 +159,7 @@
import com.android.systemui.statusbar.notification.interruption.KeyguardNotificationVisibilityProvider;
import com.android.systemui.statusbar.notification.interruption.NotificationInterruptLogger;
import com.android.systemui.statusbar.notification.interruption.NotificationInterruptStateProviderImpl;
-import com.android.systemui.statusbar.notification.logging.NotificationLogger;
-import com.android.systemui.statusbar.notification.logging.NotificationPanelLoggerFake;
import com.android.systemui.statusbar.notification.row.NotificationGutsManager;
-import com.android.systemui.statusbar.notification.stack.NotificationListContainer;
import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayout;
import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayoutController;
import com.android.systemui.statusbar.phone.dagger.CentralSurfacesComponent;
@@ -199,7 +188,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.MockitoAnnotations;
@@ -227,7 +215,6 @@
@Mock private KeyguardIndicationController mKeyguardIndicationController;
@Mock private NotificationStackScrollLayout mStackScroller;
@Mock private NotificationStackScrollLayoutController mStackScrollerController;
- @Mock private NotificationListContainer mNotificationListContainer;
@Mock private HeadsUpManagerPhone mHeadsUpManager;
@Mock private NotificationPanelViewController mNotificationPanelViewController;
@Mock private ShadeLogger mShadeLogger;
@@ -254,7 +241,6 @@
@Mock private StatusBarNotificationPresenter mNotificationPresenter;
@Mock private NotificationActivityStarter mNotificationActivityStarter;
@Mock private AmbientDisplayConfiguration mAmbientDisplayConfiguration;
- @Mock private NotificationLogger.ExpansionStateLogger mExpansionStateLogger;
@Mock private KeyguardUpdateMonitor mKeyguardUpdateMonitor;
@Mock private StatusBarSignalPolicy mStatusBarSignalPolicy;
@Mock private BroadcastDispatcher mBroadcastDispatcher;
@@ -325,18 +311,11 @@
@Mock private Lazy<CameraLauncher> mCameraLauncherLazy;
@Mock private CameraLauncher mCameraLauncher;
@Mock private AlternateBouncerInteractor mAlternateBouncerInteractor;
- /**
- * The process of registering/unregistering a predictive back callback requires a
- * ViewRootImpl, which is present IRL, but may be missing during a Mockito unit test.
- * To prevent an NPE during test execution, we explicitly craft and provide a fake ViewRootImpl.
- */
- @Mock private ViewRootImpl mViewRootImpl;
- @Mock private WindowOnBackInvokedDispatcher mOnBackInvokedDispatcher;
@Mock private UserTracker mUserTracker;
@Mock private FingerprintManager mFingerprintManager;
- @Captor private ArgumentCaptor<OnBackInvokedCallback> mOnBackInvokedCallback;
@Mock IPowerManager mPowerManagerService;
@Mock ActivityStarter mActivityStarter;
+ @Mock private WindowRootViewVisibilityInteractor mWindowRootViewVisibilityInteractor;
private ShadeController mShadeController;
private final FakeSystemClock mFakeSystemClock = new FakeSystemClock();
@@ -388,18 +367,6 @@
mContext.addMockSystemService(FingerprintManager.class, mock(FingerprintManager.class));
mMetricsLogger = new FakeMetricsLogger();
- NotificationLogger notificationLogger = new NotificationLogger(
- mNotificationListener,
- mUiBgExecutor,
- mNotifLiveDataStore,
- mVisibilityProvider,
- mock(NotifPipeline.class),
- mStatusBarStateController,
- mShadeExpansionStateManager,
- mExpansionStateLogger,
- new NotificationPanelLoggerFake()
- );
- notificationLogger.setVisibilityReporter(mock(Runnable.class));
when(mCommandQueue.asBinder()).thenReturn(new Binder());
@@ -448,6 +415,7 @@
mCommandQueue,
mMainExecutor,
mock(LogBuffer.class),
+ mock(WindowRootViewVisibilityInteractor.class),
mKeyguardStateController,
mStatusBarStateController,
mStatusBarKeyguardViewManager,
@@ -490,7 +458,6 @@
new FalsingCollectorFake(),
mBroadcastDispatcher,
mNotificationGutsManager,
- notificationLogger,
mNotificationInterruptStateProvider,
new ShadeExpansionStateManager(),
mKeyguardViewMediator,
@@ -541,6 +508,7 @@
() -> mCentralSurfacesCommandQueueCallbacks,
mPluginManager,
mShadeController,
+ mWindowRootViewVisibilityInteractor,
mStatusBarKeyguardViewManager,
mViewMediatorCallback,
mInitController,
@@ -578,16 +546,9 @@
mUserTracker,
() -> mFingerprintManager,
mActivityStarter
- ) {
- @Override
- protected ViewRootImpl getViewRootImpl() {
- return mViewRootImpl;
- }
- };
+ );
mScreenLifecycle.addObserver(mCentralSurfaces.mScreenObserver);
mCentralSurfaces.initShadeVisibilityListener();
- when(mViewRootImpl.getOnBackInvokedDispatcher())
- .thenReturn(mOnBackInvokedDispatcher);
when(mKeyguardViewMediator.registerCentralSurfaces(
any(CentralSurfacesImpl.class),
any(NotificationPanelViewController.class),
@@ -609,7 +570,6 @@
PowerManager.SCREEN_BRIGHT_WAKE_LOCK, "sysui:GestureWakeLock");
mCentralSurfaces.startKeyguard();
mInitController.executePostInitTasks();
- notificationLogger.setUpWithContainer(mNotificationListContainer);
mCentralSurfaces.registerCallbacks();
}
@@ -799,151 +759,6 @@
}
@Test
- public void testLogHidden() {
- try {
- mCentralSurfaces.handleVisibleToUserChanged(false);
- mUiBgExecutor.runAllReady();
- verify(mBarService, times(1)).onPanelHidden();
- verify(mBarService, never()).onPanelRevealed(anyBoolean(), anyInt());
- } catch (RemoteException e) {
- fail();
- }
- }
-
- /**
- * Do the following:
- * 1. verify that a predictive back callback is registered when CSurf becomes visible
- * 2. verify that the same callback is unregistered when CSurf becomes invisible
- */
- @Test
- public void testPredictiveBackCallback_registration() {
- mCentralSurfaces.handleVisibleToUserChanged(true);
- verify(mOnBackInvokedDispatcher).registerOnBackInvokedCallback(
- eq(OnBackInvokedDispatcher.PRIORITY_DEFAULT),
- mOnBackInvokedCallback.capture());
-
- mCentralSurfaces.handleVisibleToUserChanged(false);
- verify(mOnBackInvokedDispatcher).unregisterOnBackInvokedCallback(
- eq(mOnBackInvokedCallback.getValue()));
- }
-
- /**
- * Do the following:
- * 1. capture the predictive back callback during registration
- * 2. call the callback directly
- * 3. verify that the ShadeController's panel collapse animation is invoked
- */
- @Test
- public void testPredictiveBackCallback_invocationCollapsesPanel() {
- mCentralSurfaces.handleVisibleToUserChanged(true);
- verify(mOnBackInvokedDispatcher).registerOnBackInvokedCallback(
- eq(OnBackInvokedDispatcher.PRIORITY_DEFAULT),
- mOnBackInvokedCallback.capture());
-
- when(mBackActionInteractor.shouldBackBeHandled()).thenReturn(true);
- mOnBackInvokedCallback.getValue().onBackInvoked();
- verify(mBackActionInteractor).onBackRequested();
- }
-
- /**
- * When back progress is at 100%, the onBackProgressed animation driver inside
- * NotificationPanelViewController should be invoked appropriately (with 1.0f passed in).
- */
- @Test
- public void testPredictiveBackAnimation_progressMaxScalesPanel() {
- mCentralSurfaces.handleVisibleToUserChanged(true);
- verify(mOnBackInvokedDispatcher).registerOnBackInvokedCallback(
- eq(OnBackInvokedDispatcher.PRIORITY_DEFAULT),
- mOnBackInvokedCallback.capture());
-
- OnBackAnimationCallback onBackAnimationCallback =
- (OnBackAnimationCallback) (mOnBackInvokedCallback.getValue());
- when(mBackActionInteractor.shouldBackBeHandled()).thenReturn(true);
- when(mNotificationPanelViewController.canBeCollapsed()).thenReturn(true);
-
- BackEvent fakeSwipeInFromLeftEdge = new BackEvent(20.0f, 100.0f, 1.0f, BackEvent.EDGE_LEFT);
- onBackAnimationCallback.onBackProgressed(fakeSwipeInFromLeftEdge);
- verify(mNotificationPanelViewController).onBackProgressed(eq(1.0f));
- }
-
- /**
- * When back progress is at 0%, the onBackProgressed animation driver inside
- * NotificationPanelViewController should be invoked appropriately (with 0.0f passed in).
- */
- @Test
- public void testPredictiveBackAnimation_progressMinScalesPanel() {
- mCentralSurfaces.handleVisibleToUserChanged(true);
- verify(mOnBackInvokedDispatcher).registerOnBackInvokedCallback(
- eq(OnBackInvokedDispatcher.PRIORITY_DEFAULT),
- mOnBackInvokedCallback.capture());
-
- OnBackAnimationCallback onBackAnimationCallback =
- (OnBackAnimationCallback) (mOnBackInvokedCallback.getValue());
- when(mBackActionInteractor.shouldBackBeHandled()).thenReturn(true);
- when(mNotificationPanelViewController.canBeCollapsed()).thenReturn(true);
-
- BackEvent fakeSwipeInFromLeftEdge = new BackEvent(20.0f, 10.0f, 0.0f, BackEvent.EDGE_LEFT);
- onBackAnimationCallback.onBackProgressed(fakeSwipeInFromLeftEdge);
- verify(mNotificationPanelViewController).onBackProgressed(eq(0.0f));
- }
-
- @Test
- public void testPanelOpenForHeadsUp() {
- when(mDeviceProvisionedController.isDeviceProvisioned()).thenReturn(true);
- when(mHeadsUpManager.hasPinnedHeadsUp()).thenReturn(true);
- when(mNotificationsController.getActiveNotificationsCount()).thenReturn(5);
- when(mNotificationPresenter.isPresenterFullyCollapsed()).thenReturn(true);
- mCentralSurfaces.setBarStateForTest(SHADE);
-
- try {
- mCentralSurfaces.handleVisibleToUserChanged(true);
- mUiBgExecutor.runAllReady();
- verify(mBarService, never()).onPanelHidden();
- verify(mBarService, times(1)).onPanelRevealed(false, 1);
- } catch (RemoteException e) {
- fail();
- }
- mMainExecutor.runAllReady();
- }
-
- @Test
- public void testPanelOpenAndClear() {
- when(mHeadsUpManager.hasPinnedHeadsUp()).thenReturn(false);
- when(mNotificationsController.getActiveNotificationsCount()).thenReturn(5);
-
- when(mNotificationPresenter.isPresenterFullyCollapsed()).thenReturn(false);
- mCentralSurfaces.setBarStateForTest(SHADE);
-
- try {
- mCentralSurfaces.handleVisibleToUserChanged(true);
- mUiBgExecutor.runAllReady();
- verify(mBarService, never()).onPanelHidden();
- verify(mBarService, times(1)).onPanelRevealed(true, 5);
- } catch (RemoteException e) {
- fail();
- }
- mMainExecutor.runAllReady();
- }
-
- @Test
- public void testPanelOpenAndNoClear() {
- when(mHeadsUpManager.hasPinnedHeadsUp()).thenReturn(false);
- when(mNotificationsController.getActiveNotificationsCount()).thenReturn(5);
- when(mNotificationPresenter.isPresenterFullyCollapsed()).thenReturn(false);
- mCentralSurfaces.setBarStateForTest(StatusBarState.KEYGUARD);
-
- try {
- mCentralSurfaces.handleVisibleToUserChanged(true);
- mUiBgExecutor.runAllReady();
- verify(mBarService, never()).onPanelHidden();
- verify(mBarService, times(1)).onPanelRevealed(false, 5);
- } catch (RemoteException e) {
- fail();
- }
- mMainExecutor.runAllReady();
- }
-
- @Test
public void testDump_DoesNotCrash() {
mCentralSurfaces.dump(new PrintWriter(new ByteArrayOutputStream()), null);
}
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 9795b9d..71c27de 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
@@ -29,6 +29,7 @@
import com.android.systemui.SysuiTestCase
import com.android.systemui.broadcast.BroadcastDispatcher
import com.android.systemui.display.domain.interactor.ConnectedDisplayInteractor
+import com.android.systemui.display.domain.interactor.ConnectedDisplayInteractor.PendingDisplay
import com.android.systemui.display.domain.interactor.ConnectedDisplayInteractor.State
import com.android.systemui.privacy.PrivacyItemController
import com.android.systemui.privacy.logging.PrivacyLogger
@@ -316,5 +317,7 @@
suspend fun emit(value: State) = flow.emit(value)
override val connectedDisplayState: Flow<State>
get() = flow
+ override val pendingDisplay: Flow<PendingDisplay?>
+ get() = TODO("Not yet implemented")
}
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarIconControllerImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarIconControllerImplTest.kt
index 08e89fb..6f04f36 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarIconControllerImplTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarIconControllerImplTest.kt
@@ -220,30 +220,6 @@
/** Regression test for b/255428281. */
@Test
- fun internalAndExternalIconWithSameName_internalRemoved_viaRemoveAll_externalStays() {
- val slotName = "mute"
-
- // Internal
- underTest.setIcon(slotName, /* resourceId= */ 10, "contentDescription")
-
- // External
- underTest.setIconFromTile(slotName, createExternalIcon())
-
- // WHEN the internal icon is removed via #removeAllIconsForSlot
- underTest.removeAllIconsForSlot(slotName)
-
- // THEN the internal icon is removed but the external icon remains
- assertThat(iconList.slots).hasSize(2)
- assertThat(iconList.slots[0].name).isEqualTo(slotName + EXTERNAL_SLOT_SUFFIX)
- assertThat(iconList.slots[1].name).isEqualTo(slotName)
- assertThat(iconList.slots[0].hasIconsInSlot()).isTrue()
- assertThat(iconList.slots[1].hasIconsInSlot()).isFalse() // Indicates removal
-
- verify(iconGroup).onRemoveIcon(1)
- }
-
- /** Regression test for b/255428281. */
- @Test
fun internalAndExternalIconWithSameName_internalUpdatedIndependently() {
val slotName = "mute"
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarIconControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarIconControllerTest.java
index 085ec27..0ff6f20 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarIconControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarIconControllerTest.java
@@ -120,34 +120,6 @@
verify(manager, never()).onRemoveIcon(anyInt());
}
- @Test
- public void testRemoveAllIconsForSlot_ignoredForNewPipeline() {
- IconManager manager = mock(IconManager.class);
-
- // GIVEN the new pipeline is on
- StatusBarPipelineFlags flags = mock(StatusBarPipelineFlags.class);
- when(flags.isIconControlledByFlags("test_icon")).thenReturn(true);
-
- StatusBarIconController iconController = new StatusBarIconControllerImpl(
- mContext,
- mock(CommandQueue.class),
- mock(DemoModeController.class),
- mock(ConfigurationController.class),
- mock(TunerService.class),
- mock(DumpManager.class),
- mock(StatusBarIconList.class),
- flags
- );
-
- iconController.addIconGroup(manager);
-
- // WHEN a request to remove a new icon is sent
- iconController.removeAllIconsForSlot("test_icon");
-
- // THEN it is not removed for those icons
- verify(manager, never()).onRemoveIcon(anyInt());
- }
-
private <T extends IconManager & TestableIconManager> void testCallOnAdd_forManager(T manager) {
StatusBarIconHolder holder = holderForType(TYPE_ICON);
manager.onIconAdded(0, "test_slot", false, holder);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/ethernet/domain/EthernetInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/ethernet/domain/EthernetInteractorTest.kt
new file mode 100644
index 0000000..5784be3
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/ethernet/domain/EthernetInteractorTest.kt
@@ -0,0 +1,94 @@
+/*
+ * 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.statusbar.pipeline.ethernet.domain
+
+import androidx.test.filters.SmallTest
+import com.android.settingslib.AccessibilityContentDescriptions
+import com.android.systemui.R
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.common.shared.model.ContentDescription
+import com.android.systemui.common.shared.model.Icon
+import com.android.systemui.coroutines.collectLastValue
+import com.android.systemui.statusbar.pipeline.shared.data.repository.FakeConnectivityRepository
+import com.google.common.truth.Truth.assertThat
+import kotlinx.coroutines.test.TestScope
+import kotlinx.coroutines.test.runTest
+import org.junit.Test
+
+@SmallTest
+class EthernetInteractorTest : SysuiTestCase() {
+ private val connectivityRepository = FakeConnectivityRepository()
+ private val underTest = EthernetInteractor(connectivityRepository)
+
+ private val testScope = TestScope()
+
+ @Test
+ fun icon_default_validated() =
+ testScope.runTest {
+ val latest by collectLastValue(underTest.icon)
+
+ connectivityRepository.setEthernetConnected(default = true, validated = true)
+
+ val expected =
+ Icon.Resource(
+ R.drawable.stat_sys_ethernet_fully,
+ ContentDescription.Resource(
+ AccessibilityContentDescriptions.ETHERNET_CONNECTION_VALUES[1]
+ )
+ )
+
+ assertThat(latest).isEqualTo(expected)
+ }
+
+ @Test
+ fun icon_default_notValidated() =
+ testScope.runTest {
+ val latest by collectLastValue(underTest.icon)
+
+ connectivityRepository.setEthernetConnected(default = true, validated = false)
+
+ val expected =
+ Icon.Resource(
+ R.drawable.stat_sys_ethernet,
+ ContentDescription.Resource(
+ AccessibilityContentDescriptions.ETHERNET_CONNECTION_VALUES[0]
+ )
+ )
+
+ assertThat(latest).isEqualTo(expected)
+ }
+
+ @Test
+ fun icon_notDefault_validated() =
+ testScope.runTest {
+ val latest by collectLastValue(underTest.icon)
+
+ connectivityRepository.setEthernetConnected(default = false, validated = true)
+
+ assertThat(latest).isNull()
+ }
+
+ @Test
+ fun icon_notDefault_notValidated() =
+ testScope.runTest {
+ val latest by collectLastValue(underTest.icon)
+
+ connectivityRepository.setEthernetConnected(default = false, validated = false)
+
+ assertThat(latest).isNull()
+ }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/FakeMobileConnectionRepository.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/FakeMobileConnectionRepository.kt
index ff28753..812e91b 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/FakeMobileConnectionRepository.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/FakeMobileConnectionRepository.kt
@@ -64,6 +64,28 @@
_dataEnabled.value = enabled
}
+ /**
+ * Set [primaryLevel] and [cdmaLevel]. Convenient when you don't care about the connection type
+ */
+ fun setAllLevels(level: Int) {
+ cdmaLevel.value = level
+ primaryLevel.value = level
+ }
+
+ /**
+ * Set both [isRoaming] and [cdmaRoaming] properties, in the event that you don't care about the
+ * connection type
+ */
+ fun setAllRoaming(roaming: Boolean) {
+ isRoaming.value = roaming
+ cdmaRoaming.value = roaming
+ }
+
+ /** Set the correct [resolvedNetworkType] for the given group via its lookup key */
+ fun setNetworkTypeKey(key: String) {
+ resolvedNetworkType.value = ResolvedNetworkType.DefaultNetworkType(key)
+ }
+
companion object {
const val DEFAULT_NETWORK_NAME = "default name"
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/FullMobileConnectionRepositoryTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/FullMobileConnectionRepositoryTest.kt
index 9c0cb17..ede02d1 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/FullMobileConnectionRepositoryTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/FullMobileConnectionRepositoryTest.kt
@@ -674,6 +674,7 @@
val realRepo =
MobileConnectionRepositoryImpl(
SUB_ID,
+ context,
subscriptionModel,
DEFAULT_NAME_MODEL,
SEP,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/MobileConnectionRepositoryTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/MobileConnectionRepositoryTest.kt
index e50e5e3..3af960b 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/MobileConnectionRepositoryTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/MobileConnectionRepositoryTest.kt
@@ -16,18 +16,22 @@
package com.android.systemui.statusbar.pipeline.mobile.data.repository.prod
+import android.content.BroadcastReceiver
+import android.content.Context
import android.content.Intent
import android.telephony.CarrierConfigManager.KEY_INFLATE_SIGNAL_STRENGTH_BOOL
import android.telephony.NetworkRegistrationInfo
import android.telephony.ServiceState
import android.telephony.ServiceState.STATE_IN_SERVICE
import android.telephony.ServiceState.STATE_OUT_OF_SERVICE
+import android.telephony.SubscriptionManager.EXTRA_SUBSCRIPTION_INDEX
import android.telephony.TelephonyCallback
import android.telephony.TelephonyCallback.DataActivityListener
import android.telephony.TelephonyCallback.ServiceStateListener
import android.telephony.TelephonyDisplayInfo.OVERRIDE_NETWORK_TYPE_LTE_CA
import android.telephony.TelephonyDisplayInfo.OVERRIDE_NETWORK_TYPE_NONE
import android.telephony.TelephonyManager
+import android.telephony.TelephonyManager.ACTION_SERVICE_PROVIDERS_UPDATED
import android.telephony.TelephonyManager.DATA_ACTIVITY_DORMANT
import android.telephony.TelephonyManager.DATA_ACTIVITY_IN
import android.telephony.TelephonyManager.DATA_ACTIVITY_INOUT
@@ -75,6 +79,7 @@
import com.android.systemui.statusbar.pipeline.shared.data.model.DataActivityModel
import com.android.systemui.statusbar.pipeline.shared.data.model.toMobileDataActivityModel
import com.android.systemui.util.mockito.any
+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
@@ -88,6 +93,7 @@
import org.junit.Before
import org.junit.Test
import org.mockito.Mock
+import org.mockito.Mockito.verify
import org.mockito.MockitoAnnotations
@Suppress("EXPERIMENTAL_IS_NOT_ENABLED")
@@ -100,6 +106,7 @@
@Mock private lateinit var telephonyManager: TelephonyManager
@Mock private lateinit var logger: MobileInputLogger
@Mock private lateinit var tableLogger: TableLogBuffer
+ @Mock private lateinit var context: Context
private val mobileMappings = FakeMobileMappingsProxy()
private val systemUiCarrierConfig =
@@ -129,6 +136,7 @@
underTest =
MobileConnectionRepositoryImpl(
SUB_1_ID,
+ context,
subscriptionModel,
DEFAULT_NAME_MODEL,
SEP,
@@ -706,7 +714,9 @@
val job = underTest.networkName.onEach { latest = it }.launchIn(this)
val intent = spnIntent()
- fakeBroadcastDispatcher.sendIntentToMatchingReceiversOnly(context, intent)
+ val captor = argumentCaptor<BroadcastReceiver>()
+ verify(context).registerReceiver(captor.capture(), any())
+ captor.value!!.onReceive(context, intent)
assertThat(latest).isEqualTo(intent.toNetworkNameModel(SEP))
@@ -720,13 +730,16 @@
val job = underTest.networkName.onEach { latest = it }.launchIn(this)
val intent = spnIntent()
- fakeBroadcastDispatcher.sendIntentToMatchingReceiversOnly(context, intent)
+ val captor = argumentCaptor<BroadcastReceiver>()
+ verify(context).registerReceiver(captor.capture(), any())
+ captor.value!!.onReceive(context, intent)
+
assertThat(latest).isEqualTo(intent.toNetworkNameModel(SEP))
// WHEN an intent with a different subId is sent
val wrongSubIntent = spnIntent(subId = 101)
- fakeBroadcastDispatcher.sendIntentToMatchingReceiversOnly(context, wrongSubIntent)
+ captor.value!!.onReceive(context, wrongSubIntent)
// THEN the previous intent's name is still used
assertThat(latest).isEqualTo(intent.toNetworkNameModel(SEP))
@@ -741,7 +754,10 @@
val job = underTest.networkName.onEach { latest = it }.launchIn(this)
val intent = spnIntent()
- fakeBroadcastDispatcher.sendIntentToMatchingReceiversOnly(context, intent)
+ val captor = argumentCaptor<BroadcastReceiver>()
+ verify(context).registerReceiver(captor.capture(), any())
+ captor.value!!.onReceive(context, intent)
+
assertThat(latest).isEqualTo(intent.toNetworkNameModel(SEP))
val intentWithoutInfo =
@@ -750,7 +766,7 @@
showPlmn = false,
)
- fakeBroadcastDispatcher.sendIntentToMatchingReceiversOnly(context, intentWithoutInfo)
+ captor.value!!.onReceive(context, intentWithoutInfo)
assertThat(latest).isEqualTo(DEFAULT_NAME_MODEL)
@@ -893,7 +909,7 @@
plmn: String = PLMN,
): Intent =
Intent(TelephonyManager.ACTION_SERVICE_PROVIDERS_UPDATED).apply {
- putExtra(EXTRA_SUBSCRIPTION_ID, subId)
+ putExtra(EXTRA_SUBSCRIPTION_INDEX, subId)
putExtra(EXTRA_SHOW_SPN, showSpn)
putExtra(EXTRA_SPN, spn)
putExtra(EXTRA_SHOW_PLMN, showPlmn)
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/MobileConnectionTelephonySmokeTests.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/MobileConnectionTelephonySmokeTests.kt
index ea60aa7..852ed20 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/MobileConnectionTelephonySmokeTests.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/MobileConnectionTelephonySmokeTests.kt
@@ -125,6 +125,7 @@
underTest =
MobileConnectionRepositoryImpl(
SUB_1_ID,
+ context,
subscriptionModel,
DEFAULT_NAME,
SEP,
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 fd05cc4..6f9764a 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
@@ -175,6 +175,7 @@
connectionFactory =
MobileConnectionRepositoryImpl.Factory(
+ context,
fakeBroadcastDispatcher,
telephonyManager = telephonyManager,
bgDispatcher = dispatcher,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/domain/interactor/FakeMobileIconInteractor.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/domain/interactor/FakeMobileIconInteractor.kt
index a3df785..de2b6a8 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/domain/interactor/FakeMobileIconInteractor.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/domain/interactor/FakeMobileIconInteractor.kt
@@ -16,12 +16,11 @@
package com.android.systemui.statusbar.pipeline.mobile.domain.interactor
-import android.telephony.CellSignalStrength
import com.android.settingslib.mobile.TelephonyIcons
import com.android.systemui.log.table.TableLogBuffer
import com.android.systemui.statusbar.pipeline.mobile.data.model.NetworkNameModel
-import com.android.systemui.statusbar.pipeline.mobile.data.repository.MobileConnectionRepository.Companion.DEFAULT_NUM_LEVELS
import com.android.systemui.statusbar.pipeline.mobile.domain.model.NetworkTypeIconModel
+import com.android.systemui.statusbar.pipeline.mobile.domain.model.SignalIconModel
import com.android.systemui.statusbar.pipeline.shared.data.model.DataActivityModel
import kotlinx.coroutines.flow.MutableStateFlow
@@ -30,8 +29,6 @@
) : MobileIconInteractor {
override val alwaysShowDataRatIcon = MutableStateFlow(false)
- override val alwaysUseCdmaLevel = MutableStateFlow(false)
-
override val activity =
MutableStateFlow(
DataActivityModel(
@@ -55,14 +52,8 @@
override val carrierName = MutableStateFlow("demo mode")
- private val _isEmergencyOnly = MutableStateFlow(false)
- override val isEmergencyOnly = _isEmergencyOnly
-
override val isRoaming = MutableStateFlow(false)
- private val _isFailedConnection = MutableStateFlow(false)
- override val isDefaultConnectionFailed = _isFailedConnection
-
override val isDataConnected = MutableStateFlow(true)
override val isInService = MutableStateFlow(true)
@@ -70,40 +61,21 @@
private val _isDataEnabled = MutableStateFlow(true)
override val isDataEnabled = _isDataEnabled
- private val _isDefaultDataEnabled = MutableStateFlow(true)
- override val isDefaultDataEnabled = _isDefaultDataEnabled
-
- private val _level = MutableStateFlow(CellSignalStrength.SIGNAL_STRENGTH_NONE_OR_UNKNOWN)
- override val level = _level
-
- private val _numberOfLevels = MutableStateFlow(DEFAULT_NUM_LEVELS)
- override val numberOfLevels = _numberOfLevels
-
override val isForceHidden = MutableStateFlow(false)
override val isAllowedDuringAirplaneMode = MutableStateFlow(false)
- fun setIsEmergencyOnly(emergency: Boolean) {
- _isEmergencyOnly.value = emergency
- }
+ override val signalLevelIcon: MutableStateFlow<SignalIconModel> =
+ MutableStateFlow(
+ SignalIconModel(
+ level = 0,
+ numberOfLevels = 4,
+ showExclamationMark = false,
+ carrierNetworkChange = false,
+ )
+ )
fun setIsDataEnabled(enabled: Boolean) {
_isDataEnabled.value = enabled
}
-
- fun setIsDefaultDataEnabled(disabled: Boolean) {
- _isDefaultDataEnabled.value = disabled
- }
-
- fun setIsFailedConnection(failed: Boolean) {
- _isFailedConnection.value = failed
- }
-
- fun setLevel(level: Int) {
- _level.value = level
- }
-
- fun setNumberOfLevels(num: Int) {
- _numberOfLevels.value = num
- }
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/domain/interactor/FakeMobileIconsInteractor.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/domain/interactor/FakeMobileIconsInteractor.kt
index 82b7ec4..75d1869 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/domain/interactor/FakeMobileIconsInteractor.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/domain/interactor/FakeMobileIconsInteractor.kt
@@ -58,6 +58,8 @@
private val _activeDataConnectionHasDataEnabled = MutableStateFlow(false)
override val activeDataConnectionHasDataEnabled = _activeDataConnectionHasDataEnabled
+ override val activeDataIconInteractor = MutableStateFlow(null)
+
override val alwaysShowDataRatIcon = MutableStateFlow(false)
override val alwaysUseCdmaLevel = MutableStateFlow(false)
@@ -78,7 +80,7 @@
override val isForceHidden = MutableStateFlow(false)
/** Always returns a new fake interactor */
- override fun createMobileConnectionInteractorForSubId(subId: Int): MobileIconInteractor {
+ override fun getMobileConnectionInteractorForSubId(subId: Int): MobileIconInteractor {
return FakeMobileIconInteractor(tableLogBuffer).also { interactorCache[subId] = it }
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/domain/interactor/MobileIconInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/domain/interactor/MobileIconInteractorTest.kt
index e3c59ad..e2f9119 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/domain/interactor/MobileIconInteractorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/domain/interactor/MobileIconInteractorTest.kt
@@ -35,6 +35,7 @@
import com.android.systemui.statusbar.pipeline.mobile.domain.interactor.FakeMobileIconsInteractor.Companion.FOUR_G
import com.android.systemui.statusbar.pipeline.mobile.domain.interactor.FakeMobileIconsInteractor.Companion.THREE_G
import com.android.systemui.statusbar.pipeline.mobile.domain.model.NetworkTypeIconModel
+import com.android.systemui.statusbar.pipeline.mobile.domain.model.SignalIconModel
import com.android.systemui.statusbar.pipeline.mobile.util.FakeMobileMappingsProxy
import com.android.systemui.util.mockito.any
import com.android.systemui.util.mockito.mock
@@ -75,22 +76,12 @@
@Before
fun setUp() {
underTest = createInteractor()
+
+ mobileIconsInteractor.activeDataConnectionHasDataEnabled.value = true
+ connectionRepository.isInService.value = true
}
@Test
- fun gsm_level_default_unknown() =
- testScope.runTest {
- connectionRepository.isGsm.value = true
-
- var latest: Int? = null
- val job = underTest.level.onEach { latest = it }.launchIn(this)
-
- assertThat(latest).isEqualTo(CellSignalStrength.SIGNAL_STRENGTH_NONE_OR_UNKNOWN)
-
- job.cancel()
- }
-
- @Test
fun gsm_usesGsmLevel() =
testScope.runTest {
connectionRepository.isGsm.value = true
@@ -98,7 +89,7 @@
connectionRepository.cdmaLevel.value = CDMA_LEVEL
var latest: Int? = null
- val job = underTest.level.onEach { latest = it }.launchIn(this)
+ val job = underTest.signalLevelIcon.onEach { latest = it.level }.launchIn(this)
assertThat(latest).isEqualTo(GSM_LEVEL)
@@ -114,7 +105,7 @@
mobileIconsInteractor.alwaysUseCdmaLevel.value = true
var latest: Int? = null
- val job = underTest.level.onEach { latest = it }.launchIn(this)
+ val job = underTest.signalLevelIcon.onEach { latest = it.level }.launchIn(this)
assertThat(latest).isEqualTo(GSM_LEVEL)
@@ -127,7 +118,7 @@
connectionRepository.isGsm.value = false
var latest: Int? = null
- val job = underTest.level.onEach { latest = it }.launchIn(this)
+ val job = underTest.signalLevelIcon.onEach { latest = it.level }.launchIn(this)
assertThat(latest).isEqualTo(CellSignalStrength.SIGNAL_STRENGTH_NONE_OR_UNKNOWN)
job.cancel()
@@ -142,7 +133,7 @@
mobileIconsInteractor.alwaysUseCdmaLevel.value = true
var latest: Int? = null
- val job = underTest.level.onEach { latest = it }.launchIn(this)
+ val job = underTest.signalLevelIcon.onEach { latest = it.level }.launchIn(this)
assertThat(latest).isEqualTo(CDMA_LEVEL)
@@ -158,7 +149,7 @@
mobileIconsInteractor.alwaysUseCdmaLevel.value = false
var latest: Int? = null
- val job = underTest.level.onEach { latest = it }.launchIn(this)
+ val job = underTest.signalLevelIcon.onEach { latest = it.level }.launchIn(this)
assertThat(latest).isEqualTo(GSM_LEVEL)
@@ -169,7 +160,7 @@
fun numberOfLevels_comesFromRepo() =
testScope.runTest {
var latest: Int? = null
- val job = underTest.numberOfLevels.onEach { latest = it }.launchIn(this)
+ val job = underTest.signalLevelIcon.onEach { latest = it.numberOfLevels }.launchIn(this)
connectionRepository.numberOfLevels.value = 5
assertThat(latest).isEqualTo(5)
@@ -295,50 +286,6 @@
}
@Test
- fun alwaysUseCdmaLevel_matchesParent() =
- testScope.runTest {
- var latest: Boolean? = null
- val job = underTest.alwaysUseCdmaLevel.onEach { latest = it }.launchIn(this)
-
- mobileIconsInteractor.alwaysUseCdmaLevel.value = true
- assertThat(latest).isTrue()
-
- mobileIconsInteractor.alwaysUseCdmaLevel.value = false
- assertThat(latest).isFalse()
-
- job.cancel()
- }
-
- @Test
- fun test_isDefaultDataEnabled_matchesParent() =
- testScope.runTest {
- var latest: Boolean? = null
- val job = underTest.isDefaultDataEnabled.onEach { latest = it }.launchIn(this)
-
- mobileIconsInteractor.activeDataConnectionHasDataEnabled.value = true
- assertThat(latest).isTrue()
-
- mobileIconsInteractor.activeDataConnectionHasDataEnabled.value = false
- assertThat(latest).isFalse()
-
- job.cancel()
- }
-
- @Test
- fun test_isDefaultConnectionFailed_matchedParent() =
- testScope.runTest {
- val job = underTest.isDefaultConnectionFailed.launchIn(this)
-
- mobileIconsInteractor.isDefaultConnectionFailed.value = false
- assertThat(underTest.isDefaultConnectionFailed.value).isFalse()
-
- mobileIconsInteractor.isDefaultConnectionFailed.value = true
- assertThat(underTest.isDefaultConnectionFailed.value).isTrue()
-
- job.cancel()
- }
-
- @Test
fun dataState_connected() =
testScope.runTest {
var latest: Boolean? = null
@@ -541,6 +488,142 @@
assertThat(latest).isFalse()
}
+ @Test
+ fun iconId_correctLevel_notCutout() =
+ testScope.runTest {
+ connectionRepository.isInService.value = true
+ connectionRepository.primaryLevel.value = 1
+ connectionRepository.setDataEnabled(false)
+
+ var latest: SignalIconModel? = null
+ val job = underTest.signalLevelIcon.onEach { latest = it }.launchIn(this)
+
+ assertThat(latest?.level).isEqualTo(1)
+ assertThat(latest?.showExclamationMark).isFalse()
+
+ job.cancel()
+ }
+
+ @Test
+ fun icon_usesLevelFromInteractor() =
+ testScope.runTest {
+ connectionRepository.isInService.value = true
+
+ var latest: SignalIconModel? = null
+ val job = underTest.signalLevelIcon.onEach { latest = it }.launchIn(this)
+
+ connectionRepository.primaryLevel.value = 3
+ assertThat(latest!!.level).isEqualTo(3)
+
+ connectionRepository.primaryLevel.value = 1
+ assertThat(latest!!.level).isEqualTo(1)
+
+ job.cancel()
+ }
+
+ @Test
+ fun icon_usesNumberOfLevelsFromInteractor() =
+ testScope.runTest {
+ var latest: SignalIconModel? = null
+ val job = underTest.signalLevelIcon.onEach { latest = it }.launchIn(this)
+
+ connectionRepository.numberOfLevels.value = 5
+ assertThat(latest!!.numberOfLevels).isEqualTo(5)
+
+ connectionRepository.numberOfLevels.value = 2
+ assertThat(latest!!.numberOfLevels).isEqualTo(2)
+
+ job.cancel()
+ }
+
+ @Test
+ fun icon_defaultDataDisabled_showExclamationTrue() =
+ testScope.runTest {
+ mobileIconsInteractor.activeDataConnectionHasDataEnabled.value = false
+
+ var latest: SignalIconModel? = null
+ val job = underTest.signalLevelIcon.onEach { latest = it }.launchIn(this)
+
+ assertThat(latest!!.showExclamationMark).isTrue()
+
+ job.cancel()
+ }
+
+ @Test
+ fun icon_defaultConnectionFailed_showExclamationTrue() =
+ testScope.runTest {
+ mobileIconsInteractor.isDefaultConnectionFailed.value = true
+
+ var latest: SignalIconModel? = null
+ val job = underTest.signalLevelIcon.onEach { latest = it }.launchIn(this)
+
+ assertThat(latest!!.showExclamationMark).isTrue()
+
+ job.cancel()
+ }
+
+ @Test
+ fun icon_enabledAndNotFailed_showExclamationFalse() =
+ testScope.runTest {
+ connectionRepository.isInService.value = true
+ mobileIconsInteractor.activeDataConnectionHasDataEnabled.value = true
+ mobileIconsInteractor.isDefaultConnectionFailed.value = false
+
+ var latest: SignalIconModel? = null
+ val job = underTest.signalLevelIcon.onEach { latest = it }.launchIn(this)
+
+ assertThat(latest!!.showExclamationMark).isFalse()
+
+ job.cancel()
+ }
+
+ @Test
+ fun icon_usesEmptyState_whenNotInService() =
+ testScope.runTest {
+ var latest: SignalIconModel? = null
+ val job = underTest.signalLevelIcon.onEach { latest = it }.launchIn(this)
+
+ connectionRepository.isInService.value = false
+
+ assertThat(latest?.level).isEqualTo(0)
+ assertThat(latest?.showExclamationMark).isTrue()
+
+ // Changing the level doesn't overwrite the disabled state
+ connectionRepository.primaryLevel.value = 2
+ assertThat(latest?.level).isEqualTo(0)
+ assertThat(latest?.showExclamationMark).isTrue()
+
+ // Once back in service, the regular icon appears
+ connectionRepository.isInService.value = true
+ assertThat(latest?.level).isEqualTo(2)
+ assertThat(latest?.showExclamationMark).isFalse()
+
+ job.cancel()
+ }
+
+ @Test
+ fun icon_usesCarrierNetworkState_whenInCarrierNetworkChangeMode() =
+ testScope.runTest {
+ var latest: SignalIconModel? = null
+ val job = underTest.signalLevelIcon.onEach { latest = it }.launchIn(this)
+
+ connectionRepository.isInService.value = true
+ connectionRepository.carrierNetworkChangeActive.value = true
+ connectionRepository.primaryLevel.value = 1
+ connectionRepository.cdmaLevel.value = 1
+
+ assertThat(latest!!.level).isEqualTo(1)
+ assertThat(latest!!.carrierNetworkChange).isTrue()
+
+ // SignalIconModel respects the current level
+ connectionRepository.primaryLevel.value = 2
+
+ assertThat(latest!!.level).isEqualTo(2)
+ assertThat(latest!!.carrierNetworkChange).isTrue()
+
+ job.cancel()
+ }
+
private fun createInteractor(
overrides: MobileIconCarrierIdOverrides = MobileIconCarrierIdOverridesImpl()
) =
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 3e6f909..b4c7578 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
@@ -781,6 +781,16 @@
job.cancel()
}
+ @Test
+ fun iconInteractor_cachedPerSubId() =
+ testScope.runTest {
+ val interactor1 = underTest.getMobileConnectionInteractorForSubId(SUB_1_ID)
+ val interactor2 = underTest.getMobileConnectionInteractorForSubId(SUB_1_ID)
+
+ assertThat(interactor1).isNotNull()
+ assertThat(interactor1).isSameInstanceAs(interactor2)
+ }
+
/**
* 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/mobile/ui/model/SignalIconModelParameterizedTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/ui/model/SignalIconModelParameterizedTest.kt
index 01c388a..90a8946 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/ui/model/SignalIconModelParameterizedTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/ui/model/SignalIconModelParameterizedTest.kt
@@ -19,6 +19,7 @@
import androidx.test.filters.SmallTest
import com.android.settingslib.graph.SignalDrawable
import com.android.systemui.SysuiTestCase
+import com.android.systemui.statusbar.pipeline.mobile.domain.model.SignalIconModel
import com.google.common.truth.Truth.assertThat
import org.junit.Test
import org.junit.runner.RunWith
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/ui/viewmodel/LocationBasedMobileIconViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/ui/viewmodel/LocationBasedMobileIconViewModelTest.kt
index e59d90f..1878329 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/ui/viewmodel/LocationBasedMobileIconViewModelTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/ui/viewmodel/LocationBasedMobileIconViewModelTest.kt
@@ -17,18 +17,26 @@
package com.android.systemui.statusbar.pipeline.mobile.ui.viewmodel
import androidx.test.filters.SmallTest
-import com.android.settingslib.mobile.TelephonyIcons
import com.android.systemui.SysuiTestCase
import com.android.systemui.log.table.TableLogBuffer
+import com.android.systemui.statusbar.connectivity.MobileIconCarrierIdOverridesFake
import com.android.systemui.statusbar.pipeline.StatusBarPipelineFlags
import com.android.systemui.statusbar.pipeline.airplane.data.repository.FakeAirplaneModeRepository
import com.android.systemui.statusbar.pipeline.airplane.domain.interactor.AirplaneModeInteractor
-import com.android.systemui.statusbar.pipeline.mobile.domain.interactor.FakeMobileIconInteractor
-import com.android.systemui.statusbar.pipeline.mobile.domain.model.NetworkTypeIconModel
-import com.android.systemui.statusbar.pipeline.mobile.ui.model.SignalIconModel
-import com.android.systemui.statusbar.pipeline.mobile.ui.viewmodel.MobileIconViewModelTest.Companion.defaultSignal
+import com.android.systemui.statusbar.pipeline.mobile.data.model.DataConnectionState
+import com.android.systemui.statusbar.pipeline.mobile.data.model.ResolvedNetworkType
+import com.android.systemui.statusbar.pipeline.mobile.data.repository.FakeMobileConnectionRepository
+import com.android.systemui.statusbar.pipeline.mobile.data.repository.FakeMobileConnectionsRepository
+import com.android.systemui.statusbar.pipeline.mobile.data.repository.FakeUserSetupRepository
+import com.android.systemui.statusbar.pipeline.mobile.domain.interactor.MobileIconInteractor
+import com.android.systemui.statusbar.pipeline.mobile.domain.interactor.MobileIconInteractorImpl
+import com.android.systemui.statusbar.pipeline.mobile.domain.interactor.MobileIconsInteractor
+import com.android.systemui.statusbar.pipeline.mobile.domain.interactor.MobileIconsInteractorImpl
+import com.android.systemui.statusbar.pipeline.mobile.domain.model.SignalIconModel
+import com.android.systemui.statusbar.pipeline.mobile.util.FakeMobileMappingsProxy
import com.android.systemui.statusbar.pipeline.shared.ConnectivityConstants
import com.android.systemui.statusbar.pipeline.shared.data.repository.FakeConnectivityRepository
+import com.android.systemui.util.CarrierConfigTracker
import com.android.systemui.util.mockito.mock
import com.google.common.truth.Truth.assertThat
import kotlinx.coroutines.ExperimentalCoroutinesApi
@@ -50,11 +58,18 @@
private lateinit var homeIcon: HomeMobileIconViewModel
private lateinit var qsIcon: QsMobileIconViewModel
private lateinit var keyguardIcon: KeyguardMobileIconViewModel
- private lateinit var interactor: FakeMobileIconInteractor
+ private lateinit var iconsInteractor: MobileIconsInteractor
+ private lateinit var interactor: MobileIconInteractor
+ private lateinit var connectionsRepository: FakeMobileConnectionsRepository
+ private lateinit var repository: FakeMobileConnectionRepository
private lateinit var airplaneModeInteractor: AirplaneModeInteractor
+
+ private val connectivityRepository = FakeConnectivityRepository()
+
@Mock private lateinit var statusBarPipelineFlags: StatusBarPipelineFlags
@Mock private lateinit var constants: ConnectivityConstants
@Mock private lateinit var tableLogBuffer: TableLogBuffer
+ @Mock private lateinit var carrierConfigTracker: CarrierConfigTracker
private val testDispatcher = UnconfinedTestDispatcher()
private val testScope = TestScope(testDispatcher)
@@ -67,16 +82,51 @@
FakeAirplaneModeRepository(),
FakeConnectivityRepository(),
)
- interactor = FakeMobileIconInteractor(tableLogBuffer)
- interactor.apply {
- setLevel(1)
- setIsDefaultDataEnabled(true)
- setIsFailedConnection(false)
- setIsEmergencyOnly(false)
- setNumberOfLevels(4)
- networkTypeIconGroup.value = NetworkTypeIconModel.DefaultIcon(TelephonyIcons.THREE_G)
- isDataConnected.value = true
- }
+ connectionsRepository =
+ FakeMobileConnectionsRepository(FakeMobileMappingsProxy(), tableLogBuffer)
+ repository =
+ FakeMobileConnectionRepository(SUB_1_ID, tableLogBuffer).apply {
+ isInService.value = true
+ cdmaLevel.value = 1
+ primaryLevel.value = 1
+ isEmergencyOnly.value = false
+ numberOfLevels.value = 4
+ resolvedNetworkType.value = ResolvedNetworkType.DefaultNetworkType(lookupKey = "3G")
+ dataConnectionState.value = DataConnectionState.Connected
+ }
+
+ connectionsRepository.activeMobileDataRepository.value = repository
+
+ connectivityRepository.apply { setMobileConnected() }
+
+ iconsInteractor =
+ MobileIconsInteractorImpl(
+ connectionsRepository,
+ carrierConfigTracker,
+ tableLogBuffer,
+ connectivityRepository,
+ FakeUserSetupRepository(),
+ testScope.backgroundScope,
+ context,
+ )
+
+ interactor =
+ MobileIconInteractorImpl(
+ testScope.backgroundScope,
+ iconsInteractor.activeDataConnectionHasDataEnabled,
+ iconsInteractor.alwaysShowDataRatIcon,
+ iconsInteractor.alwaysUseCdmaLevel,
+ iconsInteractor.isSingleCarrier,
+ iconsInteractor.mobileIsDefault,
+ iconsInteractor.defaultMobileIconMapping,
+ iconsInteractor.defaultMobileIconGroup,
+ iconsInteractor.isDefaultConnectionFailed,
+ iconsInteractor.isForceHidden,
+ repository,
+ context,
+ MobileIconCarrierIdOverridesFake()
+ )
+
commonImpl =
MobileIconViewModel(
SUB_1_ID,
@@ -109,7 +159,7 @@
assertThat(latestQs).isEqualTo(expected)
assertThat(latestKeyguard).isEqualTo(expected)
- interactor.setLevel(2)
+ repository.setAllLevels(2)
expected = defaultSignal(level = 2)
assertThat(latestHome).isEqualTo(expected)
@@ -123,5 +173,16 @@
companion object {
private const val SUB_1_ID = 1
+ private const val NUM_LEVELS = 4
+
+ /** Convenience constructor for these tests */
+ fun defaultSignal(level: Int = 1): SignalIconModel {
+ return SignalIconModel(
+ level,
+ NUM_LEVELS,
+ showExclamationMark = false,
+ carrierNetworkChange = false,
+ )
+ }
}
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/ui/viewmodel/MobileIconViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/ui/viewmodel/MobileIconViewModelTest.kt
index 72feec7..796d5ec 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/ui/viewmodel/MobileIconViewModelTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/ui/viewmodel/MobileIconViewModelTest.kt
@@ -19,20 +19,30 @@
import androidx.test.filters.SmallTest
import com.android.settingslib.AccessibilityContentDescriptions.PHONE_SIGNAL_STRENGTH
import com.android.settingslib.AccessibilityContentDescriptions.PHONE_SIGNAL_STRENGTH_NONE
+import com.android.settingslib.mobile.MobileMappings
+import com.android.settingslib.mobile.TelephonyIcons.G
import com.android.settingslib.mobile.TelephonyIcons.THREE_G
import com.android.settingslib.mobile.TelephonyIcons.UNKNOWN
import com.android.systemui.SysuiTestCase
import com.android.systemui.common.shared.model.ContentDescription
import com.android.systemui.common.shared.model.Icon
import com.android.systemui.log.table.TableLogBuffer
+import com.android.systemui.statusbar.connectivity.MobileIconCarrierIdOverridesFake
import com.android.systemui.statusbar.pipeline.airplane.data.repository.FakeAirplaneModeRepository
import com.android.systemui.statusbar.pipeline.airplane.domain.interactor.AirplaneModeInteractor
-import com.android.systemui.statusbar.pipeline.mobile.domain.interactor.FakeMobileIconInteractor
-import com.android.systemui.statusbar.pipeline.mobile.domain.model.NetworkTypeIconModel
-import com.android.systemui.statusbar.pipeline.mobile.ui.model.SignalIconModel
+import com.android.systemui.statusbar.pipeline.mobile.data.model.DataConnectionState
+import com.android.systemui.statusbar.pipeline.mobile.data.repository.FakeMobileConnectionRepository
+import com.android.systemui.statusbar.pipeline.mobile.data.repository.FakeMobileConnectionsRepository
+import com.android.systemui.statusbar.pipeline.mobile.data.repository.FakeUserSetupRepository
+import com.android.systemui.statusbar.pipeline.mobile.domain.interactor.MobileIconInteractorImpl
+import com.android.systemui.statusbar.pipeline.mobile.domain.interactor.MobileIconsInteractorImpl
+import com.android.systemui.statusbar.pipeline.mobile.domain.model.SignalIconModel
+import com.android.systemui.statusbar.pipeline.mobile.util.FakeMobileMappingsProxy
import com.android.systemui.statusbar.pipeline.shared.ConnectivityConstants
+import com.android.systemui.statusbar.pipeline.shared.data.model.ConnectivitySlot
import com.android.systemui.statusbar.pipeline.shared.data.model.DataActivityModel
import com.android.systemui.statusbar.pipeline.shared.data.repository.FakeConnectivityRepository
+import com.android.systemui.util.CarrierConfigTracker
import com.android.systemui.util.mockito.whenever
import com.google.common.truth.Truth.assertThat
import kotlinx.coroutines.ExperimentalCoroutinesApi
@@ -51,12 +61,18 @@
@OptIn(ExperimentalCoroutinesApi::class)
@SmallTest
class MobileIconViewModelTest : SysuiTestCase() {
+ private var connectivityRepository = FakeConnectivityRepository()
+
private lateinit var underTest: MobileIconViewModel
- private lateinit var interactor: FakeMobileIconInteractor
+ private lateinit var interactor: MobileIconInteractorImpl
+ private lateinit var iconsInteractor: MobileIconsInteractorImpl
+ private lateinit var repository: FakeMobileConnectionRepository
+ private lateinit var connectionsRepository: FakeMobileConnectionsRepository
private lateinit var airplaneModeRepository: FakeAirplaneModeRepository
private lateinit var airplaneModeInteractor: AirplaneModeInteractor
@Mock private lateinit var constants: ConnectivityConstants
@Mock private lateinit var tableLogBuffer: TableLogBuffer
+ @Mock private lateinit var carrierConfigTracker: CarrierConfigTracker
private val testDispatcher = UnconfinedTestDispatcher()
private val testScope = TestScope(testDispatcher)
@@ -66,23 +82,53 @@
MockitoAnnotations.initMocks(this)
whenever(constants.hasDataCapabilities).thenReturn(true)
+ connectionsRepository =
+ FakeMobileConnectionsRepository(FakeMobileMappingsProxy(), tableLogBuffer)
+
+ repository =
+ FakeMobileConnectionRepository(SUB_1_ID, tableLogBuffer).apply {
+ setNetworkTypeKey(connectionsRepository.GSM_KEY)
+ isInService.value = true
+ dataConnectionState.value = DataConnectionState.Connected
+ dataEnabled.value = true
+ }
+ connectionsRepository.activeMobileDataRepository.value = repository
+ connectionsRepository.mobileIsDefault.value = true
+
airplaneModeRepository = FakeAirplaneModeRepository()
airplaneModeInteractor =
AirplaneModeInteractor(
airplaneModeRepository,
- FakeConnectivityRepository(),
+ connectivityRepository,
)
- interactor = FakeMobileIconInteractor(tableLogBuffer)
- interactor.apply {
- setLevel(1)
- setIsDefaultDataEnabled(true)
- setIsFailedConnection(false)
- setIsEmergencyOnly(false)
- setNumberOfLevels(4)
- interactor.networkTypeIconGroup.value = NetworkTypeIconModel.DefaultIcon(THREE_G)
- isDataConnected.value = true
- }
+ iconsInteractor =
+ MobileIconsInteractorImpl(
+ connectionsRepository,
+ carrierConfigTracker,
+ tableLogBuffer,
+ connectivityRepository,
+ FakeUserSetupRepository(),
+ testScope.backgroundScope,
+ context,
+ )
+
+ interactor =
+ MobileIconInteractorImpl(
+ testScope.backgroundScope,
+ iconsInteractor.activeDataConnectionHasDataEnabled,
+ iconsInteractor.alwaysShowDataRatIcon,
+ iconsInteractor.alwaysUseCdmaLevel,
+ iconsInteractor.isSingleCarrier,
+ iconsInteractor.mobileIsDefault,
+ iconsInteractor.defaultMobileIconMapping,
+ iconsInteractor.defaultMobileIconGroup,
+ iconsInteractor.isDefaultConnectionFailed,
+ iconsInteractor.isForceHidden,
+ repository,
+ context,
+ MobileIconCarrierIdOverridesFake()
+ )
createAndSetViewModel()
}
@@ -108,7 +154,6 @@
val job = underTest.isVisible.onEach { latest = it }.launchIn(this)
airplaneModeRepository.setIsAirplaneMode(false)
- interactor.isForceHidden.value = false
assertThat(latest).isTrue()
@@ -122,8 +167,8 @@
val job = underTest.isVisible.onEach { latest = it }.launchIn(this)
airplaneModeRepository.setIsAirplaneMode(true)
- interactor.isAllowedDuringAirplaneMode.value = false
- interactor.isForceHidden.value = false
+ repository.isAllowedDuringAirplaneMode.value = false
+ connectivityRepository.setForceHiddenIcons(setOf())
assertThat(latest).isFalse()
@@ -138,8 +183,8 @@
val job = underTest.isVisible.onEach { latest = it }.launchIn(this)
airplaneModeRepository.setIsAirplaneMode(true)
- interactor.isAllowedDuringAirplaneMode.value = true
- interactor.isForceHidden.value = false
+ repository.isAllowedDuringAirplaneMode.value = true
+ connectivityRepository.setForceHiddenIcons(setOf())
assertThat(latest).isTrue()
@@ -153,7 +198,7 @@
val job = underTest.isVisible.onEach { latest = it }.launchIn(this)
airplaneModeRepository.setIsAirplaneMode(false)
- interactor.isForceHidden.value = true
+ connectivityRepository.setForceHiddenIcons(setOf(ConnectivitySlot.MOBILE))
assertThat(latest).isFalse()
@@ -167,156 +212,29 @@
val job = underTest.isVisible.onEach { latest = it }.launchIn(this)
airplaneModeRepository.setIsAirplaneMode(false)
- interactor.isForceHidden.value = false
+ connectivityRepository.setForceHiddenIcons(setOf())
assertThat(latest).isTrue()
airplaneModeRepository.setIsAirplaneMode(true)
assertThat(latest).isFalse()
- interactor.isAllowedDuringAirplaneMode.value = true
+ repository.isAllowedDuringAirplaneMode.value = true
assertThat(latest).isTrue()
- interactor.isForceHidden.value = true
+ connectivityRepository.setForceHiddenIcons(setOf(ConnectivitySlot.MOBILE))
assertThat(latest).isFalse()
job.cancel()
}
@Test
- fun iconId_correctLevel_notCutout() =
- testScope.runTest {
- var latest: SignalIconModel? = null
- val job = underTest.icon.onEach { latest = it }.launchIn(this)
- val expected = defaultSignal()
-
- assertThat(latest).isEqualTo(expected)
-
- job.cancel()
- }
-
- @Test
- fun icon_usesLevelFromInteractor() =
- testScope.runTest {
- var latest: SignalIconModel? = null
- val job = underTest.icon.onEach { latest = it }.launchIn(this)
-
- interactor.level.value = 3
- assertThat(latest!!.level).isEqualTo(3)
-
- interactor.level.value = 1
- assertThat(latest!!.level).isEqualTo(1)
-
- job.cancel()
- }
-
- @Test
- fun icon_usesNumberOfLevelsFromInteractor() =
- testScope.runTest {
- var latest: SignalIconModel? = null
- val job = underTest.icon.onEach { latest = it }.launchIn(this)
-
- interactor.numberOfLevels.value = 5
- assertThat(latest!!.numberOfLevels).isEqualTo(5)
-
- interactor.numberOfLevels.value = 2
- assertThat(latest!!.numberOfLevels).isEqualTo(2)
-
- job.cancel()
- }
-
- @Test
- fun icon_defaultDataDisabled_showExclamationTrue() =
- testScope.runTest {
- interactor.setIsDefaultDataEnabled(false)
-
- var latest: SignalIconModel? = null
- val job = underTest.icon.onEach { latest = it }.launchIn(this)
-
- assertThat(latest!!.showExclamationMark).isTrue()
-
- job.cancel()
- }
-
- @Test
- fun icon_defaultConnectionFailed_showExclamationTrue() =
- testScope.runTest {
- interactor.isDefaultConnectionFailed.value = true
-
- var latest: SignalIconModel? = null
- val job = underTest.icon.onEach { latest = it }.launchIn(this)
-
- assertThat(latest!!.showExclamationMark).isTrue()
-
- job.cancel()
- }
-
- @Test
- fun icon_enabledAndNotFailed_showExclamationFalse() =
- testScope.runTest {
- interactor.setIsDefaultDataEnabled(true)
- interactor.isDefaultConnectionFailed.value = false
-
- var latest: SignalIconModel? = null
- val job = underTest.icon.onEach { latest = it }.launchIn(this)
-
- assertThat(latest!!.showExclamationMark).isFalse()
-
- job.cancel()
- }
-
- @Test
- fun icon_usesEmptyState_whenNotInService() =
- testScope.runTest {
- var latest: SignalIconModel? = null
- val job = underTest.icon.onEach { latest = it }.launchIn(this)
-
- interactor.isInService.value = false
-
- var expected = emptySignal()
-
- assertThat(latest).isEqualTo(expected)
-
- // Changing the level doesn't overwrite the disabled state
- interactor.level.value = 2
- assertThat(latest).isEqualTo(expected)
-
- // Once back in service, the regular icon appears
- interactor.isInService.value = true
- expected = defaultSignal(level = 2)
- assertThat(latest).isEqualTo(expected)
-
- job.cancel()
- }
-
- @Test
- fun icon_usesCarrierNetworkState_whenInCarrierNetworkChangeMode() =
- testScope.runTest {
- var latest: SignalIconModel? = null
- val job = underTest.icon.onEach { latest = it }.launchIn(this)
-
- interactor.carrierNetworkChangeActive.value = true
- interactor.level.value = 1
-
- assertThat(latest!!.level).isEqualTo(1)
- assertThat(latest!!.carrierNetworkChange).isTrue()
-
- // SignalIconModel respects the current level
- interactor.level.value = 2
-
- assertThat(latest!!.level).isEqualTo(2)
- assertThat(latest!!.carrierNetworkChange).isTrue()
-
- job.cancel()
- }
-
- @Test
fun contentDescription_notInService_usesNoPhone() =
testScope.runTest {
var latest: ContentDescription? = null
val job = underTest.contentDescription.onEach { latest = it }.launchIn(this)
- interactor.isInService.value = false
+ repository.isInService.value = false
assertThat((latest as ContentDescription.Resource).res)
.isEqualTo(PHONE_SIGNAL_STRENGTH_NONE)
@@ -330,13 +248,11 @@
var latest: ContentDescription? = null
val job = underTest.contentDescription.onEach { latest = it }.launchIn(this)
- interactor.isInService.value = true
-
- interactor.level.value = 2
+ repository.setAllLevels(2)
assertThat((latest as ContentDescription.Resource).res)
.isEqualTo(PHONE_SIGNAL_STRENGTH[2])
- interactor.level.value = 0
+ repository.setAllLevels(0)
assertThat((latest as ContentDescription.Resource).res)
.isEqualTo(PHONE_SIGNAL_STRENGTH[0])
@@ -351,7 +267,8 @@
THREE_G.dataType,
ContentDescription.Resource(THREE_G.dataContentDescription)
)
- interactor.networkTypeIconGroup.value = NetworkTypeIconModel.DefaultIcon(THREE_G)
+ connectionsRepository.mobileIsDefault.value = true
+ repository.setNetworkTypeKey(connectionsRepository.GSM_KEY)
var latest: Icon? = null
val job = underTest.networkTypeIcon.onEach { latest = it }.launchIn(this)
@@ -364,9 +281,9 @@
@Test
fun networkType_null_whenDisabled() =
testScope.runTest {
- interactor.networkTypeIconGroup.value = NetworkTypeIconModel.DefaultIcon(THREE_G)
- interactor.setIsDataEnabled(false)
- interactor.mobileIsDefault.value = true
+ repository.setNetworkTypeKey(connectionsRepository.GSM_KEY)
+ repository.setDataEnabled(false)
+ connectionsRepository.mobileIsDefault.value = true
var latest: Icon? = null
val job = underTest.networkTypeIcon.onEach { latest = it }.launchIn(this)
@@ -378,9 +295,9 @@
@Test
fun networkType_null_whenCarrierNetworkChangeActive() =
testScope.runTest {
- interactor.networkTypeIconGroup.value = NetworkTypeIconModel.DefaultIcon(THREE_G)
- interactor.carrierNetworkChangeActive.value = true
- interactor.mobileIsDefault.value = true
+ repository.setNetworkTypeKey(connectionsRepository.GSM_KEY)
+ repository.carrierNetworkChangeActive.value = true
+ connectionsRepository.mobileIsDefault.value = true
var latest: Icon? = null
val job = underTest.networkTypeIcon.onEach { latest = it }.launchIn(this)
@@ -397,10 +314,10 @@
THREE_G.dataType,
ContentDescription.Resource(THREE_G.dataContentDescription)
)
- interactor.networkTypeIconGroup.value = NetworkTypeIconModel.DefaultIcon(THREE_G)
- interactor.setIsDataEnabled(true)
- interactor.isDataConnected.value = true
- interactor.mobileIsDefault.value = true
+ repository.setNetworkTypeKey(connectionsRepository.GSM_KEY)
+ repository.setDataEnabled(true)
+ repository.dataConnectionState.value = DataConnectionState.Connected
+ connectionsRepository.mobileIsDefault.value = true
var latest: Icon? = null
val job = underTest.networkTypeIcon.onEach { latest = it }.launchIn(this)
@@ -418,15 +335,13 @@
ContentDescription.Resource(THREE_G.dataContentDescription)
)
- interactor.networkTypeIconGroup.value = NetworkTypeIconModel.DefaultIcon(THREE_G)
+ repository.setNetworkTypeKey(connectionsRepository.GSM_KEY)
var latest: Icon? = null
val job = underTest.networkTypeIcon.onEach { latest = it }.launchIn(this)
- interactor.networkTypeIconGroup.value = NetworkTypeIconModel.DefaultIcon(THREE_G)
assertThat(latest).isEqualTo(initial)
- interactor.isDataConnected.value = false
- yield()
+ repository.dataConnectionState.value = DataConnectionState.Disconnected
assertThat(latest).isNull()
@@ -441,15 +356,13 @@
THREE_G.dataType,
ContentDescription.Resource(THREE_G.dataContentDescription)
)
- interactor.networkTypeIconGroup.value = NetworkTypeIconModel.DefaultIcon(THREE_G)
- interactor.setIsDataEnabled(true)
+ repository.dataEnabled.value = true
var latest: Icon? = null
val job = underTest.networkTypeIcon.onEach { latest = it }.launchIn(this)
assertThat(latest).isEqualTo(expected)
- interactor.setIsDataEnabled(false)
- yield()
+ repository.dataEnabled.value = false
assertThat(latest).isNull()
@@ -459,9 +372,10 @@
@Test
fun networkType_alwaysShow_shownEvenWhenDisabled() =
testScope.runTest {
- interactor.networkTypeIconGroup.value = NetworkTypeIconModel.DefaultIcon(THREE_G)
- interactor.setIsDataEnabled(false)
- interactor.alwaysShowDataRatIcon.value = true
+ repository.dataEnabled.value = false
+
+ connectionsRepository.defaultDataSubRatConfig.value =
+ MobileMappings.Config().also { it.alwaysShowDataRatIcon = true }
var latest: Icon? = null
val job = underTest.networkTypeIcon.onEach { latest = it }.launchIn(this)
@@ -479,9 +393,11 @@
@Test
fun networkType_alwaysShow_shownEvenWhenDisconnected() =
testScope.runTest {
- interactor.networkTypeIconGroup.value = NetworkTypeIconModel.DefaultIcon(THREE_G)
- interactor.isDataConnected.value = false
- interactor.alwaysShowDataRatIcon.value = true
+ repository.setNetworkTypeKey(connectionsRepository.GSM_KEY)
+ repository.dataConnectionState.value = DataConnectionState.Disconnected
+
+ connectionsRepository.defaultDataSubRatConfig.value =
+ MobileMappings.Config().also { it.alwaysShowDataRatIcon = true }
var latest: Icon? = null
val job = underTest.networkTypeIcon.onEach { latest = it }.launchIn(this)
@@ -499,9 +415,10 @@
@Test
fun networkType_alwaysShow_shownEvenWhenFailedConnection() =
testScope.runTest {
- interactor.networkTypeIconGroup.value = NetworkTypeIconModel.DefaultIcon(THREE_G)
- interactor.setIsFailedConnection(true)
- interactor.alwaysShowDataRatIcon.value = true
+ repository.setNetworkTypeKey(connectionsRepository.GSM_KEY)
+ connectionsRepository.mobileIsDefault.value = true
+ connectionsRepository.defaultDataSubRatConfig.value =
+ MobileMappings.Config().also { it.alwaysShowDataRatIcon = true }
var latest: Icon? = null
val job = underTest.networkTypeIcon.onEach { latest = it }.launchIn(this)
@@ -517,16 +434,24 @@
}
@Test
- fun networkType_alwaysShow_notShownWhenInvalidDataTypeIcon() =
+ fun networkType_alwaysShow_usesDefaultIconWhenInvalid() =
testScope.runTest {
- // The UNKNOWN icon group doesn't have a valid data type icon ID
- interactor.networkTypeIconGroup.value = NetworkTypeIconModel.DefaultIcon(UNKNOWN)
- interactor.alwaysShowDataRatIcon.value = true
+ // The UNKNOWN icon group doesn't have a valid data type icon ID, and the logic from the
+ // old pipeline was to use the default icon group if the map doesn't exist
+ repository.setNetworkTypeKey(UNKNOWN.name)
+ connectionsRepository.defaultDataSubRatConfig.value =
+ MobileMappings.Config().also { it.alwaysShowDataRatIcon = true }
var latest: Icon? = null
val job = underTest.networkTypeIcon.onEach { latest = it }.launchIn(this)
- assertThat(latest).isNull()
+ val expected =
+ Icon.Resource(
+ connectionsRepository.defaultMobileIconGroup.value.dataType,
+ ContentDescription.Resource(G.dataContentDescription)
+ )
+
+ assertThat(latest).isEqualTo(expected)
job.cancel()
}
@@ -534,9 +459,10 @@
@Test
fun networkType_alwaysShow_shownWhenNotDefault() =
testScope.runTest {
- interactor.networkTypeIconGroup.value = NetworkTypeIconModel.DefaultIcon(THREE_G)
- interactor.mobileIsDefault.value = false
- interactor.alwaysShowDataRatIcon.value = true
+ repository.setNetworkTypeKey(connectionsRepository.GSM_KEY)
+ connectionsRepository.mobileIsDefault.value = false
+ connectionsRepository.defaultDataSubRatConfig.value =
+ MobileMappings.Config().also { it.alwaysShowDataRatIcon = true }
var latest: Icon? = null
val job = underTest.networkTypeIcon.onEach { latest = it }.launchIn(this)
@@ -554,9 +480,9 @@
@Test
fun networkType_notShownWhenNotDefault() =
testScope.runTest {
- interactor.networkTypeIconGroup.value = NetworkTypeIconModel.DefaultIcon(THREE_G)
- interactor.isDataConnected.value = true
- interactor.mobileIsDefault.value = false
+ repository.setNetworkTypeKey(connectionsRepository.GSM_KEY)
+ repository.dataConnectionState.value = DataConnectionState.Connected
+ connectionsRepository.mobileIsDefault.value = false
var latest: Icon? = null
val job = underTest.networkTypeIcon.onEach { latest = it }.launchIn(this)
@@ -569,13 +495,14 @@
@Test
fun roaming() =
testScope.runTest {
- interactor.isRoaming.value = true
+ repository.setAllRoaming(true)
+
var latest: Boolean? = null
val job = underTest.roaming.onEach { latest = it }.launchIn(this)
assertThat(latest).isTrue()
- interactor.isRoaming.value = false
+ repository.setAllRoaming(false)
assertThat(latest).isFalse()
@@ -599,7 +526,7 @@
val containerJob =
underTest.activityInVisible.onEach { containerVisible = it }.launchIn(this)
- interactor.activity.value =
+ repository.dataActivityDirection.value =
DataActivityModel(
hasActivityIn = true,
hasActivityOut = true,
@@ -631,7 +558,7 @@
val containerJob =
underTest.activityContainerVisible.onEach { containerVisible = it }.launchIn(this)
- interactor.activity.value =
+ repository.dataActivityDirection.value =
DataActivityModel(
hasActivityIn = true,
hasActivityOut = false,
@@ -643,7 +570,7 @@
assertThat(outVisible).isFalse()
assertThat(containerVisible).isTrue()
- interactor.activity.value =
+ repository.dataActivityDirection.value =
DataActivityModel(
hasActivityIn = false,
hasActivityOut = true,
@@ -653,7 +580,7 @@
assertThat(outVisible).isTrue()
assertThat(containerVisible).isTrue()
- interactor.activity.value =
+ repository.dataActivityDirection.value =
DataActivityModel(
hasActivityIn = false,
hasActivityOut = false,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/ui/viewmodel/MobileIconsViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/ui/viewmodel/MobileIconsViewModelTest.kt
index 065dfba..e42515e 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/ui/viewmodel/MobileIconsViewModelTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/ui/viewmodel/MobileIconsViewModelTest.kt
@@ -154,33 +154,6 @@
}
@Test
- fun caching_mobileIconInteractorIsReusedForSameSubId() =
- testScope.runTest {
- val interactor1 = underTest.mobileIconInteractorForSub(1)
- val interactor2 = underTest.mobileIconInteractorForSub(1)
-
- assertThat(interactor1).isSameInstanceAs(interactor2)
- }
-
- @Test
- fun caching_invalidInteractorssAreRemovedFromCacheWhenSubDisappears() =
- testScope.runTest {
- // Retrieve interactors to trigger caching
- val interactor1 = underTest.mobileIconInteractorForSub(1)
- val interactor2 = underTest.mobileIconInteractorForSub(2)
-
- // Both impls are cached
- assertThat(underTest.mobileIconInteractorSubIdCache)
- .containsExactly(1, interactor1, 2, interactor2)
-
- // SUB_1 is removed from the list...
- interactor.filteredSubscriptions.value = listOf(SUB_2)
-
- // ... and dropped from the cache
- assertThat(underTest.mobileIconInteractorSubIdCache).containsExactly(2, interactor2)
- }
-
- @Test
fun firstMobileSubShowingNetworkTypeIcon_noSubs_false() =
testScope.runTest {
var latest: Boolean? = null
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/shared/data/repository/FakeConnectivityRepository.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/shared/data/repository/FakeConnectivityRepository.kt
index 8f28cc0..e44ff8e 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/shared/data/repository/FakeConnectivityRepository.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/shared/data/repository/FakeConnectivityRepository.kt
@@ -27,7 +27,7 @@
MutableStateFlow(emptySet())
override val forceHiddenSlots: StateFlow<Set<ConnectivitySlot>> = _forceHiddenIcons
- override val defaultConnections: StateFlow<DefaultConnectionModel> =
+ override val defaultConnections: MutableStateFlow<DefaultConnectionModel> =
MutableStateFlow(DefaultConnectionModel())
override val vcnSubId: MutableStateFlow<Int?> = MutableStateFlow(null)
@@ -35,4 +35,43 @@
fun setForceHiddenIcons(hiddenIcons: Set<ConnectivitySlot>) {
_forceHiddenIcons.value = hiddenIcons
}
+
+ /**
+ * Convenience for setting mobile data connected, disconnected, or validated. Defaults to
+ * setting mobile connected && validated, since the default state is disconnected && not
+ * validated
+ */
+ fun setMobileConnected(
+ default: Boolean = true,
+ validated: Boolean = true,
+ ) {
+ defaultConnections.value =
+ DefaultConnectionModel(
+ mobile = DefaultConnectionModel.Mobile(default),
+ isValidated = validated,
+ )
+ }
+
+ /** Similar convenience method for ethernet */
+ fun setEthernetConnected(
+ default: Boolean = true,
+ validated: Boolean = true,
+ ) {
+ defaultConnections.value =
+ DefaultConnectionModel(
+ ethernet = DefaultConnectionModel.Ethernet(default),
+ isValidated = validated,
+ )
+ }
+
+ fun setWifiConnected(
+ default: Boolean = true,
+ validated: Boolean = true,
+ ) {
+ defaultConnections.value =
+ DefaultConnectionModel(
+ wifi = DefaultConnectionModel.Wifi(default),
+ isValidated = validated,
+ )
+ }
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/shared/ui/viewmodel/InternetTileViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/shared/ui/viewmodel/InternetTileViewModelTest.kt
new file mode 100644
index 0000000..6624ec2
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/shared/ui/viewmodel/InternetTileViewModelTest.kt
@@ -0,0 +1,352 @@
+/*
+ * 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.statusbar.pipeline.shared.ui.viewmodel
+
+import androidx.test.filters.SmallTest
+import com.android.systemui.R
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.common.shared.model.Text
+import com.android.systemui.coroutines.collectLastValue
+import com.android.systemui.log.table.TableLogBuffer
+import com.android.systemui.qs.tileimpl.QSTileImpl.ResourceIcon
+import com.android.systemui.statusbar.connectivity.WifiIcons
+import com.android.systemui.statusbar.pipeline.airplane.data.repository.FakeAirplaneModeRepository
+import com.android.systemui.statusbar.pipeline.ethernet.domain.EthernetInteractor
+import com.android.systemui.statusbar.pipeline.mobile.data.model.DataConnectionState
+import com.android.systemui.statusbar.pipeline.mobile.data.model.NetworkNameModel
+import com.android.systemui.statusbar.pipeline.mobile.data.repository.FakeMobileConnectionRepository
+import com.android.systemui.statusbar.pipeline.mobile.data.repository.FakeMobileConnectionsRepository
+import com.android.systemui.statusbar.pipeline.mobile.data.repository.FakeUserSetupRepository
+import com.android.systemui.statusbar.pipeline.mobile.domain.interactor.MobileIconsInteractor
+import com.android.systemui.statusbar.pipeline.mobile.domain.interactor.MobileIconsInteractorImpl
+import com.android.systemui.statusbar.pipeline.mobile.util.FakeMobileMappingsProxy
+import com.android.systemui.statusbar.pipeline.shared.data.model.DefaultConnectionModel
+import com.android.systemui.statusbar.pipeline.shared.data.repository.FakeConnectivityRepository
+import com.android.systemui.statusbar.pipeline.shared.ui.model.SignalIcon
+import com.android.systemui.statusbar.pipeline.shared.ui.viewmodel.InternetTileViewModel.Companion.NOT_CONNECTED_NETWORKS_UNAVAILABLE
+import com.android.systemui.statusbar.pipeline.wifi.data.repository.FakeWifiRepository
+import com.android.systemui.statusbar.pipeline.wifi.domain.interactor.WifiInteractorImpl
+import com.android.systemui.statusbar.pipeline.wifi.shared.model.WifiNetworkModel
+import com.android.systemui.statusbar.pipeline.wifi.shared.model.WifiScanEntry
+import com.android.systemui.util.CarrierConfigTracker
+import com.android.systemui.util.mockito.mock
+import com.google.common.truth.Truth.assertThat
+import kotlinx.coroutines.test.TestScope
+import kotlinx.coroutines.test.runTest
+import org.junit.Before
+import org.junit.Test
+
+@SmallTest
+class InternetTileViewModelTest : SysuiTestCase() {
+ private lateinit var underTest: InternetTileViewModel
+ private lateinit var mobileIconsInteractor: MobileIconsInteractor
+
+ private val airplaneModeRepository = FakeAirplaneModeRepository()
+ private val connectivityRepository = FakeConnectivityRepository()
+ private val ethernetInteractor = EthernetInteractor(connectivityRepository)
+ private val wifiRepository = FakeWifiRepository()
+ private val userSetupRepo = FakeUserSetupRepository()
+ private val testScope = TestScope()
+ private val wifiInteractor =
+ WifiInteractorImpl(connectivityRepository, wifiRepository, testScope.backgroundScope)
+
+ private val tableLogBuffer: TableLogBuffer = mock()
+ private val carrierConfigTracker: CarrierConfigTracker = mock()
+
+ private val mobileConnectionsRepository =
+ FakeMobileConnectionsRepository(FakeMobileMappingsProxy(), tableLogBuffer)
+ private val mobileConnectionRepository =
+ FakeMobileConnectionRepository(SUB_1_ID, tableLogBuffer)
+
+ @Before
+ fun setUp() {
+ mobileConnectionRepository.apply {
+ setNetworkTypeKey(mobileConnectionsRepository.GSM_KEY)
+ isInService.value = true
+ dataConnectionState.value = DataConnectionState.Connected
+ dataEnabled.value = true
+ }
+
+ mobileConnectionsRepository.apply {
+ activeMobileDataRepository.value = mobileConnectionRepository
+ activeMobileDataSubscriptionId.value = SUB_1_ID
+ setMobileConnectionRepositoryMap(mapOf(SUB_1_ID to mobileConnectionRepository))
+ }
+
+ mobileIconsInteractor =
+ MobileIconsInteractorImpl(
+ mobileConnectionsRepository,
+ carrierConfigTracker,
+ tableLogBuffer,
+ connectivityRepository,
+ userSetupRepo,
+ testScope.backgroundScope,
+ context,
+ )
+
+ underTest =
+ InternetTileViewModel(
+ airplaneModeRepository,
+ connectivityRepository,
+ ethernetInteractor,
+ mobileIconsInteractor,
+ wifiInteractor,
+ context,
+ testScope.backgroundScope,
+ )
+ }
+
+ @Test
+ fun noDefault_noNetworksAvailable() =
+ testScope.runTest {
+ val latest by collectLastValue(underTest.tileModel)
+
+ connectivityRepository.defaultConnections.value = DefaultConnectionModel()
+
+ assertThat(latest?.secondaryLabel)
+ .isEqualTo(Text.Resource(R.string.quick_settings_networks_unavailable))
+ assertThat(latest?.iconId).isEqualTo(R.drawable.ic_qs_no_internet_unavailable)
+ }
+
+ @Test
+ fun noDefault_networksAvailable() =
+ testScope.runTest {
+ // TODO: support [WifiInteractor.areNetworksAvailable]
+ }
+
+ @Test
+ fun wifiDefaultAndActive() =
+ testScope.runTest {
+ val latest by collectLastValue(underTest.tileModel)
+
+ val networkModel =
+ WifiNetworkModel.Active(
+ networkId = 1,
+ level = 4,
+ ssid = "test ssid",
+ )
+
+ connectivityRepository.setWifiConnected()
+ wifiRepository.setIsWifiDefault(true)
+ wifiRepository.setWifiNetwork(networkModel)
+
+ assertThat(latest?.secondaryTitle).isEqualTo("test ssid")
+ assertThat(latest?.secondaryLabel).isNull()
+ assertThat(latest?.icon)
+ .isEqualTo(ResourceIcon.get(WifiIcons.WIFI_NO_INTERNET_ICONS[4]))
+ assertThat(latest?.iconId).isNull()
+ }
+
+ @Test
+ fun wifiDefaultAndActive_hotspotNone() =
+ testScope.runTest {
+ val latest by collectLastValue(underTest.tileModel)
+
+ val networkModel =
+ WifiNetworkModel.Active(
+ networkId = 1,
+ level = 4,
+ ssid = "test ssid",
+ hotspotDeviceType = WifiNetworkModel.HotspotDeviceType.NONE,
+ )
+
+ connectivityRepository.setWifiConnected()
+ wifiRepository.setIsWifiDefault(true)
+ wifiRepository.setWifiNetwork(networkModel)
+
+ assertThat(latest?.icon)
+ .isEqualTo(ResourceIcon.get(WifiIcons.WIFI_NO_INTERNET_ICONS[4]))
+ }
+
+ @Test
+ fun wifiDefaultAndActive_hotspotTablet() =
+ testScope.runTest {
+ val latest by collectLastValue(underTest.tileModel)
+
+ setWifiNetworkWithHotspot(WifiNetworkModel.HotspotDeviceType.TABLET)
+
+ assertThat(latest?.icon)
+ .isEqualTo(ResourceIcon.get(com.android.settingslib.R.drawable.ic_hotspot_tablet))
+ }
+
+ @Test
+ fun wifiDefaultAndActive_hotspotLaptop() =
+ testScope.runTest {
+ val latest by collectLastValue(underTest.tileModel)
+
+ setWifiNetworkWithHotspot(WifiNetworkModel.HotspotDeviceType.LAPTOP)
+
+ assertThat(latest?.icon)
+ .isEqualTo(ResourceIcon.get(com.android.settingslib.R.drawable.ic_hotspot_laptop))
+ }
+
+ @Test
+ fun wifiDefaultAndActive_hotspotWatch() =
+ testScope.runTest {
+ val latest by collectLastValue(underTest.tileModel)
+
+ setWifiNetworkWithHotspot(WifiNetworkModel.HotspotDeviceType.WATCH)
+
+ assertThat(latest?.icon)
+ .isEqualTo(ResourceIcon.get(com.android.settingslib.R.drawable.ic_hotspot_watch))
+ }
+
+ @Test
+ fun wifiDefaultAndActive_hotspotAuto() =
+ testScope.runTest {
+ val latest by collectLastValue(underTest.tileModel)
+
+ setWifiNetworkWithHotspot(WifiNetworkModel.HotspotDeviceType.AUTO)
+
+ assertThat(latest?.icon)
+ .isEqualTo(ResourceIcon.get(com.android.settingslib.R.drawable.ic_hotspot_auto))
+ }
+
+ @Test
+ fun wifiDefaultAndActive_hotspotPhone() =
+ testScope.runTest {
+ val latest by collectLastValue(underTest.tileModel)
+
+ setWifiNetworkWithHotspot(WifiNetworkModel.HotspotDeviceType.PHONE)
+
+ assertThat(latest?.icon)
+ .isEqualTo(ResourceIcon.get(com.android.settingslib.R.drawable.ic_hotspot_phone))
+ }
+
+ @Test
+ fun wifiDefaultAndActive_hotspotUnknown() =
+ testScope.runTest {
+ val latest by collectLastValue(underTest.tileModel)
+
+ setWifiNetworkWithHotspot(WifiNetworkModel.HotspotDeviceType.UNKNOWN)
+
+ assertThat(latest?.icon)
+ .isEqualTo(ResourceIcon.get(com.android.settingslib.R.drawable.ic_hotspot_phone))
+ }
+
+ @Test
+ fun wifiDefaultAndActive_hotspotInvalid() =
+ testScope.runTest {
+ val latest by collectLastValue(underTest.tileModel)
+
+ setWifiNetworkWithHotspot(WifiNetworkModel.HotspotDeviceType.INVALID)
+
+ assertThat(latest?.icon)
+ .isEqualTo(ResourceIcon.get(com.android.settingslib.R.drawable.ic_hotspot_phone))
+ }
+
+ @Test
+ fun wifiDefaultAndNotActive_noNetworksAvailable() =
+ testScope.runTest {
+ val latest by collectLastValue(underTest.tileModel)
+
+ val networkModel = WifiNetworkModel.Inactive
+
+ connectivityRepository.setWifiConnected(validated = false)
+ wifiRepository.setIsWifiDefault(true)
+ wifiRepository.setWifiNetwork(networkModel)
+ wifiRepository.wifiScanResults.value = emptyList()
+
+ assertThat(latest).isEqualTo(NOT_CONNECTED_NETWORKS_UNAVAILABLE)
+ }
+
+ @Test
+ fun wifiDefaultAndNotActive_networksAvailable() =
+ testScope.runTest {
+ val latest by collectLastValue(underTest.tileModel)
+
+ val networkModel = WifiNetworkModel.Inactive
+
+ connectivityRepository.setWifiConnected(validated = false)
+ wifiRepository.setIsWifiDefault(true)
+ wifiRepository.setWifiNetwork(networkModel)
+ wifiRepository.wifiScanResults.value = listOf(WifiScanEntry("test 1"))
+
+ assertThat(latest?.secondaryLabel).isNull()
+ assertThat(latest?.secondaryTitle)
+ .isEqualTo(context.getString(R.string.quick_settings_networks_available))
+ assertThat(latest?.icon).isNull()
+ assertThat(latest?.iconId).isEqualTo(R.drawable.ic_qs_no_internet_available)
+ }
+
+ @Test
+ fun mobileDefault_usesNetworkNameAndIcon() =
+ testScope.runTest {
+ val latest by collectLastValue(underTest.tileModel)
+
+ connectivityRepository.setMobileConnected()
+ mobileConnectionsRepository.mobileIsDefault.value = true
+ mobileConnectionRepository.apply {
+ setAllLevels(3)
+ setAllRoaming(false)
+ networkName.value = NetworkNameModel.Default("test network")
+ }
+
+ assertThat(latest?.secondaryTitle).contains("test network")
+ assertThat(latest?.secondaryLabel).isNull()
+ assertThat(latest?.icon).isInstanceOf(SignalIcon::class.java)
+ assertThat(latest?.iconId).isNull()
+ }
+
+ @Test
+ fun ethernetDefault_validated_matchesInteractor() =
+ testScope.runTest {
+ val latest by collectLastValue(underTest.tileModel)
+ val ethernetIcon by collectLastValue(ethernetInteractor.icon)
+
+ connectivityRepository.setEthernetConnected(default = true, validated = true)
+
+ assertThat(latest?.secondaryLabel).isNull()
+ assertThat(latest?.secondaryTitle)
+ .isEqualTo(ethernetIcon!!.contentDescription.toString())
+ assertThat(latest?.iconId).isEqualTo(R.drawable.stat_sys_ethernet_fully)
+ assertThat(latest?.icon).isNull()
+ }
+
+ @Test
+ fun ethernetDefault_notValidated_matchesInteractor() =
+ testScope.runTest {
+ val latest by collectLastValue(underTest.tileModel)
+ val ethernetIcon by collectLastValue(ethernetInteractor.icon)
+
+ connectivityRepository.setEthernetConnected(default = true, validated = false)
+
+ assertThat(latest?.secondaryLabel).isNull()
+ assertThat(latest?.secondaryTitle)
+ .isEqualTo(ethernetIcon!!.contentDescription.toString())
+ assertThat(latest?.iconId).isEqualTo(R.drawable.stat_sys_ethernet)
+ assertThat(latest?.icon).isNull()
+ }
+
+ private fun setWifiNetworkWithHotspot(hotspot: WifiNetworkModel.HotspotDeviceType) {
+ val networkModel =
+ WifiNetworkModel.Active(
+ networkId = 1,
+ level = 4,
+ ssid = "test ssid",
+ hotspotDeviceType = hotspot,
+ )
+
+ connectivityRepository.setWifiConnected()
+ wifiRepository.setIsWifiDefault(true)
+ wifiRepository.setWifiNetwork(networkModel)
+ }
+
+ companion object {
+ const val SUB_1_ID = 1
+ }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/wifi/data/repository/FakeWifiRepository.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/wifi/data/repository/FakeWifiRepository.kt
index 4f7bb72..106b548 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/wifi/data/repository/FakeWifiRepository.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/wifi/data/repository/FakeWifiRepository.kt
@@ -19,6 +19,7 @@
import com.android.systemui.statusbar.pipeline.shared.data.model.DataActivityModel
import com.android.systemui.statusbar.pipeline.wifi.data.repository.prod.WifiRepositoryHelper.ACTIVITY_DEFAULT
import com.android.systemui.statusbar.pipeline.wifi.shared.model.WifiNetworkModel
+import com.android.systemui.statusbar.pipeline.wifi.shared.model.WifiScanEntry
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.StateFlow
@@ -39,6 +40,9 @@
private val _wifiActivity = MutableStateFlow(ACTIVITY_DEFAULT)
override val wifiActivity: StateFlow<DataActivityModel> = _wifiActivity
+ override val wifiScanResults: MutableStateFlow<List<WifiScanEntry>> =
+ MutableStateFlow(emptyList())
+
fun setIsWifiEnabled(enabled: Boolean) {
_isWifiEnabled.value = enabled
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/wifi/data/repository/prod/WifiRepositoryImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/wifi/data/repository/prod/WifiRepositoryImplTest.kt
index bea1154..c2e75aa 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/wifi/data/repository/prod/WifiRepositoryImplTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/wifi/data/repository/prod/WifiRepositoryImplTest.kt
@@ -27,6 +27,7 @@
import android.net.TransportInfo
import android.net.VpnTransportInfo
import android.net.vcn.VcnTransportInfo
+import android.net.wifi.ScanResult
import android.net.wifi.WifiInfo
import android.net.wifi.WifiManager
import android.net.wifi.WifiManager.TrafficStateCallback
@@ -45,6 +46,7 @@
import com.android.systemui.statusbar.pipeline.wifi.data.repository.prod.WifiRepositoryImpl.Companion.WIFI_NETWORK_DEFAULT
import com.android.systemui.statusbar.pipeline.wifi.shared.WifiInputLogger
import com.android.systemui.statusbar.pipeline.wifi.shared.model.WifiNetworkModel
+import com.android.systemui.statusbar.pipeline.wifi.shared.model.WifiScanEntry
import com.android.systemui.util.concurrency.FakeExecutor
import com.android.systemui.util.mockito.any
import com.android.systemui.util.mockito.argumentCaptor
@@ -1205,6 +1207,58 @@
.isEqualTo(DataActivityModel(hasActivityIn = true, hasActivityOut = true))
}
+ @Test
+ fun wifiScanResults_containsSsidList() =
+ testScope.runTest {
+ val latest by collectLastValue(underTest.wifiScanResults)
+
+ val scanResults =
+ listOf(
+ ScanResult().also { it.SSID = "ssid 1" },
+ ScanResult().also { it.SSID = "ssid 2" },
+ ScanResult().also { it.SSID = "ssid 3" },
+ ScanResult().also { it.SSID = "ssid 4" },
+ ScanResult().also { it.SSID = "ssid 5" },
+ )
+ whenever(wifiManager.scanResults).thenReturn(scanResults)
+ getScanResultsCallback().onScanResultsAvailable()
+
+ val expected =
+ listOf(
+ WifiScanEntry(ssid = "ssid 1"),
+ WifiScanEntry(ssid = "ssid 2"),
+ WifiScanEntry(ssid = "ssid 3"),
+ WifiScanEntry(ssid = "ssid 4"),
+ WifiScanEntry(ssid = "ssid 5"),
+ )
+
+ assertThat(latest).isEqualTo(expected)
+ }
+
+ @Test
+ fun wifiScanResults_updates() =
+ testScope.runTest {
+ val latest by collectLastValue(underTest.wifiScanResults)
+
+ var scanResults =
+ listOf(
+ ScanResult().also { it.SSID = "ssid 1" },
+ ScanResult().also { it.SSID = "ssid 2" },
+ ScanResult().also { it.SSID = "ssid 3" },
+ ScanResult().also { it.SSID = "ssid 4" },
+ ScanResult().also { it.SSID = "ssid 5" },
+ )
+ whenever(wifiManager.scanResults).thenReturn(scanResults)
+ getScanResultsCallback().onScanResultsAvailable()
+
+ // New scan representing no results
+ scanResults = emptyList()
+ whenever(wifiManager.scanResults).thenReturn(scanResults)
+ getScanResultsCallback().onScanResultsAvailable()
+
+ assertThat(latest).isEmpty()
+ }
+
private fun createRepo(): WifiRepositoryImpl {
return WifiRepositoryImpl(
fakeBroadcastDispatcher,
@@ -1240,6 +1294,13 @@
return callbackCaptor.value!!
}
+ private fun getScanResultsCallback(): WifiManager.ScanResultsCallback {
+ testScope.runCurrent()
+ val callbackCaptor = argumentCaptor<WifiManager.ScanResultsCallback>()
+ verify(wifiManager).registerScanResultsCallback(any(), callbackCaptor.capture())
+ return callbackCaptor.value!!
+ }
+
private fun createWifiNetworkCapabilities(
transportInfo: TransportInfo,
isValidated: Boolean = true,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/wifi/data/repository/prod/WifiRepositoryViaTrackerLibTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/wifi/data/repository/prod/WifiRepositoryViaTrackerLibTest.kt
index 662e36a..afab623 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/wifi/data/repository/prod/WifiRepositoryViaTrackerLibTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/wifi/data/repository/prod/WifiRepositoryViaTrackerLibTest.kt
@@ -16,6 +16,7 @@
package com.android.systemui.statusbar.pipeline.wifi.data.repository.prod
+import android.net.wifi.ScanResult
import android.net.wifi.WifiManager
import android.net.wifi.WifiManager.UNKNOWN_SSID
import android.net.wifi.sharedconnectivity.app.NetworkProviderInfo
@@ -32,6 +33,7 @@
import com.android.systemui.statusbar.pipeline.shared.data.model.DataActivityModel
import com.android.systemui.statusbar.pipeline.wifi.data.repository.prod.WifiRepositoryImpl.Companion.WIFI_NETWORK_DEFAULT
import com.android.systemui.statusbar.pipeline.wifi.shared.model.WifiNetworkModel
+import com.android.systemui.statusbar.pipeline.wifi.shared.model.WifiScanEntry
import com.android.systemui.util.concurrency.FakeExecutor
import com.android.systemui.util.mockito.any
import com.android.systemui.util.mockito.argumentCaptor
@@ -77,6 +79,7 @@
featureFlags,
testScope.backgroundScope,
executor,
+ dispatcher,
wifiPickerTrackerFactory,
wifiManager,
logger,
@@ -1137,6 +1140,58 @@
.isEqualTo(DataActivityModel(hasActivityIn = true, hasActivityOut = true))
}
+ @Test
+ fun wifiScanResults_containsSsidList() =
+ testScope.runTest {
+ val latest by collectLastValue(underTest.wifiScanResults)
+
+ val scanResults =
+ listOf(
+ ScanResult().also { it.SSID = "ssid 1" },
+ ScanResult().also { it.SSID = "ssid 2" },
+ ScanResult().also { it.SSID = "ssid 3" },
+ ScanResult().also { it.SSID = "ssid 4" },
+ ScanResult().also { it.SSID = "ssid 5" },
+ )
+ whenever(wifiManager.scanResults).thenReturn(scanResults)
+ getScanResultsCallback().onScanResultsAvailable()
+
+ val expected =
+ listOf(
+ WifiScanEntry(ssid = "ssid 1"),
+ WifiScanEntry(ssid = "ssid 2"),
+ WifiScanEntry(ssid = "ssid 3"),
+ WifiScanEntry(ssid = "ssid 4"),
+ WifiScanEntry(ssid = "ssid 5"),
+ )
+
+ assertThat(latest).isEqualTo(expected)
+ }
+
+ @Test
+ fun wifiScanResults_updates() =
+ testScope.runTest {
+ val latest by collectLastValue(underTest.wifiScanResults)
+
+ var scanResults =
+ listOf(
+ ScanResult().also { it.SSID = "ssid 1" },
+ ScanResult().also { it.SSID = "ssid 2" },
+ ScanResult().also { it.SSID = "ssid 3" },
+ ScanResult().also { it.SSID = "ssid 4" },
+ ScanResult().also { it.SSID = "ssid 5" },
+ )
+ whenever(wifiManager.scanResults).thenReturn(scanResults)
+ getScanResultsCallback().onScanResultsAvailable()
+
+ // New scan representing no results
+ scanResults = listOf()
+ whenever(wifiManager.scanResults).thenReturn(scanResults)
+ getScanResultsCallback().onScanResultsAvailable()
+
+ assertThat(latest).isEmpty()
+ }
+
private fun getCallback(): WifiPickerTracker.WifiPickerTrackerCallback {
testScope.runCurrent()
return callbackCaptor.value
@@ -1156,6 +1211,13 @@
}
}
+ private fun getScanResultsCallback(): WifiManager.ScanResultsCallback {
+ testScope.runCurrent()
+ val callbackCaptor = argumentCaptor<WifiManager.ScanResultsCallback>()
+ verify(wifiManager).registerScanResultsCallback(any(), callbackCaptor.capture())
+ return callbackCaptor.value!!
+ }
+
private companion object {
const val TITLE = "AB"
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/wifi/domain/interactor/WifiInteractorImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/wifi/domain/interactor/WifiInteractorImplTest.kt
index 6fe88c1..1db8065 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/wifi/domain/interactor/WifiInteractorImplTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/wifi/domain/interactor/WifiInteractorImplTest.kt
@@ -21,11 +21,13 @@
import androidx.test.filters.SmallTest
import com.android.systemui.RoboPilotTest
import com.android.systemui.SysuiTestCase
+import com.android.systemui.coroutines.collectLastValue
import com.android.systemui.statusbar.pipeline.shared.data.model.ConnectivitySlot
import com.android.systemui.statusbar.pipeline.shared.data.model.DataActivityModel
import com.android.systemui.statusbar.pipeline.shared.data.repository.FakeConnectivityRepository
import com.android.systemui.statusbar.pipeline.wifi.data.repository.FakeWifiRepository
import com.android.systemui.statusbar.pipeline.wifi.shared.model.WifiNetworkModel
+import com.android.systemui.statusbar.pipeline.wifi.shared.model.WifiScanEntry
import com.google.common.truth.Truth.assertThat
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.flow.launchIn
@@ -56,7 +58,8 @@
fun setUp() {
connectivityRepository = FakeConnectivityRepository()
wifiRepository = FakeWifiRepository()
- underTest = WifiInteractorImpl(connectivityRepository, wifiRepository)
+ underTest =
+ WifiInteractorImpl(connectivityRepository, wifiRepository, testScope.backgroundScope)
}
@Test
@@ -300,4 +303,76 @@
job.cancel()
}
+
+ @Test
+ fun areNetworksAvailable_noneActive_noResults() =
+ testScope.runTest {
+ val latest by collectLastValue(underTest.areNetworksAvailable)
+
+ wifiRepository.wifiScanResults.value = emptyList()
+ wifiRepository.setWifiNetwork(WifiNetworkModel.Inactive)
+
+ assertThat(latest).isFalse()
+ }
+
+ @Test
+ fun areNetworksAvailable_noneActive_nonEmptyResults() =
+ testScope.runTest {
+ val latest by collectLastValue(underTest.areNetworksAvailable)
+
+ wifiRepository.wifiScanResults.value =
+ listOf(
+ WifiScanEntry(ssid = "ssid 1"),
+ WifiScanEntry(ssid = "ssid 2"),
+ WifiScanEntry(ssid = "ssid 3"),
+ )
+
+ wifiRepository.setWifiNetwork(WifiNetworkModel.Inactive)
+
+ assertThat(latest).isTrue()
+ }
+
+ @Test
+ fun areNetworksAvailable_activeNetwork_resultsIncludeOtherNetworks() =
+ testScope.runTest {
+ val latest by collectLastValue(underTest.areNetworksAvailable)
+
+ wifiRepository.wifiScanResults.value =
+ listOf(
+ WifiScanEntry(ssid = "ssid 1"),
+ WifiScanEntry(ssid = "ssid 2"),
+ WifiScanEntry(ssid = "ssid 3"),
+ )
+
+ wifiRepository.setWifiNetwork(
+ WifiNetworkModel.Active(
+ ssid = "ssid 2",
+ networkId = 1,
+ level = 2,
+ )
+ )
+
+ assertThat(latest).isTrue()
+ }
+
+ @Test
+ fun areNetworksAvailable_activeNetwork_onlyResultIsTheActiveNetwork() =
+ testScope.runTest {
+ val latest by collectLastValue(underTest.areNetworksAvailable)
+
+ wifiRepository.wifiScanResults.value =
+ listOf(
+ WifiScanEntry(ssid = "ssid 2"),
+ )
+
+ wifiRepository.setWifiNetwork(
+ WifiNetworkModel.Active(
+ ssid = "ssid 2",
+ networkId = 1,
+ level = 2,
+ )
+ )
+
+ assertThat(latest).isFalse()
+ }
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/wifi/ui/view/ModernStatusBarWifiViewTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/wifi/ui/view/ModernStatusBarWifiViewTest.kt
index 3f49935..a0d4d13 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/wifi/ui/view/ModernStatusBarWifiViewTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/wifi/ui/view/ModernStatusBarWifiViewTest.kt
@@ -82,8 +82,8 @@
connectivityRepository = FakeConnectivityRepository()
wifiRepository = FakeWifiRepository()
wifiRepository.setIsWifiEnabled(true)
- interactor = WifiInteractorImpl(connectivityRepository, wifiRepository)
scope = CoroutineScope(Dispatchers.Unconfined)
+ interactor = WifiInteractorImpl(connectivityRepository, wifiRepository, scope)
airplaneModeViewModel =
AirplaneModeViewModelImpl(
AirplaneModeInteractor(
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/wifi/ui/viewmodel/WifiViewModelIconParameterizedTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/wifi/ui/viewmodel/WifiViewModelIconParameterizedTest.kt
index e6724d8..1d1b84c 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/wifi/ui/viewmodel/WifiViewModelIconParameterizedTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/wifi/ui/viewmodel/WifiViewModelIconParameterizedTest.kt
@@ -40,7 +40,7 @@
import com.android.systemui.statusbar.pipeline.wifi.shared.WifiConstants
import com.android.systemui.statusbar.pipeline.wifi.shared.model.WifiNetworkModel
import com.android.systemui.statusbar.pipeline.wifi.ui.model.WifiIcon
-import com.android.systemui.statusbar.pipeline.wifi.ui.viewmodel.WifiViewModel.Companion.NO_INTERNET
+import com.android.systemui.statusbar.pipeline.wifi.ui.model.WifiIcon.Companion.NO_INTERNET
import com.google.common.truth.Truth.assertThat
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
@@ -83,8 +83,8 @@
connectivityRepository = FakeConnectivityRepository()
wifiRepository = FakeWifiRepository()
wifiRepository.setIsWifiEnabled(true)
- interactor = WifiInteractorImpl(connectivityRepository, wifiRepository)
scope = CoroutineScope(IMMEDIATE)
+ interactor = WifiInteractorImpl(connectivityRepository, wifiRepository, scope)
airplaneModeViewModel =
AirplaneModeViewModelImpl(
AirplaneModeInteractor(
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/wifi/ui/viewmodel/WifiViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/wifi/ui/viewmodel/WifiViewModelTest.kt
index bdeba2a..a520f6c 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/wifi/ui/viewmodel/WifiViewModelTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/wifi/ui/viewmodel/WifiViewModelTest.kt
@@ -22,6 +22,7 @@
import com.android.systemui.SysuiTestCase
import com.android.systemui.coroutines.collectLastValue
import com.android.systemui.log.table.TableLogBuffer
+import com.android.systemui.statusbar.connectivity.WifiIcons
import com.android.systemui.statusbar.phone.StatusBarLocation
import com.android.systemui.statusbar.pipeline.airplane.data.repository.FakeAirplaneModeRepository
import com.android.systemui.statusbar.pipeline.airplane.domain.interactor.AirplaneModeInteractor
@@ -74,7 +75,8 @@
connectivityRepository = FakeConnectivityRepository()
wifiRepository = FakeWifiRepository()
wifiRepository.setIsWifiEnabled(true)
- interactor = WifiInteractorImpl(connectivityRepository, wifiRepository)
+ interactor =
+ WifiInteractorImpl(connectivityRepository, wifiRepository, testScope.backgroundScope)
airplaneModeViewModel =
AirplaneModeViewModelImpl(
AirplaneModeInteractor(
@@ -116,6 +118,27 @@
}
@Test
+ fun wifiIcon_validHotspot_hotspotIconNotShown() =
+ testScope.runTest {
+ val latest by collectLastValue(underTest.wifiIcon)
+
+ // Even WHEN the network has a valid hotspot type
+ wifiRepository.setWifiNetwork(
+ WifiNetworkModel.Active(
+ NETWORK_ID,
+ isValidated = true,
+ level = 1,
+ hotspotDeviceType = WifiNetworkModel.HotspotDeviceType.LAPTOP,
+ )
+ )
+
+ // THEN the hotspot icon is not used for the status bar icon, and the typical wifi icon
+ // is used instead
+ assertThat(latest).isInstanceOf(WifiIcon.Visible::class.java)
+ assertThat((latest as WifiIcon.Visible).res).isEqualTo(WifiIcons.WIFI_FULL_ICONS[1])
+ }
+
+ @Test
fun activity_showActivityConfigFalse_outputsFalse() =
testScope.runTest {
whenever(connectivityConstants.shouldShowActivityConfig).thenReturn(false)
diff --git a/packages/SystemUI/tests/src/com/android/systemui/util/kotlin/FlowProvider.kt b/packages/SystemUI/tests/src/com/android/systemui/util/kotlin/FlowProvider.kt
new file mode 100644
index 0000000..274acc9
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/util/kotlin/FlowProvider.kt
@@ -0,0 +1,22 @@
+/*
+ * 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.util.kotlin
+
+import kotlinx.coroutines.flow.MutableStateFlow
+
+/** Wrapper for flow constructors that can be retrieved from java tests */
+fun <T> getMutableStateFlow(value: T): MutableStateFlow<T> = MutableStateFlow(value)
diff --git a/packages/SystemUI/tests/src/com/android/systemui/volume/VolumeDialogImplTest.java b/packages/SystemUI/tests/src/com/android/systemui/volume/VolumeDialogImplTest.java
index c12df98..5256245 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/volume/VolumeDialogImplTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/volume/VolumeDialogImplTest.java
@@ -57,6 +57,7 @@
import com.android.systemui.Prefs;
import com.android.systemui.R;
import com.android.systemui.SysuiTestCase;
+import com.android.systemui.animation.AnimatorTestRule;
import com.android.systemui.dump.DumpManager;
import com.android.systemui.flags.FakeFeatureFlags;
import com.android.systemui.media.dialog.MediaOutputDialogFactory;
@@ -71,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;
@@ -129,6 +131,9 @@
private FakeFeatureFlags mFeatureFlags;
private int mLongestHideShowAnimationDuration = 250;
+ @Rule
+ public final AnimatorTestRule mAnimatorTestRule = new AnimatorTestRule();
+
@Before
public void setup() throws Exception {
MockitoAnnotations.initMocks(this);
@@ -242,7 +247,6 @@
| AccessibilityManager.FLAG_CONTENT_TEXT);
}
-
@Test
public void testComputeTimeout_withHovering() {
Mockito.reset(mAccessibilityMgr);
@@ -669,11 +673,12 @@
@After
public void teardown() {
- cleanUp(mDialog);
setOrientation(mOriginalOrientation);
+ mAnimatorTestRule.advanceTimeBy(mLongestHideShowAnimationDuration);
mTestableLooper.moveTimeForward(mLongestHideShowAnimationDuration);
mTestableLooper.processAllMessages();
reset(mPostureController);
+ cleanUp(mDialog);
}
private void cleanUp(VolumeDialogImpl dialog) {
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 7ebf6c8..1b623a3 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/wmshell/BubblesTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/wmshell/BubblesTest.java
@@ -341,6 +341,7 @@
mConfigurationController,
mKeyguardViewMediator,
mKeyguardBypassController,
+ syncExecutor,
mColorExtractor,
mDumpManager,
mKeyguardStateController,
@@ -419,6 +420,7 @@
syncExecutor,
mock(Handler.class),
mTaskViewTransitions,
+ mTransitions,
mock(SyncTransactionQueue.class),
mock(IWindowManager.class),
mBubbleProperties);
@@ -510,6 +512,11 @@
}
@Test
+ public void instantiateController_registerTransitionObserver() {
+ verify(mTransitions).registerObserver(any());
+ }
+
+ @Test
public void testAddBubble() {
mBubbleController.updateBubble(mBubbleEntry);
assertTrue(mBubbleController.hasBubbles());
@@ -1469,6 +1476,34 @@
}
@Test
+ public void testBroadcastReceiverCloseDialogs_reasonHomeKey() {
+ spyOn(mContext);
+ mBubbleController.updateBubble(mBubbleEntry);
+ mBubbleData.setExpanded(true);
+
+ verify(mContext).registerReceiver(mBroadcastReceiverArgumentCaptor.capture(),
+ mFilterArgumentCaptor.capture(), eq(Context.RECEIVER_EXPORTED));
+ Intent i = new Intent(Intent.ACTION_CLOSE_SYSTEM_DIALOGS);
+ i.putExtra("reason", "homekey");
+ mBroadcastReceiverArgumentCaptor.getValue().onReceive(mContext, i);
+ assertStackCollapsed();
+ }
+
+ @Test
+ public void testBroadcastReceiverCloseDialogs_reasonRecentsKey() {
+ spyOn(mContext);
+ mBubbleController.updateBubble(mBubbleEntry);
+ mBubbleData.setExpanded(true);
+
+ verify(mContext).registerReceiver(mBroadcastReceiverArgumentCaptor.capture(),
+ mFilterArgumentCaptor.capture(), eq(Context.RECEIVER_EXPORTED));
+ Intent i = new Intent(Intent.ACTION_CLOSE_SYSTEM_DIALOGS);
+ i.putExtra("reason", "recentapps");
+ mBroadcastReceiverArgumentCaptor.getValue().onReceive(mContext, i);
+ assertStackCollapsed();
+ }
+
+ @Test
public void testBroadcastReceiver_screenOff() {
spyOn(mContext);
mBubbleController.updateBubble(mBubbleEntry);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/wmshell/TestableBubbleController.java b/packages/SystemUI/tests/src/com/android/systemui/wmshell/TestableBubbleController.java
index 5855347..9ad234e1 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/wmshell/TestableBubbleController.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/wmshell/TestableBubbleController.java
@@ -43,6 +43,7 @@
import com.android.wm.shell.sysui.ShellController;
import com.android.wm.shell.sysui.ShellInit;
import com.android.wm.shell.taskview.TaskViewTransitions;
+import com.android.wm.shell.transition.Transitions;
import java.util.Optional;
@@ -74,6 +75,7 @@
ShellExecutor shellMainExecutor,
Handler shellMainHandler,
TaskViewTransitions taskViewTransitions,
+ Transitions transitions,
SyncTransactionQueue syncQueue,
IWindowManager wmService,
BubbleProperties bubbleProperties) {
@@ -82,7 +84,8 @@
windowManagerShellWrapper, userManager, launcherApps, bubbleLogger,
taskStackListener, shellTaskOrganizer, positioner, displayController,
oneHandedOptional, dragAndDropController, shellMainExecutor, shellMainHandler,
- new SyncExecutor(), taskViewTransitions, syncQueue, wmService, bubbleProperties);
+ new SyncExecutor(), taskViewTransitions, transitions,
+ syncQueue, wmService, bubbleProperties);
setInflateSynchronously(true);
onInit();
}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/common/ui/data/repository/FakeConfigurationRepository.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/common/ui/data/repository/FakeConfigurationRepository.kt
index b2a1668..72cdbbc 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/common/ui/data/repository/FakeConfigurationRepository.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/common/ui/data/repository/FakeConfigurationRepository.kt
@@ -16,22 +16,21 @@
package com.android.systemui.common.ui.data.repository
-import kotlinx.coroutines.channels.Channel
import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.MutableSharedFlow
import kotlinx.coroutines.flow.MutableStateFlow
+import kotlinx.coroutines.flow.asSharedFlow
import kotlinx.coroutines.flow.asStateFlow
-import kotlinx.coroutines.flow.receiveAsFlow
class FakeConfigurationRepository : ConfigurationRepository {
- private val onAnyConfigurationChangeChannel = Channel<Unit>()
- override val onAnyConfigurationChange: Flow<Unit> =
- onAnyConfigurationChangeChannel.receiveAsFlow()
+ private val _onAnyConfigurationChange = MutableSharedFlow<Unit>()
+ override val onAnyConfigurationChange: Flow<Unit> = _onAnyConfigurationChange.asSharedFlow()
private val _scaleForResolution = MutableStateFlow(1f)
override val scaleForResolution: Flow<Float> = _scaleForResolution.asStateFlow()
suspend fun onAnyConfigurationChange() {
- onAnyConfigurationChangeChannel.send(Unit)
+ _onAnyConfigurationChange.tryEmit(Unit)
}
fun setScaleForResolution(scale: Float) {
@@ -41,4 +40,8 @@
override fun getResolutionScale(): Float {
return _scaleForResolution.value
}
+
+ override fun getDimensionPixelSize(id: Int): Int {
+ throw IllegalStateException("Don't use for tests")
+ }
}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/display/data/repository/FakeDisplayRepository.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/display/data/repository/FakeDisplayRepository.kt
index 715d661..6f51d1b 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/display/data/repository/FakeDisplayRepository.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/display/data/repository/FakeDisplayRepository.kt
@@ -33,10 +33,17 @@
/** Fake [DisplayRepository] implementation for testing. */
class FakeDisplayRepository : DisplayRepository {
private val flow = MutableSharedFlow<Set<Display>>()
+ private val pendingDisplayFlow = MutableSharedFlow<Int?>()
/** Emits [value] as [displays] flow value. */
suspend fun emit(value: Set<Display>) = flow.emit(value)
+ /** Emits [value] as [pendingDisplayId] flow value. */
+ suspend fun emit(value: Int?) = pendingDisplayFlow.emit(value)
+
override val displays: Flow<Set<Display>>
get() = flow
+
+ override val pendingDisplayId: Flow<Int?>
+ get() = pendingDisplayFlow
}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/FakeKeyguardRepository.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/FakeKeyguardRepository.kt
index faebcaa..cc0c943 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/FakeKeyguardRepository.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/FakeKeyguardRepository.kt
@@ -81,7 +81,7 @@
override val linearDozeAmount: Flow<Float> = _dozeAmount
private val _statusBarState = MutableStateFlow(StatusBarState.SHADE)
- override val statusBarState: Flow<StatusBarState> = _statusBarState
+ override val statusBarState: StateFlow<StatusBarState> = _statusBarState
private val _dozeTransitionModel = MutableStateFlow(DozeTransitionModel())
override val dozeTransitionModel: Flow<DozeTransitionModel> = _dozeTransitionModel
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/KeyguardInteractorFactory.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/KeyguardInteractorFactory.kt
index f13c98d..8c1ef1d 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/KeyguardInteractorFactory.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/KeyguardInteractorFactory.kt
@@ -23,6 +23,7 @@
import com.android.systemui.flags.Flags
import com.android.systemui.keyguard.data.repository.FakeCommandQueue
import com.android.systemui.keyguard.data.repository.FakeKeyguardRepository
+import com.android.systemui.shade.data.repository.FakeShadeRepository
/**
* Simply put, I got tired of adding a constructor argument and then having to tweak dozens of
@@ -38,6 +39,7 @@
commandQueue: FakeCommandQueue = FakeCommandQueue(),
bouncerRepository: FakeKeyguardBouncerRepository = FakeKeyguardBouncerRepository(),
configurationRepository: FakeConfigurationRepository = FakeConfigurationRepository(),
+ shadeRepository: FakeShadeRepository = FakeShadeRepository(),
): WithDependencies {
return WithDependencies(
repository = repository,
@@ -45,12 +47,14 @@
featureFlags = featureFlags,
bouncerRepository = bouncerRepository,
configurationRepository = configurationRepository,
+ shadeRepository = shadeRepository,
KeyguardInteractor(
repository = repository,
commandQueue = commandQueue,
featureFlags = featureFlags,
bouncerRepository = bouncerRepository,
configurationRepository = configurationRepository,
+ shadeRepository = shadeRepository,
)
)
}
@@ -66,6 +70,7 @@
val featureFlags: FakeFeatureFlags,
val bouncerRepository: FakeKeyguardBouncerRepository,
val configurationRepository: FakeConfigurationRepository,
+ val shadeRepository: FakeShadeRepository,
val keyguardInteractor: KeyguardInteractor,
)
}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionInteractorFactory.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionInteractorFactory.kt
index 23faaf3..05c63b6 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionInteractorFactory.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionInteractorFactory.kt
@@ -17,7 +17,6 @@
package com.android.systemui.keyguard.domain.interactor
import com.android.systemui.keyguard.data.repository.FakeKeyguardTransitionRepository
-import com.android.systemui.keyguard.data.repository.KeyguardTransitionRepository
import com.android.systemui.util.mockito.mock
import dagger.Lazy
import kotlinx.coroutines.CoroutineScope
@@ -31,7 +30,7 @@
@JvmStatic
fun create(
scope: CoroutineScope,
- repository: KeyguardTransitionRepository = FakeKeyguardTransitionRepository(),
+ repository: FakeKeyguardTransitionRepository = FakeKeyguardTransitionRepository(),
keyguardInteractor: KeyguardInteractor =
KeyguardInteractorFactory.create().keyguardInteractor,
fromLockscreenTransitionInteractor: Lazy<FromLockscreenTransitionInteractor> = Lazy {
@@ -58,7 +57,7 @@
}
data class WithDependencies(
- val repository: KeyguardTransitionRepository,
+ val repository: FakeKeyguardTransitionRepository,
val keyguardInteractor: KeyguardInteractor,
val fromLockscreenTransitionInteractor: Lazy<FromLockscreenTransitionInteractor>,
val fromPrimaryBouncerTransitionInteractor: Lazy<FromPrimaryBouncerTransitionInteractor>,
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/power/data/repository/FakePowerRepository.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/power/data/repository/FakePowerRepository.kt
index 3334f3e..b92d946 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/power/data/repository/FakePowerRepository.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/power/data/repository/FakePowerRepository.kt
@@ -32,6 +32,8 @@
var lastWakeWhy: String? = null
var lastWakeReason: Int? = null
+ var userTouchRegistered = false
+
fun setInteractive(value: Boolean) {
_isInteractive.value = value
}
@@ -40,4 +42,8 @@
lastWakeWhy = why
lastWakeReason = wakeReason
}
+
+ override fun userTouch() {
+ userTouchRegistered = true
+ }
}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/scene/SceneTestUtils.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/scene/SceneTestUtils.kt
index f0e1111..f7db44e 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/scene/SceneTestUtils.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/scene/SceneTestUtils.kt
@@ -27,6 +27,9 @@
import com.android.systemui.bouncer.data.repository.FakeKeyguardBouncerRepository
import com.android.systemui.bouncer.domain.interactor.BouncerInteractor
import com.android.systemui.bouncer.ui.viewmodel.BouncerViewModel
+import com.android.systemui.classifier.FalsingCollector
+import com.android.systemui.classifier.FalsingCollectorFake
+import com.android.systemui.classifier.domain.interactor.FalsingInteractor
import com.android.systemui.common.ui.data.repository.FakeConfigurationRepository
import com.android.systemui.flags.FakeFeatureFlags
import com.android.systemui.flags.Flags
@@ -37,10 +40,12 @@
import com.android.systemui.keyguard.shared.model.WakeSleepReason
import com.android.systemui.keyguard.shared.model.WakefulnessModel
import com.android.systemui.keyguard.shared.model.WakefulnessState
+import com.android.systemui.power.data.repository.FakePowerRepository
import com.android.systemui.scene.data.repository.SceneContainerRepository
import com.android.systemui.scene.domain.interactor.SceneInteractor
import com.android.systemui.scene.shared.model.SceneContainerConfig
import com.android.systemui.scene.shared.model.SceneKey
+import com.android.systemui.shade.data.repository.FakeShadeRepository
import com.android.systemui.user.data.repository.FakeUserRepository
import com.android.systemui.user.data.repository.UserRepository
import com.android.systemui.util.mockito.mock
@@ -92,8 +97,13 @@
}
}
+ val powerRepository: FakePowerRepository by lazy { FakePowerRepository() }
+
private val context = test.context
+ private val falsingCollectorFake: FalsingCollector by lazy { FalsingCollectorFake() }
+ private var falsingInteractor: FalsingInteractor? = null
+
fun fakeSceneContainerRepository(
containerConfig: SceneContainerConfig = fakeSceneContainerConfig(),
): SceneContainerRepository {
@@ -125,6 +135,7 @@
return SceneInteractor(
applicationScope = applicationScope(),
repository = repository,
+ powerRepository = powerRepository,
logger = mock(),
)
}
@@ -148,17 +159,14 @@
)
}
- fun keyguardRepository(): FakeKeyguardRepository {
- return keyguardRepository
- }
-
fun keyguardInteractor(repository: KeyguardRepository): KeyguardInteractor {
return KeyguardInteractor(
repository = repository,
commandQueue = FakeCommandQueue(),
featureFlags = featureFlags,
bouncerRepository = FakeKeyguardBouncerRepository(),
- configurationRepository = FakeConfigurationRepository()
+ configurationRepository = FakeConfigurationRepository(),
+ shadeRepository = FakeShadeRepository(),
)
}
@@ -173,6 +181,7 @@
authenticationInteractor = authenticationInteractor,
sceneInteractor = sceneInteractor,
featureFlags = featureFlags,
+ falsingInteractor = falsingInteractor(),
)
}
@@ -189,6 +198,14 @@
)
}
+ fun falsingInteractor(collector: FalsingCollector = falsingCollector()): FalsingInteractor {
+ return falsingInteractor ?: FalsingInteractor(collector).also { falsingInteractor = it }
+ }
+
+ fun falsingCollector(): FalsingCollector {
+ return falsingCollectorFake
+ }
+
private fun applicationScope(): CoroutineScope {
return testScope.backgroundScope
}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/shade/data/repository/FakeShadeRepository.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/shade/data/repository/FakeShadeRepository.kt
index 492e542..ccddca2 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/shade/data/repository/FakeShadeRepository.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/shade/data/repository/FakeShadeRepository.kt
@@ -33,6 +33,9 @@
private val _udfpsTransitionToFullShadeProgress = MutableStateFlow(0f)
override val udfpsTransitionToFullShadeProgress = _udfpsTransitionToFullShadeProgress
+ private val _shadeExpansion = MutableStateFlow(0f)
+ override val shadeExpansion = _shadeExpansion
+
fun setShadeModel(model: ShadeModel) {
_shadeModel.value = model
}
@@ -44,4 +47,8 @@
override fun setUdfpsTransitionToFullShadeProgress(progress: Float) {
_udfpsTransitionToFullShadeProgress.value = progress
}
+
+ override fun setShadeExpansion(expansion: Float) {
+ _shadeExpansion.value = expansion
+ }
}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/util/mockito/KotlinMockitoHelpers.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/util/mockito/KotlinMockitoHelpers.kt
index 69575a9..7e0632b 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/util/mockito/KotlinMockitoHelpers.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/util/mockito/KotlinMockitoHelpers.kt
@@ -36,7 +36,7 @@
*
* Generic T is nullable because implicitly bounded by Any?.
*/
-fun <T> eq(obj: T): T = Mockito.eq<T>(obj)
+fun <T> eq(obj: T): T = Mockito.eq<T>(obj) ?: obj
/**
* Returns Mockito.any() as nullable type to avoid java.lang.IllegalStateException when
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/utils/leaks/FakeStatusBarIconController.java b/packages/SystemUI/tests/utils/src/com/android/systemui/utils/leaks/FakeStatusBarIconController.java
index 03e3423..3774d1d 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/utils/leaks/FakeStatusBarIconController.java
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/utils/leaks/FakeStatusBarIconController.java
@@ -84,10 +84,6 @@
}
@Override
- public void removeAllIconsForSlot(String slot) {
- }
-
- @Override
public void setIconAccessibilityLiveRegion(String slot, int mode) {
}
diff --git a/packages/WAPPushManager/tests/src/com/android/smspush/unitTests/WapPushTest.java b/packages/WAPPushManager/tests/src/com/android/smspush/unitTests/WapPushTest.java
index b9dac4e..c7abed8 100644
--- a/packages/WAPPushManager/tests/src/com/android/smspush/unitTests/WapPushTest.java
+++ b/packages/WAPPushManager/tests/src/com/android/smspush/unitTests/WapPushTest.java
@@ -2237,10 +2237,6 @@
mClassName = "com.android.smspush.unitTests.ReceiverActivity";
- // Phone dummy = new DummyPhone(getContext());
- // Phone gsm = PhoneFactory.getGsmPhone();
- // GSMPhone gsm = new GSMPhone(getContext(), new SimulatedCommands(), null, true);
- // WapPushOverSms dispatcher = new WapPushOverSms(dummy, null);
try {
// set up data
diff --git a/packages/services/VirtualCamera/OWNERS b/packages/services/VirtualCamera/OWNERS
new file mode 100644
index 0000000..c66443f
--- /dev/null
+++ b/packages/services/VirtualCamera/OWNERS
@@ -0,0 +1,3 @@
+include /services/companion/java/com/android/server/companion/virtual/OWNERS
+caen@google.com
+jsebechlebsky@google.com
\ No newline at end of file
diff --git a/services/accessibility/java/com/android/server/accessibility/AccessibilityInputFilter.java b/services/accessibility/java/com/android/server/accessibility/AccessibilityInputFilter.java
index f3a540b..cd83f8f 100644
--- a/services/accessibility/java/com/android/server/accessibility/AccessibilityInputFilter.java
+++ b/services/accessibility/java/com/android/server/accessibility/AccessibilityInputFilter.java
@@ -389,11 +389,19 @@
@Override
public void onMotionEvent(MotionEvent transformedEvent, MotionEvent rawEvent,
int policyFlags) {
+ if (!mInstalled) {
+ Slog.w(TAG, "onMotionEvent called before input filter installed!");
+ return;
+ }
sendInputEvent(transformedEvent, policyFlags);
}
@Override
public void onKeyEvent(KeyEvent event, int policyFlags) {
+ if (!mInstalled) {
+ Slog.w(TAG, "onKeyEvent called before input filter installed!");
+ return;
+ }
sendInputEvent(event, policyFlags);
}
diff --git a/services/autofill/java/com/android/server/autofill/RemoteFillService.java b/services/autofill/java/com/android/server/autofill/RemoteFillService.java
index 4688658..423b85f 100644
--- a/services/autofill/java/com/android/server/autofill/RemoteFillService.java
+++ b/services/autofill/java/com/android/server/autofill/RemoteFillService.java
@@ -56,7 +56,7 @@
private static final long TIMEOUT_IDLE_BIND_MILLIS = 5 * DateUtils.SECOND_IN_MILLIS;
private static final long TIMEOUT_REMOTE_REQUEST_MILLIS = 5 * DateUtils.SECOND_IN_MILLIS;
- private final FillServiceCallbacks mCallbacks;
+ private FillServiceCallbacks mCallbacks;
private final Object mLock = new Object();
private CompletableFuture<FillResponse> mPendingFillRequest;
private int mPendingFillRequestId = INVALID_REQUEST_ID;
@@ -128,9 +128,12 @@
*/
public int cancelCurrentRequest() {
synchronized (mLock) {
- return mPendingFillRequest != null && mPendingFillRequest.cancel(false)
+ int canceledRequestId = mPendingFillRequest != null && mPendingFillRequest.cancel(false)
? mPendingFillRequestId
: INVALID_REQUEST_ID;
+ mPendingFillRequest = null;
+ mPendingFillRequestId = INVALID_REQUEST_ID;
+ return canceledRequestId;
}
}
@@ -184,6 +187,10 @@
mPendingFillRequest = null;
mPendingFillRequestId = INVALID_REQUEST_ID;
}
+ if (mCallbacks == null) {
+ Slog.w(TAG, "Error calling RemoteFillService - service already unbound");
+ return;
+ }
if (err == null) {
mCallbacks.onFillRequestSuccess(request.getId(), res,
mComponentName.getPackageName(), request.getFlags());
@@ -220,6 +227,10 @@
return save;
}).orTimeout(TIMEOUT_REMOTE_REQUEST_MILLIS, TimeUnit.MILLISECONDS)
.whenComplete((res, err) -> Handler.getMain().post(() -> {
+ if (mCallbacks == null) {
+ Slog.w(TAG, "Error calling RemoteFillService - service already unbound");
+ return;
+ }
if (err == null) {
mCallbacks.onSaveRequestSuccess(mComponentName.getPackageName(), res);
} else {
@@ -234,6 +245,8 @@
}
public void destroy() {
+ cancelCurrentRequest();
unbind();
+ mCallbacks = null;
}
}
diff --git a/services/backup/java/com/android/server/backup/UserBackupManagerService.java b/services/backup/java/com/android/server/backup/UserBackupManagerService.java
index b573800..3b02be5 100644
--- a/services/backup/java/com/android/server/backup/UserBackupManagerService.java
+++ b/services/backup/java/com/android/server/backup/UserBackupManagerService.java
@@ -1750,12 +1750,8 @@
synchronized (mClearDataLock) {
mClearingData = true;
- try {
- mActivityManager.clearApplicationUserData(packageName, keepSystemState, observer,
- mUserId);
- } catch (RemoteException e) {
- // can't happen because the activity manager is in this process
- }
+ mActivityManagerInternal.clearApplicationUserData(packageName, keepSystemState,
+ /*isRestore=*/ true, observer, mUserId);
// Only wait 30 seconds for the clear data to happen.
long timeoutMark = System.currentTimeMillis() + CLEAR_DATA_TIMEOUT_INTERVAL;
diff --git a/services/core/Android.bp b/services/core/Android.bp
index 66a77a3..b941aaf 100644
--- a/services/core/Android.bp
+++ b/services/core/Android.bp
@@ -181,6 +181,7 @@
"android.hardware.power.stats-V2-java",
"android.hidl.manager-V1.2-java",
"cbor-java",
+ "display_flags_lib",
"icu4j_calendar_astronomer",
"netd-client",
"overlayable_policy_aidl-java",
@@ -192,6 +193,7 @@
"power_optimization_flags_lib",
"notification_flags_lib",
"pm_flags_lib",
+ "camera_platform_flags_core_java_lib",
],
javac_shard_size: 50,
javacflags: [
diff --git a/services/core/java/com/android/server/PinnerService.java b/services/core/java/com/android/server/PinnerService.java
index 3fd6fe8..9bab261 100644
--- a/services/core/java/com/android/server/PinnerService.java
+++ b/services/core/java/com/android/server/PinnerService.java
@@ -78,11 +78,14 @@
import java.io.PrintWriter;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
+import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.List;
import java.util.zip.ZipEntry;
import java.util.zip.ZipFile;
+import sun.misc.Unsafe;
+
/**
* <p>PinnerService pins important files for key processes in memory.</p>
* <p>Files to pin are specified in the config_defaultPinnerServiceFiles
@@ -150,6 +153,11 @@
@GuardedBy("this")
private ArraySet<Integer> mPinKeys;
+ private static final long MAX_ANON_SIZE = 2L * (1L << 30); // 2GB
+ private long mPinAnonSize;
+ private long mPinAnonAddress;
+ private long mCurrentlyPinnedAnonSize;
+
// Resource-configured pinner flags;
private final boolean mConfiguredToPinCamera;
private final boolean mConfiguredToPinHome;
@@ -550,6 +558,11 @@
pinKeys.add(KEY_ASSISTANT);
}
+ mPinAnonSize = DeviceConfig.getLong(DeviceConfig.NAMESPACE_RUNTIME_NATIVE_BOOT,
+ "pin_anon_size",
+ SystemProperties.getLong("pinner.pin_anon_size", 0));
+ mPinAnonSize = Math.max(0, Math.min(mPinAnonSize, MAX_ANON_SIZE));
+
return pinKeys;
}
@@ -589,6 +602,7 @@
int key = currentPinKeys.valueAt(i);
pinApp(key, userHandle, true /* force */);
}
+ pinAnonRegion();
}
/**
@@ -673,6 +687,64 @@
}
/**
+ * Pin an empty anonymous region. This should only be used for ablation experiments.
+ */
+ private void pinAnonRegion() {
+ if (mPinAnonSize == 0) {
+ return;
+ }
+ long alignedPinSize = mPinAnonSize;
+ if (alignedPinSize % PAGE_SIZE != 0) {
+ alignedPinSize -= alignedPinSize % PAGE_SIZE;
+ Slog.e(TAG, "pinAnonRegion: aligning size to " + alignedPinSize);
+ }
+ if (mPinAnonAddress != 0
+ && mCurrentlyPinnedAnonSize != alignedPinSize) {
+ unpinAnonRegion();
+ }
+ long address = 0;
+ try {
+ address = Os.mmap(0, alignedPinSize,
+ OsConstants.PROT_READ | OsConstants.PROT_WRITE,
+ OsConstants.MAP_PRIVATE | OsConstants.MAP_ANONYMOUS,
+ new FileDescriptor(), /*offset=*/0);
+
+ Unsafe tempUnsafe = null;
+ Class<sun.misc.Unsafe> clazz = sun.misc.Unsafe.class;
+ for (java.lang.reflect.Field f : clazz.getDeclaredFields()) {
+ f.setAccessible(true);
+ Object obj = f.get(null);
+ if (clazz.isInstance(obj)) {
+ tempUnsafe = clazz.cast(obj);
+ }
+ }
+ if (tempUnsafe == null) {
+ throw new Exception("Couldn't get Unsafe");
+ }
+ Method setMemory = clazz.getMethod("setMemory", long.class, long.class, byte.class);
+ setMemory.invoke(tempUnsafe, address, alignedPinSize, (byte) 1);
+ Os.mlock(address, alignedPinSize);
+ mCurrentlyPinnedAnonSize = alignedPinSize;
+ mPinAnonAddress = address;
+ address = -1;
+ Slog.e(TAG, "pinAnonRegion success, size=" + mCurrentlyPinnedAnonSize);
+ } catch (Exception ex) {
+ Slog.e(TAG, "Could not pin anon region of size " + alignedPinSize, ex);
+ return;
+ } finally {
+ if (address >= 0) {
+ safeMunmap(address, alignedPinSize);
+ }
+ }
+ }
+
+ private void unpinAnonRegion() {
+ if (mPinAnonAddress != 0) {
+ safeMunmap(mPinAnonAddress, mCurrentlyPinnedAnonSize);
+ }
+ }
+
+ /**
* @return The maximum amount of bytes to be pinned for an app of type {@code key}.
*/
private int getSizeLimitForKey(@AppKey int key) {
@@ -1083,6 +1155,9 @@
totalSize += pf.bytesPinned;
}
}
+ if (mPinAnonAddress != 0) {
+ pw.format("Pinned anon region: %s\n", mCurrentlyPinnedAnonSize);
+ }
pw.format("Total size: %s\n", totalSize);
pw.println();
if (!mPendingRepin.isEmpty()) {
diff --git a/services/core/java/com/android/server/StorageManagerService.java b/services/core/java/com/android/server/StorageManagerService.java
index 4d249ab..a787644 100644
--- a/services/core/java/com/android/server/StorageManagerService.java
+++ b/services/core/java/com/android/server/StorageManagerService.java
@@ -32,6 +32,7 @@
import static android.content.pm.PackageManager.MATCH_DIRECT_BOOT_UNAWARE;
import static android.content.pm.PackageManager.MATCH_UNINSTALLED_PACKAGES;
import static android.content.pm.PackageManager.PERMISSION_GRANTED;
+import static android.os.IInstalld.IFsveritySetupAuthToken;
import static android.os.ParcelFileDescriptor.MODE_READ_WRITE;
import static android.os.storage.OnObbStateChangeListener.ERROR_ALREADY_MOUNTED;
import static android.os.storage.OnObbStateChangeListener.ERROR_COULD_NOT_MOUNT;
@@ -678,6 +679,7 @@
private static final int H_VOLUME_STATE_CHANGED = 15;
private static final int H_CLOUD_MEDIA_PROVIDER_CHANGED = 16;
private static final int H_SECURE_KEYGUARD_STATE_CHANGED = 17;
+ private static final int H_REMOUNT_VOLUMES_ON_MOVE = 18;
class StorageManagerServiceHandler extends Handler {
public StorageManagerServiceHandler(Looper looper) {
@@ -833,6 +835,10 @@
}
break;
}
+ case H_REMOUNT_VOLUMES_ON_MOVE: {
+ remountVolumesForRunningUsersOnMove();
+ break;
+ }
}
}
}
@@ -1286,6 +1292,44 @@
}
}
+ /**
+ * This method informs vold and storaged that the user has stopped and started whenever move
+ * storage is performed. This ensures that the correct emulated volumes are mounted for the
+ * users other than the current user. This solves an edge case wherein the correct emulated
+ * volumes are not mounted, this will cause the media data to be still stored on internal
+ * storage whereas the data should be stored in the adopted primary storage. This method stops
+ * the users at vold first which will remove the old volumes which and starts the users at vold
+ * which will reattach the correct volumes. This does not performs a full reset as full reset
+ * clears every state from vold and SMS {@link #resetIfRebootedAndConnected} which is expensive
+ * and causes instability.
+ */
+ private void remountVolumesForRunningUsersOnMove() {
+ // Do not want to hold the lock for long
+ final List<Integer> unlockedUsers = new ArrayList<>();
+ synchronized (mLock) {
+ for (int userId : mSystemUnlockedUsers) {
+ if (userId == mCurrentUserId) continue;
+ unlockedUsers.add(userId);
+ }
+ }
+ for (Integer userId : unlockedUsers) {
+ try {
+ mVold.onUserStopped(userId);
+ mStoraged.onUserStopped(userId);
+ } catch (Exception e) {
+ Slog.wtf(TAG, e);
+ }
+ }
+ for (Integer userId : unlockedUsers) {
+ try {
+ mVold.onUserStarted(userId);
+ mStoraged.onUserStarted(userId);
+ } catch (Exception e) {
+ Slog.wtf(TAG, e);
+ }
+ }
+ }
+
private boolean supportsBlockCheckpoint() throws RemoteException {
enforcePermission(android.Manifest.permission.MOUNT_FORMAT_FILESYSTEMS);
return mVold.supportsBlockCheckpoint();
@@ -1820,6 +1864,7 @@
mPrimaryStorageUuid = mMoveTargetUuid;
writeSettingsLocked();
+ mHandler.obtainMessage(H_REMOUNT_VOLUMES_ON_MOVE).sendToTarget();
}
if (PackageManager.isMoveStatusFinished(status)) {
@@ -4640,7 +4685,7 @@
pw.print(") total size: ");
pw.print(pair.second);
pw.print(" (");
- pw.print(DataUnit.MEBIBYTES.toBytes(pair.second));
+ pw.print(pair.second / DataUnit.MEBIBYTES.toBytes(1L));
pw.println(" MiB)");
}
@@ -4950,5 +4995,24 @@
}
}
+ @Override
+ public IFsveritySetupAuthToken createFsveritySetupAuthToken(ParcelFileDescriptor authFd,
+ int appUid, @UserIdInt int userId) throws IOException {
+ try {
+ return mInstaller.createFsveritySetupAuthToken(authFd, appUid, userId);
+ } catch (Installer.InstallerException e) {
+ throw new IOException(e);
+ }
+ }
+
+ @Override
+ public int enableFsverity(IFsveritySetupAuthToken authToken, String filePath,
+ String packageName) throws IOException {
+ try {
+ return mInstaller.enableFsverity(authToken, filePath, packageName);
+ } catch (Installer.InstallerException e) {
+ throw new IOException(e);
+ }
+ }
}
}
diff --git a/services/core/java/com/android/server/TEST_MAPPING b/services/core/java/com/android/server/TEST_MAPPING
index 03022b0..cd0a9d2 100644
--- a/services/core/java/com/android/server/TEST_MAPPING
+++ b/services/core/java/com/android/server/TEST_MAPPING
@@ -74,6 +74,15 @@
{
"name": "CtsVcnTestCases",
"file_patterns": ["VcnManagementService\\.java"]
+ },
+ {
+ "name": "FrameworksNetTests",
+ "options": [
+ {
+ "exclude-annotation": "com.android.testutils.SkipPresubmit"
+ }
+ ],
+ "file_patterns": ["VpnManagerService\\.java"]
}
],
"presubmit-large": [
@@ -91,6 +100,21 @@
}
],
"file_patterns": ["ClipboardService\\.java"]
+ },
+ {
+ "name": "CtsHostsideNetworkTests",
+ "options": [
+ {
+ "exclude-annotation": "androidx.test.filters.FlakyTest"
+ },
+ {
+ "exclude-annotation": "android.platform.test.annotations.FlakyTest"
+ },
+ {
+ "exclude-annotation": "com.android.testutils.SkipPresubmit"
+ }
+ ],
+ "file_patterns": ["VpnManagerService\\.java"]
}
]
}
diff --git a/services/core/java/com/android/server/Watchdog.java b/services/core/java/com/android/server/Watchdog.java
index 6baae4b..e17424b 100644
--- a/services/core/java/com/android/server/Watchdog.java
+++ b/services/core/java/com/android/server/Watchdog.java
@@ -937,7 +937,7 @@
mActivity.addErrorToDropBox(
dropboxTag, null, "system_server", null, null, null,
null, report.toString(), stack, null, null, null,
- errorId);
+ errorId, null);
}
}
};
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index 605485d..594712f 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -2115,6 +2115,34 @@
mVoiceInteractionManagerProvider = provider;
}
+ /**
+ * Represents volatile states associated with a Dropbox entry.
+ * <p>
+ * These states, such as the process frozen state, can change quickly over time and thus
+ * should be captured as soon as possible to ensure accurate state. If a state is undefined,
+ * it means that the state was not read early and a fallback value can be used.
+ * </p>
+ */
+ static class VolatileDropboxEntryStates {
+ private final Boolean mIsProcessFrozen;
+
+ private VolatileDropboxEntryStates(Boolean frozenState) {
+ this.mIsProcessFrozen = frozenState;
+ }
+
+ public static VolatileDropboxEntryStates withProcessFrozenState(boolean frozenState) {
+ return new VolatileDropboxEntryStates(frozenState);
+ }
+
+ public static VolatileDropboxEntryStates emptyVolatileDropboxEnytyStates() {
+ return new VolatileDropboxEntryStates(null);
+ }
+
+ public Boolean isProcessFrozen() {
+ return mIsProcessFrozen;
+ }
+ }
+
static class MemBinder extends Binder {
ActivityManagerService mActivityManagerService;
private final PriorityDump.PriorityDumper mPriorityDumper =
@@ -3503,6 +3531,12 @@
@Override
public boolean clearApplicationUserData(final String packageName, boolean keepState,
final IPackageDataObserver observer, int userId) {
+ return clearApplicationUserData(packageName, keepState, /*isRestore=*/ false, observer,
+ userId);
+ }
+
+ private boolean clearApplicationUserData(final String packageName, boolean keepState,
+ boolean isRestore, final IPackageDataObserver observer, int userId) {
enforceNotIsolatedCaller("clearApplicationUserData");
int uid = Binder.getCallingUid();
int pid = Binder.getCallingPid();
@@ -3597,6 +3631,9 @@
intent.putExtra(Intent.EXTRA_UID,
(appInfo != null) ? appInfo.uid : INVALID_UID);
intent.putExtra(Intent.EXTRA_USER_HANDLE, resolvedUserId);
+ if (isRestore) {
+ intent.putExtra(Intent.EXTRA_IS_RESTORE, true);
+ }
if (isInstantApp) {
intent.putExtra(Intent.EXTRA_PACKAGE_NAME, packageName);
}
@@ -8943,7 +8980,7 @@
addErrorToDropBox(
eventType, r, processName, null, null, null, null, null, null, crashInfo,
- new Float(loadingProgress), incrementalMetrics, null);
+ new Float(loadingProgress), incrementalMetrics, null, null);
// For GWP-ASan recoverable crashes, don't make the app crash (the whole point of
// 'recoverable' is that the app doesn't crash). Normally, for nonrecoreable native crashes,
@@ -9054,7 +9091,7 @@
final StringBuilder sb = new StringBuilder(1024);
synchronized (sb) {
- appendDropBoxProcessHeaders(process, processName, sb);
+ appendDropBoxProcessHeaders(process, processName, null, sb);
sb.append("Build: ").append(Build.FINGERPRINT).append("\n");
sb.append("System-App: ").append(isSystemApp).append("\n");
sb.append("Uptime-Millis: ").append(info.violationUptimeMillis).append("\n");
@@ -9157,7 +9194,7 @@
callingPid, (r != null) ? r.getProcessClassEnum() : 0);
addErrorToDropBox("wtf", r, processName, null, null, null, tag, null, null, crashInfo,
- null, null, null);
+ null, null, null, null);
return r;
}
@@ -9182,7 +9219,7 @@
for (Pair<String, ApplicationErrorReport.CrashInfo> p = list.poll();
p != null; p = list.poll()) {
addErrorToDropBox("wtf", proc, "system_server", null, null, null, p.first, null, null,
- p.second, null, null, null);
+ p.second, null, null, null, null);
}
}
@@ -9205,7 +9242,7 @@
* to append various headers to the dropbox log text.
*/
void appendDropBoxProcessHeaders(ProcessRecord process, String processName,
- final StringBuilder sb) {
+ final VolatileDropboxEntryStates volatileStates, final StringBuilder sb) {
// Watchdog thread ends up invoking this function (with
// a null ProcessRecord) to add the stack file to dropbox.
// Do not acquire a lock on this (am) in such cases, as it
@@ -9224,7 +9261,12 @@
sb.append("PID: ").append(process.getPid()).append("\n");
sb.append("UID: ").append(process.uid).append("\n");
if (process.mOptRecord != null) {
- sb.append("Frozen: ").append(process.mOptRecord.isFrozen()).append("\n");
+ // Use 'isProcessFrozen' from 'volatileStates' if it'snon-null (present),
+ // otherwise use 'isFrozen' from 'mOptRecord'.
+ sb.append("Frozen: ").append(
+ (volatileStates != null && volatileStates.isProcessFrozen() != null)
+ ? volatileStates.isProcessFrozen() : process.mOptRecord.isFrozen()
+ ).append("\n");
}
int flags = process.info.flags;
final IPackageManager pm = AppGlobals.getPackageManager();
@@ -9337,7 +9379,7 @@
String subject, final String report, final File dataFile,
final ApplicationErrorReport.CrashInfo crashInfo,
@Nullable Float loadingProgress, @Nullable IncrementalMetrics incrementalMetrics,
- @Nullable UUID errorId) {
+ @Nullable UUID errorId, @Nullable VolatileDropboxEntryStates volatileStates) {
// NOTE -- this must never acquire the ActivityManagerService lock,
// otherwise the watchdog may be prevented from resetting the system.
@@ -9359,7 +9401,7 @@
if (rateLimitResult.shouldRateLimit()) return;
final StringBuilder sb = new StringBuilder(1024);
- appendDropBoxProcessHeaders(process, processName, sb);
+ appendDropBoxProcessHeaders(process, processName, volatileStates, sb);
if (process != null) {
sb.append("Foreground: ")
.append(process.isInterestingToUserLocked() ? "Yes" : "No")
@@ -18983,6 +19025,13 @@
return mAppProfiler.mCachedAppsWatermarkData.getCachedAppsHighWatermarkStats(
atomTag, resetAfterPull);
}
+
+ @Override
+ public boolean clearApplicationUserData(final String packageName, boolean keepState,
+ boolean isRestore, final IPackageDataObserver observer, int userId) {
+ return ActivityManagerService.this.clearApplicationUserData(packageName, keepState,
+ isRestore, observer, userId);
+ }
}
long inputDispatchingTimedOut(int pid, final boolean aboveSystem, TimeoutRecord timeoutRecord) {
diff --git a/services/core/java/com/android/server/am/AppProfiler.java b/services/core/java/com/android/server/am/AppProfiler.java
index e7f4bf9..46e5523 100644
--- a/services/core/java/com/android/server/am/AppProfiler.java
+++ b/services/core/java/com/android/server/am/AppProfiler.java
@@ -1843,7 +1843,8 @@
dropBuilder.append(catSw.toString());
FrameworkStatsLog.write(FrameworkStatsLog.LOW_MEM_REPORTED);
mService.addErrorToDropBox("lowmem", null, "system_server", null,
- null, null, tag.toString(), dropBuilder.toString(), null, null, null, null, null);
+ null, null, tag.toString(), dropBuilder.toString(), null, null, null, null, null,
+ null);
synchronized (mService) {
long now = SystemClock.uptimeMillis();
if (mLastMemUsageReportTime < now) {
diff --git a/services/core/java/com/android/server/am/CachedAppOptimizer.java b/services/core/java/com/android/server/am/CachedAppOptimizer.java
index 1a58556..5fa0ffa 100644
--- a/services/core/java/com/android/server/am/CachedAppOptimizer.java
+++ b/services/core/java/com/android/server/am/CachedAppOptimizer.java
@@ -1514,7 +1514,7 @@
mFreezeHandler.obtainMessage(REPORT_UNFREEZE_MSG,
pid,
(int) Math.min(opt.getFreezeUnfreezeTime() - freezeTime, Integer.MAX_VALUE),
- new Pair<String, Integer>(app.processName, reason)));
+ new Pair<ProcessRecord, Integer>(app, reason)));
}
}
@@ -2159,11 +2159,12 @@
case REPORT_UNFREEZE_MSG: {
int pid = msg.arg1;
int frozenDuration = msg.arg2;
- Pair<String, Integer> obj = (Pair<String, Integer>) msg.obj;
- String processName = obj.first;
+ Pair<ProcessRecord, Integer> obj = (Pair<ProcessRecord, Integer>) msg.obj;
+ ProcessRecord app = obj.first;
+ String processName = app.processName;
int reason = obj.second;
- reportUnfreeze(pid, frozenDuration, processName, reason);
+ reportUnfreeze(app, pid, frozenDuration, processName, reason);
} break;
case UID_FROZEN_STATE_CHANGED_MSG: {
final boolean frozen = (msg.arg1 == 1);
@@ -2358,10 +2359,11 @@
}
}
- private void reportUnfreeze(int pid, int frozenDuration, String processName,
- @UnfreezeReason int reason) {
+ private void reportUnfreeze(ProcessRecord app, int pid, int frozenDuration,
+ String processName, @UnfreezeReason int reason) {
EventLog.writeEvent(EventLogTags.AM_UNFREEZE, pid, processName, reason);
+ app.onProcessUnfrozen();
// See above for why we're not taking mPhenotypeFlagLock here
if (mRandom.nextFloat() < mFreezerStatsdSampleRate) {
diff --git a/services/core/java/com/android/server/am/ProcessErrorStateRecord.java b/services/core/java/com/android/server/am/ProcessErrorStateRecord.java
index 09df277..40b1de6 100644
--- a/services/core/java/com/android/server/am/ProcessErrorStateRecord.java
+++ b/services/core/java/com/android/server/am/ProcessErrorStateRecord.java
@@ -297,6 +297,7 @@
ArrayList<Integer> firstPids = new ArrayList<>(5);
SparseBooleanArray lastPids = new SparseBooleanArray(20);
+ ActivityManagerService.VolatileDropboxEntryStates volatileDropboxEntriyStates = null;
mApp.getWindowProcessController().appEarlyNotResponding(annotation, () -> {
latencyTracker.waitingOnAMSLockStarted();
@@ -343,6 +344,9 @@
synchronized (mProcLock) {
latencyTracker.waitingOnProcLockEnded();
setNotResponding(true);
+ volatileDropboxEntriyStates =
+ ActivityManagerService.VolatileDropboxEntryStates
+ .withProcessFrozenState(mApp.mOptRecord.isFrozen());
}
// Log the ANR to the event log.
@@ -620,7 +624,8 @@
? (ProcessRecord) parentProcess.mOwner : null;
mService.addErrorToDropBox("anr", mApp, mApp.processName, activityShortComponentName,
parentShortComponentName, parentPr, null, report.toString(), tracesFile,
- null, new Float(loadingProgress), incrementalMetrics, errorId);
+ null, new Float(loadingProgress), incrementalMetrics, errorId,
+ volatileDropboxEntriyStates);
if (mApp.getWindowProcessController().appNotResponding(info.toString(),
() -> {
diff --git a/services/core/java/com/android/server/am/ProcessList.java b/services/core/java/com/android/server/am/ProcessList.java
index e484a6c..3d11c68 100644
--- a/services/core/java/com/android/server/am/ProcessList.java
+++ b/services/core/java/com/android/server/am/ProcessList.java
@@ -4907,6 +4907,18 @@
@GuardedBy(anyOf = {"mService", "mProcLock"})
void updateApplicationInfoLOSP(List<String> packagesToUpdate, int userId,
boolean updateFrameworkRes) {
+ final ArrayMap<String, ApplicationInfo> applicationInfoByPackage = new ArrayMap<>();
+ for (int i = packagesToUpdate.size() - 1; i >= 0; i--) {
+ final String packageName = packagesToUpdate.get(i);
+ final ApplicationInfo ai = mService.getPackageManagerInternal().getApplicationInfo(
+ packageName, STOCK_PM_FLAGS, Process.SYSTEM_UID, userId);
+ if (ai != null) {
+ applicationInfoByPackage.put(packageName, ai);
+ }
+ }
+ mService.mActivityTaskManager.updateActivityApplicationInfo(userId,
+ applicationInfoByPackage);
+
final ArrayList<WindowProcessController> targetProcesses = new ArrayList<>();
for (int i = mLruProcesses.size() - 1; i >= 0; i--) {
final ProcessRecord app = mLruProcesses.get(i);
@@ -4921,8 +4933,7 @@
app.getPkgList().forEachPackage(packageName -> {
if (updateFrameworkRes || packagesToUpdate.contains(packageName)) {
try {
- final ApplicationInfo ai = AppGlobals.getPackageManager()
- .getApplicationInfo(packageName, STOCK_PM_FLAGS, app.userId);
+ final ApplicationInfo ai = applicationInfoByPackage.get(packageName);
if (ai != null) {
if (ai.packageName.equals(app.info.packageName)) {
app.info = ai;
@@ -5632,7 +5643,7 @@
if (logToDropbox) {
final long now = SystemClock.elapsedRealtime();
final StringBuilder sb = new StringBuilder();
- mService.appendDropBoxProcessHeaders(app, app.processName, sb);
+ mService.appendDropBoxProcessHeaders(app, app.processName, null, sb);
sb.append("Reason: " + reason).append("\n");
sb.append("Requester UID: " + requester).append("\n");
dbox.addText(DROPBOX_TAG_IMPERCEPTIBLE_KILL, sb.toString());
diff --git a/services/core/java/com/android/server/am/ProcessProfileRecord.java b/services/core/java/com/android/server/am/ProcessProfileRecord.java
index 5ad49a4..db74f1a 100644
--- a/services/core/java/com/android/server/am/ProcessProfileRecord.java
+++ b/services/core/java/com/android/server/am/ProcessProfileRecord.java
@@ -225,6 +225,32 @@
mBaseProcessTracker = baseProcessTracker;
}
+ void onProcessFrozen() {
+ synchronized (mService.mProcessStats.mLock) {
+ final ProcessState tracker = mBaseProcessTracker;
+ if (tracker != null) {
+ final PackageList pkgList = mApp.getPkgList();
+ final long now = SystemClock.uptimeMillis();
+ synchronized (pkgList) {
+ tracker.onProcessFrozen(now, pkgList.getPackageListLocked());
+ }
+ }
+ }
+ }
+
+ void onProcessUnfrozen() {
+ synchronized (mService.mProcessStats.mLock) {
+ final ProcessState tracker = mBaseProcessTracker;
+ if (tracker != null) {
+ final PackageList pkgList = mApp.getPkgList();
+ final long now = SystemClock.uptimeMillis();
+ synchronized (pkgList) {
+ tracker.onProcessUnfrozen(now, pkgList.getPackageListLocked());
+ }
+ }
+ }
+ }
+
void onProcessActive(IApplicationThread thread, ProcessStatsService tracker) {
if (mThread == null) {
synchronized (mProfilerLock) {
diff --git a/services/core/java/com/android/server/am/ProcessRecord.java b/services/core/java/com/android/server/am/ProcessRecord.java
index e2edd8a..cfbb5a5 100644
--- a/services/core/java/com/android/server/am/ProcessRecord.java
+++ b/services/core/java/com/android/server/am/ProcessRecord.java
@@ -1359,6 +1359,15 @@
return false;
}
+ void onProcessFrozen() {
+ mProfile.onProcessFrozen();
+ }
+
+ void onProcessUnfrozen() {
+ mProfile.onProcessUnfrozen();
+ }
+
+
/*
* Delete all packages from list except the package indicated in info
*/
diff --git a/services/core/java/com/android/server/appop/TEST_MAPPING b/services/core/java/com/android/server/appop/TEST_MAPPING
index 68062b5..65f6af7 100644
--- a/services/core/java/com/android/server/appop/TEST_MAPPING
+++ b/services/core/java/com/android/server/appop/TEST_MAPPING
@@ -4,7 +4,7 @@
"name": "CtsAppOpsTestCases",
"options": [
{
- "exclude-annotation": "android.platform.test.annotations.FlakyTest"
+ "exclude-annotation": "androidx.test.filters.FlakyTest"
}
]
},
@@ -31,7 +31,7 @@
"name": "CtsPermissionTestCases",
"options": [
{
- "exclude-annotation": "android.platform.test.annotations.FlakyTest"
+ "exclude-annotation": "androidx.test.filters.FlakyTest"
},
{
"include-filter": "android.permission.cts.BackgroundPermissionsTest"
diff --git a/services/core/java/com/android/server/audio/AudioDeviceBroker.java b/services/core/java/com/android/server/audio/AudioDeviceBroker.java
index 6d42da6..c05a1f6 100644
--- a/services/core/java/com/android/server/audio/AudioDeviceBroker.java
+++ b/services/core/java/com/android/server/audio/AudioDeviceBroker.java
@@ -772,7 +772,6 @@
final int mVolume;
final boolean mIsLeOutput;
final @NonNull String mEventSource;
- final @AudioSystem.AudioFormatNativeEnumForBtCodec int mCodec;
final int mAudioSystemDevice;
final int mMusicDevice;
@@ -787,7 +786,6 @@
mEventSource = d.mEventSource;
mAudioSystemDevice = audioDevice;
mMusicDevice = AudioSystem.DEVICE_NONE;
- mCodec = codec;
}
// constructor used by AudioDeviceBroker to search similar message
@@ -796,7 +794,6 @@
mProfile = profile;
mEventSource = "";
mMusicDevice = AudioSystem.DEVICE_NONE;
- mCodec = AudioSystem.AUDIO_FORMAT_DEFAULT;
mAudioSystemDevice = 0;
mState = 0;
mSupprNoisy = false;
@@ -811,7 +808,6 @@
mProfile = profile;
mEventSource = "";
mMusicDevice = musicDevice;
- mCodec = AudioSystem.AUDIO_FORMAT_DEFAULT;
mAudioSystemDevice = audioSystemDevice;
mState = state;
mSupprNoisy = false;
@@ -829,7 +825,6 @@
mEventSource = src.mEventSource;
mAudioSystemDevice = src.mAudioSystemDevice;
mMusicDevice = src.mMusicDevice;
- mCodec = src.mCodec;
}
// redefine equality op so we can match messages intended for this device
@@ -847,6 +842,19 @@
}
return false;
}
+
+ @Override
+ public String toString() {
+ return "BtDeviceInfo: device=" + mDevice.toString()
+ + " state=" + mState
+ + " prof=" + mProfile
+ + " supprNoisy=" + mSupprNoisy
+ + " volume=" + mVolume
+ + " isLeOutput=" + mIsLeOutput
+ + " eventSource=" + mEventSource
+ + " audioSystemDevice=" + mAudioSystemDevice
+ + " musicDevice=" + mMusicDevice;
+ }
}
BtDeviceInfo createBtDeviceInfo(@NonNull BtDeviceChangedData d, @NonNull BluetoothDevice device,
@@ -859,9 +867,6 @@
break;
case BluetoothProfile.A2DP:
audioDevice = AudioSystem.DEVICE_OUT_BLUETOOTH_A2DP;
- synchronized (mDeviceStateLock) {
- codec = mBtHelper.getA2dpCodec(device);
- }
break;
case BluetoothProfile.HEARING_AID:
audioDevice = AudioSystem.DEVICE_OUT_HEARING_AID;
@@ -1696,8 +1701,11 @@
case MSG_L_SET_BT_ACTIVE_DEVICE:
synchronized (mSetModeLock) {
synchronized (mDeviceStateLock) {
- BtDeviceInfo btInfo = (BtDeviceInfo) msg.obj;
- mDeviceInventory.onSetBtActiveDevice(btInfo,
+ final BtDeviceInfo btInfo = (BtDeviceInfo) msg.obj;
+ @AudioSystem.AudioFormatNativeEnumForBtCodec final int codec =
+ mBtHelper.getA2dpCodecWithFallbackToSBC(
+ btInfo.mDevice, "MSG_L_SET_BT_ACTIVE_DEVICE");
+ mDeviceInventory.onSetBtActiveDevice(btInfo, codec,
(btInfo.mProfile
!= BluetoothProfile.LE_AUDIO || btInfo.mIsLeOutput)
? mAudioService.getBluetoothContextualVolumeStream()
@@ -1730,12 +1738,16 @@
(String) msg.obj, msg.arg1);
}
break;
- case MSG_L_BLUETOOTH_DEVICE_CONFIG_CHANGE:
+ case MSG_L_BLUETOOTH_DEVICE_CONFIG_CHANGE: {
+ final BtDeviceInfo btInfo = (BtDeviceInfo) msg.obj;
synchronized (mDeviceStateLock) {
+ @AudioSystem.AudioFormatNativeEnumForBtCodec final int codec =
+ mBtHelper.getA2dpCodecWithFallbackToSBC(
+ btInfo.mDevice, "MSG_L_BLUETOOTH_DEVICE_CONFIG_CHANGE");
mDeviceInventory.onBluetoothDeviceConfigChange(
- (BtDeviceInfo) msg.obj, BtHelper.EVENT_DEVICE_CONFIG_CHANGE);
+ btInfo, codec, BtHelper.EVENT_DEVICE_CONFIG_CHANGE);
}
- break;
+ } break;
case MSG_BROADCAST_AUDIO_BECOMING_NOISY:
onSendBecomingNoisyIntent();
break;
@@ -1831,20 +1843,12 @@
}
break;
case MSG_L_BT_ACTIVE_DEVICE_CHANGE_EXT: {
- final BtDeviceInfo info = (BtDeviceInfo) msg.obj;
- if (info.mDevice == null) break;
+ final BtDeviceInfo btInfo = (BtDeviceInfo) msg.obj;
+ if (btInfo.mDevice == null) break;
AudioService.sDeviceLogger.enqueue((new EventLogger.StringEvent(
- "msg: onBluetoothActiveDeviceChange "
- + " state=" + info.mState
- // only querying address as this is the only readily available
- // field on the device
- + " addr=" + info.mDevice.getAddress()
- + " prof=" + info.mProfile
- + " supprNoisy=" + info.mSupprNoisy
- + " src=" + info.mEventSource
- )).printLog(TAG));
+ "msg: onBluetoothActiveDeviceChange " + btInfo)).printLog(TAG));
synchronized (mDeviceStateLock) {
- mDeviceInventory.setBluetoothActiveDevice(info);
+ mDeviceInventory.setBluetoothActiveDevice(btInfo);
}
} break;
case MSG_IL_SAVE_PREF_DEVICES_FOR_STRATEGY: {
diff --git a/services/core/java/com/android/server/audio/AudioDeviceInventory.java b/services/core/java/com/android/server/audio/AudioDeviceInventory.java
index da89502..60af280 100644
--- a/services/core/java/com/android/server/audio/AudioDeviceInventory.java
+++ b/services/core/java/com/android/server/audio/AudioDeviceInventory.java
@@ -503,9 +503,11 @@
}
}
- // @GuardedBy("AudioDeviceBroker.mSetModeLock")
- @GuardedBy("AudioDeviceBroker.mDeviceStateLock")
- void onSetBtActiveDevice(@NonNull AudioDeviceBroker.BtDeviceInfo btInfo, int streamType) {
+ // @GuardedBy("mDeviceBroker.mSetModeLock")
+ @GuardedBy("AudioDeviceBroker.this.mDeviceStateLock")
+ void onSetBtActiveDevice(@NonNull AudioDeviceBroker.BtDeviceInfo btInfo,
+ @AudioSystem.AudioFormatNativeEnumForBtCodec int codec,
+ int streamType) {
if (AudioService.DEBUG_DEVICES) {
Log.d(TAG, "onSetBtActiveDevice"
+ " btDevice=" + btInfo.mDevice
@@ -518,10 +520,7 @@
}
AudioService.sDeviceLogger.enqueue(new EventLogger.StringEvent("BT connected:"
- + " addr=" + address
- + " profile=" + btInfo.mProfile
- + " state=" + btInfo.mState
- + " codec=" + AudioSystem.audioFormatToString(btInfo.mCodec)));
+ + btInfo + " codec=" + AudioSystem.audioFormatToString(codec)));
new MediaMetrics.Item(mMetricsId + "onSetBtActiveDevice")
.set(MediaMetrics.Property.STATUS, btInfo.mProfile)
@@ -529,7 +528,7 @@
AudioSystem.getDeviceName(btInfo.mAudioSystemDevice))
.set(MediaMetrics.Property.ADDRESS, address)
.set(MediaMetrics.Property.ENCODING,
- AudioSystem.audioFormatToString(btInfo.mCodec))
+ AudioSystem.audioFormatToString(codec))
.set(MediaMetrics.Property.EVENT, "onSetBtActiveDevice")
.set(MediaMetrics.Property.STREAM_TYPE,
AudioSystem.streamToString(streamType))
@@ -568,7 +567,7 @@
btInfo.mVolume * 10, btInfo.mAudioSystemDevice,
"onSetBtActiveDevice");
}
- makeA2dpDeviceAvailable(btInfo, "onSetBtActiveDevice");
+ makeA2dpDeviceAvailable(btInfo, codec, "onSetBtActiveDevice");
}
break;
case BluetoothProfile.HEARING_AID:
@@ -594,9 +593,10 @@
}
- @GuardedBy("AudioDeviceBroker.mDeviceStateLock")
+ @GuardedBy("AudioDeviceBroker.this.mDeviceStateLock")
/*package*/ void onBluetoothDeviceConfigChange(
- @NonNull AudioDeviceBroker.BtDeviceInfo btInfo, int event) {
+ @NonNull AudioDeviceBroker.BtDeviceInfo btInfo,
+ @AudioSystem.AudioFormatNativeEnumForBtCodec int codec, int event) {
MediaMetrics.Item mmi = new MediaMetrics.Item(mMetricsId
+ "onBluetoothDeviceConfigChange")
.set(MediaMetrics.Property.EVENT, BtHelper.deviceEventToString(event));
@@ -610,7 +610,6 @@
Log.d(TAG, "onBluetoothDeviceConfigChange btDevice=" + btDevice);
}
int volume = btInfo.mVolume;
- @AudioSystem.AudioFormatNativeEnumForBtCodec final int audioCodec = btInfo.mCodec;
String address = btDevice.getAddress();
if (!BluetoothAdapter.checkBluetoothAddress(address)) {
@@ -639,8 +638,7 @@
}
mmi.set(MediaMetrics.Property.ADDRESS, address)
- .set(MediaMetrics.Property.ENCODING,
- AudioSystem.audioFormatToString(audioCodec))
+ .set(MediaMetrics.Property.ENCODING, AudioSystem.audioFormatToString(codec))
.set(MediaMetrics.Property.INDEX, volume)
.set(MediaMetrics.Property.NAME, di.mDeviceName);
@@ -648,20 +646,19 @@
if (event == BtHelper.EVENT_DEVICE_CONFIG_CHANGE) {
boolean a2dpCodecChange = false;
if (btInfo.mProfile == BluetoothProfile.A2DP) {
- if (di.mDeviceCodecFormat != audioCodec) {
- di.mDeviceCodecFormat = audioCodec;
+ if (di.mDeviceCodecFormat != codec) {
+ di.mDeviceCodecFormat = codec;
mConnectedDevices.replace(key, di);
a2dpCodecChange = true;
}
final int res = mAudioSystem.handleDeviceConfigChange(
- btInfo.mAudioSystemDevice, address,
- BtHelper.getName(btDevice), audioCodec);
+ btInfo.mAudioSystemDevice, address, BtHelper.getName(btDevice), codec);
if (res != AudioSystem.AUDIO_STATUS_OK) {
AudioService.sDeviceLogger.enqueue(new EventLogger.StringEvent(
"APM handleDeviceConfigChange failed for A2DP device addr="
+ address + " codec="
- + AudioSystem.audioFormatToString(audioCodec))
+ + AudioSystem.audioFormatToString(codec))
.printLog(TAG));
// force A2DP device disconnection in case of error so that AudioService
@@ -672,7 +669,7 @@
AudioService.sDeviceLogger.enqueue(new EventLogger.StringEvent(
"APM handleDeviceConfigChange success for A2DP device addr="
+ address
- + " codec=" + AudioSystem.audioFormatToString(audioCodec))
+ + " codec=" + AudioSystem.audioFormatToString(codec))
.printLog(TAG));
}
@@ -1338,7 +1335,7 @@
* @param device the device whose connection state is queried
* @return true if connected
*/
- // called with AudioDeviceBroker.mDeviceStateLock lock held
+ @GuardedBy("AudioDeviceBroker.this.mDeviceStateLock")
public boolean isDeviceConnected(@NonNull AudioDeviceAttributes device) {
final String key = DeviceInfo.makeDeviceListKey(device.getInternalType(),
device.getAddress());
@@ -1489,7 +1486,7 @@
}
}
- @GuardedBy("AudioDeviceBroker.mDeviceStateLock")
+ @GuardedBy("AudioDeviceBroker.this.mDeviceStateLock")
/*package*/ void onBtProfileDisconnected(int profile) {
switch (profile) {
case BluetoothProfile.HEADSET:
@@ -1554,7 +1551,7 @@
disconnectLeAudio(AudioSystem.DEVICE_OUT_BLE_BROADCAST);
}
- @GuardedBy("AudioDeviceBroker.mDeviceStateLock")
+ @GuardedBy("AudioDeviceBroker.this.mDeviceStateLock")
private void disconnectHeadset() {
boolean disconnect = false;
synchronized (mDevicesLock) {
@@ -1594,9 +1591,10 @@
return mCurAudioRoutes;
}
- // only public for mocking/spying
- @GuardedBy("AudioDeviceBroker.mDeviceStateLock")
- @VisibleForTesting
+ /**
+ * Set a Bluetooth device to active.
+ */
+ @GuardedBy("AudioDeviceBroker.this.mDeviceStateLock")
public int setBluetoothActiveDevice(@NonNull AudioDeviceBroker.BtDeviceInfo info) {
int delay;
synchronized (mDevicesLock) {
@@ -1617,12 +1615,7 @@
}
if (AudioService.DEBUG_DEVICES) {
- Log.i(TAG, "setBluetoothActiveDevice device: " + info.mDevice
- + " profile: " + BluetoothProfile.getProfileName(info.mProfile)
- + " state: " + BluetoothProfile.getConnectionStateName(info.mState)
- + " delay(ms): " + delay
- + " codec:" + Integer.toHexString(info.mCodec)
- + " suppressNoisyIntent: " + info.mSupprNoisy);
+ Log.i(TAG, "setBluetoothActiveDevice " + info.toString() + " delay(ms): " + delay);
}
mDeviceBroker.postBluetoothActiveDevice(info, delay);
if (info.mProfile == BluetoothProfile.HEARING_AID
@@ -1658,10 +1651,10 @@
@GuardedBy("mDevicesLock")
private void makeA2dpDeviceAvailable(AudioDeviceBroker.BtDeviceInfo btInfo,
+ @AudioSystem.AudioFormatNativeEnumForBtCodec int codec,
String eventSource) {
final String address = btInfo.mDevice.getAddress();
final String name = BtHelper.getName(btInfo.mDevice);
- final int a2dpCodec = btInfo.mCodec;
// enable A2DP before notifying A2DP connection to avoid unnecessary processing in
// audio policy manager
@@ -1671,7 +1664,7 @@
AudioDeviceAttributes ada = new AudioDeviceAttributes(
AudioSystem.DEVICE_OUT_BLUETOOTH_A2DP, address, name);
final int res = mAudioSystem.setDeviceConnectionState(ada,
- AudioSystem.DEVICE_STATE_AVAILABLE, a2dpCodec);
+ AudioSystem.DEVICE_STATE_AVAILABLE, codec);
// TODO: log in MediaMetrics once distinction between connection failure and
// double connection is made.
@@ -1694,7 +1687,7 @@
// time_low = 0, time_mid = 0, time_hi = 0, clock_seq = 0, node = MAC Address
UUID sensorUuid = UuidUtils.uuidFromAudioDeviceAttributes(ada);
final DeviceInfo di = new DeviceInfo(AudioSystem.DEVICE_OUT_BLUETOOTH_A2DP, name,
- address, a2dpCodec, sensorUuid);
+ address, codec, sensorUuid);
final String diKey = di.getKey();
mConnectedDevices.put(diKey, di);
// on a connection always overwrite the device seen by AudioPolicy, see comment above when
@@ -1910,9 +1903,9 @@
}
@GuardedBy("mDevicesLock")
- private void makeA2dpDeviceUnavailableNow(String address, int a2dpCodec) {
+ private void makeA2dpDeviceUnavailableNow(String address, int codec) {
MediaMetrics.Item mmi = new MediaMetrics.Item(mMetricsId + "a2dp." + address)
- .set(MediaMetrics.Property.ENCODING, AudioSystem.audioFormatToString(a2dpCodec))
+ .set(MediaMetrics.Property.ENCODING, AudioSystem.audioFormatToString(codec))
.set(MediaMetrics.Property.EVENT, "makeA2dpDeviceUnavailableNow");
if (address == null) {
@@ -1939,7 +1932,7 @@
AudioDeviceAttributes ada = new AudioDeviceAttributes(
AudioSystem.DEVICE_OUT_BLUETOOTH_A2DP, address);
final int res = mAudioSystem.setDeviceConnectionState(ada,
- AudioSystem.DEVICE_STATE_UNAVAILABLE, a2dpCodec);
+ AudioSystem.DEVICE_STATE_UNAVAILABLE, codec);
if (res != AudioSystem.AUDIO_STATUS_OK) {
AudioService.sDeviceLogger.enqueue(new EventLogger.StringEvent(
diff --git a/services/core/java/com/android/server/audio/BtHelper.java b/services/core/java/com/android/server/audio/BtHelper.java
index 1462b3c..6af409e 100644
--- a/services/core/java/com/android/server/audio/BtHelper.java
+++ b/services/core/java/com/android/server/audio/BtHelper.java
@@ -173,8 +173,8 @@
//----------------------------------------------------------------------
// Interface for AudioDeviceBroker
- // @GuardedBy("AudioDeviceBroker.mSetModeLock")
- @GuardedBy("AudioDeviceBroker.mDeviceStateLock")
+ // @GuardedBy("mDeviceBroker.mSetModeLock")
+ @GuardedBy("AudioDeviceBroker.this.mDeviceStateLock")
/*package*/ synchronized void onSystemReady() {
mScoConnectionState = android.media.AudioManager.SCO_AUDIO_STATE_ERROR;
resetBluetoothSco();
@@ -263,8 +263,20 @@
return AudioSystem.bluetoothCodecToAudioFormat(btCodecConfig.getCodecType());
}
- // @GuardedBy("AudioDeviceBroker.mSetModeLock")
- @GuardedBy("AudioDeviceBroker.mDeviceStateLock")
+ /*package*/ synchronized @AudioSystem.AudioFormatNativeEnumForBtCodec
+ int getA2dpCodecWithFallbackToSBC(
+ @NonNull BluetoothDevice device, @NonNull String source) {
+ @AudioSystem.AudioFormatNativeEnumForBtCodec int codec = getA2dpCodec(device);
+ if (codec == AudioSystem.AUDIO_FORMAT_DEFAULT) {
+ AudioService.sDeviceLogger.enqueue(new EventLogger.StringEvent(
+ "getA2dpCodec DEFAULT from " + source + " fallback to SBC"));
+ return AudioSystem.AUDIO_FORMAT_SBC;
+ }
+ return codec;
+ }
+
+ // @GuardedBy("mDeviceBroker.mSetModeLock")
+ @GuardedBy("AudioDeviceBroker.this.mDeviceStateLock")
/*package*/ synchronized void onReceiveBtEvent(Intent intent) {
final String action = intent.getAction();
@@ -283,8 +295,8 @@
* Exclusively called from AudioDeviceBroker when handling MSG_L_RECEIVED_BT_EVENT
* as part of the serialization of the communication route selection
*/
- // @GuardedBy("AudioDeviceBroker.mSetModeLock")
- @GuardedBy("AudioDeviceBroker.mDeviceStateLock")
+ // @GuardedBy("mDeviceBroker.mSetModeLock")
+ @GuardedBy("AudioDeviceBroker.this.mDeviceStateLock")
private void onScoAudioStateChanged(int state) {
boolean broadcast = false;
int scoAudioState = AudioManager.SCO_AUDIO_STATE_ERROR;
@@ -355,16 +367,16 @@
== BluetoothHeadset.STATE_AUDIO_CONNECTED;
}
- // @GuardedBy("AudioDeviceBroker.mSetModeLock")
- @GuardedBy("AudioDeviceBroker.mDeviceStateLock")
+ // @GuardedBy("mDeviceBroker.mSetModeLock")
+ @GuardedBy("AudioDeviceBroker.this.mDeviceStateLock")
/*package*/ synchronized boolean startBluetoothSco(int scoAudioMode,
@NonNull String eventSource) {
AudioService.sDeviceLogger.enqueue(new EventLogger.StringEvent(eventSource));
return requestScoState(BluetoothHeadset.STATE_AUDIO_CONNECTED, scoAudioMode);
}
- // @GuardedBy("AudioDeviceBroker.mSetModeLock")
- @GuardedBy("AudioDeviceBroker.mDeviceStateLock")
+ // @GuardedBy("mDeviceBroker.mSetModeLock")
+ @GuardedBy("AudioDeviceBroker.this.mDeviceStateLock")
/*package*/ synchronized boolean stopBluetoothSco(@NonNull String eventSource) {
AudioService.sDeviceLogger.enqueue(new EventLogger.StringEvent(eventSource));
return requestScoState(BluetoothHeadset.STATE_AUDIO_DISCONNECTED, SCO_MODE_VIRTUAL_CALL);
@@ -435,8 +447,8 @@
mScoConnectionState = state;
}
- // @GuardedBy("AudioDeviceBroker.mSetModeLock")
- //@GuardedBy("AudioDeviceBroker.mDeviceStateLock")
+ // @GuardedBy("mDeviceBroker.mSetModeLock")
+ @GuardedBy("AudioDeviceBroker.this.mDeviceStateLock")
/*package*/ synchronized void resetBluetoothSco() {
mScoAudioState = SCO_STATE_INACTIVE;
broadcastScoConnectionState(AudioManager.SCO_AUDIO_STATE_DISCONNECTED);
@@ -445,7 +457,8 @@
mDeviceBroker.setBluetoothScoOn(false, "resetBluetoothSco");
}
- //@GuardedBy("AudioDeviceBroker.mDeviceStateLock")
+ // @GuardedBy("mDeviceBroker.mSetModeLock")
+ @GuardedBy("AudioDeviceBroker.this.mDeviceStateLock")
/*package*/ synchronized void onBtProfileDisconnected(int profile) {
AudioService.sDeviceLogger.enqueue(new EventLogger.StringEvent(
"BT profile " + BluetoothProfile.getProfileName(profile) + " disconnected"));
@@ -474,7 +487,8 @@
}
}
- @GuardedBy("AudioDeviceBroker.mDeviceStateLock")
+ // @GuardedBy("mDeviceBroker.mSetModeLock")
+ @GuardedBy("AudioDeviceBroker.this.mDeviceStateLock")
/*package*/ synchronized void onBtProfileConnected(int profile, BluetoothProfile proxy) {
AudioService.sDeviceLogger.enqueue(new EventLogger.StringEvent(
"BT profile " + BluetoothProfile.getProfileName(profile) + " connected to proxy "
@@ -522,8 +536,8 @@
}
}
- // @GuardedBy("AudioDeviceBroker.mSetModeLock")
- @GuardedBy("AudioDeviceBroker.mDeviceStateLock")
+ // @GuardedBy("mDeviceBroker.mSetModeLock")
+ @GuardedBy("AudioDeviceBroker.this.mDeviceStateLock")
private void onHeadsetProfileConnected(BluetoothHeadset headset) {
// Discard timeout message
mDeviceBroker.handleCancelFailureToConnectToBtHeadsetService();
@@ -642,8 +656,8 @@
return btDevice == null ? "(null)" : btDevice.getAnonymizedAddress();
}
- // @GuardedBy("AudioDeviceBroker.mSetModeLock")
- //@GuardedBy("AudioDeviceBroker.mDeviceStateLock")
+ // @GuardedBy("mDeviceBroker.mSetModeLock")
+ @GuardedBy("AudioDeviceBroker.this.mDeviceStateLock")
/*package */ synchronized void onSetBtScoActiveDevice(BluetoothDevice btDevice) {
Log.i(TAG, "onSetBtScoActiveDevice: " + getAnonymizedAddress(mBluetoothHeadsetDevice)
+ " -> " + getAnonymizedAddress(btDevice));
@@ -712,8 +726,8 @@
//----------------------------------------------------------------------
- // @GuardedBy("AudioDeviceBroker.mSetModeLock")
- //@GuardedBy("AudioDeviceBroker.mDeviceStateLock")
+ // @GuardedBy("mDeviceBroker.mSetModeLock")
+ // @GuardedBy("AudioDeviceBroker.this.mDeviceStateLock")
@GuardedBy("BtHelper.this")
private boolean requestScoState(int state, int scoAudioMode) {
checkScoAudioState();
diff --git a/services/core/java/com/android/server/audio/UuidUtils.java b/services/core/java/com/android/server/audio/UuidUtils.java
index 2003619..035bea3 100644
--- a/services/core/java/com/android/server/audio/UuidUtils.java
+++ b/services/core/java/com/android/server/audio/UuidUtils.java
@@ -45,26 +45,24 @@
* Generate a headtracking UUID from AudioDeviceAttributes
*/
public static UUID uuidFromAudioDeviceAttributes(AudioDeviceAttributes device) {
- switch (device.getInternalType()) {
- case AudioSystem.DEVICE_OUT_BLUETOOTH_A2DP:
- String address = device.getAddress().replace(":", "");
- if (address.length() != 12) {
- return null;
- }
- address = "0x" + address;
- long lsb = LSB_PREFIX_BT;
- try {
- lsb |= Long.decode(address).longValue();
- } catch (NumberFormatException e) {
- return null;
- }
- if (AudioService.DEBUG_DEVICES) {
- Slog.i(TAG, "uuidFromAudioDeviceAttributes lsb: " + Long.toHexString(lsb));
- }
- return new UUID(0, lsb);
- default:
- // Handle other device types here
- return null;
+ if (!AudioSystem.isBluetoothA2dpOutDevice(device.getInternalType())
+ && !AudioSystem.isBluetoothLeOutDevice(device.getInternalType())) {
+ return null;
}
+ String address = device.getAddress().replace(":", "");
+ if (address.length() != 12) {
+ return null;
+ }
+ address = "0x" + address;
+ long lsb = LSB_PREFIX_BT;
+ try {
+ lsb |= Long.decode(address).longValue();
+ } catch (NumberFormatException e) {
+ return null;
+ }
+ if (AudioService.DEBUG_DEVICES) {
+ Slog.i(TAG, "uuidFromAudioDeviceAttributes lsb: " + Long.toHexString(lsb));
+ }
+ return new UUID(0, lsb);
}
}
diff --git a/services/core/java/com/android/server/biometrics/AuthenticationStats.java b/services/core/java/com/android/server/biometrics/AuthenticationStats.java
index e109cc8..707240b 100644
--- a/services/core/java/com/android/server/biometrics/AuthenticationStats.java
+++ b/services/core/java/com/android/server/biometrics/AuthenticationStats.java
@@ -90,6 +90,11 @@
mRejectedAttempts = 0;
}
+ /** Update enrollment notification counter after sending a notification. */
+ public void updateNotificationCounter() {
+ mEnrollmentNotifications++;
+ }
+
@Override
public boolean equals(Object obj) {
if (this == obj) {
diff --git a/services/core/java/com/android/server/biometrics/AuthenticationStatsCollector.java b/services/core/java/com/android/server/biometrics/AuthenticationStatsCollector.java
index 54b34de..e8a20de 100644
--- a/services/core/java/com/android/server/biometrics/AuthenticationStatsCollector.java
+++ b/services/core/java/com/android/server/biometrics/AuthenticationStatsCollector.java
@@ -45,12 +45,12 @@
private static final String TAG = "AuthenticationStatsCollector";
// The minimum number of attempts that will calculate the FRR and trigger the notification.
- private static final int MINIMUM_ATTEMPTS = 500;
+ private static final int MINIMUM_ATTEMPTS = 150;
// Upload the data every 50 attempts (average number of daily authentications).
private static final int AUTHENTICATION_UPLOAD_INTERVAL = 50;
// The maximum number of eligible biometric enrollment notification can be sent.
@VisibleForTesting
- static final int MAXIMUM_ENROLLMENT_NOTIFICATIONS = 2;
+ static final int MAXIMUM_ENROLLMENT_NOTIFICATIONS = 1;
@NonNull private final Context mContext;
@@ -68,8 +68,10 @@
@Override
public void onReceive(@NonNull Context context, @NonNull Intent intent) {
final int userId = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, UserHandle.USER_NULL);
+
if (userId != UserHandle.USER_NULL
&& intent.getAction().equals(Intent.ACTION_USER_REMOVED)) {
+ Slog.d(TAG, "Removing data for user: " + userId);
onUserRemoved(userId);
}
}
@@ -84,7 +86,9 @@
mModality = modality;
mBiometricNotification = biometricNotification;
- context.registerReceiver(mBroadcastReceiver, new IntentFilter(Intent.ACTION_USER_REMOVED));
+ IntentFilter intentFilter = new IntentFilter();
+ intentFilter.addAction(Intent.ACTION_USER_REMOVED);
+ context.registerReceiver(mBroadcastReceiver, intentFilter);
}
private void initializeUserAuthenticationStatsMap() {
@@ -121,10 +125,11 @@
authenticationStats.authenticate(authenticated);
+ sendNotificationIfNeeded(userId);
+
if (mPersisterInitialized) {
persistDataIfNeeded(userId);
}
- sendNotificationIfNeeded(userId);
}
/** Check if a notification should be sent after a calculation cycle. */
@@ -164,8 +169,10 @@
}
if (hasEnrolledFace && !hasEnrolledFingerprint) {
mBiometricNotification.sendFpEnrollNotification(mContext);
+ authenticationStats.updateNotificationCounter();
} else if (!hasEnrolledFace && hasEnrolledFingerprint) {
mBiometricNotification.sendFaceEnrollNotification(mContext);
+ authenticationStats.updateNotificationCounter();
}
}
diff --git a/services/core/java/com/android/server/biometrics/AuthenticationStatsPersister.java b/services/core/java/com/android/server/biometrics/AuthenticationStatsPersister.java
index 74e1410..8122b1d 100644
--- a/services/core/java/com/android/server/biometrics/AuthenticationStatsPersister.java
+++ b/services/core/java/com/android/server/biometrics/AuthenticationStatsPersister.java
@@ -59,7 +59,7 @@
// The package info in the context isn't initialized in the way it is for normal apps,
// so the standard, name-based context.getSharedPreferences doesn't work. Instead, we
// build the path manually below using the same policy that appears in ContextImpl.
- final File prefsFile = new File(Environment.getDataSystemDeDirectory(), FILE_NAME);
+ final File prefsFile = new File(Environment.getDataSystemDirectory(), FILE_NAME);
mSharedPreferences = context.getSharedPreferences(prefsFile, Context.MODE_PRIVATE);
}
@@ -137,16 +137,19 @@
iterator.remove();
break;
}
+ // Reset frrStatJson when user doesn't exist.
+ frrStatJson = null;
}
- // If there's existing frr stats in the file, we want to update the stats for the given
- // modality and keep the stats for other modalities.
+ // Checks if this is a new user and there's no JSON for this user in the storage.
if (frrStatJson == null) {
frrStatJson = new JSONObject().put(USER_ID, userId);
}
frrStatsSet.add(buildFrrStats(frrStatJson, totalAttempts, rejectedAttempts,
enrollmentNotifications, modality));
+ Slog.d(TAG, "frrStatsSet to persist: " + frrStatsSet);
+
mSharedPreferences.edit().putStringSet(KEY, frrStatsSet).apply();
} catch (JSONException e) {
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 2ff695d..f1c74f0 100644
--- a/services/core/java/com/android/server/biometrics/sensors/BiometricNotificationUtils.java
+++ b/services/core/java/com/android/server/biometrics/sensors/BiometricNotificationUtils.java
@@ -78,7 +78,8 @@
null /* options */, UserHandle.CURRENT);
showNotificationHelper(context, name, title, content, pendingIntent, FACE_RE_ENROLL_CHANNEL,
- FACE_RE_ENROLL_NOTIFICATION_TAG, Notification.VISIBILITY_SECRET);
+ Notification.CATEGORY_SYSTEM, FACE_RE_ENROLL_NOTIFICATION_TAG,
+ Notification.VISIBILITY_SECRET);
}
/**
@@ -95,15 +96,14 @@
final Intent intent = new Intent(FACE_ENROLL_ACTION);
intent.setPackage(SETTINGS_PACKAGE);
- intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
- context.startActivity(intent);
final PendingIntent pendingIntent = PendingIntent.getActivityAsUser(context,
0 /* requestCode */, intent, PendingIntent.FLAG_IMMUTABLE /* flags */,
null /* options */, UserHandle.CURRENT);
showNotificationHelper(context, name, title, content, pendingIntent, FACE_ENROLL_CHANNEL,
- FACE_ENROLL_NOTIFICATION_TAG, Notification.VISIBILITY_PUBLIC);
+ Notification.CATEGORY_RECOMMENDATION, FACE_ENROLL_NOTIFICATION_TAG,
+ Notification.VISIBILITY_PUBLIC);
}
/**
@@ -120,16 +120,14 @@
final Intent intent = new Intent(FINGERPRINT_ENROLL_ACTION);
intent.setPackage(SETTINGS_PACKAGE);
- intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
- context.startActivity(intent);
final PendingIntent pendingIntent = PendingIntent.getActivityAsUser(context,
0 /* requestCode */, intent, PendingIntent.FLAG_IMMUTABLE /* flags */,
null /* options */, UserHandle.CURRENT);
showNotificationHelper(context, name, title, content, pendingIntent,
- FINGERPRINT_ENROLL_CHANNEL, FINGERPRINT_ENROLL_NOTIFICATION_TAG,
- Notification.VISIBILITY_PUBLIC);
+ Notification.CATEGORY_RECOMMENDATION, FINGERPRINT_ENROLL_CHANNEL,
+ FINGERPRINT_ENROLL_NOTIFICATION_TAG, Notification.VISIBILITY_PUBLIC);
}
/**
@@ -163,13 +161,13 @@
null /* options */, UserHandle.CURRENT);
showNotificationHelper(context, name, title, content, pendingIntent,
- FINGERPRINT_BAD_CALIBRATION_CHANNEL, BAD_CALIBRATION_NOTIFICATION_TAG,
- Notification.VISIBILITY_SECRET);
+ Notification.CATEGORY_SYSTEM, FINGERPRINT_BAD_CALIBRATION_CHANNEL,
+ BAD_CALIBRATION_NOTIFICATION_TAG, Notification.VISIBILITY_SECRET);
}
private static void showNotificationHelper(Context context, String name, String title,
- String content, PendingIntent pendingIntent, String channelName,
- String notificationTag, int visibility) {
+ String content, PendingIntent pendingIntent, String category,
+ String channelName, String notificationTag, int visibility) {
final NotificationManager notificationManager =
context.getSystemService(NotificationManager.class);
final NotificationChannel channel = new NotificationChannel(channelName, name,
@@ -182,7 +180,7 @@
.setOnlyAlertOnce(true)
.setLocalOnly(true)
.setAutoCancel(true)
- .setCategory(Notification.CATEGORY_SYSTEM)
+ .setCategory(category)
.setContentIntent(pendingIntent)
.setVisibility(visibility)
.build();
diff --git a/services/core/java/com/android/server/connectivity/TEST_MAPPING b/services/core/java/com/android/server/connectivity/TEST_MAPPING
new file mode 100644
index 0000000..687d4b0
--- /dev/null
+++ b/services/core/java/com/android/server/connectivity/TEST_MAPPING
@@ -0,0 +1,30 @@
+{
+ "presubmit": [
+ {
+ "name": "FrameworksNetTests",
+ "options": [
+ {
+ "exclude-annotation": "com.android.testutils.SkipPresubmit"
+ }
+ ],
+ "file_patterns": ["Vpn\\.java", "VpnIkeV2Utils\\.java", "VpnProfileStore\\.java"]
+ }
+ ],
+ "presubmit-large": [
+ {
+ "name": "CtsHostsideNetworkTests",
+ "options": [
+ {
+ "exclude-annotation": "androidx.test.filters.FlakyTest"
+ },
+ {
+ "exclude-annotation": "android.platform.test.annotations.FlakyTest"
+ },
+ {
+ "exclude-annotation": "com.android.testutils.SkipPresubmit"
+ }
+ ],
+ "file_patterns": ["Vpn\\.java", "VpnIkeV2Utils\\.java", "VpnProfileStore\\.java"]
+ }
+ ]
+}
\ No newline at end of file
diff --git a/services/core/java/com/android/server/connectivity/Vpn.java b/services/core/java/com/android/server/connectivity/Vpn.java
index cba5039..ff35b19 100644
--- a/services/core/java/com/android/server/connectivity/Vpn.java
+++ b/services/core/java/com/android/server/connectivity/Vpn.java
@@ -3296,13 +3296,6 @@
}
agentConnect(this::onValidationStatus);
return; // Link properties are already sent.
- } else {
- // Underlying networks also set in agentConnect()
- doSetUnderlyingNetworks(networkAgent, Collections.singletonList(network));
- mNetworkCapabilities =
- new NetworkCapabilities.Builder(mNetworkCapabilities)
- .setUnderlyingNetworks(Collections.singletonList(network))
- .build();
}
lp = makeLinkProperties(); // Accesses VPN instance fields; must be locked
@@ -3384,8 +3377,6 @@
final LinkProperties oldLp = makeLinkProperties();
- final boolean underlyingNetworkHasChanged =
- !Arrays.equals(mConfig.underlyingNetworks, new Network[]{network});
mConfig.underlyingNetworks = new Network[] {network};
mConfig.mtu = calculateVpnMtu();
@@ -3417,18 +3408,9 @@
removed.getAddress(), removed.getPrefixLength());
}
} else {
- // Put below 3 updates into else block is because agentConnect() will do
- // those things, so there is no need to do the redundant work.
+ // Put below update into else block is because agentConnect() will do
+ // the same things, so there is no need to do the redundant work.
if (!newLp.equals(oldLp)) doSendLinkProperties(mNetworkAgent, newLp);
- if (underlyingNetworkHasChanged) {
- mNetworkCapabilities =
- new NetworkCapabilities.Builder(mNetworkCapabilities)
- .setUnderlyingNetworks(
- Collections.singletonList(network))
- .build();
- doSetUnderlyingNetworks(mNetworkAgent,
- Collections.singletonList(network));
- }
}
}
@@ -3554,10 +3536,28 @@
*/
private void startOrMigrateIkeSession(@Nullable Network underlyingNetwork) {
if (underlyingNetwork == null) {
+ // For null underlyingNetwork case, there will not be a NetworkAgent available so
+ // no underlying network update is necessary here. Note that updating
+ // mNetworkCapabilities here would also be reasonable, but it will be updated next
+ // time the VPN connects anyway.
Log.d(TAG, "There is no active network for starting an IKE session");
return;
}
+ final List<Network> networks = Collections.singletonList(underlyingNetwork);
+ // Update network capabilities if underlying network is changed.
+ if (!networks.equals(mNetworkCapabilities.getUnderlyingNetworks())) {
+ mNetworkCapabilities =
+ new NetworkCapabilities.Builder(mNetworkCapabilities)
+ .setUnderlyingNetworks(networks)
+ .build();
+ // No NetworkAgent case happens when Vpn tries to start a new VPN. The underlying
+ // network update will be done later with NetworkAgent connected event.
+ if (mNetworkAgent != null) {
+ doSetUnderlyingNetworks(mNetworkAgent, networks);
+ }
+ }
+
if (maybeMigrateIkeSessionAndUpdateVpnTransportInfo(underlyingNetwork)) return;
startIkeSession(underlyingNetwork);
diff --git a/services/core/java/com/android/server/display/DisplayManagerService.java b/services/core/java/com/android/server/display/DisplayManagerService.java
index fe445a6..b994105 100644
--- a/services/core/java/com/android/server/display/DisplayManagerService.java
+++ b/services/core/java/com/android/server/display/DisplayManagerService.java
@@ -21,6 +21,7 @@
import static android.Manifest.permission.CAPTURE_SECURE_VIDEO_OUTPUT;
import static android.Manifest.permission.CAPTURE_VIDEO_OUTPUT;
import static android.Manifest.permission.INTERNAL_SYSTEM_WINDOW;
+import static android.Manifest.permission.MANAGE_DISPLAYS;
import static android.app.ActivityManager.RunningAppProcessInfo.IMPORTANCE_CACHED;
import static android.app.ActivityManager.RunningAppProcessInfo.IMPORTANCE_GONE;
import static android.hardware.display.DisplayManager.EventsMask;
@@ -44,6 +45,7 @@
import static android.os.Process.ROOT_UID;
import android.Manifest;
+import android.annotation.EnforcePermission;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.RequiresPermission;
@@ -152,6 +154,7 @@
import com.android.server.companion.virtual.VirtualDeviceManagerInternal;
import com.android.server.display.DisplayDeviceConfig.SensorData;
import com.android.server.display.feature.DeviceConfigParameterProvider;
+import com.android.server.display.feature.DisplayManagerFlags;
import com.android.server.display.layout.Layout;
import com.android.server.display.mode.DisplayModeDirector;
import com.android.server.display.utils.SensorUtils;
@@ -509,6 +512,8 @@
private final DeviceConfigParameterProvider mConfigParameterProvider;
+ private final DisplayManagerFlags mFlags;
+
/**
* Applications use {@link android.view.Display#getRefreshRate} and
* {@link android.view.Display.Mode#getRefreshRate} to know what is the display refresh rate.
@@ -538,12 +543,13 @@
super(context);
mInjector = injector;
mContext = context;
+ mFlags = injector.getFlags();
mHandler = new DisplayManagerHandler(DisplayThread.get().getLooper());
mUiHandler = UiThread.getHandler();
mDisplayDeviceRepo = new DisplayDeviceRepository(mSyncRoot, mPersistentDataStore);
mLogicalDisplayMapper = new LogicalDisplayMapper(mContext, mDisplayDeviceRepo,
new LogicalDisplayListener(), mSyncRoot, mHandler,
- new FoldSettingWrapper(mContext.getContentResolver()));
+ new FoldSettingWrapper(mContext.getContentResolver()), mFlags);
mDisplayModeDirector = new DisplayModeDirector(context, mHandler);
mBrightnessSynchronizer = new BrightnessSynchronizer(mContext);
Resources resources = mContext.getResources();
@@ -745,6 +751,11 @@
}
@VisibleForTesting
+ LogicalDisplayMapper getLogicalDisplayMapper() {
+ return mLogicalDisplayMapper;
+ }
+
+ @VisibleForTesting
boolean isMinimalPostProcessingAllowed() {
synchronized (mSyncRoot) {
return mMinimalPostProcessingAllowed;
@@ -888,8 +899,14 @@
mDisplayStates.setValueAt(index, state);
brightnessPair.brightness = brightnessState;
brightnessPair.sdrBrightness = sdrBrightnessState;
- runnable = updateDisplayStateLocked(mLogicalDisplayMapper.getDisplayLocked(displayId)
- .getPrimaryDisplayDeviceLocked());
+ // TODO(b/297503094) Preventing disabled display from being turned on should happen
+ // elsewhere.
+ LogicalDisplay display = mLogicalDisplayMapper.getDisplayLocked(displayId);
+ if (!display.isEnabledLocked() && state != Display.STATE_OFF) {
+ // If the display is disabled, any request other than turning it off should fail.
+ return;
+ }
+ runnable = updateDisplayStateLocked(display.getPrimaryDisplayDeviceLocked());
if (Trace.isTagEnabled(Trace.TRACE_TAG_POWER)) {
Trace.asyncTraceForTrackEnd(Trace.TRACE_TAG_POWER,
"requestDisplayStateInternal:" + displayId, displayId);
@@ -1796,7 +1813,17 @@
adapter.registerLocked();
}
- private void handleLogicalDisplayAddedLocked(LogicalDisplay display) {
+ @GuardedBy("mSyncRoot")
+ private void handleLogicalDisplayDisconnectedLocked(LogicalDisplay display) {
+ if (!mFlags.isConnectedDisplayManagementEnabled()) {
+ Slog.e(TAG, "DisplayDisconnected shouldn't be received when the flag is off");
+ return;
+ }
+ releaseDisplayAndEmitEvent(display, DisplayManagerGlobal.EVENT_DISPLAY_DISCONNECTED);
+ }
+
+ @RequiresPermission(Manifest.permission.READ_DEVICE_CONFIG)
+ private void setupLogicalDisplay(LogicalDisplay display) {
final DisplayDevice device = display.getPrimaryDisplayDeviceLocked();
final int displayId = display.getDisplayIdLocked();
final boolean isDefault = displayId == Display.DEFAULT_DISPLAY;
@@ -1814,8 +1841,8 @@
} else {
configurePreferredDisplayModeLocked(display);
}
- DisplayPowerControllerInterface dpc = addDisplayPowerControllerLocked(display);
+ DisplayPowerControllerInterface dpc = addDisplayPowerControllerLocked(display);
if (dpc != null) {
final int leadDisplayId = display.getLeadDisplayIdLocked();
updateDisplayPowerControllerLeaderLocked(dpc, leadDisplayId);
@@ -1840,19 +1867,51 @@
new BrightnessPair(brightnessDefault, brightnessDefault));
DisplayManagerGlobal.invalidateLocalDisplayInfoCaches();
+ }
+
+ private void updateLogicalDisplayState(LogicalDisplay display) {
+ Runnable work = updateDisplayStateLocked(display.getPrimaryDisplayDeviceLocked());
+ if (work != null) {
+ work.run();
+ }
+ scheduleTraversalLocked(false);
+ }
+
+ @SuppressLint("AndroidFrameworkRequiresPermission")
+ private void handleLogicalDisplayConnectedLocked(LogicalDisplay display) {
+ if (!mFlags.isConnectedDisplayManagementEnabled()) {
+ Slog.e(TAG, "DisplayConnected shouldn't be received when the flag is off");
+ return;
+ }
+
+ setupLogicalDisplay(display);
+
+ // TODO(b/292196201) Remove when the display can be disabled before DPC is created.
+ if (display.getDisplayInfoLocked().type == Display.TYPE_EXTERNAL) {
+ display.setEnabledLocked(false);
+ }
+
+ sendDisplayEventLocked(display, DisplayManagerGlobal.EVENT_DISPLAY_CONNECTED);
+
+ updateLogicalDisplayState(display);
+ }
+
+ @SuppressLint("AndroidFrameworkRequiresPermission")
+ private void handleLogicalDisplayAddedLocked(LogicalDisplay display) {
+ final int displayId = display.getDisplayIdLocked();
+ final boolean isDefault = displayId == Display.DEFAULT_DISPLAY;
+ if (!mFlags.isConnectedDisplayManagementEnabled()) {
+ setupLogicalDisplay(display);
+ }
// Wake up waitForDefaultDisplay.
if (isDefault) {
mSyncRoot.notifyAll();
}
- sendDisplayEventLocked(display, DisplayManagerGlobal.EVENT_DISPLAY_ADDED);
+ sendDisplayEventIfEnabledLocked(display, DisplayManagerGlobal.EVENT_DISPLAY_ADDED);
- Runnable work = updateDisplayStateLocked(device);
- if (work != null) {
- work.run();
- }
- scheduleTraversalLocked(false);
+ updateLogicalDisplayState(display);
}
private void handleLogicalDisplayChangedLocked(@NonNull LogicalDisplay display) {
@@ -1865,7 +1924,13 @@
// We don't bother invalidating the display info caches here because any changes to the
// display info will trigger a cache invalidation inside of LogicalDisplay before we hit
// this point.
- sendDisplayEventLocked(display, DisplayManagerGlobal.EVENT_DISPLAY_CHANGED);
+ sendDisplayEventIfEnabledLocked(display, DisplayManagerGlobal.EVENT_DISPLAY_CHANGED);
+
+ applyDisplayChangedLocked(display);
+ }
+
+ private void applyDisplayChangedLocked(@NonNull LogicalDisplay display) {
+ final int displayId = display.getDisplayIdLocked();
scheduleTraversalLocked(false);
mPersistentDataStore.saveIfNeeded();
@@ -1920,7 +1985,28 @@
}
private void handleLogicalDisplayRemovedLocked(@NonNull LogicalDisplay display) {
+ // With display management, the display is removed when disabled, and it might still exist.
+ // Resources must only be released when the disconnected signal is received.
+ if (mFlags.isConnectedDisplayManagementEnabled()) {
+ if (display.isValidLocked()) {
+ updateViewportPowerStateLocked(display);
+ }
+
+ // Note: This method is only called if the display was enabled before being removed.
+ sendDisplayEventLocked(display, DisplayManagerGlobal.EVENT_DISPLAY_REMOVED);
+
+ if (display.isValidLocked()) {
+ applyDisplayChangedLocked(display);
+ }
+ return;
+ }
+
+ releaseDisplayAndEmitEvent(display, DisplayManagerGlobal.EVENT_DISPLAY_REMOVED);
+ }
+
+ private void releaseDisplayAndEmitEvent(LogicalDisplay display, int event) {
final int displayId = display.getDisplayIdLocked();
+
final DisplayPowerControllerInterface dpc =
mDisplayPowerControllers.removeReturnOld(displayId);
if (dpc != null) {
@@ -1930,12 +2016,13 @@
mDisplayStates.delete(displayId);
mDisplayBrightnesses.delete(displayId);
DisplayManagerGlobal.invalidateLocalDisplayInfoCaches();
- sendDisplayEventLocked(display, DisplayManagerGlobal.EVENT_DISPLAY_REMOVED);
+
+ sendDisplayEventLocked(display, event);
scheduleTraversalLocked(false);
if (mDisplayWindowPolicyControllers.contains(displayId)) {
- final IVirtualDevice virtualDevice = mDisplayWindowPolicyControllers.removeReturnOld(
- displayId).first;
+ final IVirtualDevice virtualDevice =
+ mDisplayWindowPolicyControllers.removeReturnOld(displayId).first;
if (virtualDevice != null) {
mHandler.post(() -> {
getLocalService(VirtualDeviceManagerInternal.class)
@@ -1956,7 +2043,8 @@
}
private void handleLogicalDisplayHdrSdrRatioChangedLocked(@NonNull LogicalDisplay display) {
- sendDisplayEventLocked(display, DisplayManagerGlobal.EVENT_DISPLAY_HDR_SDR_RATIO_CHANGED);
+ sendDisplayEventIfEnabledLocked(display,
+ DisplayManagerGlobal.EVENT_DISPLAY_HDR_SDR_RATIO_CHANGED);
}
private void notifyDefaultDisplayDeviceUpdated(LogicalDisplay display) {
@@ -2812,15 +2900,21 @@
}
}
- private void sendDisplayEventLocked(@NonNull LogicalDisplay display, @DisplayEvent int event) {
+ // Send a display event if the display is enabled
+ private void sendDisplayEventIfEnabledLocked(@NonNull LogicalDisplay display,
+ @DisplayEvent int event) {
// Only send updates outside of DisplayManagerService for enabled displays
if (display.isEnabledLocked()) {
- int displayId = display.getDisplayIdLocked();
- Message msg = mHandler.obtainMessage(MSG_DELIVER_DISPLAY_EVENT, displayId, event);
- mHandler.sendMessage(msg);
+ sendDisplayEventLocked(display, event);
}
}
+ private void sendDisplayEventLocked(@NonNull LogicalDisplay display, @DisplayEvent int event) {
+ int displayId = display.getDisplayIdLocked();
+ Message msg = mHandler.obtainMessage(MSG_DELIVER_DISPLAY_EVENT, displayId, event);
+ mHandler.sendMessage(msg);
+ }
+
private void sendDisplayGroupEvent(int groupId, int event) {
Message msg = mHandler.obtainMessage(MSG_DELIVER_DISPLAY_GROUP_EVENT, groupId, event);
mHandler.sendMessage(msg);
@@ -3051,6 +3145,12 @@
&& mode.getRefreshRate() > 0.0f;
}
+ void enableConnectedDisplay(int displayId, boolean enabled) {
+ synchronized (mSyncRoot) {
+ mLogicalDisplayMapper.setDisplayEnabledLocked(displayId, enabled);
+ }
+ }
+
/**
* This is the object that everything in the display manager locks on.
* We make it an inner class within the {@link DisplayManagerService} to so that it is
@@ -3068,7 +3168,8 @@
}
LocalDisplayAdapter getLocalDisplayAdapter(SyncRoot syncRoot, Context context,
- Handler handler, DisplayAdapter.Listener displayAdapterListener) {
+ Handler handler,
+ DisplayAdapter.Listener displayAdapterListener) {
return new LocalDisplayAdapter(syncRoot, context, handler, displayAdapterListener);
}
@@ -3098,6 +3199,10 @@
IBinder b = ServiceManager.getService(Context.MEDIA_PROJECTION_SERVICE);
return IMediaProjectionManager.Stub.asInterface(b);
}
+
+ DisplayManagerFlags getFlags() {
+ return new DisplayManagerFlags();
+ }
}
@VisibleForTesting
@@ -3167,7 +3272,8 @@
private void handleBrightnessChange(LogicalDisplay display) {
synchronized (mSyncRoot) {
- sendDisplayEventLocked(display, DisplayManagerGlobal.EVENT_DISPLAY_BRIGHTNESS_CHANGED);
+ sendDisplayEventIfEnabledLocked(display,
+ DisplayManagerGlobal.EVENT_DISPLAY_BRIGHTNESS_CHANGED);
}
}
@@ -3261,6 +3367,8 @@
}
private final class LogicalDisplayListener implements LogicalDisplayMapper.Listener {
+
+ @GuardedBy("mSyncRoot")
@Override
public void onLogicalDisplayEventLocked(LogicalDisplay display, int event) {
switch (event) {
@@ -3291,6 +3399,14 @@
case LogicalDisplayMapper.LOGICAL_DISPLAY_EVENT_HDR_SDR_RATIO_CHANGED:
handleLogicalDisplayHdrSdrRatioChangedLocked(display);
break;
+
+ case LogicalDisplayMapper.LOGICAL_DISPLAY_EVENT_CONNECTED:
+ handleLogicalDisplayConnectedLocked(display);
+ break;
+
+ case LogicalDisplayMapper.LOGICAL_DISPLAY_EVENT_DISCONNECTED:
+ handleLogicalDisplayDisconnectedLocked(display);
+ break;
}
}
@@ -3367,6 +3483,10 @@
return (mask & DisplayManager.EVENT_FLAG_DISPLAY_REMOVED) != 0;
case DisplayManagerGlobal.EVENT_DISPLAY_HDR_SDR_RATIO_CHANGED:
return (mask & DisplayManager.EVENT_FLAG_HDR_SDR_RATIO_CHANGED) != 0;
+ case DisplayManagerGlobal.EVENT_DISPLAY_CONNECTED:
+ // fallthrough
+ case DisplayManagerGlobal.EVENT_DISPLAY_DISCONNECTED:
+ return (mask & DisplayManager.EVENT_FLAG_DISPLAY_CONNECTION_CHANGED) != 0;
default:
// This should never happen.
Slog.e(TAG, "Unknown display event " + event);
@@ -3484,6 +3604,7 @@
}
@Override // Binder call
+ @SuppressLint("AndroidFrameworkRequiresPermission") // Permission only required sometimes
public void registerCallbackWithEventMask(IDisplayManagerCallback callback,
@EventsMask long eventsMask) {
if (callback == null) {
@@ -3492,6 +3613,14 @@
final int callingPid = Binder.getCallingPid();
final int callingUid = Binder.getCallingUid();
+
+ if (mFlags.isConnectedDisplayManagementEnabled()) {
+ if ((eventsMask & DisplayManager.EVENT_FLAG_DISPLAY_CONNECTION_CHANGED) != 0) {
+ mContext.enforceCallingOrSelfPermission(MANAGE_DISPLAYS,
+ "Permission required to get signals about connection events.");
+ }
+ }
+
final long token = Binder.clearCallingIdentity();
try {
registerCallbackInternal(callback, callingPid, callingUid, eventsMask);
@@ -3500,7 +3629,7 @@
}
}
- @android.annotation.EnforcePermission(android.Manifest.permission.CONFIGURE_WIFI_DISPLAY)
+ @EnforcePermission(android.Manifest.permission.CONFIGURE_WIFI_DISPLAY)
@Override // Binder call
public void startWifiDisplayScan() {
startWifiDisplayScan_enforcePermission();
@@ -3514,7 +3643,7 @@
}
}
- @android.annotation.EnforcePermission(android.Manifest.permission.CONFIGURE_WIFI_DISPLAY)
+ @EnforcePermission(android.Manifest.permission.CONFIGURE_WIFI_DISPLAY)
@Override // Binder call
public void stopWifiDisplayScan() {
stopWifiDisplayScan_enforcePermission();
@@ -3591,7 +3720,7 @@
}
}
- @android.annotation.EnforcePermission(android.Manifest.permission.CONFIGURE_WIFI_DISPLAY)
+ @EnforcePermission(android.Manifest.permission.CONFIGURE_WIFI_DISPLAY)
@Override // Binder call
public void pauseWifiDisplay() {
pauseWifiDisplay_enforcePermission();
@@ -3604,7 +3733,7 @@
}
}
- @android.annotation.EnforcePermission(android.Manifest.permission.CONFIGURE_WIFI_DISPLAY)
+ @EnforcePermission(android.Manifest.permission.CONFIGURE_WIFI_DISPLAY)
@Override // Binder call
public void resumeWifiDisplay() {
resumeWifiDisplay_enforcePermission();
@@ -3630,7 +3759,7 @@
}
}
- @android.annotation.EnforcePermission(android.Manifest.permission.WRITE_SECURE_SETTINGS)
+ @EnforcePermission(android.Manifest.permission.WRITE_SECURE_SETTINGS)
@Override // Binder call
public void setUserDisabledHdrTypes(int[] userDisabledFormats) {
setUserDisabledHdrTypes_enforcePermission();
@@ -3656,7 +3785,7 @@
DisplayControl.overrideHdrTypes(displayToken, modes);
}
- @android.annotation.EnforcePermission(android.Manifest.permission.WRITE_SECURE_SETTINGS)
+ @EnforcePermission(android.Manifest.permission.WRITE_SECURE_SETTINGS)
@Override // Binder call
public void setAreUserDisabledHdrTypesAllowed(boolean areUserDisabledHdrTypesAllowed) {
setAreUserDisabledHdrTypesAllowed_enforcePermission();
@@ -3682,7 +3811,7 @@
}
}
- @android.annotation.EnforcePermission(android.Manifest.permission.CONFIGURE_DISPLAY_COLOR_MODE)
+ @EnforcePermission(android.Manifest.permission.CONFIGURE_DISPLAY_COLOR_MODE)
@Override // Binder call
public void requestColorMode(int displayId, int colorMode) {
requestColorMode_enforcePermission();
@@ -3762,7 +3891,7 @@
}
}
- @android.annotation.EnforcePermission(android.Manifest.permission.BRIGHTNESS_SLIDER_USAGE)
+ @EnforcePermission(android.Manifest.permission.BRIGHTNESS_SLIDER_USAGE)
@Override // Binder call
public ParceledListSlice<BrightnessChangeEvent> getBrightnessEvents(String callingPackage) {
getBrightnessEvents_enforcePermission();
@@ -3794,7 +3923,7 @@
}
}
- @android.annotation.EnforcePermission(android.Manifest.permission.ACCESS_AMBIENT_LIGHT_STATS)
+ @EnforcePermission(android.Manifest.permission.ACCESS_AMBIENT_LIGHT_STATS)
@Override // Binder call
public ParceledListSlice<AmbientBrightnessDayStats> getAmbientBrightnessStats() {
getAmbientBrightnessStats_enforcePermission();
@@ -3811,7 +3940,7 @@
}
}
- @android.annotation.EnforcePermission(android.Manifest.permission.CONFIGURE_DISPLAY_BRIGHTNESS)
+ @EnforcePermission(android.Manifest.permission.CONFIGURE_DISPLAY_BRIGHTNESS)
@Override // Binder call
public void setBrightnessConfigurationForUser(
BrightnessConfiguration c, @UserIdInt int userId, String packageName) {
@@ -3840,7 +3969,7 @@
}
}
- @android.annotation.EnforcePermission(android.Manifest.permission.CONFIGURE_DISPLAY_BRIGHTNESS)
+ @EnforcePermission(android.Manifest.permission.CONFIGURE_DISPLAY_BRIGHTNESS)
@Override // Binder call
public void setBrightnessConfigurationForDisplay(BrightnessConfiguration c,
String uniqueId, int userId, String packageName) {
@@ -3859,7 +3988,7 @@
}
}
- @android.annotation.EnforcePermission(android.Manifest.permission.CONFIGURE_DISPLAY_BRIGHTNESS)
+ @EnforcePermission(android.Manifest.permission.CONFIGURE_DISPLAY_BRIGHTNESS)
@Override // Binder call
public BrightnessConfiguration getBrightnessConfigurationForDisplay(String uniqueId,
int userId) {
@@ -3907,7 +4036,7 @@
}
- @android.annotation.EnforcePermission(android.Manifest.permission.CONFIGURE_DISPLAY_BRIGHTNESS)
+ @EnforcePermission(android.Manifest.permission.CONFIGURE_DISPLAY_BRIGHTNESS)
@Override // Binder call
public BrightnessConfiguration getDefaultBrightnessConfiguration() {
getDefaultBrightnessConfiguration_enforcePermission();
@@ -3922,7 +4051,7 @@
}
}
- @android.annotation.EnforcePermission(android.Manifest.permission.CONTROL_DISPLAY_BRIGHTNESS)
+ @EnforcePermission(android.Manifest.permission.CONTROL_DISPLAY_BRIGHTNESS)
@Override
public BrightnessInfo getBrightnessInfo(int displayId) {
getBrightnessInfo_enforcePermission();
@@ -3953,7 +4082,7 @@
}
}
- @android.annotation.EnforcePermission(android.Manifest.permission.CONTROL_DISPLAY_BRIGHTNESS)
+ @EnforcePermission(android.Manifest.permission.CONTROL_DISPLAY_BRIGHTNESS)
@Override // Binder call
public void setTemporaryBrightness(int displayId, float brightness) {
setTemporaryBrightness_enforcePermission();
@@ -3968,7 +4097,7 @@
}
}
- @android.annotation.EnforcePermission(android.Manifest.permission.CONTROL_DISPLAY_BRIGHTNESS)
+ @EnforcePermission(android.Manifest.permission.CONTROL_DISPLAY_BRIGHTNESS)
@Override // Binder call
public void setBrightness(int displayId, float brightness) {
setBrightness_enforcePermission();
@@ -4010,7 +4139,7 @@
return brightness;
}
- @android.annotation.EnforcePermission(android.Manifest.permission.CONTROL_DISPLAY_BRIGHTNESS)
+ @EnforcePermission(android.Manifest.permission.CONTROL_DISPLAY_BRIGHTNESS)
@Override // Binder call
public void setTemporaryAutoBrightnessAdjustment(float adjustment) {
setTemporaryAutoBrightnessAdjustment_enforcePermission();
@@ -4029,8 +4158,8 @@
public void onShellCommand(FileDescriptor in, FileDescriptor out,
FileDescriptor err, String[] args, ShellCallback callback,
ResultReceiver resultReceiver) {
- new DisplayManagerShellCommand(DisplayManagerService.this).exec(this, in, out, err,
- args, callback, resultReceiver);
+ new DisplayManagerShellCommand(DisplayManagerService.this, mFlags).exec(this, in, out,
+ err, args, callback, resultReceiver);
}
@Override // Binder call
@@ -4053,7 +4182,7 @@
}
}
- @android.annotation.EnforcePermission(android.Manifest.permission.MODIFY_USER_PREFERRED_DISPLAY_MODE)
+ @EnforcePermission(android.Manifest.permission.MODIFY_USER_PREFERRED_DISPLAY_MODE)
@Override // Binder call
public void setUserPreferredDisplayMode(int displayId, Display.Mode mode) {
setUserPreferredDisplayMode_enforcePermission();
@@ -4141,7 +4270,7 @@
}
}
- @android.annotation.EnforcePermission(android.Manifest.permission.OVERRIDE_DISPLAY_MODE_REQUESTS)
+ @EnforcePermission(android.Manifest.permission.OVERRIDE_DISPLAY_MODE_REQUESTS)
@Override // Binder call
public void setShouldAlwaysRespectAppRequestedMode(boolean enabled) {
setShouldAlwaysRespectAppRequestedMode_enforcePermission();
@@ -4153,7 +4282,7 @@
}
}
- @android.annotation.EnforcePermission(android.Manifest.permission.OVERRIDE_DISPLAY_MODE_REQUESTS)
+ @EnforcePermission(android.Manifest.permission.OVERRIDE_DISPLAY_MODE_REQUESTS)
@Override // Binder call
public boolean shouldAlwaysRespectAppRequestedMode() {
shouldAlwaysRespectAppRequestedMode_enforcePermission();
@@ -4165,7 +4294,7 @@
}
}
- @android.annotation.EnforcePermission(android.Manifest.permission.MODIFY_REFRESH_RATE_SWITCHING_TYPE)
+ @EnforcePermission(android.Manifest.permission.MODIFY_REFRESH_RATE_SWITCHING_TYPE)
@Override // Binder call
public void setRefreshRateSwitchingType(int newValue) {
setRefreshRateSwitchingType_enforcePermission();
@@ -4217,6 +4346,28 @@
Binder.restoreCallingIdentity(token);
}
}
+
+ @EnforcePermission(MANAGE_DISPLAYS)
+ public void enableConnectedDisplay(int displayId) {
+ enableConnectedDisplay_enforcePermission();
+ if (!mFlags.isConnectedDisplayManagementEnabled()) {
+ Slog.w(TAG, "External display management is not enabled on your device: "
+ + "cannot enable display.");
+ return;
+ }
+ DisplayManagerService.this.enableConnectedDisplay(displayId, true);
+ }
+
+ @EnforcePermission(MANAGE_DISPLAYS)
+ public void disableConnectedDisplay(int displayId) {
+ disableConnectedDisplay_enforcePermission();
+ if (!mFlags.isConnectedDisplayManagementEnabled()) {
+ Slog.w(TAG, "External display management is not enabled on your device: "
+ + "cannot disable display.");
+ return;
+ }
+ DisplayManagerService.this.enableConnectedDisplay(displayId, false);
+ }
}
private static boolean isValidBrightness(float brightness) {
diff --git a/services/core/java/com/android/server/display/DisplayManagerShellCommand.java b/services/core/java/com/android/server/display/DisplayManagerShellCommand.java
index a4f4954..9b022d8 100644
--- a/services/core/java/com/android/server/display/DisplayManagerShellCommand.java
+++ b/services/core/java/com/android/server/display/DisplayManagerShellCommand.java
@@ -23,6 +23,8 @@
import android.util.Slog;
import android.view.Display;
+import com.android.server.display.feature.DisplayManagerFlags;
+
import java.io.PrintWriter;
import java.util.Arrays;
@@ -30,9 +32,11 @@
private static final String TAG = "DisplayManagerShellCommand";
private final DisplayManagerService mService;
+ private final DisplayManagerFlags mFlags;
- DisplayManagerShellCommand(DisplayManagerService service) {
+ DisplayManagerShellCommand(DisplayManagerService service, DisplayManagerFlags flags) {
mService = service;
+ mFlags = flags;
}
@Override
@@ -82,6 +86,10 @@
return setDockedAndIdle();
case "undock":
return unsetDockedAndIdle();
+ case "enable-display":
+ return setDisplayEnabled(true);
+ case "disable-display":
+ return setDisplayEnabled(false);
default:
return handleDefaultCommands(cmd);
}
@@ -142,6 +150,12 @@
pw.println(" Sets brightness to docked + idle screen brightness mode");
pw.println(" undock");
pw.println(" Sets brightness to active (normal) screen brightness mode");
+ if (mFlags.isConnectedDisplayManagementEnabled()) {
+ pw.println(" enable-display DISPLAY_ID");
+ pw.println(" Enable the DISPLAY_ID. Only possible if this is a connected display.");
+ pw.println(" disable-display DISPLAY_ID");
+ pw.println(" Disable the DISPLAY_ID. Only possible if this is a connected display.");
+ }
pw.println();
Intent.printIntentArgsHelp(pw , "");
}
@@ -423,4 +437,26 @@
mService.setDockedAndIdleEnabled(false, Display.DEFAULT_DISPLAY);
return 0;
}
+
+ private int setDisplayEnabled(boolean enable) {
+ if (!mFlags.isConnectedDisplayManagementEnabled()) {
+ getErrPrintWriter()
+ .println("Error: external display management is not available on this device.");
+ return 1;
+ }
+ final String displayIdText = getNextArg();
+ if (displayIdText == null) {
+ getErrPrintWriter().println("Error: no displayId specified");
+ return 1;
+ }
+ final int displayId;
+ try {
+ displayId = Integer.parseInt(displayIdText);
+ } catch (NumberFormatException e) {
+ getErrPrintWriter().println("Error: invalid displayId: '" + displayIdText + "'");
+ return 1;
+ }
+ mService.enableConnectedDisplay(displayId, enable);
+ return 0;
+ }
}
diff --git a/services/core/java/com/android/server/display/HighBrightnessModeController.java b/services/core/java/com/android/server/display/HighBrightnessModeController.java
index c04c279..39172b8 100644
--- a/services/core/java/com/android/server/display/HighBrightnessModeController.java
+++ b/services/core/java/com/android/server/display/HighBrightnessModeController.java
@@ -25,6 +25,7 @@
import android.os.IBinder;
import android.os.PowerManager;
import android.os.SystemClock;
+import android.os.Trace;
import android.os.UserHandle;
import android.provider.Settings;
import android.util.MathUtils;
@@ -33,6 +34,7 @@
import android.view.SurfaceControlHdrLayerInfoListener;
import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.display.BrightnessSynchronizer;
import com.android.internal.util.FrameworkStatsLog;
import com.android.server.display.DisplayDeviceConfig.HighBrightnessModeData;
import com.android.server.display.DisplayManagerService.Clock;
@@ -99,6 +101,7 @@
private boolean mIsHdrLayerPresent = false;
// mMaxDesiredHdrSdrRatio should only be applied when there is a valid backlight->nits mapping
private float mMaxDesiredHdrSdrRatio = DEFAULT_MAX_DESIRED_HDR_SDR_RATIO;
+ private boolean mForceHbmChangeCallback = false;
private boolean mIsBlockedByLowPowerMode = false;
private int mWidth;
private int mHeight;
@@ -484,7 +487,8 @@
private void updateHbmMode() {
int newHbmMode = calculateHighBrightnessMode();
updateHbmStats(newHbmMode);
- if (mHbmMode != newHbmMode) {
+ if (mHbmMode != newHbmMode || mForceHbmChangeCallback) {
+ mForceHbmChangeCallback = false;
mHbmMode = newHbmMode;
mHbmChangeCallback.run();
}
@@ -600,26 +604,32 @@
public void onHdrInfoChanged(IBinder displayToken, int numberOfHdrLayers,
int maxW, int maxH, int flags, float maxDesiredHdrSdrRatio) {
mHandler.post(() -> {
+ Trace.traceBegin(Trace.TRACE_TAG_POWER, "HBMController#onHdrInfoChanged");
mIsHdrLayerPresent = numberOfHdrLayers > 0
&& (float) (maxW * maxH) >= ((float) (mWidth * mHeight)
* mHbmData.minimumHdrPercentOfScreen);
- final float candidateDesiredHdrSdrRatio =
+ float candidateDesiredHdrSdrRatio =
mIsHdrLayerPresent && mHdrBrightnessCfg != null
? maxDesiredHdrSdrRatio
: DEFAULT_MAX_DESIRED_HDR_SDR_RATIO;
- if (candidateDesiredHdrSdrRatio >= 1.0f) {
- mMaxDesiredHdrSdrRatio = candidateDesiredHdrSdrRatio;
- } else {
+ if (candidateDesiredHdrSdrRatio < 1.0f) {
Slog.w(TAG, "Ignoring invalid desired HDR/SDR Ratio: "
+ candidateDesiredHdrSdrRatio);
- mMaxDesiredHdrSdrRatio = DEFAULT_MAX_DESIRED_HDR_SDR_RATIO;
+ candidateDesiredHdrSdrRatio = DEFAULT_MAX_DESIRED_HDR_SDR_RATIO;
+ }
+
+ if (!BrightnessSynchronizer.floatEquals(
+ mMaxDesiredHdrSdrRatio, candidateDesiredHdrSdrRatio)) {
+ mForceHbmChangeCallback = true;
+ mMaxDesiredHdrSdrRatio = candidateDesiredHdrSdrRatio;
}
// Calling the brightness update so that we can recalculate
// brightness with HDR in mind.
onBrightnessChanged(mBrightness, mUnthrottledBrightness, mThrottlingReason);
+ Trace.traceEnd(Trace.TRACE_TAG_POWER);
});
}
}
diff --git a/services/core/java/com/android/server/display/LocalDisplayAdapter.java b/services/core/java/com/android/server/display/LocalDisplayAdapter.java
index b00b7a1..924b1b3 100644
--- a/services/core/java/com/android/server/display/LocalDisplayAdapter.java
+++ b/services/core/java/com/android/server/display/LocalDisplayAdapter.java
@@ -83,14 +83,14 @@
private Context mOverlayContext;
// Called with SyncRoot lock held.
- public LocalDisplayAdapter(DisplayManagerService.SyncRoot syncRoot,
- Context context, Handler handler, Listener listener) {
+ LocalDisplayAdapter(DisplayManagerService.SyncRoot syncRoot, Context context,
+ Handler handler, Listener listener) {
this(syncRoot, context, handler, listener, new Injector());
}
@VisibleForTesting
- LocalDisplayAdapter(DisplayManagerService.SyncRoot syncRoot,
- Context context, Handler handler, Listener listener, Injector injector) {
+ LocalDisplayAdapter(DisplayManagerService.SyncRoot syncRoot, Context context, Handler handler,
+ Listener listener, Injector injector) {
super(syncRoot, context, handler, listener, TAG);
mInjector = injector;
mSurfaceControlProxy = mInjector.getSurfaceControlProxy();
@@ -231,7 +231,6 @@
private SurfaceControl.DisplayMode mActiveSfDisplayMode;
// The active display vsync period in SurfaceFlinger
private float mActiveRenderFrameRate;
-
private DisplayEventReceiver.FrameRateOverride[] mFrameRateOverrides =
new DisplayEventReceiver.FrameRateOverride[0];
diff --git a/services/core/java/com/android/server/display/LogicalDisplay.java b/services/core/java/com/android/server/display/LogicalDisplay.java
index 9c271ff5..0405ebe 100644
--- a/services/core/java/com/android/server/display/LogicalDisplay.java
+++ b/services/core/java/com/android/server/display/LogicalDisplay.java
@@ -193,7 +193,7 @@
private SparseArray<SurfaceControl.RefreshRateRange> mThermalRefreshRateThrottling =
new SparseArray<>();
- public LogicalDisplay(int displayId, int layerStack, DisplayDevice primaryDisplayDevice) {
+ LogicalDisplay(int displayId, int layerStack, DisplayDevice primaryDisplayDevice) {
mDisplayId = displayId;
mLayerStack = layerStack;
mPrimaryDisplayDevice = primaryDisplayDevice;
@@ -872,7 +872,10 @@
* @param enabled True if enabled, false otherwise.
*/
public void setEnabledLocked(boolean enabled) {
- mIsEnabled = enabled;
+ if (enabled != mIsEnabled) {
+ mDirty = true;
+ mIsEnabled = enabled;
+ }
}
/**
diff --git a/services/core/java/com/android/server/display/LogicalDisplayMapper.java b/services/core/java/com/android/server/display/LogicalDisplayMapper.java
index 26f8029..cbe0fc7 100644
--- a/services/core/java/com/android/server/display/LogicalDisplayMapper.java
+++ b/services/core/java/com/android/server/display/LogicalDisplayMapper.java
@@ -40,6 +40,7 @@
import android.view.DisplayInfo;
import com.android.internal.annotations.VisibleForTesting;
+import com.android.server.display.feature.DisplayManagerFlags;
import com.android.server.display.layout.DisplayIdProducer;
import com.android.server.display.layout.Layout;
import com.android.server.utils.FoldSettingWrapper;
@@ -71,6 +72,8 @@
public static final int LOGICAL_DISPLAY_EVENT_FRAME_RATE_OVERRIDES_CHANGED = 5;
public static final int LOGICAL_DISPLAY_EVENT_DEVICE_STATE_TRANSITION = 6;
public static final int LOGICAL_DISPLAY_EVENT_HDR_SDR_RATIO_CHANGED = 7;
+ public static final int LOGICAL_DISPLAY_EVENT_CONNECTED = 8;
+ public static final int LOGICAL_DISPLAY_EVENT_DISCONNECTED = 9;
public static final int DISPLAY_GROUP_EVENT_ADDED = 1;
public static final int DISPLAY_GROUP_EVENT_CHANGED = 2;
@@ -124,6 +127,9 @@
private final SparseArray<LogicalDisplay> mLogicalDisplays =
new SparseArray<LogicalDisplay>();
+ // Cache whether or not the display was enabled on the last update.
+ private final SparseBooleanArray mDisplaysEnabledCache = new SparseBooleanArray();
+
/** Map of all display groups indexed by display group id. */
private final SparseArray<DisplayGroup> mDisplayGroups = new SparseArray<>();
@@ -188,19 +194,21 @@
private int mDeviceStateToBeAppliedAfterBoot = DeviceStateManager.INVALID_DEVICE_STATE;
private boolean mBootCompleted = false;
private boolean mInteractive;
+ private final DisplayManagerFlags mFlags;
LogicalDisplayMapper(@NonNull Context context, @NonNull DisplayDeviceRepository repo,
@NonNull Listener listener, @NonNull DisplayManagerService.SyncRoot syncRoot,
- @NonNull Handler handler, FoldSettingWrapper foldSettingWrapper) {
- this(context, repo, listener, syncRoot, handler,
- new DeviceStateToLayoutMap((isDefault) -> isDefault ? DEFAULT_DISPLAY
- : sNextNonDefaultDisplayId++), foldSettingWrapper);
+ @NonNull Handler handler, FoldSettingWrapper foldSettingWrapper,
+ DisplayManagerFlags flags) {
+ this(context, repo, listener, syncRoot, handler, new DeviceStateToLayoutMap(
+ (isDefault) -> isDefault ? DEFAULT_DISPLAY : sNextNonDefaultDisplayId++),
+ foldSettingWrapper, flags);
}
LogicalDisplayMapper(@NonNull Context context, @NonNull DisplayDeviceRepository repo,
@NonNull Listener listener, @NonNull DisplayManagerService.SyncRoot syncRoot,
@NonNull Handler handler, @NonNull DeviceStateToLayoutMap deviceStateToLayoutMap,
- FoldSettingWrapper foldSettingWrapper) {
+ FoldSettingWrapper foldSettingWrapper, DisplayManagerFlags flags) {
mSyncRoot = syncRoot;
mPowerManager = context.getSystemService(PowerManager.class);
mInteractive = mPowerManager.isInteractive();
@@ -217,6 +225,7 @@
com.android.internal.R.array.config_deviceStatesOnWhichToSleep));
mDisplayDeviceRepo.addListener(this);
mDeviceStateToLayoutMap = deviceStateToLayoutMap;
+ mFlags = flags;
}
@Override
@@ -664,10 +673,21 @@
}
}
- private void updateLogicalDisplaysLocked() {
+ @VisibleForTesting
+ void updateLogicalDisplays() {
+ synchronized (mSyncRoot) {
+ updateLogicalDisplaysLocked();
+ }
+ }
+
+ void updateLogicalDisplaysLocked() {
updateLogicalDisplaysLocked(DisplayDeviceInfo.DIFF_EVERYTHING);
}
+ private void updateLogicalDisplaysLocked(int diff) {
+ updateLogicalDisplaysLocked(diff, /* isSecondLoop= */ false);
+ }
+
/**
* Updates the rest of the display system once all the changes are applied for display
* devices and logical displays. The includes releasing invalid/empty LogicalDisplays,
@@ -676,8 +696,10 @@
*
* @param diff The DisplayDeviceInfo.DIFF_* of what actually changed to enable finer-grained
* display update listeners
+ * @param isSecondLoop If true, this is the second time this is called for the same change.
*/
- private void updateLogicalDisplaysLocked(int diff) {
+ private void updateLogicalDisplaysLocked(int diff, boolean isSecondLoop) {
+ boolean reloop = false;
// Go through all the displays and figure out if they need to be updated.
// Loops in reverse so that displays can be removed during the loop without affecting the
// rest of the loop.
@@ -694,11 +716,11 @@
final DisplayInfo newDisplayInfo = display.getDisplayInfoLocked();
final int updateState = mUpdatedLogicalDisplays.get(displayId, UPDATE_STATE_NEW);
final boolean wasPreviouslyUpdated = updateState != UPDATE_STATE_NEW;
+ final boolean wasPreviouslyEnabled = mDisplaysEnabledCache.get(displayId);
+ final boolean isCurrentlyEnabled = display.isEnabledLocked();
// The display is no longer valid and needs to be removed.
if (!display.isValidLocked()) {
- mUpdatedLogicalDisplays.delete(displayId);
-
// Remove from group
final DisplayGroup displayGroup = getDisplayGroupLocked(
getDisplayGroupIdFromDisplayIdLocked(displayId));
@@ -709,8 +731,20 @@
if (wasPreviouslyUpdated) {
// The display isn't actually removed from our internal data structures until
// after the notification is sent; see {@link #sendUpdatesForDisplaysLocked}.
- Slog.i(TAG, "Removing display: " + displayId);
- mLogicalDisplaysToUpdate.put(displayId, LOGICAL_DISPLAY_EVENT_REMOVED);
+ if (mFlags.isConnectedDisplayManagementEnabled()) {
+ if (mDisplaysEnabledCache.get(displayId)) {
+ // We still need to send LOGICAL_DISPLAY_EVENT_DISCONNECTED
+ reloop = true;
+ mLogicalDisplaysToUpdate.put(displayId, LOGICAL_DISPLAY_EVENT_REMOVED);
+ } else {
+ mUpdatedLogicalDisplays.delete(displayId);
+ mLogicalDisplaysToUpdate.put(displayId,
+ LOGICAL_DISPLAY_EVENT_DISCONNECTED);
+ }
+ } else {
+ mUpdatedLogicalDisplays.delete(displayId);
+ mLogicalDisplaysToUpdate.put(displayId, LOGICAL_DISPLAY_EVENT_REMOVED);
+ }
} else {
// This display never left this class, safe to remove without notification
mLogicalDisplays.removeAt(i);
@@ -719,14 +753,23 @@
// The display is new.
} else if (!wasPreviouslyUpdated) {
- Slog.i(TAG, "Adding new display: " + displayId + ": " + newDisplayInfo);
- mLogicalDisplaysToUpdate.put(displayId, LOGICAL_DISPLAY_EVENT_ADDED);
-
+ if (mFlags.isConnectedDisplayManagementEnabled()) {
+ // We still need to send LOGICAL_DISPLAY_EVENT_ADDED
+ reloop = true;
+ mLogicalDisplaysToUpdate.put(displayId, LOGICAL_DISPLAY_EVENT_CONNECTED);
+ } else {
+ mLogicalDisplaysToUpdate.put(displayId, LOGICAL_DISPLAY_EVENT_ADDED);
+ }
// Underlying displays device has changed to a different one.
} else if (!TextUtils.equals(mTempDisplayInfo.uniqueId, newDisplayInfo.uniqueId)) {
mLogicalDisplaysToUpdate.put(displayId, LOGICAL_DISPLAY_EVENT_SWAPPED);
// Something about the display device has changed.
+ } else if (mFlags.isConnectedDisplayManagementEnabled()
+ && wasPreviouslyEnabled != isCurrentlyEnabled) {
+ int event = isCurrentlyEnabled ? LOGICAL_DISPLAY_EVENT_ADDED :
+ LOGICAL_DISPLAY_EVENT_REMOVED;
+ mLogicalDisplaysToUpdate.put(displayId, event);
} else if (wasDirty || !mTempDisplayInfo.equals(newDisplayInfo)) {
// If only the hdr/sdr ratio changed, then send just the event for that case
if ((diff == DisplayDeviceInfo.DIFF_HDR_SDR_RATIO)) {
@@ -789,9 +832,15 @@
sendUpdatesForDisplaysLocked(LOGICAL_DISPLAY_EVENT_DEVICE_STATE_TRANSITION);
sendUpdatesForGroupsLocked(DISPLAY_GROUP_EVENT_ADDED);
sendUpdatesForDisplaysLocked(LOGICAL_DISPLAY_EVENT_REMOVED);
+ if (mFlags.isConnectedDisplayManagementEnabled()) {
+ sendUpdatesForDisplaysLocked(LOGICAL_DISPLAY_EVENT_DISCONNECTED);
+ }
sendUpdatesForDisplaysLocked(LOGICAL_DISPLAY_EVENT_CHANGED);
sendUpdatesForDisplaysLocked(LOGICAL_DISPLAY_EVENT_FRAME_RATE_OVERRIDES_CHANGED);
sendUpdatesForDisplaysLocked(LOGICAL_DISPLAY_EVENT_SWAPPED);
+ if (mFlags.isConnectedDisplayManagementEnabled()) {
+ sendUpdatesForDisplaysLocked(LOGICAL_DISPLAY_EVENT_CONNECTED);
+ }
sendUpdatesForDisplaysLocked(LOGICAL_DISPLAY_EVENT_ADDED);
sendUpdatesForDisplaysLocked(LOGICAL_DISPLAY_EVENT_HDR_SDR_RATIO_CHANGED);
sendUpdatesForGroupsLocked(DISPLAY_GROUP_EVENT_CHANGED);
@@ -799,6 +848,14 @@
mLogicalDisplaysToUpdate.clear();
mDisplayGroupsToUpdate.clear();
+
+ if (reloop) {
+ if (isSecondLoop) {
+ Slog.wtf(TAG, "Trying to loop a third time");
+ return;
+ }
+ updateLogicalDisplaysLocked(diff, /* isSecondLoop= */ true);
+ }
}
/**
@@ -819,8 +876,22 @@
Slog.d(TAG, "Sending " + displayEventToString(msg) + " for display=" + id
+ " with device=" + uniqueId);
}
+
+ if (mFlags.isConnectedDisplayManagementEnabled()) {
+ if (msg == LOGICAL_DISPLAY_EVENT_ADDED) {
+ mDisplaysEnabledCache.put(id, true);
+ } else if (msg == LOGICAL_DISPLAY_EVENT_REMOVED) {
+ mDisplaysEnabledCache.delete(id);
+ }
+ }
+
mListener.onLogicalDisplayEventLocked(display, msg);
- if (msg == LOGICAL_DISPLAY_EVENT_REMOVED) {
+
+ if (mFlags.isConnectedDisplayManagementEnabled()) {
+ if (msg == LOGICAL_DISPLAY_EVENT_DISCONNECTED) {
+ mLogicalDisplays.delete(id);
+ }
+ } else if (msg == LOGICAL_DISPLAY_EVENT_REMOVED) {
// We wait until we sent the EVENT_REMOVED event before actually removing the
// display.
mLogicalDisplays.delete(id);
@@ -1083,7 +1154,8 @@
return display;
}
- private void setEnabledLocked(LogicalDisplay display, boolean isEnabled) {
+ @VisibleForTesting
+ void setEnabledLocked(LogicalDisplay display, boolean isEnabled) {
final int displayId = display.getDisplayIdLocked();
final DisplayInfo info = display.getDisplayInfoLocked();
@@ -1165,10 +1237,30 @@
return "removed";
case LOGICAL_DISPLAY_EVENT_HDR_SDR_RATIO_CHANGED:
return "hdr_sdr_ratio_changed";
+ case LOGICAL_DISPLAY_EVENT_CONNECTED:
+ return "connected";
+ case LOGICAL_DISPLAY_EVENT_DISCONNECTED:
+ return "disconnected";
}
return null;
}
+ void setDisplayEnabledLocked(int displayId, boolean enabled) {
+ LogicalDisplay display = getDisplayLocked(displayId);
+ if (display == null) {
+ Slog.w(TAG, "Cannot find display " + displayId);
+ return;
+ }
+ boolean isEnabled = display.isEnabledLocked();
+ if (isEnabled == enabled) {
+ Slog.w(TAG, "Display is already " + (isEnabled ? "enabled" : "disabled") + ": "
+ + displayId);
+ return;
+ }
+ setEnabledLocked(display, enabled);
+ updateLogicalDisplaysLocked();
+ }
+
public interface Listener {
void onLogicalDisplayEventLocked(LogicalDisplay display, int event);
void onDisplayGroupEventLocked(int groupId, int event);
diff --git a/services/core/java/com/android/server/display/WifiDisplayController.java b/services/core/java/com/android/server/display/WifiDisplayController.java
index f6d06aa..955b8d9 100644
--- a/services/core/java/com/android/server/display/WifiDisplayController.java
+++ b/services/core/java/com/android/server/display/WifiDisplayController.java
@@ -1062,8 +1062,10 @@
}
private static WifiDisplay createWifiDisplay(WifiP2pDevice device) {
+ WifiP2pWfdInfo wfdInfo = device.getWfdInfo();
+ boolean isSessionAvailable = wfdInfo != null && wfdInfo.isSessionAvailable();
return new WifiDisplay(device.deviceAddress, device.deviceName, null,
- true, device.getWfdInfo().isSessionAvailable(), false);
+ true, isSessionAvailable, false);
}
private final BroadcastReceiver mWifiP2pReceiver = new BroadcastReceiver() {
diff --git a/services/core/java/com/android/server/display/feature/Android.bp b/services/core/java/com/android/server/display/feature/Android.bp
new file mode 100644
index 0000000..27c48ed
--- /dev/null
+++ b/services/core/java/com/android/server/display/feature/Android.bp
@@ -0,0 +1,12 @@
+aconfig_declarations {
+ name: "display_flags",
+ package: "com.android.server.display.feature.flags",
+ srcs: [
+ "*.aconfig",
+ ],
+}
+
+java_aconfig_library {
+ name: "display_flags_lib",
+ aconfig_declarations: "display_flags",
+}
diff --git a/services/core/java/com/android/server/display/feature/DisplayManagerFlags.java b/services/core/java/com/android/server/display/feature/DisplayManagerFlags.java
new file mode 100644
index 0000000..78c2e9f
--- /dev/null
+++ b/services/core/java/com/android/server/display/feature/DisplayManagerFlags.java
@@ -0,0 +1,59 @@
+/*
+ * 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.server.display.feature;
+
+import android.util.Slog;
+
+import com.android.server.display.feature.flags.Flags;
+
+/**
+ * Utility class to read the flags used in the display manager server.
+ */
+public class DisplayManagerFlags {
+ private static final boolean DEBUG = false;
+ private static final String TAG = "DisplayManagerFlags";
+ private static final boolean DEFAULT_IS_CONNECTED_DISPLAY_MANAGEMENT_ENABLED = false;
+ private boolean mIsConnectedDisplayManagementEnabled = false;
+ private boolean mIsConnectedDisplayManagementEnabledSet = false;
+
+ // TODO(b/297159910): Simplify using READ-ONLY flags when available.
+ /** Returns whether connected display management is enabled or not. */
+ public boolean isConnectedDisplayManagementEnabled() {
+ if (mIsConnectedDisplayManagementEnabledSet) {
+ if (DEBUG) {
+ Slog.d(TAG, "isConnectedDisplayManagementEnabled. Recall = "
+ + mIsConnectedDisplayManagementEnabled);
+ }
+ return mIsConnectedDisplayManagementEnabled;
+ }
+ try {
+ mIsConnectedDisplayManagementEnabled = Flags.enableConnectedDisplayManagement();
+ if (DEBUG) {
+ Slog.d(TAG, "isConnectedDisplayManagementEnabled. Flag value = "
+ + mIsConnectedDisplayManagementEnabled);
+ }
+ } catch (Throwable ex) {
+ if (DEBUG) {
+ Slog.i(TAG, "isConnectedDisplayManagementEnabled not available: set to "
+ + DEFAULT_IS_CONNECTED_DISPLAY_MANAGEMENT_ENABLED, ex);
+ }
+ mIsConnectedDisplayManagementEnabled = DEFAULT_IS_CONNECTED_DISPLAY_MANAGEMENT_ENABLED;
+ }
+ mIsConnectedDisplayManagementEnabledSet = true;
+ return mIsConnectedDisplayManagementEnabled;
+ }
+}
diff --git a/services/core/java/com/android/server/display/feature/display_flags.aconfig b/services/core/java/com/android/server/display/feature/display_flags.aconfig
new file mode 100644
index 0000000..4d8c02b
--- /dev/null
+++ b/services/core/java/com/android/server/display/feature/display_flags.aconfig
@@ -0,0 +1,10 @@
+package: "com.android.server.display.feature.flags"
+
+# Important: Flags must be accessed through DisplayManagerFlags.
+
+flag {
+ name: "enable_connected_display_management"
+ namespace: "display_manager"
+ description: "Feature flag for Connected Display managment"
+ bug: "280739508"
+}
diff --git a/services/core/java/com/android/server/net/NetworkManagementService.java b/services/core/java/com/android/server/net/NetworkManagementService.java
index 39b8bfd..36adea7 100644
--- a/services/core/java/com/android/server/net/NetworkManagementService.java
+++ b/services/core/java/com/android/server/net/NetworkManagementService.java
@@ -73,6 +73,7 @@
import com.android.internal.app.IBatteryStats;
import com.android.internal.util.DumpUtils;
import com.android.internal.util.HexDump;
+import com.android.modules.utils.build.SdkLevel;
import com.android.net.module.util.NetdUtils;
import com.android.net.module.util.NetdUtils.ModifyOperation;
import com.android.net.module.util.PermissionUtils;
@@ -782,7 +783,10 @@
@Override
public boolean getIpForwardingEnabled() throws IllegalStateException{
PermissionUtils.enforceNetworkStackPermission(mContext);
-
+ if (SdkLevel.isAtLeastV()) {
+ throw new UnsupportedOperationException(
+ "NMS#getIpForwardingEnabled not supported in V+");
+ }
try {
return mNetdService.ipfwdEnabled();
} catch (RemoteException | ServiceSpecificException e) {
@@ -793,7 +797,10 @@
@Override
public void setIpForwardingEnabled(boolean enable) {
PermissionUtils.enforceNetworkStackPermission(mContext);
- try {
+ if (SdkLevel.isAtLeastV()) {
+ throw new UnsupportedOperationException(
+ "NMS#setIpForwardingEnabled not supported in V+");
+ } try {
if (enable) {
mNetdService.ipfwdEnableForwarding("tethering");
} else {
@@ -807,6 +814,9 @@
@Override
public void startTethering(String[] dhcpRange) {
PermissionUtils.enforceNetworkStackPermission(mContext);
+ if (SdkLevel.isAtLeastV()) {
+ throw new UnsupportedOperationException("NMS#startTethering not supported in V+");
+ }
try {
NetdUtils.tetherStart(mNetdService, true /* usingLegacyDnsProxy */, dhcpRange);
} catch (RemoteException | ServiceSpecificException e) {
@@ -817,6 +827,9 @@
@Override
public void stopTethering() {
PermissionUtils.enforceNetworkStackPermission(mContext);
+ if (SdkLevel.isAtLeastV()) {
+ throw new UnsupportedOperationException("NMS#stopTethering not supported in V+");
+ }
try {
mNetdService.tetherStop();
} catch (RemoteException | ServiceSpecificException e) {
@@ -827,6 +840,9 @@
@Override
public boolean isTetheringStarted() {
PermissionUtils.enforceNetworkStackPermission(mContext);
+ if (SdkLevel.isAtLeastV()) {
+ throw new UnsupportedOperationException("NMS#isTetheringStarted not supported in V+");
+ }
try {
return mNetdService.tetherIsEnabled();
} catch (RemoteException | ServiceSpecificException e) {
@@ -837,6 +853,9 @@
@Override
public void tetherInterface(String iface) {
PermissionUtils.enforceNetworkStackPermission(mContext);
+ if (SdkLevel.isAtLeastV()) {
+ throw new UnsupportedOperationException("NMS#tetherInterface not supported in V+");
+ }
try {
final LinkAddress addr = getInterfaceConfig(iface).getLinkAddress();
final IpPrefix dest = new IpPrefix(addr.getAddress(), addr.getPrefixLength());
@@ -849,6 +868,9 @@
@Override
public void untetherInterface(String iface) {
PermissionUtils.enforceNetworkStackPermission(mContext);
+ if (SdkLevel.isAtLeastV()) {
+ throw new UnsupportedOperationException("NMS#untetherInterface not supported in V+");
+ }
try {
NetdUtils.untetherInterface(mNetdService, iface);
} catch (RemoteException | ServiceSpecificException e) {
@@ -859,6 +881,10 @@
@Override
public String[] listTetheredInterfaces() {
PermissionUtils.enforceNetworkStackPermission(mContext);
+ if (SdkLevel.isAtLeastV()) {
+ throw new UnsupportedOperationException(
+ "NMS#listTetheredInterfaces not supported in V+");
+ }
try {
return mNetdService.tetherInterfaceList();
} catch (RemoteException | ServiceSpecificException e) {
@@ -869,6 +895,9 @@
@Override
public void enableNat(String internalInterface, String externalInterface) {
PermissionUtils.enforceNetworkStackPermission(mContext);
+ if (SdkLevel.isAtLeastV()) {
+ throw new UnsupportedOperationException("NMS#enableNat not supported in V+");
+ }
try {
mNetdService.tetherAddForward(internalInterface, externalInterface);
} catch (RemoteException | ServiceSpecificException e) {
@@ -879,6 +908,9 @@
@Override
public void disableNat(String internalInterface, String externalInterface) {
PermissionUtils.enforceNetworkStackPermission(mContext);
+ if (SdkLevel.isAtLeastV()) {
+ throw new UnsupportedOperationException("NMS#disableNat not supported in V+");
+ }
try {
mNetdService.tetherRemoveForward(internalInterface, externalInterface);
} catch (RemoteException | ServiceSpecificException e) {
diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java
index 6f0a4b4..1afa3ed 100755
--- a/services/core/java/com/android/server/notification/NotificationManagerService.java
+++ b/services/core/java/com/android/server/notification/NotificationManagerService.java
@@ -118,7 +118,6 @@
import static android.service.notification.NotificationListenerService.TRIM_LIGHT;
import static android.view.WindowManager.LayoutParams.TYPE_TOAST;
-import static com.android.internal.config.sysui.SystemUiSystemPropertiesFlags.NotificationFlags.WAKE_LOCK_FOR_POSTING_NOTIFICATION;
import static com.android.internal.util.FrameworkStatsLog.DND_MODE_RULE;
import static com.android.internal.util.FrameworkStatsLog.PACKAGE_NOTIFICATION_CHANNEL_GROUP_PREFERENCES;
import static com.android.internal.util.FrameworkStatsLog.PACKAGE_NOTIFICATION_CHANNEL_PREFERENCES;
@@ -533,6 +532,8 @@
@EnabledAfter(targetSdkVersion = Build.VERSION_CODES.S_V2)
private static final long NOTIFICATION_LOG_ASSISTANT_CANCEL = 195579280L;
+ private static final Duration POST_WAKE_LOCK_TIMEOUT = Duration.ofSeconds(30);
+
private IActivityManager mAm;
private ActivityTaskManagerInternal mAtm;
private ActivityManager mActivityManager;
@@ -2328,8 +2329,7 @@
mRankingHandler = rankingHandler;
mConditionProviders = conditionProviders;
mZenModeHelper = new ZenModeHelper(getContext(), mHandler.getLooper(), mConditionProviders,
- new SysUiStatsEvent.BuilderFactory(), flagResolver,
- new ZenModeEventLogger(mPackageManagerClient));
+ flagResolver, new ZenModeEventLogger(mPackageManagerClient));
mZenModeHelper.addCallback(new ZenModeHelper.Callback() {
@Override
public void onConfigChanged() {
@@ -2390,7 +2390,6 @@
mNotificationChannelLogger,
mAppOps,
mUserProfiles,
- new SysUiStatsEvent.BuilderFactory(),
mShowReviewPermissionsNotification);
mRankingHelper = new RankingHelper(getContext(),
mRankingHandler,
@@ -6678,22 +6677,14 @@
}
private PostNotificationTracker acquireWakeLockForPost(String pkg, int uid) {
- if (mFlagResolver.isEnabled(WAKE_LOCK_FOR_POSTING_NOTIFICATION)
- && Binder.withCleanCallingIdentity(
- () -> DeviceConfig.getBoolean(DeviceConfig.NAMESPACE_SYSTEMUI,
- SystemUiDeviceConfigFlags.NOTIFY_WAKELOCK, false))) {
- // The package probably doesn't have WAKE_LOCK permission and should not require it.
- return Binder.withCleanCallingIdentity(() -> {
- WakeLock wakeLock = mPowerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK,
- "NotificationManagerService:post:" + pkg);
- wakeLock.setWorkSource(new WorkSource(uid, pkg));
- // TODO(b/275044361): Adjust to a more reasonable number when we have the data.
- wakeLock.acquire(30_000);
- return mPostNotificationTrackerFactory.newTracker(wakeLock);
- });
- } else {
- return mPostNotificationTrackerFactory.newTracker(null);
- }
+ // The package probably doesn't have WAKE_LOCK permission and should not require it.
+ return Binder.withCleanCallingIdentity(() -> {
+ WakeLock wakeLock = mPowerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK,
+ "NotificationManagerService:post:" + pkg);
+ wakeLock.setWorkSource(new WorkSource(uid, pkg));
+ wakeLock.acquire(POST_WAKE_LOCK_TIMEOUT.toMillis());
+ return mPostNotificationTrackerFactory.newTracker(wakeLock);
+ });
}
/**
diff --git a/services/core/java/com/android/server/notification/PreferencesHelper.java b/services/core/java/com/android/server/notification/PreferencesHelper.java
index 0e37f10..b132a23 100644
--- a/services/core/java/com/android/server/notification/PreferencesHelper.java
+++ b/services/core/java/com/android/server/notification/PreferencesHelper.java
@@ -25,7 +25,6 @@
import static android.app.NotificationManager.IMPORTANCE_MAX;
import static android.app.NotificationManager.IMPORTANCE_NONE;
import static android.app.NotificationManager.IMPORTANCE_UNSPECIFIED;
-import static android.util.StatsLog.ANNOTATION_ID_IS_UID;
import static com.android.internal.util.FrameworkStatsLog.PACKAGE_NOTIFICATION_CHANNEL_GROUP_PREFERENCES;
import static com.android.internal.util.FrameworkStatsLog.PACKAGE_NOTIFICATION_CHANNEL_PREFERENCES;
@@ -78,6 +77,7 @@
import com.android.internal.config.sysui.SystemUiSystemPropertiesFlags;
import com.android.internal.config.sysui.SystemUiSystemPropertiesFlags.NotificationFlags;
import com.android.internal.logging.MetricsLogger;
+import com.android.internal.util.FrameworkStatsLog;
import com.android.internal.util.Preconditions;
import com.android.internal.util.XmlUtils;
import com.android.modules.utils.TypedXmlPullParser;
@@ -169,7 +169,6 @@
* fields.
*/
private static final int DEFAULT_LOCKED_APP_FIELDS = 0;
- private final SysUiStatsEvent.BuilderFactory mStatsEventBuilderFactory;
/**
* All user-lockable fields for a given application.
@@ -208,7 +207,6 @@
ZenModeHelper zenHelper, PermissionHelper permHelper, PermissionManager permManager,
NotificationChannelLogger notificationChannelLogger,
AppOpsManager appOpsManager, ManagedServices.UserProfiles userProfiles,
- SysUiStatsEvent.BuilderFactory statsEventBuilderFactory,
boolean showReviewPermissionsNotification) {
mContext = context;
mZenModeHelper = zenHelper;
@@ -219,7 +217,6 @@
mNotificationChannelLogger = notificationChannelLogger;
mAppOps = appOpsManager;
mUserProfiles = userProfiles;
- mStatsEventBuilderFactory = statsEventBuilderFactory;
mShowReviewPermissionsNotification = showReviewPermissionsNotification;
XML_VERSION = 4;
@@ -2190,11 +2187,7 @@
break;
}
pulledEvents++;
- SysUiStatsEvent.Builder event = mStatsEventBuilderFactory.newBuilder()
- .setAtomId(PACKAGE_NOTIFICATION_PREFERENCES);
final PackagePreferences r = mPackagePreferences.valueAt(i);
- event.writeInt(r.uid);
- event.addBooleanAnnotation(ANNOTATION_ID_IS_UID, true);
// collect whether this package's importance info was user-set for later, if needed
// before the migration is enabled, this will simply default to false in all cases.
@@ -2214,15 +2207,7 @@
pkgsWithPermissionsToHandle.remove(key);
}
- event.writeInt(importance);
- event.writeInt(r.visibility);
- event.writeInt(r.lockedAppFields);
-
- // optional bool user_set_importance = 5;
- event.writeBoolean(importanceIsUserSet);
-
- // optional FsiState fsi_state = 6;
final boolean isStickyHunFlagEnabled = SystemUiSystemPropertiesFlags.getResolver()
.isEnabled(NotificationFlags.SHOW_STICKY_HUN_FOR_DENIED_FSI);
@@ -2232,20 +2217,23 @@
final int fsiState = getFsiState(r.pkg, r.uid, requestedFSIPermission,
isStickyHunFlagEnabled);
- event.writeInt(fsiState);
-
- // optional bool is_fsi_permission_user_set = 7;
final int currentPermissionFlags = mPm.getPermissionFlags(
android.Manifest.permission.USE_FULL_SCREEN_INTENT, r.pkg,
UserHandle.getUserHandleForUid(r.uid));
- final boolean isUserSet =
+ final boolean fsiIsUserSet =
isFsiPermissionUserSet(r.pkg, r.uid, fsiState, currentPermissionFlags,
isStickyHunFlagEnabled);
- event.writeBoolean(isUserSet);
-
- events.add(event.build());
+ events.add(FrameworkStatsLog.buildStatsEvent(
+ PACKAGE_NOTIFICATION_PREFERENCES,
+ /* optional int32 uid = 1 [(is_uid) = true] */ r.uid,
+ /* optional int32 importance = 2 */ importance,
+ /* optional int32 visibility = 3 */ r.visibility,
+ /* optional int32 user_locked_fields = 4 */ r.lockedAppFields,
+ /* optional bool user_set_importance = 5 */ importanceIsUserSet,
+ /* optional FsiState fsi_state = 6 */ fsiState,
+ /* optional bool is_fsi_permission_user_set = 7 */ fsiIsUserSet));
}
}
@@ -2256,18 +2244,18 @@
break;
}
pulledEvents++;
- SysUiStatsEvent.Builder event = mStatsEventBuilderFactory.newBuilder()
- .setAtomId(PACKAGE_NOTIFICATION_PREFERENCES);
- event.writeInt(p.first);
- event.addBooleanAnnotation(ANNOTATION_ID_IS_UID, true);
- event.writeInt(pkgPermissions.get(p).first ? IMPORTANCE_DEFAULT : IMPORTANCE_NONE);
-
- // fill out the rest of the fields with default values so as not to confuse the
- // builder
- event.writeInt(DEFAULT_VISIBILITY);
- event.writeInt(DEFAULT_LOCKED_APP_FIELDS);
- event.writeBoolean(pkgPermissions.get(p).second); // user_set_importance field
- events.add(event.build());
+ // Because all fields are required in FrameworkStatsLog.buildStatsEvent, we have
+ // to fill in default values for all the unspecified fields.
+ events.add(FrameworkStatsLog.buildStatsEvent(
+ PACKAGE_NOTIFICATION_PREFERENCES,
+ /* optional int32 uid = 1 [(is_uid) = true] */ p.first,
+ /* optional int32 importance = 2 */ pkgPermissions.get(p).first
+ ? IMPORTANCE_DEFAULT : IMPORTANCE_NONE,
+ /* optional int32 visibility = 3 */ DEFAULT_VISIBILITY,
+ /* optional int32 user_locked_fields = 4 */ DEFAULT_LOCKED_APP_FIELDS,
+ /* optional bool user_set_importance = 5 */ pkgPermissions.get(p).second,
+ /* optional FsiState fsi_state = 6 */ 0,
+ /* optional bool is_fsi_permission_user_set = 7 */ false));
}
}
}
@@ -2288,20 +2276,21 @@
if (++totalChannelsPulled > NOTIFICATION_CHANNEL_PULL_LIMIT) {
break;
}
- SysUiStatsEvent.Builder event = mStatsEventBuilderFactory.newBuilder()
- .setAtomId(PACKAGE_NOTIFICATION_CHANNEL_PREFERENCES);
- event.writeInt(r.uid);
- event.addBooleanAnnotation(ANNOTATION_ID_IS_UID, true);
- event.writeString(channel.getId());
- event.writeString(channel.getName().toString());
- event.writeString(channel.getDescription());
- event.writeInt(channel.getImportance());
- event.writeInt(channel.getUserLockedFields());
- event.writeBoolean(channel.isDeleted());
- event.writeBoolean(channel.getConversationId() != null);
- event.writeBoolean(channel.isDemoted());
- event.writeBoolean(channel.isImportantConversation());
- events.add(event.build());
+ events.add(FrameworkStatsLog.buildStatsEvent(
+ PACKAGE_NOTIFICATION_CHANNEL_PREFERENCES,
+ /* optional int32 uid = 1 [(is_uid) = true] */ r.uid,
+ /* optional string channel_id = 2 */ channel.getId(),
+ /* optional string channel_name = 3 */ channel.getName().toString(),
+ /* optional string description = 4 */ channel.getDescription(),
+ /* optional int32 importance = 5 */ channel.getImportance(),
+ /* optional int32 user_locked_fields = 6 */
+ channel.getUserLockedFields(),
+ /* optional bool is_deleted = 7 */ channel.isDeleted(),
+ /* optional bool is_conversation = 8 */
+ channel.getConversationId() != null,
+ /* optional bool is_demoted_conversation = 9 */ channel.isDemoted(),
+ /* optional bool is_important_conversation = 10 */
+ channel.isImportantConversation()));
}
}
}
@@ -2323,16 +2312,15 @@
if (++totalGroupsPulled > NOTIFICATION_CHANNEL_GROUP_PULL_LIMIT) {
break;
}
- SysUiStatsEvent.Builder event = mStatsEventBuilderFactory.newBuilder()
- .setAtomId(PACKAGE_NOTIFICATION_CHANNEL_GROUP_PREFERENCES);
- event.writeInt(r.uid);
- event.addBooleanAnnotation(ANNOTATION_ID_IS_UID, true);
- event.writeString(groupChannel.getId());
- event.writeString(groupChannel.getName().toString());
- event.writeString(groupChannel.getDescription());
- event.writeBoolean(groupChannel.isBlocked());
- event.writeInt(groupChannel.getUserLockedFields());
- events.add(event.build());
+ events.add(FrameworkStatsLog.buildStatsEvent(
+ PACKAGE_NOTIFICATION_CHANNEL_GROUP_PREFERENCES,
+ /* optional int32 uid = 1 [(is_uid) = true] */ r.uid,
+ /* optional string group_id = 2 */ groupChannel.getId(),
+ /* optional string group_name = 3 */ groupChannel.getName().toString(),
+ /* optional string description = 4 */ groupChannel.getDescription(),
+ /* optional bool is_blocked = 5 */ groupChannel.isBlocked(),
+ /* optional int32 user_locked_fields = 6 */
+ groupChannel.getUserLockedFields()));
}
}
}
diff --git a/services/core/java/com/android/server/notification/ZenModeHelper.java b/services/core/java/com/android/server/notification/ZenModeHelper.java
index 1f5bd3e..a700d32 100644
--- a/services/core/java/com/android/server/notification/ZenModeHelper.java
+++ b/services/core/java/com/android/server/notification/ZenModeHelper.java
@@ -21,7 +21,6 @@
import static android.app.NotificationManager.AUTOMATIC_RULE_STATUS_REMOVED;
import static android.app.NotificationManager.Policy.PRIORITY_SENDERS_ANY;
import static android.service.notification.NotificationServiceProto.ROOT_CONFIG;
-import static android.util.StatsLog.ANNOTATION_ID_IS_UID;
import static com.android.internal.util.FrameworkStatsLog.DND_MODE_RULE;
@@ -81,6 +80,7 @@
import com.android.internal.logging.MetricsLogger;
import com.android.internal.messages.nano.SystemMessageProto.SystemMessage;
import com.android.internal.notification.SystemNotificationChannels;
+import com.android.internal.util.FrameworkStatsLog;
import com.android.internal.util.XmlUtils;
import com.android.modules.utils.TypedXmlPullParser;
import com.android.modules.utils.TypedXmlSerializer;
@@ -116,7 +116,6 @@
private final SettingsObserver mSettingsObserver;
private final AppOpsManager mAppOps;
private final NotificationManager mNotificationManager;
- private final SysUiStatsEvent.BuilderFactory mStatsEventBuilderFactory;
private ZenModeConfig mDefaultConfig;
private final ArrayList<Callback> mCallbacks = new ArrayList<Callback>();
private final ZenModeFiltering mFiltering;
@@ -152,7 +151,6 @@
private String[] mPriorityOnlyDndExemptPackages;
public ZenModeHelper(Context context, Looper looper, ConditionProviders conditionProviders,
- SysUiStatsEvent.BuilderFactory statsEventBuilderFactory,
SystemUiSystemPropertiesFlags.FlagResolver flagResolver,
ZenModeEventLogger zenModeEventLogger) {
mContext = context;
@@ -174,7 +172,6 @@
mFiltering = new ZenModeFiltering(mContext);
mConditions = new ZenModeConditions(this, conditionProviders);
mServiceConfig = conditionProviders.getConfig();
- mStatsEventBuilderFactory = statsEventBuilderFactory;
mFlagResolver = flagResolver;
mZenModeEventLogger = zenModeEventLogger;
}
@@ -1104,8 +1101,11 @@
.allowAlarms(true)
.allowMedia(true)
.build());
- } else {
+ } else if (rule.zenPolicy != null) {
policy.apply(rule.zenPolicy);
+ } else {
+ // active rule with no specified policy inherits the default settings
+ policy.apply(mConfig.toZenPolicy());
}
}
@@ -1314,17 +1314,14 @@
for (int i = 0; i < numConfigs; i++) {
final int user = mConfigs.keyAt(i);
final ZenModeConfig config = mConfigs.valueAt(i);
- SysUiStatsEvent.Builder data = mStatsEventBuilderFactory.newBuilder()
- .setAtomId(DND_MODE_RULE)
- .writeInt(user)
- .writeBoolean(config.manualRule != null) // enabled
- .writeBoolean(config.areChannelsBypassingDnd)
- .writeInt(ROOT_CONFIG)
- .writeString("") // name, empty for root config
- .writeInt(Process.SYSTEM_UID) // system owns root config
- .addBooleanAnnotation(ANNOTATION_ID_IS_UID, true)
- .writeByteArray(config.toZenPolicy().toProto());
- events.add(data.build());
+ events.add(FrameworkStatsLog.buildStatsEvent(DND_MODE_RULE,
+ /* optional int32 user = 1 */ user,
+ /* optional bool enabled = 2 */ config.manualRule != null,
+ /* optional bool channels_bypassing = 3 */ config.areChannelsBypassingDnd,
+ /* optional LoggedZenMode zen_mode = 4 */ ROOT_CONFIG,
+ /* optional string id = 5 */ "", // empty for root config
+ /* optional int32 uid = 6 */ Process.SYSTEM_UID, // system owns root config
+ /* optional DNDPolicyProto policy = 7 */ config.toZenPolicy().toProto()));
if (config.manualRule != null) {
ruleToProtoLocked(user, config.manualRule, true, events);
}
@@ -1355,21 +1352,18 @@
}
SysUiStatsEvent.Builder data;
- data = mStatsEventBuilderFactory.newBuilder()
- .setAtomId(DND_MODE_RULE)
- .writeInt(user)
- .writeBoolean(rule.enabled)
- .writeBoolean(false) // channels_bypassing unused for rules
- .writeInt(rule.zenMode)
- .writeString(id)
- .writeInt(getPackageUid(pkg, user))
- .addBooleanAnnotation(ANNOTATION_ID_IS_UID, true);
byte[] policyProto = new byte[]{};
if (rule.zenPolicy != null) {
policyProto = rule.zenPolicy.toProto();
}
- data.writeByteArray(policyProto);
- events.add(data.build());
+ events.add(FrameworkStatsLog.buildStatsEvent(DND_MODE_RULE,
+ /* optional int32 user = 1 */ user,
+ /* optional bool enabled = 2 */ rule.enabled,
+ /* optional bool channels_bypassing = 3 */ false, // unused for rules
+ /* optional android.stats.dnd.ZenMode zen_mode = 4 */ rule.zenMode,
+ /* optional string id = 5 */ id,
+ /* optional int32 uid = 6 */ getPackageUid(pkg, user),
+ /* optional DNDPolicyProto policy = 7 */ policyProto));
}
private int getPackageUid(String pkg, int user) {
diff --git a/services/core/java/com/android/server/pm/DeletePackageHelper.java b/services/core/java/com/android/server/pm/DeletePackageHelper.java
index 66a1703..b8feb4d 100644
--- a/services/core/java/com/android/server/pm/DeletePackageHelper.java
+++ b/services/core/java/com/android/server/pm/DeletePackageHelper.java
@@ -20,12 +20,10 @@
import static android.Manifest.permission.MANAGE_PROFILE_AND_DEVICE_OWNERS;
import static android.content.pm.PackageManager.COMPONENT_ENABLED_STATE_DEFAULT;
import static android.content.pm.PackageManager.COMPONENT_ENABLED_STATE_ENABLED;
+import static android.content.pm.PackageManager.DELETE_KEEP_DATA;
import static android.content.pm.PackageManager.DELETE_SUCCEEDED;
import static android.content.pm.PackageManager.MATCH_KNOWN_PACKAGES;
import static android.content.pm.PackageManager.PERMISSION_GRANTED;
-import static android.os.storage.StorageManager.FLAG_STORAGE_CE;
-import static android.os.storage.StorageManager.FLAG_STORAGE_DE;
-import static android.os.storage.StorageManager.FLAG_STORAGE_EXTERNAL;
import static com.android.server.pm.InstructionSets.getAppDexInstructionSets;
import static com.android.server.pm.PackageManagerService.DEBUG_COMPRESSION;
@@ -66,20 +64,20 @@
import com.android.internal.util.Preconditions;
import com.android.server.pm.permission.PermissionManagerServiceInternal;
import com.android.server.pm.pkg.AndroidPackage;
+import com.android.server.pm.pkg.ArchiveState;
import com.android.server.pm.pkg.PackageStateInternal;
import com.android.server.pm.pkg.PackageUserState;
import com.android.server.wm.ActivityTaskManagerInternal;
import dalvik.system.VMRuntime;
-import java.util.Collections;
import java.util.List;
/**
* Deletes a package. Uninstall if installed, or at least deletes the base directory if it's called
* from a failed installation. Fixes user state after deletion.
* Handles special treatments to system apps.
- * Relies on RemovePackageHelper to clear internal data structures.
+ * Relies on RemovePackageHelper to clear internal data structures and remove app data.
*/
final class DeletePackageHelper {
private static final boolean DEBUG_CLEAN_APKS = false;
@@ -90,24 +88,17 @@
private final UserManagerInternal mUserManagerInternal;
private final PermissionManagerServiceInternal mPermissionManager;
private final RemovePackageHelper mRemovePackageHelper;
- private final AppDataHelper mAppDataHelper;
// TODO(b/198166813): remove PMS dependency
- DeletePackageHelper(PackageManagerService pm, RemovePackageHelper removePackageHelper,
- AppDataHelper appDataHelper) {
+ DeletePackageHelper(PackageManagerService pm, RemovePackageHelper removePackageHelper) {
mPm = pm;
mUserManagerInternal = mPm.mInjector.getUserManagerInternal();
mPermissionManager = mPm.mInjector.getPermissionManagerServiceInternal();
mRemovePackageHelper = removePackageHelper;
- mAppDataHelper = appDataHelper;
}
DeletePackageHelper(PackageManagerService pm) {
- mPm = pm;
- mAppDataHelper = new AppDataHelper(mPm);
- mUserManagerInternal = mPm.mInjector.getUserManagerInternal();
- mPermissionManager = mPm.mInjector.getPermissionManagerServiceInternal();
- mRemovePackageHelper = new RemovePackageHelper(mPm, mAppDataHelper);
+ this(pm, new RemovePackageHelper(pm));
}
/**
@@ -454,7 +445,7 @@
// semantics than normal for uninstalling system apps.
final boolean clearPackageStateAndReturn;
synchronized (mPm.mLock) {
- markPackageUninstalledForUserLPw(ps, user);
+ markPackageUninstalledForUserLPw(ps, user, flags);
if (!systemApp) {
// Do not uninstall the APK if an app should be cached
boolean keepUninstalledPackage =
@@ -484,7 +475,7 @@
}
}
if (clearPackageStateAndReturn) {
- clearPackageStateForUserLIF(ps, userId, outInfo, flags);
+ mRemovePackageHelper.clearPackageStateForUserLIF(ps, userId, outInfo, flags);
mPm.scheduleWritePackageRestrictions(user);
return;
}
@@ -531,55 +522,6 @@
}
}
- private void clearPackageStateForUserLIF(PackageSetting ps, int userId,
- PackageRemovedInfo outInfo, int flags) {
- final AndroidPackage pkg;
- final SharedUserSetting sus;
- synchronized (mPm.mLock) {
- pkg = mPm.mPackages.get(ps.getPackageName());
- sus = mPm.mSettings.getSharedUserSettingLPr(ps);
- }
-
- mAppDataHelper.destroyAppProfilesLIF(pkg);
-
- final List<AndroidPackage> sharedUserPkgs =
- sus != null ? sus.getPackages() : Collections.emptyList();
- final PreferredActivityHelper preferredActivityHelper = new PreferredActivityHelper(mPm);
- final int[] userIds = (userId == UserHandle.USER_ALL) ? mUserManagerInternal.getUserIds()
- : new int[] {userId};
- for (int nextUserId : userIds) {
- if (DEBUG_REMOVE) {
- Slog.d(TAG, "Updating package:" + ps.getPackageName() + " install state for user:"
- + nextUserId);
- }
- if ((flags & PackageManager.DELETE_KEEP_DATA) == 0) {
- mAppDataHelper.destroyAppDataLIF(pkg, nextUserId,
- FLAG_STORAGE_DE | FLAG_STORAGE_CE | FLAG_STORAGE_EXTERNAL);
- ps.setCeDataInode(-1, nextUserId);
- }
- mAppDataHelper.clearKeystoreData(nextUserId, ps.getAppId());
- preferredActivityHelper.clearPackagePreferredActivities(ps.getPackageName(),
- nextUserId);
- mPm.mDomainVerificationManager.clearPackageForUser(ps.getPackageName(), nextUserId);
- }
- mPermissionManager.onPackageUninstalled(ps.getPackageName(), ps.getAppId(), ps, pkg,
- sharedUserPkgs, userId);
-
- if (outInfo != null) {
- if ((flags & PackageManager.DELETE_KEEP_DATA) == 0) {
- outInfo.mDataRemoved = true;
- }
- outInfo.mRemovedPackage = ps.getPackageName();
- outInfo.mInstallerPackageName = ps.getInstallSource().mInstallerPackageName;
- outInfo.mIsStaticSharedLib = pkg != null && pkg.getStaticSharedLibraryName() != null;
- outInfo.mRemovedAppId = ps.getAppId();
- outInfo.mRemovedUsers = userIds;
- outInfo.mBroadcastUsers = userIds;
- outInfo.mIsExternal = ps.isExternalStorage();
- outInfo.mRemovedPackageVersionCode = ps.getVersionCode();
- }
- }
-
@GuardedBy("mPm.mInstallLock")
private void deleteInstalledPackageLIF(PackageSetting ps,
boolean deleteCodeAndResources, int flags, @NonNull int[] allUserHandles,
@@ -607,7 +549,7 @@
}
@GuardedBy("mPm.mLock")
- private void markPackageUninstalledForUserLPw(PackageSetting ps, UserHandle user) {
+ private void markPackageUninstalledForUserLPw(PackageSetting ps, UserHandle user, int flags) {
final int[] userIds = (user == null || user.getIdentifier() == UserHandle.USER_ALL)
? mUserManagerInternal.getUserIds()
: new int[] {user.getIdentifier()};
@@ -616,6 +558,12 @@
Slog.d(TAG, "Marking package:" + ps.getPackageName()
+ " uninstalled for user:" + nextUserId);
}
+ // Preserve ArchiveState if this is not a full uninstall
+ ArchiveState archiveState =
+ (flags & DELETE_KEEP_DATA) == 0
+ ? null
+ : ps.getUserStateOrDefault(nextUserId).getArchiveState();
+
ps.setUserState(nextUserId,
ps.getCeDataInode(nextUserId),
COMPONENT_ENABLED_STATE_DEFAULT,
@@ -636,7 +584,7 @@
null /*splashScreenTheme*/,
0 /*firstInstallTime*/,
PackageManager.USER_MIN_ASPECT_RATIO_UNSET,
- null /*archiveState*/);
+ archiveState);
}
mPm.mSettings.writeKernelMappingLPr(ps);
}
diff --git a/services/core/java/com/android/server/pm/DumpHelper.java b/services/core/java/com/android/server/pm/DumpHelper.java
index f3ea42e..2a00a44 100644
--- a/services/core/java/com/android/server/pm/DumpHelper.java
+++ b/services/core/java/com/android/server/pm/DumpHelper.java
@@ -619,7 +619,7 @@
pw.println(" --checkin: dump for a checkin");
pw.println(" -f: print details of intent filters");
pw.println(" -h: print this help");
- pw.println(" ---proto: dump data to proto");
+ pw.println(" --proto: dump data to proto");
pw.println(" --all-components: include all component names in package dump");
pw.println(" --include-apex: includes the apex packages in package dump");
pw.println(" cmd may be one of:");
diff --git a/services/core/java/com/android/server/pm/IPackageManagerBase.java b/services/core/java/com/android/server/pm/IPackageManagerBase.java
index 76203ac..8f7b721 100644
--- a/services/core/java/com/android/server/pm/IPackageManagerBase.java
+++ b/services/core/java/com/android/server/pm/IPackageManagerBase.java
@@ -30,6 +30,7 @@
import android.content.IntentFilter;
import android.content.pm.ActivityInfo;
import android.content.pm.ApplicationInfo;
+import android.content.pm.IPackageArchiverService;
import android.content.pm.IPackageDeleteObserver;
import android.content.pm.IPackageDeleteObserver2;
import android.content.pm.IPackageInstaller;
@@ -99,6 +100,9 @@
private final PackageInstallerService mInstallerService;
@NonNull
+ private final PackageArchiverService mPackageArchiverService;
+
+ @NonNull
private final PackageProperty mPackageProperty;
@NonNull
@@ -127,7 +131,8 @@
@Nullable ComponentName instantAppResolverSettingsComponent,
@NonNull String requiredSupplementalProcessPackage,
@Nullable String servicesExtensionPackageName,
- @Nullable String sharedSystemSharedLibraryPackageName) {
+ @Nullable String sharedSystemSharedLibraryPackageName,
+ @NonNull PackageArchiverService packageArchiverService) {
mService = service;
mContext = context;
mDexOptHelper = dexOptHelper;
@@ -143,6 +148,7 @@
mRequiredSupplementalProcessPackage = requiredSupplementalProcessPackage;
mServicesExtensionPackageName = servicesExtensionPackageName;
mSharedSystemSharedLibraryPackageName = sharedSystemSharedLibraryPackageName;
+ mPackageArchiverService = packageArchiverService;
}
protected Computer snapshot() {
@@ -616,6 +622,12 @@
@Override
@Deprecated
+ public final IPackageArchiverService getPackageArchiverService() {
+ return mPackageArchiverService;
+ }
+
+ @Override
+ @Deprecated
public final void getPackageSizeInfo(final String packageName, int userId,
final IPackageStatsObserver observer) {
throw new UnsupportedOperationException(
diff --git a/services/core/java/com/android/server/pm/InstallPackageHelper.java b/services/core/java/com/android/server/pm/InstallPackageHelper.java
index 3e18387..dd04340 100644
--- a/services/core/java/com/android/server/pm/InstallPackageHelper.java
+++ b/services/core/java/com/android/server/pm/InstallPackageHelper.java
@@ -1144,8 +1144,16 @@
Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "parsePackage");
final ParsedPackage parsedPackage;
try (PackageParser2 pp = mPm.mInjector.getPreparingPackageParser()) {
- parsedPackage = pp.parsePackage(tmpPackageFile, parseFlags, false);
- AndroidPackageUtils.validatePackageDexMetadata(parsedPackage);
+ if (request.getPackageLite() == null || !PackageInstallerSession.isArchivedInstallation(
+ request.getInstallFlags())) {
+ // TODO: pass packageLite from install request instead of reparsing the package
+ parsedPackage = pp.parsePackage(tmpPackageFile, parseFlags, false);
+ AndroidPackageUtils.validatePackageDexMetadata(parsedPackage);
+ } else {
+ // Archived install mode, no APK.
+ parsedPackage = pp.parsePackageFromPackageLite(request.getPackageLite(),
+ parseFlags);
+ }
} catch (PackageManagerException e) {
throw new PrepareFailure("Failed parse during installPackageLI", e);
} finally {
@@ -1547,6 +1555,7 @@
// TODO: Are these system flags actually set properly at this stage?
boolean isUpdatedSystemAppInferred =
pkgSetting != null && pkgSetting.isSystem();
+ // derivePackageAbi works OK for archived packages despite logging some errors.
final Pair<PackageAbiHelper.Abis, PackageAbiHelper.NativeLibraryPaths>
derivedAbi = mPackageAbiHelper.derivePackageAbi(parsedPackage,
systemApp, (isUpdatedSystemAppFromExistingSetting
@@ -1582,7 +1591,8 @@
final PackageFreezer freezer =
freezePackageForInstall(pkgName, UserHandle.USER_ALL, installFlags,
- "installPackageLI", ApplicationExitInfo.REASON_PACKAGE_UPDATED);
+ "installPackageLI", ApplicationExitInfo.REASON_PACKAGE_UPDATED, request);
+
boolean shouldCloseFreezerBeforeReturn = true;
try {
final PackageState oldPackageState;
@@ -2032,11 +2042,11 @@
}
private PackageFreezer freezePackageForInstall(String packageName, int userId, int installFlags,
- String killReason, int exitInfoReason) {
+ String killReason, int exitInfoReason, InstallRequest request) {
if ((installFlags & PackageManager.INSTALL_DONT_KILL_APP) != 0) {
- return new PackageFreezer(mPm);
+ return new PackageFreezer(mPm, request);
} else {
- return mPm.freezePackage(packageName, userId, killReason, exitInfoReason);
+ return mPm.freezePackage(packageName, userId, killReason, exitInfoReason, request);
}
}
@@ -3207,7 +3217,7 @@
try (PackageFreezer freezer =
mPm.freezePackage(stubPkg.getPackageName(), UserHandle.USER_ALL,
"setEnabledSetting",
- ApplicationExitInfo.REASON_PACKAGE_UPDATED)) {
+ ApplicationExitInfo.REASON_PACKAGE_UPDATED, null /* request */)) {
pkg = installStubPackageLI(stubPkg, parseFlags, 0 /*scanFlags*/);
mAppDataHelper.prepareAppDataAfterInstallLIF(pkg);
synchronized (mPm.mLock) {
@@ -3231,7 +3241,8 @@
try (PackageFreezer freezer =
mPm.freezePackage(stubPkg.getPackageName(), UserHandle.USER_ALL,
"setEnabledSetting",
- ApplicationExitInfo.REASON_PACKAGE_UPDATED)) {
+ ApplicationExitInfo.REASON_PACKAGE_UPDATED,
+ null /* request */)) {
synchronized (mPm.mLock) {
// NOTE: Ensure the system package is enabled; even for a compressed stub.
// If we don't, installing the system package fails during scan
@@ -4291,7 +4302,8 @@
+ " name: " + pkgSetting.getPackageName());
try (@SuppressWarnings("unused") PackageFreezer freezer = mPm.freezePackage(
parsedPackage.getPackageName(), UserHandle.USER_ALL,
- "scanPackageInternalLI", ApplicationExitInfo.REASON_OTHER)) {
+ "scanPackageInternalLI", ApplicationExitInfo.REASON_OTHER,
+ null /* request */)) {
DeletePackageHelper deletePackageHelper = new DeletePackageHelper(mPm);
deletePackageHelper.deletePackageLIF(parsedPackage.getPackageName(), null, true,
mPm.mUserManager.getUserIds(), 0, null, false);
diff --git a/services/core/java/com/android/server/pm/InstallRequest.java b/services/core/java/com/android/server/pm/InstallRequest.java
index 6fc14e8..fe7c086 100644
--- a/services/core/java/com/android/server/pm/InstallRequest.java
+++ b/services/core/java/com/android/server/pm/InstallRequest.java
@@ -35,6 +35,7 @@
import android.content.pm.PackageManager;
import android.content.pm.SharedLibraryInfo;
import android.content.pm.SigningDetails;
+import android.content.pm.parsing.PackageLite;
import android.net.Uri;
import android.os.Build;
import android.os.Process;
@@ -93,6 +94,8 @@
private int[] mNewUsers;
@Nullable
private AndroidPackage mPkg;
+ @Nullable
+ private PackageLite mPackageLite;
private int mReturnCode;
private int mInternalErrorCode;
@Nullable
@@ -142,6 +145,7 @@
params.mInstallReason, params.mInstallScenario, params.mForceQueryableOverride,
params.mDataLoaderType, params.mPackageSource,
params.mApplicationEnabledSettingPersistent);
+ mPackageLite = params.mPackageLite;
mPackageMetrics = new PackageMetrics(this);
mIsInstallInherit = params.mIsInherit;
mSessionId = params.mSessionId;
@@ -306,6 +310,11 @@
}
@Nullable
+ public PackageLite getPackageLite() {
+ return mPackageLite;
+ }
+
+ @Nullable
public String getTraceMethod() {
return mInstallArgs == null ? null : mInstallArgs.mTraceMethod;
}
@@ -831,4 +840,16 @@
}
}
}
+
+ public void onFreezeStarted() {
+ if (mPackageMetrics != null) {
+ mPackageMetrics.onStepStarted(PackageMetrics.STEP_FREEZE_INSTALL);
+ }
+ }
+
+ public void onFreezeCompleted() {
+ if (mPackageMetrics != null) {
+ mPackageMetrics.onStepFinished(PackageMetrics.STEP_FREEZE_INSTALL);
+ }
+ }
}
diff --git a/services/core/java/com/android/server/pm/Installer.java b/services/core/java/com/android/server/pm/Installer.java
index 9ac983d..6233c9b 100644
--- a/services/core/java/com/android/server/pm/Installer.java
+++ b/services/core/java/com/android/server/pm/Installer.java
@@ -29,6 +29,7 @@
import android.os.CreateAppDataResult;
import android.os.IBinder;
import android.os.IInstalld;
+import android.os.ParcelFileDescriptor;
import android.os.ReconcileSdkDataArgs;
import android.os.RemoteException;
import android.os.ServiceManager;
@@ -1161,6 +1162,55 @@
}
}
+ /**
+ * Returns an auth token for the provided writable FD.
+ *
+ * @param authFd a file descriptor to proof that the caller can write to the file.
+ * @param appUid uid of the calling app.
+ * @param userId id of the user whose app file to enable fs-verity.
+ *
+ * @return authToken, or null if a remote call shouldn't be continued. See {@link
+ * #checkBeforeRemote}.
+ *
+ * @throws InstallerException if the remote call failed.
+ */
+ public IInstalld.IFsveritySetupAuthToken createFsveritySetupAuthToken(
+ ParcelFileDescriptor authFd, int appUid, @UserIdInt int userId)
+ throws InstallerException {
+ if (!checkBeforeRemote()) {
+ return null;
+ }
+ try {
+ return mInstalld.createFsveritySetupAuthToken(authFd, appUid, userId);
+ } catch (Exception e) {
+ throw InstallerException.from(e);
+ }
+ }
+
+ /**
+ * Enables fs-verity to the given app file.
+ *
+ * @param authToken a token previously returned from {@link #createFsveritySetupAuthToken}.
+ * @param filePath file path of the package to enable fs-verity.
+ * @param packageName name of the package.
+ *
+ * @return 0 if the operation was successful, otherwise {@code errno}.
+ *
+ * @throws InstallerException if the remote call failed (e.g. see {@link #checkBeforeRemote}).
+ */
+ public int enableFsverity(IInstalld.IFsveritySetupAuthToken authToken, String filePath,
+ String packageName) throws InstallerException {
+ if (!checkBeforeRemote()) {
+ throw new InstallerException("fs-verity wasn't enabled with an isolated installer");
+ }
+ BlockGuard.getVmPolicy().onPathAccess(filePath);
+ try {
+ return mInstalld.enableFsverity(authToken, filePath, packageName);
+ } catch (Exception e) {
+ throw InstallerException.from(e);
+ }
+ }
+
public static class InstallerException extends Exception {
public InstallerException(String detailMessage) {
super(detailMessage);
diff --git a/services/core/java/com/android/server/pm/InstallingSession.java b/services/core/java/com/android/server/pm/InstallingSession.java
index 30a23bf..fe6a8a1 100644
--- a/services/core/java/com/android/server/pm/InstallingSession.java
+++ b/services/core/java/com/android/server/pm/InstallingSession.java
@@ -543,7 +543,6 @@
mInstallPackageHelper.installPackagesTraced(installRequests);
for (InstallRequest request : installRequests) {
- request.onInstallCompleted();
doPostInstall(request);
}
}
diff --git a/services/core/java/com/android/server/pm/MovePackageHelper.java b/services/core/java/com/android/server/pm/MovePackageHelper.java
index 01c2734..148e0df 100644
--- a/services/core/java/com/android/server/pm/MovePackageHelper.java
+++ b/services/core/java/com/android/server/pm/MovePackageHelper.java
@@ -147,7 +147,8 @@
final PackageFreezer freezer;
synchronized (mPm.mLock) {
freezer = mPm.freezePackage(packageName, UserHandle.USER_ALL,
- "movePackageInternal", ApplicationExitInfo.REASON_USER_REQUESTED);
+ "movePackageInternal", ApplicationExitInfo.REASON_USER_REQUESTED,
+ null /* request */);
}
final Bundle extras = new Bundle();
diff --git a/services/core/java/com/android/server/pm/ArchiveManager.java b/services/core/java/com/android/server/pm/PackageArchiverService.java
similarity index 62%
rename from services/core/java/com/android/server/pm/ArchiveManager.java
rename to services/core/java/com/android/server/pm/PackageArchiverService.java
index 5435206..9c31dc9 100644
--- a/services/core/java/com/android/server/pm/ArchiveManager.java
+++ b/services/core/java/com/android/server/pm/PackageArchiverService.java
@@ -22,11 +22,13 @@
import android.annotation.Nullable;
import android.content.Context;
import android.content.IntentSender;
+import android.content.pm.IPackageArchiverService;
import android.content.pm.LauncherActivityInfo;
import android.content.pm.LauncherApps;
import android.content.pm.PackageManager;
import android.content.pm.VersionedPackage;
import android.os.Binder;
+import android.os.ParcelableException;
import android.os.UserHandle;
import android.text.TextUtils;
@@ -47,7 +49,9 @@
* while the data directory is kept. Archived apps are included in the list of launcher apps where
* tapping them re-installs the full app.
*/
-final class ArchiveManager {
+public class PackageArchiverService extends IPackageArchiverService.Stub {
+
+ private static final String TAG = "PackageArchiver";
private final Context mContext;
private final PackageManagerService mPm;
@@ -55,36 +59,39 @@
@Nullable
private LauncherApps mLauncherApps;
- ArchiveManager(Context context, PackageManagerService mPm) {
+ public PackageArchiverService(Context context, PackageManagerService mPm) {
this.mContext = context;
this.mPm = mPm;
}
- void archiveApp(
+ @Override
+ public void requestArchive(
@NonNull String packageName,
@NonNull String callerPackageName,
- @NonNull UserHandle user,
- @NonNull IntentSender intentSender) throws PackageManager.NameNotFoundException {
+ @NonNull IntentSender intentSender,
+ @NonNull UserHandle userHandle) {
Objects.requireNonNull(packageName);
Objects.requireNonNull(callerPackageName);
- Objects.requireNonNull(user);
Objects.requireNonNull(intentSender);
+ Objects.requireNonNull(userHandle);
Computer snapshot = mPm.snapshotComputer();
- int callingUid = Binder.getCallingUid();
- int userId = user.getIdentifier();
- String callingPackageName = snapshot.getNameForUid(callingUid);
- snapshot.enforceCrossUserPermission(callingUid, userId, true, true,
+ int userId = userHandle.getIdentifier();
+ int binderUid = Binder.getCallingUid();
+ int providedUid = snapshot.getPackageUid(callerPackageName, 0, userId);
+ snapshot.enforceCrossUserPermission(binderUid, userId, true, true,
"archiveApp");
- verifyCaller(callerPackageName, callingPackageName);
- PackageStateInternal ps = getPackageState(packageName, snapshot, callingUid, user);
- verifyInstaller(packageName, ps.getInstallSource());
+ verifyCaller(providedUid, binderUid);
+ PackageStateInternal ps = getPackageState(packageName, snapshot, binderUid, userId);
+ verifyInstaller(packageName, ps);
+ // TODO(b/291569242) Verify that this list is not empty and return failure with
+ // intentsender
List<LauncherActivityInfo> mainActivities = getLauncherApps().getActivityList(
ps.getPackageName(),
new UserHandle(userId));
- // TODO(b/291569242) Verify that this list is not empty and return failure with intentsender
+ // TODO(b/282952870) Bug: should happen after the uninstall completes successfully
storeArchiveState(ps, mainActivities, userId);
// TODO(b/278553670) Add special strings for the delete dialog
@@ -93,15 +100,25 @@
callerPackageName, DELETE_KEEP_DATA, intentSender, userId);
}
+ private static void verifyInstaller(String packageName, PackageStateInternal ps) {
+ if (ps.getInstallSource().mUpdateOwnerPackageName == null
+ && ps.getInstallSource().mInstallerPackageName == null) {
+ throw new ParcelableException(
+ new PackageManager.NameNotFoundException(
+ TextUtils.formatSimple("No installer found to archive app %s.",
+ packageName)));
+ }
+ }
+
@NonNull
private static PackageStateInternal getPackageState(String packageName,
- Computer snapshot, int callingUid, UserHandle user)
- throws PackageManager.NameNotFoundException {
+ Computer snapshot, int callingUid, int userId) {
PackageStateInternal ps = snapshot.getPackageStateFiltered(packageName, callingUid,
- user.getIdentifier());
+ userId);
if (ps == null) {
- throw new PackageManager.NameNotFoundException(
- TextUtils.formatSimple("Package %s not found.", packageName));
+ throw new ParcelableException(
+ new PackageManager.NameNotFoundException(
+ TextUtils.formatSimple("Package %s not found.", packageName)));
}
return ps;
}
@@ -114,8 +131,7 @@
}
private void storeArchiveState(PackageStateInternal ps,
- List<LauncherActivityInfo> mainActivities, int userId)
- throws PackageManager.NameNotFoundException {
+ List<LauncherActivityInfo> mainActivities, int userId) {
List<ArchiveActivityInfo> activityInfos = new ArrayList<>();
for (int i = 0; i < mainActivities.size(); i++) {
// TODO(b/278553670) Extract and store launcher icons
@@ -130,41 +146,34 @@
? installSource.mUpdateOwnerPackageName : installSource.mInstallerPackageName;
synchronized (mPm.mLock) {
- getPackageSetting(ps.getPackageName(), userId).modifyUserState(userId).setArchiveState(
- new ArchiveState(activityInfos, installerPackageName));
+ PackageSetting packageSetting = getPackageSettingLocked(ps.getPackageName(), userId);
+ packageSetting
+ .modifyUserState(userId)
+ .setArchiveState(new ArchiveState(activityInfos, installerPackageName));
}
}
@NonNull
@GuardedBy("mPm.mLock")
- private PackageSetting getPackageSetting(String packageName, int userId)
- throws PackageManager.NameNotFoundException {
+ private PackageSetting getPackageSettingLocked(String packageName, int userId) {
PackageSetting ps = mPm.mSettings.getPackageLPr(packageName);
+ // Shouldn't happen, we already verify presence of the package in getPackageState()
if (ps == null || !ps.getUserStateOrDefault(userId).isInstalled()) {
- throw new PackageManager.NameNotFoundException(
- TextUtils.formatSimple("Package %s not found.", packageName));
+ throw new ParcelableException(
+ new PackageManager.NameNotFoundException(
+ TextUtils.formatSimple("Package %s not found.", packageName)));
}
return ps;
}
- private static void verifyCaller(String callerPackageName, String callingPackageName) {
- if (!TextUtils.equals(callingPackageName, callerPackageName)) {
+ private static void verifyCaller(int providedUid, int binderUid) {
+ if (providedUid != binderUid) {
throw new SecurityException(
TextUtils.formatSimple(
- "The callerPackageName %s set by the caller doesn't match the "
- + "caller's own package name %s.",
- callerPackageName,
- callingPackageName));
- }
- }
-
- private static void verifyInstaller(String packageName, InstallSource installSource) {
- // TODO(b/291060290) Verify installer supports unarchiving
- if (installSource.mUpdateOwnerPackageName == null
- && installSource.mInstallerPackageName == null) {
- throw new SecurityException(
- TextUtils.formatSimple("No installer found to archive app %s.",
- packageName));
+ "The UID %s of callerPackageName set by the caller doesn't match the "
+ + "caller's actual UID %s.",
+ providedUid,
+ binderUid));
}
}
}
diff --git a/services/core/java/com/android/server/pm/PackageFreezer.java b/services/core/java/com/android/server/pm/PackageFreezer.java
index 841b66e..7c56157 100644
--- a/services/core/java/com/android/server/pm/PackageFreezer.java
+++ b/services/core/java/com/android/server/pm/PackageFreezer.java
@@ -17,6 +17,7 @@
package com.android.server.pm;
import android.annotation.NonNull;
+import android.annotation.Nullable;
import android.content.pm.PackageManager;
import dalvik.system.CloseGuard;
@@ -29,6 +30,8 @@
* app code/data to prevent the app from running while you're working.
*/
final class PackageFreezer implements AutoCloseable {
+ @Nullable private InstallRequest mInstallRequest;
+
private final String mPackageName;
private final AtomicBoolean mClosed = new AtomicBoolean();
@@ -43,18 +46,29 @@
* {@link PackageManager#INSTALL_DONT_KILL_APP} or
* {@link PackageManager#DELETE_DONT_KILL_APP}.
*/
- PackageFreezer(PackageManagerService pm) {
+
+ PackageFreezer(PackageManagerService pm, @Nullable InstallRequest request) {
mPm = pm;
mPackageName = null;
mClosed.set(true);
mCloseGuard.open("close");
+ mInstallRequest = request;
+ // We only focus on the install Freeze metrics now
+ if (mInstallRequest != null) {
+ mInstallRequest.onFreezeStarted();
+ }
}
PackageFreezer(String packageName, int userId, String killReason,
- PackageManagerService pm, int exitInfoReason) {
+ PackageManagerService pm, int exitInfoReason, @Nullable InstallRequest request) {
mPm = pm;
mPackageName = packageName;
+ mInstallRequest = request;
final PackageSetting ps;
+ // We only focus on the install Freeze metrics now
+ if (mInstallRequest != null) {
+ mInstallRequest.onFreezeStarted();
+ }
synchronized (mPm.mLock) {
final int refCounts = mPm.mFrozenPackages
.getOrDefault(mPackageName, 0 /* defaultValue */) + 1;
@@ -92,5 +106,10 @@
}
}
}
+ // We only focus on the install Freeze metrics now
+ if (mInstallRequest != null) {
+ mInstallRequest.onFreezeCompleted();
+ mInstallRequest = null;
+ }
}
}
diff --git a/services/core/java/com/android/server/pm/PackageHandler.java b/services/core/java/com/android/server/pm/PackageHandler.java
index 83d2f6a..b4ca477 100644
--- a/services/core/java/com/android/server/pm/PackageHandler.java
+++ b/services/core/java/com/android/server/pm/PackageHandler.java
@@ -93,6 +93,7 @@
mPm.mRunningInstalls.delete(msg.arg1);
request.closeFreezer();
+ request.onInstallCompleted();
request.runPostInstallRunnable();
if (!request.isInstallExistingForUser()) {
mInstallPackageHelper.handlePackagePostInstall(request, didRestore);
diff --git a/services/core/java/com/android/server/pm/PackageInstallerSession.java b/services/core/java/com/android/server/pm/PackageInstallerSession.java
index 923bbab..bf88580 100644
--- a/services/core/java/com/android/server/pm/PackageInstallerSession.java
+++ b/services/core/java/com/android/server/pm/PackageInstallerSession.java
@@ -31,6 +31,7 @@
import static android.content.pm.PackageManager.INSTALL_FAILED_MEDIA_UNAVAILABLE;
import static android.content.pm.PackageManager.INSTALL_FAILED_MISSING_SPLIT;
import static android.content.pm.PackageManager.INSTALL_FAILED_PRE_APPROVAL_NOT_AVAILABLE;
+import static android.content.pm.PackageManager.INSTALL_FAILED_VERIFICATION_FAILURE;
import static android.content.pm.PackageManager.INSTALL_PARSE_FAILED_NO_CERTIFICATES;
import static android.content.pm.PackageManager.INSTALL_STAGED;
import static android.content.pm.PackageManager.INSTALL_SUCCEEDED;
@@ -52,6 +53,7 @@
import static com.android.server.pm.PackageInstallerService.prepareStageDir;
import static com.android.server.pm.PackageManagerService.APP_METADATA_FILE_NAME;
import static com.android.server.pm.PackageManagerServiceUtils.isInstalledByAdb;
+import static com.android.server.pm.PackageManagerShellCommandDataLoader.Metadata;
import android.Manifest;
import android.annotation.AnyThread;
@@ -838,6 +840,10 @@
params.dataLoaderParams.getComponentName().getPackageName());
}
+ static boolean isArchivedInstallation(int installFlags) {
+ return (installFlags & PackageManager.INSTALL_ARCHIVED) != 0;
+ }
+
private final Handler.Callback mHandlerCallback = new Handler.Callback() {
@Override
public boolean handleMessage(Message msg) {
@@ -896,6 +902,10 @@
return isSystemDataLoaderInstallation(this.params);
}
+ private boolean isArchivedInstallation() {
+ return isArchivedInstallation(this.params.installFlags);
+ }
+
/**
* @return {@code true} iff the installing is app an device owner or affiliated profile owner.
*/
@@ -1146,6 +1156,17 @@
if (isIncrementalInstallation() && !IncrementalManager.isAllowed()) {
throw new IllegalArgumentException("Incremental installation not allowed.");
}
+
+ if (isArchivedInstallation()) {
+ if (params.mode != SessionParams.MODE_FULL_INSTALL) {
+ throw new IllegalArgumentException(
+ "Archived installation can only be full install.");
+ }
+ if (!isStreamingInstallation() || !isSystemDataLoaderInstallation()) {
+ throw new IllegalArgumentException(
+ "Archived installation can only use Streaming System DataLoader.");
+ }
+ }
}
/**
@@ -1462,6 +1483,58 @@
}
@GuardedBy("mLock")
+ private List<ApkLite> getAddedApkLitesLocked() throws PackageManagerException {
+ if (!isArchivedInstallation()) {
+ List<File> files = getAddedApksLocked();
+ final List<ApkLite> result = new ArrayList<>(files.size());
+
+ final ParseTypeImpl input = ParseTypeImpl.forDefaultParsing();
+ for (int i = 0, size = files.size(); i < size; ++i) {
+ final ParseResult<ApkLite> parseResult = ApkLiteParseUtils.parseApkLite(
+ input.reset(), files.get(i),
+ ParsingPackageUtils.PARSE_COLLECT_CERTIFICATES);
+ if (parseResult.isError()) {
+ throw new PackageManagerException(parseResult.getErrorCode(),
+ parseResult.getErrorMessage(), parseResult.getException());
+ }
+ result.add(parseResult.getResult());
+ }
+
+ return result;
+ }
+
+ InstallationFile[] files = getInstallationFilesLocked();
+ final List<ApkLite> result = new ArrayList<>(files.length);
+
+ for (int i = 0, size = files.length; i < size; ++i) {
+ File file = new File(stageDir, files[i].getName());
+ if (!sAddedApkFilter.accept(file)) {
+ continue;
+ }
+
+ final Metadata metadata;
+ try {
+ metadata = Metadata.fromByteArray(files[i].getMetadata());
+ } catch (IOException e) {
+ throw new PackageManagerException(INSTALL_FAILED_INTERNAL_ERROR,
+ "Failed to ", e);
+ }
+ if (metadata.getMode() != Metadata.ARCHIVED) {
+ throw new PackageManagerException(INSTALL_FAILED_VERIFICATION_FAILURE,
+ "File metadata is not for ARCHIVED package: " + file);
+ }
+
+ var archPkg = metadata.getArchivedPackage();
+ if (archPkg.packageName == null || archPkg.signingDetails == null) {
+ throw new PackageManagerException(INSTALL_FAILED_VERIFICATION_FAILURE,
+ "ArchivedPackage does not contain required info: " + file);
+ }
+ result.add(new ApkLite(file.getAbsolutePath(), archPkg));
+ }
+ return result;
+ }
+
+ @GuardedBy("mLock")
private List<File> getRemovedFilesLocked() {
String[] names = getNamesLocked();
return filterFiles(stageDir, names, sRemovedFilter);
@@ -3176,7 +3249,7 @@
}
}
- final List<File> addedFiles = getAddedApksLocked();
+ final List<ApkLite> addedFiles = getAddedApkLitesLocked();
if (addedFiles.isEmpty()
&& (removeSplitList.size() == 0 || getStagedAppMetadataFile() != null)) {
throw new PackageManagerException(INSTALL_FAILED_INVALID_APK,
@@ -3190,15 +3263,7 @@
final ArraySet<String> requiredSplitTypes = new ArraySet<>();
final ArrayMap<String, ApkLite> splitApks = new ArrayMap<>();
final ParseTypeImpl input = ParseTypeImpl.forDefaultParsing();
- for (File addedFile : addedFiles) {
- final ParseResult<ApkLite> result = ApkLiteParseUtils.parseApkLite(input.reset(),
- addedFile, ParsingPackageUtils.PARSE_COLLECT_CERTIFICATES);
- if (result.isError()) {
- throw new PackageManagerException(result.getErrorCode(),
- result.getErrorMessage(), result.getException());
- }
-
- final ApkLite apk = result.getResult();
+ for (ApkLite apk : addedFiles) {
if (!stagedSplits.add(apk.getSplitName())) {
throw new PackageManagerException(INSTALL_FAILED_INVALID_APK,
"Split " + apk.getSplitName() + " was defined multiple times");
@@ -3214,7 +3279,7 @@
}
mHasDeviceAdminReceiver = apk.isHasDeviceAdminReceiver();
- assertApkConsistentLocked(String.valueOf(addedFile), apk);
+ assertApkConsistentLocked(String.valueOf(apk), apk);
// Take this opportunity to enforce uniform naming
final String targetName = ApkLiteParseUtils.splitNameToFileName(apk);
@@ -3235,7 +3300,10 @@
}
final File targetFile = new File(stageDir, targetName);
- resolveAndStageFileLocked(addedFile, targetFile, apk.getSplitName());
+ if (!isArchivedInstallation()) {
+ final File sourceFile = new File(apk.getPath());
+ resolveAndStageFileLocked(sourceFile, targetFile, apk.getSplitName());
+ }
// Base is coming from session
if (apk.getSplitName() == null) {
@@ -4005,6 +4073,11 @@
NativeLibraryHelper.removeNativeBinariesFromDirLI(libDir, true);
}
+ // Skip native libraries processing for archival installation.
+ if (isArchivedInstallation()) {
+ return;
+ }
+
NativeLibraryHelper.Handle handle = null;
try {
handle = NativeLibraryHelper.Handle.create(packageLite);
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index f0bbd35..64e8f7a 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -77,6 +77,7 @@
import android.content.IntentSender.SendIntentException;
import android.content.pm.ActivityInfo;
import android.content.pm.ApplicationInfo;
+import android.content.pm.ArchivedPackageParcel;
import android.content.pm.AuxiliaryResolveInfo;
import android.content.pm.ChangedPackages;
import android.content.pm.Checksum;
@@ -796,6 +797,8 @@
final PackageInstallerService mInstallerService;
+ final PackageArchiverService mArchiverService;
+
final ArtManagerService mArtManagerService;
// TODO(b/260124949): Remove these.
@@ -1624,7 +1627,8 @@
(i, pm) -> new CrossProfileIntentFilterHelper(i.getSettings(),
i.getUserManagerService(), i.getLock(), i.getUserManagerInternal(),
context),
- (i, pm) -> new UpdateOwnershipHelper());
+ (i, pm) -> new UpdateOwnershipHelper(),
+ (i, pm) -> new PackageArchiverService(i.getContext(), pm));
if (Build.VERSION.SDK_INT <= 0) {
Slog.w(TAG, "**** ro.build.version.sdk not set!");
@@ -1769,6 +1773,7 @@
mFactoryTest = testParams.factoryTest;
mIncrementalManager = testParams.incrementalManager;
mInstallerService = testParams.installerService;
+ mArchiverService = testParams.archiverService;
mInstantAppRegistry = testParams.instantAppRegistry;
mChangedPackagesTracker = testParams.changedPackagesTracker;
mInstantAppResolverConnection = testParams.instantAppResolverConnection;
@@ -1967,9 +1972,6 @@
mApexManager = injector.getApexManager();
mAppsFilter = mInjector.getAppsFilter();
- mInstantAppRegistry = new InstantAppRegistry(mContext, mPermissionManager,
- mInjector.getUserManagerInternal(), new DeletePackageHelper(this));
-
mChangedPackagesTracker = new ChangedPackagesTracker();
mAppInstallDir = new File(Environment.getDataDirectory(), "app");
@@ -1983,8 +1985,11 @@
mAppDataHelper = new AppDataHelper(this);
mInstallPackageHelper = new InstallPackageHelper(this, mAppDataHelper);
mRemovePackageHelper = new RemovePackageHelper(this, mAppDataHelper);
- mDeletePackageHelper = new DeletePackageHelper(this, mRemovePackageHelper,
- mAppDataHelper);
+ mDeletePackageHelper = new DeletePackageHelper(this, mRemovePackageHelper);
+
+ mInstantAppRegistry = new InstantAppRegistry(mContext, mPermissionManager,
+ mInjector.getUserManagerInternal(), mDeletePackageHelper);
+
mSharedLibraries.setDeletePackageHelper(mDeletePackageHelper);
mPreferredActivityHelper = new PreferredActivityHelper(this);
mResolveIntentHelper = new ResolveIntentHelper(mContext, mPreferredActivityHelper,
@@ -2348,6 +2353,7 @@
});
mInstallerService = mInjector.getPackageInstallerService();
+ mArchiverService = mInjector.getPackageArchiverService();
final ComponentName instantAppResolverComponent = getInstantAppResolver(computer);
if (instantAppResolverComponent != null) {
if (DEBUG_INSTANT) {
@@ -2892,10 +2898,13 @@
private void notifyPackageUseInternal(String packageName, int reason) {
long time = System.currentTimeMillis();
- this.commitPackageStateMutation(null, mutator -> {
- final PackageStateWrite state = mutator.forPackage(packageName);
- state.setLastPackageUsageTime(reason, time);
- });
+ synchronized (mLock) {
+ final PackageSetting pkgSetting = mSettings.getPackageLPr(packageName);
+ if (pkgSetting == null) {
+ return;
+ }
+ pkgSetting.getPkgState().setLastPackageUsageTimeInMills(reason, time);
+ }
}
/*package*/ DexManager getDexManager() {
@@ -4286,16 +4295,17 @@
}
public PackageFreezer freezePackage(String packageName, int userId, String killReason,
- int exitInfoReason) {
- return new PackageFreezer(packageName, userId, killReason, this, exitInfoReason);
+ int exitInfoReason, InstallRequest request) {
+ return new PackageFreezer(packageName, userId, killReason, this, exitInfoReason, request);
}
public PackageFreezer freezePackageForDelete(String packageName, int userId, int deleteFlags,
String killReason, int exitInfoReason) {
if ((deleteFlags & PackageManager.DELETE_DONT_KILL_APP) != 0) {
- return new PackageFreezer(this);
+ return new PackageFreezer(this, null /* request */);
} else {
- return freezePackage(packageName, userId, killReason, exitInfoReason);
+ return freezePackage(packageName, userId, killReason, exitInfoReason,
+ null /* request */);
}
}
@@ -4605,7 +4615,7 @@
mDomainVerificationConnection, mInstallerService, mPackageProperty,
mResolveComponentName, mInstantAppResolverSettingsComponent,
mRequiredSdkSandboxPackage, mServicesExtensionPackageName,
- mSharedSystemSharedLibraryPackageName);
+ mSharedSystemSharedLibraryPackageName, mArchiverService);
}
@Override
@@ -4624,7 +4634,7 @@
try (PackageFreezer ignored =
freezePackage(packageName, UserHandle.USER_ALL,
"clearApplicationProfileData",
- ApplicationExitInfo.REASON_OTHER)) {
+ ApplicationExitInfo.REASON_OTHER, null /* request */)) {
synchronized (mInstallLock) {
mAppDataHelper.clearAppProfilesLIF(pkg);
}
@@ -4667,7 +4677,7 @@
final boolean succeeded;
try (PackageFreezer freezer = freezePackage(packageName, UserHandle.USER_ALL,
"clearApplicationUserData",
- ApplicationExitInfo.REASON_USER_REQUESTED)) {
+ ApplicationExitInfo.REASON_USER_REQUESTED, null /* request */)) {
synchronized (mInstallLock) {
succeeded = clearApplicationUserDataLIF(snapshotComputer(), packageName,
userId);
@@ -6289,6 +6299,39 @@
}
}
+ @Override
+ public ArchivedPackageParcel getArchivedPackage(String apkPath) {
+ final ParsedPackage parsedPackage;
+ try (PackageParser2 pp = mInjector.getPreparingPackageParser()) {
+ parsedPackage = pp.parsePackage(new File(apkPath),
+ getDefParseFlags() | ParsingPackageUtils.PARSE_COLLECT_CERTIFICATES, false);
+ } catch (PackageManagerException e) {
+ throw new IllegalArgumentException("Failed parse", e);
+ }
+
+ ArchivedPackageParcel archPkg = new ArchivedPackageParcel();
+ archPkg.packageName = parsedPackage.getPackageName();
+ archPkg.signingDetails = parsedPackage.getSigningDetails();
+
+ long longVersionCode = parsedPackage.getLongVersionCode();
+ archPkg.versionCodeMajor = (int) (longVersionCode >> 32);
+ archPkg.versionCode = (int) longVersionCode;
+
+ archPkg.targetSdkVersion = parsedPackage.getTargetSdkVersion();
+
+ // These get translated in flags important for user data management.
+ archPkg.clearUserDataAllowed = parsedPackage.isClearUserDataAllowed();
+ archPkg.backupAllowed = parsedPackage.isBackupAllowed();
+ archPkg.defaultToDeviceProtectedStorage =
+ parsedPackage.isDefaultToDeviceProtectedStorage();
+ archPkg.requestLegacyExternalStorage = parsedPackage.isRequestLegacyExternalStorage();
+ archPkg.userDataFragile = parsedPackage.isUserDataFragile();
+ archPkg.clearUserDataOnFailedRestoreAllowed =
+ parsedPackage.isClearUserDataOnFailedRestoreAllowed();
+
+ return archPkg;
+ }
+
/**
* Wait for the handler to finish handling all pending messages.
* @param timeoutMillis Maximum time in milliseconds to wait.
diff --git a/services/core/java/com/android/server/pm/PackageManagerServiceInjector.java b/services/core/java/com/android/server/pm/PackageManagerServiceInjector.java
index 51840e7..9495279 100644
--- a/services/core/java/com/android/server/pm/PackageManagerServiceInjector.java
+++ b/services/core/java/com/android/server/pm/PackageManagerServiceInjector.java
@@ -127,6 +127,8 @@
mPreparingPackageParserProducer;
private final Singleton<PackageInstallerService>
mPackageInstallerServiceProducer;
+ private final Singleton<PackageArchiverService>
+ mPackageArchiverServiceProducer;
private final ProducerWithArgument<InstantAppResolverConnection, ComponentName>
mInstantAppResolverConnectionProducer;
private final Singleton<LegacyPermissionManagerInternal>
@@ -185,7 +187,8 @@
Producer<IBackupManager> iBackupManager,
Producer<SharedLibrariesImpl> sharedLibrariesProducer,
Producer<CrossProfileIntentFilterHelper> crossProfileIntentFilterHelperProducer,
- Producer<UpdateOwnershipHelper> updateOwnershipHelperProducer) {
+ Producer<UpdateOwnershipHelper> updateOwnershipHelperProducer,
+ Producer<PackageArchiverService> packageArchiverServiceProducer) {
mContext = context;
mLock = lock;
mInstaller = installer;
@@ -241,6 +244,7 @@
mCrossProfileIntentFilterHelperProducer = new Singleton<>(
crossProfileIntentFilterHelperProducer);
mUpdateOwnershipHelperProducer = new Singleton<>(updateOwnershipHelperProducer);
+ mPackageArchiverServiceProducer = new Singleton<>(packageArchiverServiceProducer);
}
/**
@@ -387,6 +391,10 @@
return mPackageInstallerServiceProducer.get(this, mPackageManager);
}
+ public PackageArchiverService getPackageArchiverService() {
+ return mPackageArchiverServiceProducer.get(this, mPackageManager);
+ }
+
public InstantAppResolverConnection getInstantAppResolverConnection(
ComponentName instantAppResolverComponent) {
return mInstantAppResolverConnectionProducer.produce(
diff --git a/services/core/java/com/android/server/pm/PackageManagerServiceTestParams.java b/services/core/java/com/android/server/pm/PackageManagerServiceTestParams.java
index ca57209..b91ce4b 100644
--- a/services/core/java/com/android/server/pm/PackageManagerServiceTestParams.java
+++ b/services/core/java/com/android/server/pm/PackageManagerServiceTestParams.java
@@ -60,6 +60,7 @@
public @Nullable String incidentReportApproverPackage;
public IncrementalManager incrementalManager;
public PackageInstallerService installerService;
+ public PackageArchiverService archiverService;
public InstantAppRegistry instantAppRegistry;
public ChangedPackagesTracker changedPackagesTracker = new ChangedPackagesTracker();
public InstantAppResolverConnection instantAppResolverConnection;
diff --git a/services/core/java/com/android/server/pm/PackageManagerServiceUtils.java b/services/core/java/com/android/server/pm/PackageManagerServiceUtils.java
index db997d8..2028231 100644
--- a/services/core/java/com/android/server/pm/PackageManagerServiceUtils.java
+++ b/services/core/java/com/android/server/pm/PackageManagerServiceUtils.java
@@ -919,16 +919,22 @@
final File packageFile = new File(packagePath);
final long sizeBytes;
- try {
- sizeBytes = InstallLocationUtils.calculateInstalledSize(pkg, abiOverride);
- } catch (IOException e) {
- if (!packageFile.exists()) {
- ret.recommendedInstallLocation = InstallLocationUtils.RECOMMEND_FAILED_INVALID_URI;
- } else {
- ret.recommendedInstallLocation = InstallLocationUtils.RECOMMEND_FAILED_INVALID_APK;
- }
+ if (!PackageInstallerSession.isArchivedInstallation(flags)) {
+ try {
+ sizeBytes = InstallLocationUtils.calculateInstalledSize(pkg, abiOverride);
+ } catch (IOException e) {
+ if (!packageFile.exists()) {
+ ret.recommendedInstallLocation =
+ InstallLocationUtils.RECOMMEND_FAILED_INVALID_URI;
+ } else {
+ ret.recommendedInstallLocation =
+ InstallLocationUtils.RECOMMEND_FAILED_INVALID_APK;
+ }
- return ret;
+ return ret;
+ }
+ } else {
+ sizeBytes = 0;
}
final PackageInstaller.SessionParams sessionParams = new PackageInstaller.SessionParams(
diff --git a/services/core/java/com/android/server/pm/PackageManagerShellCommand.java b/services/core/java/com/android/server/pm/PackageManagerShellCommand.java
index 8bdbe04..d9f1df5 100644
--- a/services/core/java/com/android/server/pm/PackageManagerShellCommand.java
+++ b/services/core/java/com/android/server/pm/PackageManagerShellCommand.java
@@ -43,6 +43,7 @@
import android.content.Intent;
import android.content.IntentSender;
import android.content.pm.ApplicationInfo;
+import android.content.pm.ArchivedPackageParcel;
import android.content.pm.FeatureInfo;
import android.content.pm.IPackageDataObserver;
import android.content.pm.IPackageInstaller;
@@ -82,6 +83,7 @@
import android.os.Binder;
import android.os.Build;
import android.os.Bundle;
+import android.os.Environment;
import android.os.IBinder;
import android.os.IUserManager;
import android.os.ParcelFileDescriptor;
@@ -105,6 +107,7 @@
import android.text.format.DateUtils;
import android.util.ArrayMap;
import android.util.IntArray;
+import android.util.Pair;
import android.util.PrintWriterPrinter;
import android.util.Slog;
import android.util.SparseArray;
@@ -246,6 +249,8 @@
return runStreamingInstall();
case "install-incremental":
return runIncrementalInstall();
+ case "install-archived":
+ return runArchivedInstall();
case "install-abandon":
case "install-destroy":
return runInstallAbandon();
@@ -1549,6 +1554,16 @@
return doRunInstall(params);
}
+ private int runArchivedInstall() throws RemoteException {
+ final InstallParams params = makeInstallParams(UNSUPPORTED_INSTALL_CMD_OPTS);
+ params.sessionParams.installFlags |= PackageManager.INSTALL_ARCHIVED;
+ if (params.sessionParams.dataLoaderParams == null) {
+ params.sessionParams.setDataLoaderParams(
+ PackageManagerShellCommandDataLoader.getStreamingDataLoaderParams(this));
+ }
+ return doRunInstall(params);
+ }
+
private int runIncrementalInstall() throws RemoteException {
final InstallParams params = makeInstallParams(UNSUPPORTED_INSTALL_CMD_OPTS);
if (params.sessionParams.dataLoaderParams == null) {
@@ -1565,9 +1580,22 @@
private int doRunInstall(final InstallParams params) throws RemoteException {
final PrintWriter pw = getOutPrintWriter();
+ int requestUserId = params.userId;
+ if (requestUserId != UserHandle.USER_ALL && requestUserId != UserHandle.USER_CURRENT) {
+ UserManagerInternal umi =
+ LocalServices.getService(UserManagerInternal.class);
+ UserInfo userInfo = umi.getUserInfo(requestUserId);
+ if (userInfo == null) {
+ pw.println("Failure [user " + requestUserId + " doesn't exist]");
+ return 1;
+ }
+ }
+
final boolean isStreaming = params.sessionParams.dataLoaderParams != null;
final boolean isApex =
(params.sessionParams.installFlags & PackageManager.INSTALL_APEX) != 0;
+ final boolean installArchived =
+ (params.sessionParams.installFlags & PackageManager.INSTALL_ARCHIVED) != 0;
ArrayList<String> args = getRemainingArgs();
@@ -1584,6 +1612,13 @@
return 1;
}
+ if (installArchived) {
+ if (hasSplits) {
+ pw.println("Error: can't have SPLIT(s) for Archival install");
+ return 1;
+ }
+ }
+
if (!isStreaming) {
if (fromStdIn && hasSplits) {
pw.println("Error: can't specify SPLIT(s) along with STDIN");
@@ -1602,8 +1637,8 @@
boolean abandonSession = true;
try {
if (isStreaming) {
- if (doAddFiles(sessionId, args, params.sessionParams.sizeBytes, isApex)
- != PackageInstaller.STATUS_SUCCESS) {
+ if (doAddFiles(sessionId, args, params.sessionParams.sizeBytes, isApex,
+ installArchived) != PackageInstaller.STATUS_SUCCESS) {
return 1;
}
} else {
@@ -2319,6 +2354,15 @@
break;
case "--user":
userId = UserHandle.parseUserArg(getNextArgRequired());
+ if (userId != UserHandle.USER_ALL && userId != UserHandle.USER_CURRENT) {
+ UserManagerInternal umi =
+ LocalServices.getService(UserManagerInternal.class);
+ UserInfo userInfo = umi.getUserInfo(userId);
+ if (userInfo == null) {
+ pw.println("Failure [user " + userId + " doesn't exist]");
+ return 1;
+ }
+ }
break;
case "--versionCode":
versionCode = Long.parseLong(getNextArgRequired());
@@ -3836,7 +3880,7 @@
}
private int doAddFiles(int sessionId, ArrayList<String> args, long sessionSizeBytes,
- boolean isApex) throws RemoteException {
+ boolean isApex, boolean installArchived) throws RemoteException {
PackageInstaller.Session session = null;
try {
session = new PackageInstaller.Session(
@@ -3845,9 +3889,17 @@
// 1. Single file from stdin.
if (args.isEmpty() || STDIN_PATH.equals(args.get(0))) {
final String name = "base" + RANDOM.nextInt() + "." + (isApex ? "apex" : "apk");
- final Metadata metadata = Metadata.forStdIn(name);
- session.addFile(LOCATION_DATA_APP, name, sessionSizeBytes,
- metadata.toByteArray(), null);
+ final long size;
+ final Metadata metadata;
+ if (!installArchived) {
+ metadata = Metadata.forStdIn(name);
+ size = sessionSizeBytes;
+ } else {
+ metadata = Metadata.forArchived(
+ getArchivedPackage(STDIN_PATH, sessionSizeBytes));
+ size = -1;
+ }
+ session.addFile(LOCATION_DATA_APP, name, size, metadata.toByteArray(), null);
return 0;
}
@@ -3856,16 +3908,21 @@
if (delimLocation != -1) {
// 2. File with specified size read from stdin.
+ if (installArchived) {
+ getOutPrintWriter().println(
+ "Error: can't install with size from STDIN for Archival install");
+ return 1;
+ }
if (processArgForStdin(arg, session) != 0) {
return 1;
}
} else {
// 3. Local file.
- processArgForLocalFile(arg, session);
+ processArgForLocalFile(arg, session, installArchived);
}
}
return 0;
- } catch (IllegalArgumentException e) {
+ } catch (IOException | IllegalArgumentException e) {
getErrPrintWriter().println("Failed to add file(s), reason: " + e);
getOutPrintWriter().println("Failure [failed to add file(s)]");
return 1;
@@ -3954,26 +4011,67 @@
}
}
- private void processArgForLocalFile(String arg, PackageInstaller.Session session) {
+ private ArchivedPackageParcel getArchivedPackage(String inPath, long sizeBytes)
+ throws RemoteException, IOException {
+ final var fdWithSize = openInFile(inPath, sizeBytes);
+ if (fdWithSize.first == null) {
+ throw new IllegalArgumentException("Error: Can't open file: " + inPath);
+ }
+
+ File tmpFile = null;
+ final ParcelFileDescriptor fd = fdWithSize.first;
+ try (InputStream inStream = new AutoCloseInputStream(fd)) {
+ final long identity = Binder.clearCallingIdentity();
+ try {
+ File tmpStagingDir = Environment.getDataAppDirectory(null);
+ tmpFile = new File(tmpStagingDir, "tmdl" + RANDOM.nextInt() + ".tmp");
+
+ try (OutputStream outStream = new FileOutputStream(tmpFile)) {
+ Streams.copy(inStream, outStream);
+ }
+
+ return mInterface.getArchivedPackage(tmpFile.getAbsolutePath());
+ } finally {
+ if (tmpFile != null) {
+ tmpFile.delete();
+ }
+ Binder.restoreCallingIdentity(identity);
+ }
+ } catch (IOException e) {
+ throw new IllegalArgumentException("Error: Can't stage file: " + inPath, e);
+ }
+ }
+
+ private void processArgForLocalFile(String arg, PackageInstaller.Session session,
+ boolean installArchived) throws IOException, RemoteException {
final String inPath = arg;
final File file = new File(inPath);
final String name = file.getName();
- final long size = getFileStatSize(file);
- final Metadata metadata = Metadata.forLocalFile(inPath);
+ final long size;
+ final Metadata metadata;
+ if (installArchived) {
+ metadata = Metadata.forArchived(getArchivedPackage(inPath, -1));
+ size = 0;
+ } else {
+ metadata = Metadata.forLocalFile(inPath);
+ size = getFileStatSize(file);
+ }
byte[] v4signatureBytes = null;
- // Try to load the v4 signature file for the APK; it might not exist.
- final String v4SignaturePath = inPath + V4Signature.EXT;
- final ParcelFileDescriptor pfd = openFileForSystem(v4SignaturePath, "r");
- if (pfd != null) {
- try {
- final V4Signature v4signature = V4Signature.readFrom(pfd);
- v4signatureBytes = v4signature.toByteArray();
- } catch (IOException ex) {
- Slog.e(TAG, "V4 signature file exists but failed to be parsed.", ex);
- } finally {
- IoUtils.closeQuietly(pfd);
+ if (!installArchived) {
+ // Try to load the v4 signature file for the APK; it might not exist.
+ final String v4SignaturePath = inPath + V4Signature.EXT;
+ final ParcelFileDescriptor pfd = openFileForSystem(v4SignaturePath, "r");
+ if (pfd != null) {
+ try {
+ final V4Signature v4signature = V4Signature.readFrom(pfd);
+ v4signatureBytes = v4signature.toByteArray();
+ } catch (IOException ex) {
+ Slog.e(TAG, "V4 signature file exists but failed to be parsed.", ex);
+ } finally {
+ IoUtils.closeQuietly(pfd);
+ }
}
}
@@ -3995,6 +4093,32 @@
return 0;
}
+ private Pair<ParcelFileDescriptor, Long> openInFile(String inPath, long sizeBytes)
+ throws IOException {
+ final ParcelFileDescriptor fd;
+ if (STDIN_PATH.equals(inPath)) {
+ fd = ParcelFileDescriptor.dup(getInFileDescriptor());
+ } else if (inPath != null) {
+ fd = openFileForSystem(inPath, "r");
+ if (fd == null) {
+ return Pair.create(null, -1L);
+ }
+ sizeBytes = fd.getStatSize();
+ if (sizeBytes < 0) {
+ fd.close();
+ getErrPrintWriter().println("Unable to get size of: " + inPath);
+ return Pair.create(null, -1L);
+ }
+ } else {
+ fd = ParcelFileDescriptor.dup(getInFileDescriptor());
+ }
+ if (sizeBytes <= 0) {
+ getErrPrintWriter().println("Error: must specify an APK size");
+ return Pair.create(null, 1L);
+ }
+ return Pair.create(fd, sizeBytes);
+ }
+
private int doWriteSplit(int sessionId, String inPath, long sizeBytes, String splitName,
boolean logSuccess) throws RemoteException {
PackageInstaller.Session session = null;
@@ -4004,26 +4128,13 @@
final PrintWriter pw = getOutPrintWriter();
- final ParcelFileDescriptor fd;
- if (STDIN_PATH.equals(inPath)) {
- fd = ParcelFileDescriptor.dup(getInFileDescriptor());
- } else if (inPath != null) {
- fd = openFileForSystem(inPath, "r");
- if (fd == null) {
- return -1;
- }
- sizeBytes = fd.getStatSize();
- if (sizeBytes < 0) {
- getErrPrintWriter().println("Unable to get size of: " + inPath);
- return -1;
- }
- } else {
- fd = ParcelFileDescriptor.dup(getInFileDescriptor());
+ final var fdWithSize = openInFile(inPath, sizeBytes);
+ if (fdWithSize.first == null) {
+ long resultCode = fdWithSize.second;
+ return (int) resultCode;
}
- if (sizeBytes <= 0) {
- getErrPrintWriter().println("Error: must specify an APK size");
- return 1;
- }
+ final ParcelFileDescriptor fd = fdWithSize.first;
+ sizeBytes = fdWithSize.second;
session.write(splitName, 0, sizeBytes, fd);
diff --git a/services/core/java/com/android/server/pm/PackageManagerShellCommandDataLoader.java b/services/core/java/com/android/server/pm/PackageManagerShellCommandDataLoader.java
index a1e5153..fbe5a51 100644
--- a/services/core/java/com/android/server/pm/PackageManagerShellCommandDataLoader.java
+++ b/services/core/java/com/android/server/pm/PackageManagerShellCommandDataLoader.java
@@ -18,9 +18,11 @@
import android.annotation.NonNull;
import android.content.ComponentName;
+import android.content.pm.ArchivedPackageParcel;
import android.content.pm.DataLoaderParams;
import android.content.pm.InstallationFile;
import android.content.pm.PackageInstaller;
+import android.os.Parcel;
import android.os.ParcelFileDescriptor;
import android.os.ShellCommand;
import android.service.dataloader.DataLoaderService;
@@ -37,6 +39,7 @@
import java.nio.ByteOrder;
import java.nio.charset.StandardCharsets;
import java.security.SecureRandom;
+import java.util.Arrays;
import java.util.Collection;
import java.util.concurrent.atomic.AtomicLong;
@@ -136,9 +139,13 @@
* Everything streamed.
*/
static final byte STREAMING = 3;
+ /**
+ * Archived install.
+ */
+ static final byte ARCHIVED = 4;
private final byte mMode;
- private final String mData;
+ private final byte[] mData;
private final String mSalt;
private static AtomicLong sGlobalSalt = new AtomicLong((new SecureRandom()).nextLong());
@@ -156,6 +163,21 @@
return new Metadata(LOCAL_FILE, filePath, nextGlobalSalt().toString());
}
+ /** @hide */
+ @VisibleForTesting
+ public static Metadata forArchived(ArchivedPackageParcel archivedPackage) {
+ Parcel parcel = Parcel.obtain();
+ byte[] bytes;
+ try {
+ parcel.writeParcelable(archivedPackage, 0);
+ bytes = parcel.marshall();
+ } finally {
+ parcel.recycle();
+ }
+
+ return new Metadata(ARCHIVED, bytes, null);
+ }
+
static Metadata forDataOnlyStreaming(String fileId) {
return new Metadata(DATA_ONLY_STREAMING, fileId);
}
@@ -169,8 +191,12 @@
}
private Metadata(byte mode, String data, String salt) {
+ this(mode, (data != null ? data : "").getBytes(StandardCharsets.UTF_8), salt);
+ }
+
+ private Metadata(byte mode, byte[] data, String salt) {
this.mMode = mode;
- this.mData = (data == null) ? "" : data;
+ this.mData = data;
this.mSalt = salt;
}
@@ -181,22 +207,21 @@
int offset = 0;
final byte mode = bytes[offset];
offset += 1;
- final String data;
+ final byte[] data;
final String salt;
switch (mode) {
case LOCAL_FILE: {
int dataSize = ByteBuffer.wrap(bytes, offset, 4).order(
ByteOrder.LITTLE_ENDIAN).getInt();
offset += 4;
- data = new String(bytes, offset, dataSize, StandardCharsets.UTF_8);
+ data = Arrays.copyOfRange(bytes, offset, offset + dataSize);
offset += dataSize;
salt = new String(bytes, offset, bytes.length - offset,
StandardCharsets.UTF_8);
break;
}
default:
- data = new String(bytes, offset, bytes.length - offset,
- StandardCharsets.UTF_8);
+ data = Arrays.copyOfRange(bytes, offset, bytes.length);
salt = null;
break;
}
@@ -207,7 +232,7 @@
@VisibleForTesting
public byte[] toByteArray() {
final byte[] result;
- final byte[] dataBytes = this.mData.getBytes(StandardCharsets.UTF_8);
+ final byte[] dataBytes = this.mData;
switch (this.mMode) {
case LOCAL_FILE: {
int dataSize = dataBytes.length;
@@ -237,9 +262,26 @@
return this.mMode;
}
- String getData() {
+ byte[] getData() {
return this.mData;
}
+
+ ArchivedPackageParcel getArchivedPackage() {
+ if (getMode() != ARCHIVED) {
+ throw new IllegalStateException("Not an archived package metadata.");
+ }
+
+ Parcel parcel = Parcel.obtain();
+ ArchivedPackageParcel result;
+ try {
+ parcel.unmarshall(this.mData, 0, this.mData.length);
+ parcel.setDataPosition(0);
+ result = parcel.readParcelable(ArchivedPackageParcel.class.getClassLoader());
+ } finally {
+ parcel.recycle();
+ }
+ return result;
+ }
}
private static class DataLoader implements DataLoaderService.DataLoader {
@@ -278,7 +320,9 @@
case Metadata.LOCAL_FILE: {
ParcelFileDescriptor incomingFd = null;
try {
- incomingFd = getLocalFilePFD(shellCommand, metadata.getData());
+ final String filePath = new String(metadata.getData(),
+ StandardCharsets.UTF_8);
+ incomingFd = getLocalFilePFD(shellCommand, filePath);
mConnector.writeData(file.getName(), 0, incomingFd.getStatSize(),
incomingFd);
} finally {
@@ -286,6 +330,10 @@
}
break;
}
+ case Metadata.ARCHIVED: {
+ // Do nothing, metadata already contains everything needed for install.
+ break;
+ }
default:
Slog.e(TAG, "Unsupported metadata mode: " + metadata.getMode());
return false;
diff --git a/services/core/java/com/android/server/pm/PackageMetrics.java b/services/core/java/com/android/server/pm/PackageMetrics.java
index 2d58fe5..c1580c4b 100644
--- a/services/core/java/com/android/server/pm/PackageMetrics.java
+++ b/services/core/java/com/android/server/pm/PackageMetrics.java
@@ -51,13 +51,15 @@
public static final int STEP_RECONCILE = 3;
public static final int STEP_COMMIT = 4;
public static final int STEP_DEXOPT = 5;
+ public static final int STEP_FREEZE_INSTALL = 6;
@IntDef(prefix = {"STEP_"}, value = {
STEP_PREPARE,
STEP_SCAN,
STEP_RECONCILE,
STEP_COMMIT,
- STEP_DEXOPT
+ STEP_DEXOPT,
+ STEP_FREEZE_INSTALL
})
@Retention(RetentionPolicy.SOURCE)
public @interface StepInt {
@@ -109,10 +111,17 @@
long versionCode = 0, apksSize = 0;
if (success) {
- final PackageSetting ps = mInstallRequest.getScannedPackageSetting();
- if (ps != null) {
- versionCode = ps.getVersionCode();
- apksSize = getApksSize(ps.getPath());
+ // TODO: Remove temp try-catch to avoid IllegalStateException. The reason is because
+ // the scan result is null for installExistingPackageAsUser(). Because it's installing
+ // a package that's already existing, there's no scanning or parsing involved
+ try {
+ final PackageSetting ps = mInstallRequest.getScannedPackageSetting();
+ if (ps != null) {
+ versionCode = ps.getVersionCode();
+ apksSize = getApksSize(ps.getPath());
+ }
+ } catch (IllegalStateException e) {
+ // no-op
}
}
diff --git a/services/core/java/com/android/server/pm/PrepareFailure.java b/services/core/java/com/android/server/pm/PrepareFailure.java
index 3180bac..09cb6b9 100644
--- a/services/core/java/com/android/server/pm/PrepareFailure.java
+++ b/services/core/java/com/android/server/pm/PrepareFailure.java
@@ -42,7 +42,8 @@
}
PrepareFailure(String message, Exception e) {
- super(((PackageManagerException) e).error,
+ super(e instanceof PackageManagerException ? ((PackageManagerException) e).error
+ : PackageManager.INSTALL_FAILED_INTERNAL_ERROR,
ExceptionUtils.getCompleteMessage(message, e));
}
diff --git a/services/core/java/com/android/server/pm/RemovePackageHelper.java b/services/core/java/com/android/server/pm/RemovePackageHelper.java
index 6d3b26c..d4f30fe 100644
--- a/services/core/java/com/android/server/pm/RemovePackageHelper.java
+++ b/services/core/java/com/android/server/pm/RemovePackageHelper.java
@@ -253,6 +253,56 @@
}
}
+ public void clearPackageStateForUserLIF(PackageSetting ps, int userId,
+ PackageRemovedInfo outInfo, int flags) {
+ final AndroidPackage pkg;
+ final SharedUserSetting sus;
+ synchronized (mPm.mLock) {
+ pkg = mPm.mPackages.get(ps.getPackageName());
+ sus = mPm.mSettings.getSharedUserSettingLPr(ps);
+ }
+
+ mAppDataHelper.destroyAppProfilesLIF(pkg);
+
+ final List<AndroidPackage> sharedUserPkgs =
+ sus != null ? sus.getPackages() : Collections.emptyList();
+ final PreferredActivityHelper preferredActivityHelper = new PreferredActivityHelper(mPm);
+ final int[] userIds = (userId == UserHandle.USER_ALL) ? mUserManagerInternal.getUserIds()
+ : new int[] {userId};
+ for (int nextUserId : userIds) {
+ if (DEBUG_REMOVE) {
+ Slog.d(TAG, "Updating package:" + ps.getPackageName() + " install state for user:"
+ + nextUserId);
+ }
+ if ((flags & PackageManager.DELETE_KEEP_DATA) == 0) {
+ mAppDataHelper.destroyAppDataLIF(pkg, nextUserId,
+ FLAG_STORAGE_DE | FLAG_STORAGE_CE | FLAG_STORAGE_EXTERNAL);
+ ps.setCeDataInode(-1, nextUserId);
+ }
+ mAppDataHelper.clearKeystoreData(nextUserId, ps.getAppId());
+ preferredActivityHelper.clearPackagePreferredActivities(ps.getPackageName(),
+ nextUserId);
+ mPm.mDomainVerificationManager.clearPackageForUser(ps.getPackageName(), nextUserId);
+ }
+ mPermissionManager.onPackageUninstalled(ps.getPackageName(), ps.getAppId(), ps, pkg,
+ sharedUserPkgs, userId);
+
+ if (outInfo != null) {
+ if ((flags & PackageManager.DELETE_KEEP_DATA) == 0) {
+ outInfo.mDataRemoved = true;
+ }
+ outInfo.mRemovedPackage = ps.getPackageName();
+ outInfo.mInstallerPackageName = ps.getInstallSource().mInstallerPackageName;
+ outInfo.mIsStaticSharedLib = pkg != null && pkg.getStaticSharedLibraryName() != null;
+ outInfo.mRemovedAppId = ps.getAppId();
+ outInfo.mRemovedUsers = userIds;
+ outInfo.mBroadcastUsers = userIds;
+ outInfo.mIsExternal = ps.isExternalStorage();
+ outInfo.mRemovedPackageVersionCode = ps.getVersionCode();
+ }
+ }
+
+ // Called to clean up disabled system packages
public void removePackageData(final PackageSetting deletedPs, @NonNull int[] allUserHandles,
PackageRemovedInfo outInfo, int flags, boolean writeSettings) {
synchronized (mPm.mInstallLock) {
@@ -314,7 +364,6 @@
int removedAppId = -1;
// writer
- boolean installedStateChanged = false;
if ((flags & PackageManager.DELETE_KEEP_DATA) == 0) {
final SparseBooleanArray changedUsers = new SparseBooleanArray();
synchronized (mPm.mLock) {
@@ -354,9 +403,10 @@
mPm.postPreferredActivityChangedBroadcast(UserHandle.USER_ALL);
}
}
- // make sure to preserve per-user disabled state if this removal was just
+ // make sure to preserve per-user installed state if this removal was just
// a downgrade of a system app to the factory package
- if (outInfo != null && outInfo.mOrigUsers != null) {
+ boolean installedStateChanged = false;
+ if (outInfo != null && outInfo.mOrigUsers != null && deletedPs.isSystem()) {
if (DEBUG_REMOVE) {
Slog.d(TAG, "Propagating install state across downgrade");
}
diff --git a/services/core/java/com/android/server/pm/Settings.java b/services/core/java/com/android/server/pm/Settings.java
index 307867c..d2adfdd 100644
--- a/services/core/java/com/android/server/pm/Settings.java
+++ b/services/core/java/com/android/server/pm/Settings.java
@@ -4893,8 +4893,6 @@
pw.print("]");
}
pw.println();
- File dataDir = PackageInfoUtils.getDataDir(ps, UserHandle.myUserId());
- pw.print(prefix); pw.print(" dataDir="); pw.println(dataDir.getAbsolutePath());
if (pkg != null) {
pw.print(prefix); pw.print(" versionName="); pw.println(pkg.getVersionName());
pw.print(prefix); pw.print(" usesNonSdkApi="); pw.println(pkg.isNonSdkApiRequested());
@@ -5195,6 +5193,10 @@
pw.print(" installReason=");
pw.println(userState.getInstallReason());
+ final File dataDir = PackageInfoUtils.getDataDir(ps, user.id);
+ pw.print(" dataDir=");
+ pw.println(dataDir == null ? "null" : dataDir.getAbsolutePath());
+
final PackageUserStateInternal pus = ps.readUserState(user.id);
pw.print(" firstInstallTime=");
date.setTime(pus.getFirstInstallTimeMillis());
diff --git a/services/core/java/com/android/server/pm/ShortcutService.java b/services/core/java/com/android/server/pm/ShortcutService.java
index dd434fbe..3e4dd16 100644
--- a/services/core/java/com/android/server/pm/ShortcutService.java
+++ b/services/core/java/com/android/server/pm/ShortcutService.java
@@ -3611,8 +3611,8 @@
// Otherwise check persisted shortcuts
getShortcutInfoAsync(launcherUserId, packageName, shortcutId, userId, si -> {
- cb.complete(getShortcutIconUriInternal(launcherUserId, launcherPackage,
- packageName, si, userId));
+ cb.complete(si == null ? null : getShortcutIconUriInternal(launcherUserId,
+ launcherPackage, packageName, si, userId));
});
}
diff --git a/services/core/java/com/android/server/pm/ShortcutUser.java b/services/core/java/com/android/server/pm/ShortcutUser.java
index d32eb22..e9c6aab 100644
--- a/services/core/java/com/android/server/pm/ShortcutUser.java
+++ b/services/core/java/com/android/server/pm/ShortcutUser.java
@@ -363,14 +363,14 @@
private void saveShortcutPackageItem(TypedXmlSerializer out, ShortcutPackageItem spi,
boolean forBackup) throws IOException, XmlPullParserException {
- spi.waitForBitmapSaves();
if (forBackup) {
if (spi.getPackageUserId() != spi.getOwnerUserId()) {
return; // Don't save cross-user information.
}
+ spi.waitForBitmapSaves();
spi.saveToXml(out, forBackup);
} else {
- spi.saveShortcutPackageItem();
+ spi.scheduleSave();
}
}
diff --git a/services/core/java/com/android/server/pm/StorageEventHelper.java b/services/core/java/com/android/server/pm/StorageEventHelper.java
index 6f0fe63..db5b9b1 100644
--- a/services/core/java/com/android/server/pm/StorageEventHelper.java
+++ b/services/core/java/com/android/server/pm/StorageEventHelper.java
@@ -155,7 +155,8 @@
for (PackageStateInternal ps : packages) {
freezers.add(mPm.freezePackage(ps.getPackageName(), UserHandle.USER_ALL,
- "loadPrivatePackagesInner", ApplicationExitInfo.REASON_OTHER));
+ "loadPrivatePackagesInner", ApplicationExitInfo.REASON_OTHER,
+ null /* request */));
synchronized (mPm.mInstallLock) {
final AndroidPackage pkg;
try {
diff --git a/services/core/java/com/android/server/pm/UserTypeFactory.java b/services/core/java/com/android/server/pm/UserTypeFactory.java
index d5231b5..b7f9aaf 100644
--- a/services/core/java/com/android/server/pm/UserTypeFactory.java
+++ b/services/core/java/com/android/server/pm/UserTypeFactory.java
@@ -275,18 +275,18 @@
.setBaseType(FLAG_PROFILE)
.setMaxAllowedPerParent(1)
.setLabel(0)
- .setIconBadge(com.android.internal.R.drawable.ic_test_icon_badge_experiment)
- .setBadgePlain(com.android.internal.R.drawable.ic_test_badge_experiment)
- .setBadgeNoBackground(com.android.internal.R.drawable.ic_test_badge_no_background)
- .setStatusBarIcon(com.android.internal.R.drawable.ic_test_badge_experiment)
+ .setIconBadge(com.android.internal.R.drawable.ic_private_profile_icon_badge)
+ .setBadgePlain(com.android.internal.R.drawable.ic_private_profile_badge)
+ // Private Profile doesn't use BadgeNoBackground, so just set to BadgePlain
+ // as a placeholder.
+ .setBadgeNoBackground(com.android.internal.R.drawable.ic_private_profile_badge)
+ .setStatusBarIcon(com.android.internal.R.drawable.stat_sys_private_profile_status)
.setBadgeLabels(
- com.android.internal.R.string.managed_profile_label_badge,
- com.android.internal.R.string.managed_profile_label_badge_2,
- com.android.internal.R.string.managed_profile_label_badge_3)
+ com.android.internal.R.string.private_profile_label_badge)
.setBadgeColors(
- com.android.internal.R.color.profile_badge_2)
+ com.android.internal.R.color.system_accent1_900)
.setDarkThemeBadgeColors(
- com.android.internal.R.color.profile_badge_2_dark)
+ com.android.internal.R.color.system_accent1_900)
.setDefaultRestrictions(getDefaultProfileRestrictions())
.setDefaultSecureSettings(getDefaultNonManagedProfileSecureSettings())
.setDefaultUserProperties(new UserProperties.Builder()
diff --git a/services/core/java/com/android/server/pm/VerifyingSession.java b/services/core/java/com/android/server/pm/VerifyingSession.java
index 3a1fd7c..8c73ce8 100644
--- a/services/core/java/com/android/server/pm/VerifyingSession.java
+++ b/services/core/java/com/android/server/pm/VerifyingSession.java
@@ -191,7 +191,7 @@
// Perform package verification and enable rollback (unless we are simply moving the
// package).
if (!mOriginInfo.mExisting) {
- if (!isApex()) {
+ if (!isApex() && !isArchivedInstallation()) {
// TODO(b/182426975): treat APEX as APK when APK verification is concerned
sendApkVerificationRequest(pkgLite);
}
@@ -896,6 +896,9 @@
public boolean isApex() {
return (mInstallFlags & PackageManager.INSTALL_APEX) != 0;
}
+ public boolean isArchivedInstallation() {
+ return (mInstallFlags & PackageManager.INSTALL_ARCHIVED) != 0;
+ }
public boolean isStaged() {
return mIsStaged;
}
diff --git a/services/core/java/com/android/server/pm/dex/ArtManagerService.java b/services/core/java/com/android/server/pm/dex/ArtManagerService.java
index 31856f1..f4f03f4 100644
--- a/services/core/java/com/android/server/pm/dex/ArtManagerService.java
+++ b/services/core/java/com/android/server/pm/dex/ArtManagerService.java
@@ -544,11 +544,6 @@
*/
public boolean compileLayouts(@NonNull PackageStateInternal ps, @NonNull AndroidPackage pkg) {
try {
- final String packageName = pkg.getPackageName();
- final String apkPath = pkg.getSplits().get(0).getPath();
- // TODO(b/143971007): Use a cross-user directory
- File dataDir = PackageInfoUtils.getDataDir(ps, UserHandle.myUserId());
- final String outDexFile = dataDir.getAbsolutePath() + "/code_cache/compiled_view.dex";
if (ps.isPrivileged() || pkg.isUseEmbeddedDex()
|| pkg.isDefaultToDeviceProtectedStorage()) {
// Privileged apps prefer to load trusted code so they don't use compiled views.
@@ -558,6 +553,14 @@
// selinux permissions required for writing to user_de.
return false;
}
+ final String packageName = pkg.getPackageName();
+ final String apkPath = pkg.getSplits().get(0).getPath();
+ final File dataDir = PackageInfoUtils.getDataDir(ps, UserHandle.myUserId());
+ if (dataDir == null) {
+ // The app is not installed on the target user and doesn't have a data dir
+ return false;
+ }
+ final String outDexFile = dataDir.getAbsolutePath() + "/code_cache/compiled_view.dex";
Log.i("PackageManager", "Compiling layouts in " + packageName + " (" + apkPath +
") to " + outDexFile);
final long callingId = Binder.clearCallingIdentity();
diff --git a/services/core/java/com/android/server/pm/dex/ViewCompiler.java b/services/core/java/com/android/server/pm/dex/ViewCompiler.java
index 6405ea5..c5b65a3 100644
--- a/services/core/java/com/android/server/pm/dex/ViewCompiler.java
+++ b/services/core/java/com/android/server/pm/dex/ViewCompiler.java
@@ -40,8 +40,11 @@
public boolean compileLayouts(PackageStateInternal ps, String apkPath) {
try {
final String packageName = ps.getPackageName();
- // TODO(b/143971007): Use a cross-user directory
- File dataDir = PackageInfoUtils.getDataDir(ps, UserHandle.myUserId());
+ final File dataDir = PackageInfoUtils.getDataDir(ps, UserHandle.myUserId());
+ if (dataDir == null) {
+ // The app is not installed on the target user and doesn't have a data dir
+ return false;
+ }
final String outDexFile = dataDir.getAbsolutePath() + "/code_cache/compiled_view.dex";
Log.i("PackageManager", "Compiling layouts in " + packageName + " (" + apkPath +
") to " + outDexFile);
diff --git a/services/core/java/com/android/server/pm/parsing/PackageInfoUtils.java b/services/core/java/com/android/server/pm/parsing/PackageInfoUtils.java
index 27812df..fc07909 100644
--- a/services/core/java/com/android/server/pm/parsing/PackageInfoUtils.java
+++ b/services/core/java/com/android/server/pm/parsing/PackageInfoUtils.java
@@ -443,7 +443,7 @@
updateApplicationInfo(info, flags, state);
- initForUser(info, pkg, userId);
+ initForUser(info, pkg, userId, state);
// TODO(b/135203078): Remove PackageParser1/toAppInfoWithoutState and clean all this up
PackageStateUnserialized pkgState = pkgSetting.getTransientState();
@@ -689,7 +689,7 @@
info.splitDependencies = pkg.getSplitDependencies().size() == 0
? null : pkg.getSplitDependencies();
- initForUser(info, pkg, userId);
+ initForUser(info, pkg, userId, state);
info.primaryCpuAbi = pkgSetting.getPrimaryCpuAbi();
info.secondaryCpuAbi = pkgSetting.getSecondaryCpuAbi();
@@ -1001,7 +1001,7 @@
}
private static void initForUser(ApplicationInfo output, AndroidPackage input,
- @UserIdInt int userId) {
+ @UserIdInt int userId, PackageUserStateInternal state) {
PackageImpl pkg = ((PackageImpl) input);
String packageName = input.getPackageName();
output.uid = UserHandle.getUid(userId, UserHandle.getAppId(input.getUid()));
@@ -1011,6 +1011,12 @@
return;
}
+ if (!pkg.isSystem() && state.getCeDataInode() <= 0) {
+ // The data dir has been deleted
+ output.dataDir = null;
+ return;
+ }
+
// For performance reasons, all these paths are built as strings
if (userId == UserHandle.USER_SYSTEM) {
output.credentialProtectedDataDir =
@@ -1045,7 +1051,7 @@
// This duplicates the ApplicationInfo variant because it uses field assignment and the classes
// don't inherit from each other, unfortunately. Consolidating logic would introduce overhead.
private static void initForUser(InstrumentationInfo output, AndroidPackage input,
- @UserIdInt int userId) {
+ @UserIdInt int userId, PackageUserStateInternal state) {
PackageImpl pkg = ((PackageImpl) input);
String packageName = input.getPackageName();
if ("android".equals(packageName)) {
@@ -1053,6 +1059,12 @@
return;
}
+ if (!pkg.isSystem() && state.getCeDataInode() <= 0) {
+ // The data dir has been deleted
+ output.dataDir = null;
+ return;
+ }
+
// For performance reasons, all these paths are built as strings
if (userId == UserHandle.USER_SYSTEM) {
output.credentialProtectedDataDir =
@@ -1084,12 +1096,21 @@
}
}
- @NonNull
+ /**
+ * Returns the data dir of the app for the target user. Return null if the app isn't installed
+ * on the target user and doesn't have a data dir on the target user.
+ */
+ @Nullable
public static File getDataDir(PackageStateInternal ps, int userId) {
if ("android".equals(ps.getPackageName())) {
return Environment.getDataSystemDirectory();
}
+ if (!ps.isSystem() && ps.getUserStateOrDefault(userId).getCeDataInode() <= 0) {
+ // The data dir has been deleted
+ return null;
+ }
+
if (ps.isDefaultToDeviceProtectedStorage()
&& PackageManager.APPLY_DEFAULT_TO_DEVICE_PROTECTED_STORAGE) {
return Environment.getDataUserDePackageDirectory(ps.getVolumeUuid(), userId,
diff --git a/services/core/java/com/android/server/pm/parsing/PackageParser2.java b/services/core/java/com/android/server/pm/parsing/PackageParser2.java
index f5ba3f6..d82a500 100644
--- a/services/core/java/com/android/server/pm/parsing/PackageParser2.java
+++ b/services/core/java/com/android/server/pm/parsing/PackageParser2.java
@@ -22,6 +22,7 @@
import android.app.ActivityThread;
import android.content.Context;
import android.content.pm.ApplicationInfo;
+import android.content.pm.parsing.PackageLite;
import android.content.pm.parsing.result.ParseInput;
import android.content.pm.parsing.result.ParseResult;
import android.content.pm.parsing.result.ParseTypeImpl;
@@ -183,6 +184,23 @@
}
/**
+ * Creates a ParsedPackage from PackageLite without any additional parsing or processing.
+ * Most fields will get reasonable default values, corresponding to "deleted-keep-data".
+ */
+ @AnyThread
+ public ParsedPackage parsePackageFromPackageLite(PackageLite packageLite, int flags)
+ throws PackageManagerException {
+ ParseInput input = mSharedResult.get().reset();
+ ParseResult<ParsingPackage> result = parsingUtils.parsePackageFromPackageLite(input,
+ packageLite, flags);
+ if (result.isError()) {
+ throw new PackageManagerException(result.getErrorCode(), result.getErrorMessage(),
+ result.getException());
+ }
+ return result.getResult().hideAsParsed();
+ }
+
+ /**
* Removes the cached value for the thread the parser was created on. It is assumed that
* any threads created for parallel parsing will be created and released, so they don't
* need an explicit close call.
diff --git a/services/core/java/com/android/server/pm/permission/PermissionManagerService.java b/services/core/java/com/android/server/pm/permission/PermissionManagerService.java
index 7897195..b01a89e 100644
--- a/services/core/java/com/android/server/pm/permission/PermissionManagerService.java
+++ b/services/core/java/com/android/server/pm/permission/PermissionManagerService.java
@@ -72,7 +72,6 @@
import com.android.internal.annotations.GuardedBy;
import com.android.internal.util.ArrayUtils;
import com.android.internal.util.Preconditions;
-import com.android.internal.util.function.QuadFunction;
import com.android.internal.util.function.TriFunction;
import com.android.server.LocalServices;
import com.android.server.pm.UserManagerInternal;
@@ -94,6 +93,7 @@
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicInteger;
+import java.util.function.BiFunction;
/**
* Manages all permissions and handles permissions related tasks.
@@ -233,11 +233,11 @@
}
if (checkPermissionDelegate == null) {
- return mPermissionManagerServiceImpl.checkPermission(packageName, permissionName,
- deviceId, userId);
+ return mPermissionManagerServiceImpl.checkPermission(
+ packageName, permissionName, userId);
}
- return checkPermissionDelegate.checkPermission(packageName, permissionName,
- deviceId, userId, mPermissionManagerServiceImpl::checkPermission);
+ return checkPermissionDelegate.checkPermission(packageName, permissionName, userId,
+ mPermissionManagerServiceImpl::checkPermission);
}
@Override
@@ -254,10 +254,10 @@
}
if (checkPermissionDelegate == null) {
- return mPermissionManagerServiceImpl.checkUidPermission(uid, permissionName, deviceId);
+ return mPermissionManagerServiceImpl.checkUidPermission(uid, permissionName);
}
return checkPermissionDelegate.checkUidPermission(uid, permissionName,
- deviceId, mPermissionManagerServiceImpl::checkUidPermission);
+ mPermissionManagerServiceImpl::checkUidPermission);
}
@Override
@@ -511,14 +511,14 @@
public int getPermissionFlags(String packageName, String permissionName, int deviceId,
int userId) {
return mPermissionManagerServiceImpl
- .getPermissionFlags(packageName, permissionName, deviceId, userId);
+ .getPermissionFlags(packageName, permissionName, userId);
}
@Override
public void updatePermissionFlags(String packageName, String permissionName, int flagMask,
int flagValues, boolean checkAdjustPolicyFlagPermission, int deviceId, int userId) {
mPermissionManagerServiceImpl.updatePermissionFlags(packageName, permissionName, flagMask,
- flagValues, checkAdjustPolicyFlagPermission, deviceId, userId);
+ flagValues, checkAdjustPolicyFlagPermission, userId);
}
@Override
@@ -560,15 +560,14 @@
@Override
public void grantRuntimePermission(String packageName, String permissionName, int deviceId,
int userId) {
- mPermissionManagerServiceImpl.grantRuntimePermission(packageName, permissionName,
- deviceId, userId);
+ mPermissionManagerServiceImpl.grantRuntimePermission(packageName, permissionName, userId);
}
@Override
public void revokeRuntimePermission(String packageName, String permissionName, int deviceId,
int userId, String reason) {
mPermissionManagerServiceImpl.revokeRuntimePermission(packageName, permissionName,
- deviceId, userId, reason);
+ userId, reason);
}
@Override
@@ -581,14 +580,14 @@
public boolean shouldShowRequestPermissionRationale(String packageName, String permissionName,
int deviceId, int userId) {
return mPermissionManagerServiceImpl.shouldShowRequestPermissionRationale(packageName,
- permissionName, deviceId, userId);
+ permissionName, userId);
}
@Override
public boolean isPermissionRevokedByPolicy(String packageName, String permissionName,
int deviceId, int userId) {
- return mPermissionManagerServiceImpl.isPermissionRevokedByPolicy(packageName,
- permissionName, deviceId, userId);
+ return mPermissionManagerServiceImpl
+ .isPermissionRevokedByPolicy(packageName, permissionName, userId);
}
@Override
@@ -869,7 +868,6 @@
*
* @param packageName the name of the package to be checked
* @param permissionName the name of the permission to be checked
- * @param deviceId The device ID
* @param userId the user ID
* @param superImpl the original implementation that can be delegated to
* @return {@link android.content.pm.PackageManager#PERMISSION_GRANTED} if the package has
@@ -878,21 +876,20 @@
* @see android.content.pm.PackageManager#checkPermission(String, String)
*/
int checkPermission(@NonNull String packageName, @NonNull String permissionName,
- int deviceId, @UserIdInt int userId,
- @NonNull QuadFunction<String, String, Integer, Integer, Integer> superImpl);
+ @UserIdInt int userId,
+ @NonNull TriFunction<String, String, Integer, Integer> superImpl);
/**
* Check whether the given UID has been granted the specified permission.
*
* @param uid the UID to be checked
* @param permissionName the name of the permission to be checked
- * @param deviceId The device ID
* @param superImpl the original implementation that can be delegated to
* @return {@link android.content.pm.PackageManager#PERMISSION_GRANTED} if the package has
* the permission, or {@link android.content.pm.PackageManager#PERMISSION_DENIED} otherwise
*/
- int checkUidPermission(int uid, @NonNull String permissionName, int deviceId,
- TriFunction<Integer, String, Integer, Integer> superImpl);
+ int checkUidPermission(int uid, @NonNull String permissionName,
+ BiFunction<Integer, String, Integer> superImpl);
/**
* @return list of delegated permissions
@@ -921,32 +918,31 @@
@Override
public int checkPermission(@NonNull String packageName, @NonNull String permissionName,
- int deviceId, int userId,
- @NonNull QuadFunction<String, String, Integer, Integer, Integer> superImpl) {
+ int userId, @NonNull TriFunction<String, String, Integer, Integer> superImpl) {
if (mDelegatedPackageName.equals(packageName)
&& isDelegatedPermission(permissionName)) {
final long identity = Binder.clearCallingIdentity();
try {
- return superImpl.apply("com.android.shell", permissionName, deviceId, userId);
+ return superImpl.apply("com.android.shell", permissionName, userId);
} finally {
Binder.restoreCallingIdentity(identity);
}
}
- return superImpl.apply(packageName, permissionName, deviceId, userId);
+ return superImpl.apply(packageName, permissionName, userId);
}
@Override
- public int checkUidPermission(int uid, @NonNull String permissionName, int deviceId,
- @NonNull TriFunction<Integer, String, Integer, Integer> superImpl) {
+ public int checkUidPermission(int uid, @NonNull String permissionName,
+ @NonNull BiFunction<Integer, String, Integer> superImpl) {
if (uid == mDelegatedUid && isDelegatedPermission(permissionName)) {
final long identity = Binder.clearCallingIdentity();
try {
- return superImpl.apply(Process.SHELL_UID, permissionName, deviceId);
+ return superImpl.apply(Process.SHELL_UID, permissionName);
} finally {
Binder.restoreCallingIdentity(identity);
}
}
- return superImpl.apply(uid, permissionName, deviceId);
+ return superImpl.apply(uid, permissionName);
}
@Override
diff --git a/services/core/java/com/android/server/pm/permission/PermissionManagerServiceImpl.java b/services/core/java/com/android/server/pm/permission/PermissionManagerServiceImpl.java
index 6764e08..4353c57 100644
--- a/services/core/java/com/android/server/pm/permission/PermissionManagerServiceImpl.java
+++ b/services/core/java/com/android/server/pm/permission/PermissionManagerServiceImpl.java
@@ -681,7 +681,7 @@
}
@Override
- public int getPermissionFlags(String packageName, String permName, int deviceId, int userId) {
+ public int getPermissionFlags(String packageName, String permName, int userId) {
final int callingUid = Binder.getCallingUid();
return getPermissionFlagsInternal(packageName, permName, callingUid, userId);
}
@@ -724,7 +724,7 @@
@Override
public void updatePermissionFlags(String packageName, String permName, int flagMask,
- int flagValues, boolean checkAdjustPolicyFlagPermission, int deviceId, int userId) {
+ int flagValues, boolean checkAdjustPolicyFlagPermission, int userId) {
final int callingUid = Binder.getCallingUid();
boolean overridePolicy = false;
@@ -908,12 +908,8 @@
}
}
- private int checkPermission(String pkgName, String permName, int userId) {
- return checkPermission(pkgName, permName, Context.DEVICE_ID_DEFAULT, userId);
- }
-
@Override
- public int checkPermission(String pkgName, String permName, int deviceId, int userId) {
+ public int checkPermission(String pkgName, String permName, int userId) {
if (!mUserManagerInt.exists(userId)) {
return PackageManager.PERMISSION_DENIED;
}
@@ -979,12 +975,8 @@
return true;
}
- private int checkUidPermission(int uid, String permName) {
- return checkUidPermission(uid, permName, Context.DEVICE_ID_DEFAULT);
- }
-
@Override
- public int checkUidPermission(int uid, String permName, int deviceId) {
+ public int checkUidPermission(int uid, String permName) {
final int userId = UserHandle.getUserId(uid);
if (!mUserManagerInt.exists(userId)) {
return PackageManager.PERMISSION_DENIED;
@@ -1303,8 +1295,7 @@
}
@Override
- public void grantRuntimePermission(String packageName, String permName, int deviceId,
- int userId) {
+ public void grantRuntimePermission(String packageName, String permName, final int userId) {
final int callingUid = Binder.getCallingUid();
final boolean overridePolicy =
checkUidPermission(callingUid, ADJUST_RUNTIME_PERMISSIONS_POLICY)
@@ -1477,11 +1468,11 @@
}
@Override
- public void revokeRuntimePermission(String packageName, String permName, int deviceId,
- int userId, String reason) {
+ public void revokeRuntimePermission(String packageName, String permName, int userId,
+ String reason) {
final int callingUid = Binder.getCallingUid();
final boolean overridePolicy =
- checkUidPermission(callingUid, ADJUST_RUNTIME_PERMISSIONS_POLICY, deviceId)
+ checkUidPermission(callingUid, ADJUST_RUNTIME_PERMISSIONS_POLICY)
== PackageManager.PERMISSION_GRANTED;
revokeRuntimePermissionInternal(packageName, permName, overridePolicy, callingUid, userId,
@@ -1868,7 +1859,7 @@
@Override
public boolean shouldShowRequestPermissionRationale(String packageName, String permName,
- int deviceId, @UserIdInt int userId) {
+ @UserIdInt int userId) {
final int callingUid = Binder.getCallingUid();
if (UserHandle.getCallingUserId() != userId) {
mContext.enforceCallingPermission(
@@ -1931,8 +1922,7 @@
}
@Override
- public boolean isPermissionRevokedByPolicy(String packageName, String permName, int deviceId,
- int userId) {
+ public boolean isPermissionRevokedByPolicy(String packageName, String permName, int userId) {
if (UserHandle.getCallingUserId() != userId) {
mContext.enforceCallingPermission(
android.Manifest.permission.INTERACT_ACROSS_USERS_FULL,
@@ -2069,8 +2059,8 @@
continue;
}
boolean isSystemOrPolicyFixed = (getPermissionFlags(newPackage.getPackageName(),
- permInfo.name, Context.DEVICE_ID_DEFAULT, userId) & (
- FLAG_PERMISSION_SYSTEM_FIXED | FLAG_PERMISSION_POLICY_FIXED)) != 0;
+ permInfo.name, userId) & (FLAG_PERMISSION_SYSTEM_FIXED
+ | FLAG_PERMISSION_POLICY_FIXED)) != 0;
if (isSystemOrPolicyFixed) {
continue;
}
@@ -2236,8 +2226,7 @@
for (final int userId : userIds) {
final int permissionState = checkPermission(packageName, permName,
userId);
- final int flags = getPermissionFlags(packageName, permName,
- Context.DEVICE_ID_DEFAULT, userId);
+ final int flags = getPermissionFlags(packageName, permName, userId);
final int flagMask = FLAG_PERMISSION_SYSTEM_FIXED
| FLAG_PERMISSION_POLICY_FIXED
| FLAG_PERMISSION_GRANTED_BY_DEFAULT
@@ -5133,7 +5122,8 @@
@NonNull
@Override
- public Set<String> getGrantedPermissions(@NonNull String packageName, @UserIdInt int userId) {
+ public Set<String> getGrantedPermissions(@NonNull String packageName,
+ @UserIdInt int userId) {
Objects.requireNonNull(packageName, "packageName");
Preconditions.checkArgumentNonNegative(userId, "userId");
return getGrantedPermissionsInternal(packageName, userId);
diff --git a/services/core/java/com/android/server/pm/permission/PermissionManagerServiceInterface.java b/services/core/java/com/android/server/pm/permission/PermissionManagerServiceInterface.java
index 2d824aa..128f847 100644
--- a/services/core/java/com/android/server/pm/permission/PermissionManagerServiceInterface.java
+++ b/services/core/java/com/android/server/pm/permission/PermissionManagerServiceInterface.java
@@ -25,6 +25,7 @@
import android.content.pm.PermissionInfo;
import android.content.pm.permission.SplitPermissionInfoParcelable;
import android.permission.IOnPermissionsChangeListener;
+import android.permission.PermissionManager;
import android.permission.PermissionManagerInternal;
import com.android.server.pm.pkg.AndroidPackage;
@@ -136,16 +137,14 @@
void removePermission(String permName);
/**
- * Gets the permission state flags associated with a permission.
+ * Gets the state flags associated with a permission.
*
* @param packageName the package name for which to get the flags
* @param permName the permission for which to get the flags
- * @param deviceId The device for which to get the flags
* @param userId the user for which to get permission flags
* @return the permission flags
*/
- int getPermissionFlags(String packageName, String permName, int deviceId,
- @UserIdInt int userId);
+ int getPermissionFlags(String packageName, String permName, int userId);
/**
* Updates the flags associated with a permission by replacing the flags in the specified mask
@@ -155,11 +154,10 @@
* @param permName The permission for which to update the flags
* @param flagMask The flags which to replace
* @param flagValues The flags with which to replace
- * @param deviceId The device for which to update the permission flags
* @param userId The user for which to update the permission flags
*/
- void updatePermissionFlags(String packageName, String permName, int flagMask, int flagValues,
- boolean checkAdjustPolicyFlagPermission, int deviceId, @UserIdInt int userId);
+ void updatePermissionFlags(String packageName, String permName, int flagMask,
+ int flagValues, boolean checkAdjustPolicyFlagPermission, int userId);
/**
* Update the permission flags for all packages and runtime permissions of a user in order
@@ -293,13 +291,11 @@
*
* @param packageName the package to which to grant the permission
* @param permName the permission name to grant
- * @param deviceId the device for which to grant the permission
* @param userId the user for which to grant the permission
*
- * @see #revokeRuntimePermission(String, String, int, int, String)
+ * @see #revokeRuntimePermission(String, String, android.os.UserHandle, String)
*/
- void grantRuntimePermission(String packageName, String permName, int deviceId,
- @UserIdInt int userId);
+ void grantRuntimePermission(String packageName, String permName, int userId);
/**
* Revoke a runtime permission that was previously granted by
@@ -314,14 +310,13 @@
*
* @param packageName the package from which to revoke the permission
* @param permName the permission name to revoke
- * @param deviceId the device for which to revoke the permission
* @param userId the user for which to revoke the permission
* @param reason the reason for the revoke, or {@code null} for unspecified
*
- * @see #grantRuntimePermission(String, String, int, int)
+ * @see #grantRuntimePermission(String, String, android.os.UserHandle)
*/
- void revokeRuntimePermission(String packageName, String permName, int deviceId,
- @UserIdInt int userId, String reason);
+ void revokeRuntimePermission(String packageName, String permName, int userId,
+ String reason);
/**
* Revoke the POST_NOTIFICATIONS permission, without killing the app. This method must ONLY BE
@@ -338,29 +333,24 @@
* does not clearly communicate to the user what would be the benefit from grating this
* permission.
*
- * @param packageName the package name
* @param permName a permission your app wants to request
- * @param deviceId the device for which to check the permission
- * @param userId the user for which to check the permission
* @return whether you can show permission rationale UI
*/
boolean shouldShowRequestPermissionRationale(String packageName, String permName,
- int deviceId, @UserIdInt int userId);
+ @UserIdInt int userId);
/**
- * Checks whether a particular permission has been revoked for a package by policy. Typically,
+ * Checks whether a particular permissions has been revoked for a package by policy. Typically
* the device owner or the profile owner may apply such a policy. The user cannot grant policy
* revoked permissions, hence the only way for an app to get such a permission is by a policy
* change.
*
* @param packageName the name of the package you are checking against
* @param permName the name of the permission you are checking for
- * @param deviceId the device for which you are checking the permission
- * @param userId the device for which you are checking the permission
+ *
* @return whether the permission is restricted by policy
*/
- boolean isPermissionRevokedByPolicy(String packageName, String permName, int deviceId,
- @UserIdInt int userId);
+ boolean isPermissionRevokedByPolicy(String packageName, String permName, int userId);
/**
* Get set of permissions that have been split into more granular or dependent permissions.
@@ -383,25 +373,14 @@
List<SplitPermissionInfoParcelable> getSplitPermissions();
/**
- * Check whether a permission is granted or not to a package.
- *
- * @param pkgName package name
- * @param permName permission name
- * @param deviceId device ID
- * @param userId user ID
- * @return permission result {@link PackageManager.PermissionResult}
+ * TODO:theianchen add doc describing this is the old checkPermissionImpl
*/
- int checkPermission(String pkgName, String permName, int deviceId, @UserIdInt int userId);
+ int checkPermission(String pkgName, String permName, int userId);
/**
- * Check whether a permission is granted or not to an UID.
- *
- * @param uid UID
- * @param permName permission name
- * @param deviceId device ID
- * @return permission result {@link PackageManager.PermissionResult}
+ * TODO:theianchen add doc describing this is the old checkUidPermissionImpl
*/
- int checkUidPermission(int uid, String permName, int deviceId);
+ int checkUidPermission(int uid, String permName);
/**
* Get all the package names requesting app op permissions.
@@ -421,11 +400,15 @@
@UserIdInt int userId);
/**
- * Reset the runtime permission state changes for a package for all devices.
+ * Reset the runtime permission state changes for a package.
*
* TODO(zhanghai): Turn this into package change callback?
+ *
+ * @param pkg the package
+ * @param userId the user ID
*/
- void resetRuntimePermissions(@NonNull AndroidPackage pkg, @UserIdInt int userId);
+ void resetRuntimePermissions(@NonNull AndroidPackage pkg,
+ @UserIdInt int userId);
/**
* Reset the runtime permission state changes for all packages in a user.
@@ -466,8 +449,8 @@
/**
* Get all the permissions granted to a package.
*
- * @param packageName package name
- * @param userId user ID
+ * @param packageName the name of the package
+ * @param userId the user ID
* @return the names of the granted permissions
*/
@NonNull
diff --git a/services/core/java/com/android/server/pm/permission/PermissionManagerServiceLoggingDecorator.java b/services/core/java/com/android/server/pm/permission/PermissionManagerServiceLoggingDecorator.java
index dacb8c6..7f98e21 100644
--- a/services/core/java/com/android/server/pm/permission/PermissionManagerServiceLoggingDecorator.java
+++ b/services/core/java/com/android/server/pm/permission/PermissionManagerServiceLoggingDecorator.java
@@ -120,21 +120,21 @@
}
@Override
- public int getPermissionFlags(String packageName, String permName, int deviceId, int userId) {
+ public int getPermissionFlags(String packageName, String permName, int userId) {
Log.i(LOG_TAG, "getPermissionFlags(packageName = " + packageName + ", permName = "
- + permName + ", deviceId = " + deviceId + ", userId = " + userId + ")");
- return mService.getPermissionFlags(packageName, permName, deviceId, userId);
+ + permName + ", userId = " + userId + ")");
+ return mService.getPermissionFlags(packageName, permName, userId);
}
@Override
public void updatePermissionFlags(String packageName, String permName, int flagMask,
- int flagValues, boolean checkAdjustPolicyFlagPermission, int deviceId, int userId) {
+ int flagValues, boolean checkAdjustPolicyFlagPermission, int userId) {
Log.i(LOG_TAG, "updatePermissionFlags(packageName = " + packageName + ", permName = "
+ permName + ", flagMask = " + flagMask + ", flagValues = " + flagValues
+ ", checkAdjustPolicyFlagPermission = " + checkAdjustPolicyFlagPermission
- + ", deviceId = " + deviceId + ", userId = " + userId + ")");
+ + ", userId = " + userId + ")");
mService.updatePermissionFlags(packageName, permName, flagMask, flagValues,
- checkAdjustPolicyFlagPermission, deviceId, userId);
+ checkAdjustPolicyFlagPermission, userId);
}
@Override
@@ -182,20 +182,18 @@
}
@Override
- public void grantRuntimePermission(String packageName, String permName, int deviceId,
- int userId) {
+ public void grantRuntimePermission(String packageName, String permName, int userId) {
Log.i(LOG_TAG, "grantRuntimePermission(packageName = " + packageName + ", permName = "
- + permName + ", deviceId = " + deviceId + ", userId = " + userId + ")");
- mService.grantRuntimePermission(packageName, permName, deviceId, userId);
+ + permName + ", userId = " + userId + ")");
+ mService.grantRuntimePermission(packageName, permName, userId);
}
@Override
- public void revokeRuntimePermission(String packageName, String permName, int deviceId,
- int userId, String reason) {
+ public void revokeRuntimePermission(String packageName, String permName, int userId,
+ String reason) {
Log.i(LOG_TAG, "revokeRuntimePermission(packageName = " + packageName + ", permName = "
- + permName + ", deviceId = " + deviceId + ", userId = " + userId
- + ", reason = " + reason + ")");
- mService.revokeRuntimePermission(packageName, permName, deviceId, userId, reason);
+ + permName + ", userId = " + userId + ", reason = " + reason + ")");
+ mService.revokeRuntimePermission(packageName, permName, userId, reason);
}
@Override
@@ -207,20 +205,17 @@
@Override
public boolean shouldShowRequestPermissionRationale(String packageName, String permName,
- int deviceId, int userId) {
+ int userId) {
Log.i(LOG_TAG, "shouldShowRequestPermissionRationale(packageName = " + packageName
- + ", permName = " + permName + ", deviceId = " + deviceId
- + ", userId = " + userId + ")");
- return mService.shouldShowRequestPermissionRationale(packageName, permName, deviceId,
- userId);
+ + ", permName = " + permName + ", userId = " + userId + ")");
+ return mService.shouldShowRequestPermissionRationale(packageName, permName, userId);
}
@Override
- public boolean isPermissionRevokedByPolicy(String packageName, String permName, int deviceId,
- int userId) {
+ public boolean isPermissionRevokedByPolicy(String packageName, String permName, int userId) {
Log.i(LOG_TAG, "isPermissionRevokedByPolicy(packageName = " + packageName + ", permName = "
- + permName + ", deviceId = " + deviceId + ", userId = " + userId + ")");
- return mService.isPermissionRevokedByPolicy(packageName, permName, deviceId, userId);
+ + permName + ", userId = " + userId + ")");
+ return mService.isPermissionRevokedByPolicy(packageName, permName, userId);
}
@Override
@@ -230,17 +225,16 @@
}
@Override
- public int checkPermission(String pkgName, String permName, int deviceId, int userId) {
+ public int checkPermission(String pkgName, String permName, int userId) {
Log.i(LOG_TAG, "checkPermission(pkgName = " + pkgName + ", permName = " + permName
- + ", deviceId = " + deviceId + ", userId = " + userId + ")");
- return mService.checkPermission(pkgName, permName, deviceId, userId);
+ + ", userId = " + userId + ")");
+ return mService.checkPermission(pkgName, permName, userId);
}
@Override
- public int checkUidPermission(int uid, String permName, int deviceId) {
- Log.i(LOG_TAG, "checkUidPermission(uid = " + uid + ", permName = " + permName
- + ", deviceId = " + deviceId + ")");
- return mService.checkUidPermission(uid, permName, deviceId);
+ public int checkUidPermission(int uid, String permName) {
+ Log.i(LOG_TAG, "checkUidPermission(uid = " + uid + ", permName = " + permName + ")");
+ return mService.checkUidPermission(uid, permName);
}
@Override
diff --git a/services/core/java/com/android/server/pm/permission/PermissionManagerServiceTestingShim.java b/services/core/java/com/android/server/pm/permission/PermissionManagerServiceTestingShim.java
index 35d165b..d4c6d42 100644
--- a/services/core/java/com/android/server/pm/permission/PermissionManagerServiceTestingShim.java
+++ b/services/core/java/com/android/server/pm/permission/PermissionManagerServiceTestingShim.java
@@ -153,10 +153,9 @@
}
@Override
- public int getPermissionFlags(String packageName, String permName, int deviceId,
- @UserIdInt int userId) {
- int oldVal = mOldImplementation.getPermissionFlags(packageName, permName, deviceId, userId);
- int newVal = mNewImplementation.getPermissionFlags(packageName, permName, deviceId, userId);
+ public int getPermissionFlags(String packageName, String permName, int userId) {
+ int oldVal = mOldImplementation.getPermissionFlags(packageName, permName, userId);
+ int newVal = mNewImplementation.getPermissionFlags(packageName, permName, userId);
if (!Objects.equals(oldVal, newVal)) {
signalImplDifference("getPermissionFlags");
@@ -166,12 +165,11 @@
@Override
public void updatePermissionFlags(String packageName, String permName, int flagMask,
- int flagValues, boolean checkAdjustPolicyFlagPermission, int deviceId,
- @UserIdInt int userId) {
+ int flagValues, boolean checkAdjustPolicyFlagPermission, int userId) {
mOldImplementation.updatePermissionFlags(packageName, permName, flagMask, flagValues,
- checkAdjustPolicyFlagPermission, deviceId, userId);
+ checkAdjustPolicyFlagPermission, userId);
mNewImplementation.updatePermissionFlags(packageName, permName, flagMask, flagValues,
- checkAdjustPolicyFlagPermission, deviceId, userId);
+ checkAdjustPolicyFlagPermission, userId);
}
@Override
@@ -236,17 +234,16 @@
}
@Override
- public void grantRuntimePermission(String packageName, String permName, int deviceId,
- @UserIdInt int userId) {
- mOldImplementation.grantRuntimePermission(packageName, permName, deviceId, userId);
- mNewImplementation.grantRuntimePermission(packageName, permName, deviceId, userId);
+ public void grantRuntimePermission(String packageName, String permName, int userId) {
+ mOldImplementation.grantRuntimePermission(packageName, permName, userId);
+ mNewImplementation.grantRuntimePermission(packageName, permName, userId);
}
@Override
- public void revokeRuntimePermission(String packageName, String permName, int deviceId,
- @UserIdInt int userId, String reason) {
- mOldImplementation.revokeRuntimePermission(packageName, permName, deviceId, userId, reason);
- mNewImplementation.revokeRuntimePermission(packageName, permName, deviceId, userId, reason);
+ public void revokeRuntimePermission(String packageName, String permName, int userId,
+ String reason) {
+ mOldImplementation.grantRuntimePermission(packageName, permName, userId);
+ mNewImplementation.grantRuntimePermission(packageName, permName, userId);
}
@Override
@@ -258,11 +255,11 @@
@Override
public boolean shouldShowRequestPermissionRationale(String packageName, String permName,
- int deviceId, @UserIdInt int userId) {
- boolean oldVal = mOldImplementation.shouldShowRequestPermissionRationale(packageName,
- permName, deviceId, userId);
- boolean newVal = mNewImplementation.shouldShowRequestPermissionRationale(packageName,
- permName, deviceId, userId);
+ int userId) {
+ boolean oldVal = mOldImplementation
+ .shouldShowRequestPermissionRationale(packageName, permName, userId);
+ boolean newVal = mNewImplementation
+ .shouldShowRequestPermissionRationale(packageName, permName, userId);
if (!Objects.equals(oldVal, newVal)) {
signalImplDifference("shouldShowRequestPermissionRationale");
@@ -271,12 +268,11 @@
}
@Override
- public boolean isPermissionRevokedByPolicy(String packageName, String permName, int deviceId,
- @UserIdInt int userId) {
- boolean oldVal = mOldImplementation.isPermissionRevokedByPolicy(packageName, permName,
- deviceId, userId);
+ public boolean isPermissionRevokedByPolicy(String packageName, String permName, int userId) {
+ boolean oldVal = mOldImplementation
+ .isPermissionRevokedByPolicy(packageName, permName, userId);
boolean newVal = mNewImplementation.isPermissionRevokedByPolicy(packageName, permName,
- deviceId, userId);
+ userId);
if (!Objects.equals(oldVal, newVal)) {
signalImplDifference("isPermissionRevokedByPolicy");
@@ -296,9 +292,9 @@
}
@Override
- public int checkPermission(String pkgName, String permName, int deviceId, int userId) {
- int oldVal = mOldImplementation.checkPermission(pkgName, permName, deviceId, userId);
- int newVal = mNewImplementation.checkPermission(pkgName, permName, deviceId, userId);
+ public int checkPermission(String pkgName, String permName, int userId) {
+ int oldVal = mOldImplementation.checkPermission(pkgName, permName, userId);
+ int newVal = mNewImplementation.checkPermission(pkgName, permName, userId);
if (!Objects.equals(oldVal, newVal)) {
signalImplDifference("checkPermission");
@@ -307,9 +303,9 @@
}
@Override
- public int checkUidPermission(int uid, String permName, int deviceId) {
- int oldVal = mOldImplementation.checkUidPermission(uid, permName, deviceId);
- int newVal = mNewImplementation.checkUidPermission(uid, permName, deviceId);
+ public int checkUidPermission(int uid, String permName) {
+ int oldVal = mOldImplementation.checkUidPermission(uid, permName);
+ int newVal = mNewImplementation.checkUidPermission(uid, permName);
if (!Objects.equals(oldVal, newVal)) {
signalImplDifference("checkUidPermission");
@@ -376,7 +372,7 @@
@NonNull
@Override
- public Set<String> getGrantedPermissions(@NonNull String packageName, @UserIdInt int userId) {
+ public Set<String> getGrantedPermissions(@NonNull String packageName, int userId) {
Set<String> oldVal = mOldImplementation.getGrantedPermissions(packageName, userId);
Set<String> newVal = mNewImplementation.getGrantedPermissions(packageName, userId);
diff --git a/services/core/java/com/android/server/pm/permission/PermissionManagerServiceTracingDecorator.java b/services/core/java/com/android/server/pm/permission/PermissionManagerServiceTracingDecorator.java
index cbeede0..4e72fae 100644
--- a/services/core/java/com/android/server/pm/permission/PermissionManagerServiceTracingDecorator.java
+++ b/services/core/java/com/android/server/pm/permission/PermissionManagerServiceTracingDecorator.java
@@ -158,10 +158,10 @@
}
@Override
- public int getPermissionFlags(String packageName, String permName, int deviceId, int userId) {
+ public int getPermissionFlags(String packageName, String permName, int userId) {
Trace.traceBegin(TRACE_TAG, "TaggedTracingPermissionManagerServiceImpl#getPermissionFlags");
try {
- return mService.getPermissionFlags(packageName, permName, deviceId, userId);
+ return mService.getPermissionFlags(packageName, permName, userId);
} finally {
Trace.traceEnd(TRACE_TAG);
}
@@ -169,12 +169,12 @@
@Override
public void updatePermissionFlags(String packageName, String permName, int flagMask,
- int flagValues, boolean checkAdjustPolicyFlagPermission, int deviceId, int userId) {
+ int flagValues, boolean checkAdjustPolicyFlagPermission, int userId) {
Trace.traceBegin(TRACE_TAG,
"TaggedTracingPermissionManagerServiceImpl#updatePermissionFlags");
try {
mService.updatePermissionFlags(packageName, permName, flagMask, flagValues,
- checkAdjustPolicyFlagPermission, deviceId, userId);
+ checkAdjustPolicyFlagPermission, userId);
} finally {
Trace.traceEnd(TRACE_TAG);
}
@@ -253,24 +253,23 @@
}
@Override
- public void grantRuntimePermission(String packageName, String permName, int deviceId,
- int userId) {
+ public void grantRuntimePermission(String packageName, String permName, int userId) {
Trace.traceBegin(TRACE_TAG,
"TaggedTracingPermissionManagerServiceImpl#grantRuntimePermission");
try {
- mService.grantRuntimePermission(packageName, permName, deviceId, userId);
+ mService.grantRuntimePermission(packageName, permName, userId);
} finally {
Trace.traceEnd(TRACE_TAG);
}
}
@Override
- public void revokeRuntimePermission(String packageName, String permName, int deviceId,
- int userId, String reason) {
+ public void revokeRuntimePermission(String packageName, String permName, int userId,
+ String reason) {
Trace.traceBegin(TRACE_TAG,
"TaggedTracingPermissionManagerServiceImpl#revokeRuntimePermission");
try {
- mService.revokeRuntimePermission(packageName, permName, deviceId, userId, reason);
+ mService.revokeRuntimePermission(packageName, permName, userId, reason);
} finally {
Trace.traceEnd(TRACE_TAG);
}
@@ -289,24 +288,22 @@
@Override
public boolean shouldShowRequestPermissionRationale(String packageName, String permName,
- int deviceId, int userId) {
+ int userId) {
Trace.traceBegin(TRACE_TAG,
"TaggedTracingPermissionManagerServiceImpl#shouldShowRequestPermissionRationale");
try {
- return mService.shouldShowRequestPermissionRationale(
- packageName, permName, deviceId, userId);
+ return mService.shouldShowRequestPermissionRationale(packageName, permName, userId);
} finally {
Trace.traceEnd(TRACE_TAG);
}
}
@Override
- public boolean isPermissionRevokedByPolicy(String packageName, String permName, int deviceId,
- int userId) {
+ public boolean isPermissionRevokedByPolicy(String packageName, String permName, int userId) {
Trace.traceBegin(TRACE_TAG,
"TaggedTracingPermissionManagerServiceImpl#isPermissionRevokedByPolicy");
try {
- return mService.isPermissionRevokedByPolicy(packageName, permName, deviceId, userId);
+ return mService.isPermissionRevokedByPolicy(packageName, permName, userId);
} finally {
Trace.traceEnd(TRACE_TAG);
}
@@ -324,20 +321,20 @@
}
@Override
- public int checkPermission(String pkgName, String permName, int deviceId, int userId) {
+ public int checkPermission(String pkgName, String permName, int userId) {
Trace.traceBegin(TRACE_TAG, "TaggedTracingPermissionManagerServiceImpl#checkPermission");
try {
- return mService.checkPermission(pkgName, permName, deviceId, userId);
+ return mService.checkPermission(pkgName, permName, userId);
} finally {
Trace.traceEnd(TRACE_TAG);
}
}
@Override
- public int checkUidPermission(int uid, String permName, int deviceId) {
+ public int checkUidPermission(int uid, String permName) {
Trace.traceBegin(TRACE_TAG, "TaggedTracingPermissionManagerServiceImpl#checkUidPermission");
try {
- return mService.checkUidPermission(uid, permName, deviceId);
+ return mService.checkUidPermission(uid, permName);
} finally {
Trace.traceEnd(TRACE_TAG);
}
diff --git a/services/core/java/com/android/server/pm/permission/TEST_MAPPING b/services/core/java/com/android/server/pm/permission/TEST_MAPPING
index b2dcf37..24323c8 100644
--- a/services/core/java/com/android/server/pm/permission/TEST_MAPPING
+++ b/services/core/java/com/android/server/pm/permission/TEST_MAPPING
@@ -4,7 +4,7 @@
"name": "CtsPermissionTestCases",
"options": [
{
- "exclude-annotation": "android.platform.test.annotations.FlakyTest"
+ "exclude-annotation": "androidx.test.filters.FlakyTest"
},
{
"include-filter": "android.permission.cts.BackgroundPermissionsTest"
@@ -32,7 +32,7 @@
"name": "CtsPermissionPolicyTestCases",
"options": [
{
- "exclude-annotation": "android.platform.test.annotations.FlakyTest"
+ "exclude-annotation": "androidx.test.filters.FlakyTest"
},
{
"include-filter": "android.permissionpolicy.cts.RestrictedPermissionsTest"
diff --git a/services/core/java/com/android/server/pm/pkg/parsing/ParsingPackage.java b/services/core/java/com/android/server/pm/pkg/parsing/ParsingPackage.java
index f1f0fa3..699ccbd 100644
--- a/services/core/java/com/android/server/pm/pkg/parsing/ParsingPackage.java
+++ b/services/core/java/com/android/server/pm/pkg/parsing/ParsingPackage.java
@@ -375,6 +375,10 @@
ParsingPackage setBaseRevisionCode(int baseRevisionCode);
+ ParsingPackage setVersionCode(int vesionCode);
+
+ ParsingPackage setVersionCodeMajor(int vesionCodeMajor);
+
ParsingPackage setVersionName(String versionName);
ParsingPackage setCompileSdkVersion(int compileSdkVersion);
diff --git a/services/core/java/com/android/server/pm/pkg/parsing/ParsingPackageUtils.java b/services/core/java/com/android/server/pm/pkg/parsing/ParsingPackageUtils.java
index dc022f7..d9ad353 100644
--- a/services/core/java/com/android/server/pm/pkg/parsing/ParsingPackageUtils.java
+++ b/services/core/java/com/android/server/pm/pkg/parsing/ParsingPackageUtils.java
@@ -474,15 +474,117 @@
}
}
- private ParseResult<ParsingPackage> parseBaseApk(ParseInput input, File apkFile,
- String codePath, SplitAssetLoader assetLoader, int flags) {
- final String apkPath = apkFile.getAbsolutePath();
+ /**
+ * Creates ParsingPackage using only PackageLite.
+ * Missing fields will contain reasonable defaults.
+ * Used for packageless (aka archived) package installation.
+ */
+ public ParseResult<ParsingPackage> parsePackageFromPackageLite(ParseInput input,
+ PackageLite lite, int flags) {
+ final String volumeUuid = getVolumeUuid(lite.getPath());
+ final String pkgName = lite.getPackageName();
+ final TypedArray manifestArray = null;
+ final ParsingPackage pkg = mCallback.startParsingPackage(pkgName,
+ lite.getBaseApkPath(), lite.getPath(), manifestArray, lite.isCoreApp());
+
+ final int targetSdk = lite.getTargetSdk();
+ final String versionName = null;
+ final int compileSdkVersion = 0;
+ final String compileSdkVersionCodeName = null;
+ final boolean isolatedSplitLoading = false;
+
+ // Normally set from manifestArray.
+ pkg.setVersionCode(lite.getVersionCode());
+ pkg.setVersionCodeMajor(lite.getVersionCodeMajor());
+ pkg.setBaseRevisionCode(lite.getBaseRevisionCode());
+ pkg.setVersionName(versionName);
+ pkg.setCompileSdkVersion(compileSdkVersion);
+ pkg.setCompileSdkVersionCodeName(compileSdkVersionCodeName);
+ pkg.setIsolatedSplitLoading(isolatedSplitLoading);
+ pkg.setTargetSdkVersion(targetSdk);
+
+ // parseBaseApkTags
+ pkg.setInstallLocation(lite.getInstallLocation())
+ .setTargetSandboxVersion(PARSE_DEFAULT_TARGET_SANDBOX)
+ /* Set the global "on SD card" flag */
+ .setExternalStorage((flags & PARSE_EXTERNAL_STORAGE) != 0);
+
+ // parseBaseAppBasicFlags
+ pkg
+ // Default true
+ .setBackupAllowed(true)
+ .setClearUserDataAllowed(true)
+ .setClearUserDataOnFailedRestoreAllowed(true)
+ .setAllowNativeHeapPointerTagging(true)
+ .setEnabled(true)
+ .setExtractNativeLibrariesRequested(true)
+ // targetSdkVersion gated
+ .setAllowAudioPlaybackCapture(targetSdk >= Build.VERSION_CODES.Q)
+ .setHardwareAccelerated(targetSdk >= Build.VERSION_CODES.ICE_CREAM_SANDWICH)
+ .setRequestLegacyExternalStorage(targetSdk < Build.VERSION_CODES.Q)
+ .setCleartextTrafficAllowed(targetSdk < Build.VERSION_CODES.P)
+ // Ints
+ .setCategory(ApplicationInfo.CATEGORY_UNDEFINED)
+ // Floats Default 0f
+ .setMaxAspectRatio(0f)
+ .setMinAspectRatio(0f);
+
+ // No APK - no code.
+ pkg.setDeclaredHavingCode(false);
+
+ final String taskAffinity = null;
+ ParseResult<String> taskAffinityResult = ComponentParseUtils.buildTaskAffinityName(
+ pkgName, pkgName, taskAffinity, input);
+ if (taskAffinityResult.isError()) {
+ return input.error(taskAffinityResult);
+ }
+ pkg.setTaskAffinity(taskAffinityResult.getResult());
+
+ final CharSequence pname = null;
+ ParseResult<String> processNameResult = ComponentParseUtils.buildProcessName(
+ pkgName, null /*defProc*/, pname, flags, mSeparateProcesses, input);
+ if (processNameResult.isError()) {
+ return input.error(processNameResult);
+ }
+ pkg.setProcessName(processNameResult.getResult());
+
+ pkg.setGwpAsanMode(-1);
+ pkg.setMemtagMode(-1);
+
+ afterParseBaseApplication(pkg);
+
+ final ParseResult<ParsingPackage> result = validateBaseApkTags(input, pkg);
+ if (result.isError()) {
+ return result;
+ }
+
+ pkg.setVolumeUuid(volumeUuid);
+
+ if ((flags & PARSE_COLLECT_CERTIFICATES) != 0) {
+ pkg.setSigningDetails(lite.getSigningDetails());
+ } else {
+ pkg.setSigningDetails(SigningDetails.UNKNOWN);
+ }
+
+ return input.success(pkg
+ .set32BitAbiPreferred(lite.isUse32bitAbi()));
+ }
+
+ private static String getVolumeUuid(final String apkPath) {
String volumeUuid = null;
if (apkPath.startsWith(MNT_EXPAND)) {
final int end = apkPath.indexOf('/', MNT_EXPAND.length());
volumeUuid = apkPath.substring(MNT_EXPAND.length(), end);
}
+ return volumeUuid;
+ }
+
+ private ParseResult<ParsingPackage> parseBaseApk(ParseInput input, File apkFile,
+ String codePath, SplitAssetLoader assetLoader, int flags) {
+ final String apkPath = apkFile.getAbsolutePath();
+
+ final String volumeUuid = getVolumeUuid(apkPath);
if (DEBUG_JAR) Slog.d(TAG, "Scanning base APK: " + apkPath);
@@ -882,7 +984,7 @@
}
pkg.setInstallLocation(anInteger(PARSE_DEFAULT_INSTALL_LOCATION,
- R.styleable.AndroidManifest_installLocation, sa))
+ R.styleable.AndroidManifest_installLocation, sa))
.setTargetSandboxVersion(anInteger(PARSE_DEFAULT_TARGET_SANDBOX,
R.styleable.AndroidManifest_targetSandboxVersion, sa))
/* Set the global "on SD card" flag */
@@ -932,6 +1034,10 @@
}
}
+ return validateBaseApkTags(input, pkg);
+ }
+
+ private ParseResult<ParsingPackage> validateBaseApkTags(ParseInput input, ParsingPackage pkg) {
if (!ParsedAttributionUtils.isCombinationValid(pkg.getAttributions())) {
return input.error(
INSTALL_PARSE_FAILED_BAD_MANIFEST,
@@ -2199,15 +2305,19 @@
pkg.sortServices();
}
- // Must be run after the entire {@link ApplicationInfo} has been fully processed and after
- // every activity info has had a chance to set it from its attributes.
+ afterParseBaseApplication(pkg);
+
+ return input.success(pkg);
+ }
+
+ // Must be run after the entire {@link ApplicationInfo} has been fully processed and after
+ // every activity info has had a chance to set it from its attributes.
+ private void afterParseBaseApplication(ParsingPackage pkg) {
setMaxAspectRatio(pkg);
setMinAspectRatio(pkg);
setSupportsSizeChanges(pkg);
pkg.setHasDomainUrls(hasDomainURLs(pkg));
-
- return input.success(pkg);
}
/**
diff --git a/services/core/java/com/android/server/pm/split/DefaultSplitAssetLoader.java b/services/core/java/com/android/server/pm/split/DefaultSplitAssetLoader.java
index a2177e8..0bb969f 100644
--- a/services/core/java/com/android/server/pm/split/DefaultSplitAssetLoader.java
+++ b/services/core/java/com/android/server/pm/split/DefaultSplitAssetLoader.java
@@ -17,13 +17,13 @@
import android.content.pm.parsing.ApkLiteParseUtils;
import android.content.pm.parsing.PackageLite;
-import com.android.server.pm.pkg.parsing.ParsingPackageUtils;
-import com.android.server.pm.pkg.parsing.ParsingPackageUtils.ParseFlags;
import android.content.res.ApkAssets;
import android.content.res.AssetManager;
import android.os.Build;
import com.android.internal.util.ArrayUtils;
+import com.android.server.pm.pkg.parsing.ParsingPackageUtils;
+import com.android.server.pm.pkg.parsing.ParsingPackageUtils.ParseFlags;
import libcore.io.IoUtils;
@@ -82,8 +82,8 @@
}
AssetManager assets = new AssetManager();
- assets.setConfiguration(0, 0, null, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
- Build.VERSION.RESOURCES_SDK_INT);
+ assets.setConfiguration(0, 0, null, new String[0], 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, Build.VERSION.RESOURCES_SDK_INT);
assets.setApkAssets(apkAssets, false /*invalidateCaches*/);
mCachedAssetManager = assets;
diff --git a/services/core/java/com/android/server/pm/split/SplitAssetDependencyLoader.java b/services/core/java/com/android/server/pm/split/SplitAssetDependencyLoader.java
index 1a8c1996..56d92fb 100644
--- a/services/core/java/com/android/server/pm/split/SplitAssetDependencyLoader.java
+++ b/services/core/java/com/android/server/pm/split/SplitAssetDependencyLoader.java
@@ -80,8 +80,8 @@
private static AssetManager createAssetManagerWithAssets(ApkAssets[] apkAssets) {
final AssetManager assets = new AssetManager();
- assets.setConfiguration(0, 0, null, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
- Build.VERSION.RESOURCES_SDK_INT);
+ assets.setConfiguration(0, 0, null, new String[0], 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, Build.VERSION.RESOURCES_SDK_INT);
assets.setApkAssets(apkAssets, false /*invalidateCaches*/);
return assets;
}
diff --git a/services/core/java/com/android/server/power/LowPowerStandbyController.java b/services/core/java/com/android/server/power/LowPowerStandbyController.java
index 3b4b20f..fbad762 100644
--- a/services/core/java/com/android/server/power/LowPowerStandbyController.java
+++ b/services/core/java/com/android/server/power/LowPowerStandbyController.java
@@ -322,11 +322,26 @@
interface Clock {
/** Returns milliseconds since boot, including time spent in sleep. */
long elapsedRealtime();
+
+ /** Returns milliseconds since boot, not counting time spent in deep sleep. */
+ long uptimeMillis();
+ }
+
+ private static class RealClock implements Clock {
+ @Override
+ public long elapsedRealtime() {
+ return SystemClock.elapsedRealtime();
+ }
+
+ @Override
+ public long uptimeMillis() {
+ return SystemClock.uptimeMillis();
+ }
}
public LowPowerStandbyController(Context context, Looper looper) {
- this(context, looper, SystemClock::elapsedRealtime,
- new DeviceConfigWrapper(), () -> ActivityManager.getService(),
+ this(context, looper, new RealClock(), new DeviceConfigWrapper(),
+ () -> ActivityManager.getService(),
new File(Environment.getDataSystemDirectory(), "low_power_standby_policy.xml"));
}
@@ -572,9 +587,9 @@
@GuardedBy("mLock")
private void updateActiveLocked() {
- final long now = mClock.elapsedRealtime();
+ final long nowElapsed = mClock.elapsedRealtime();
final boolean standbyTimeoutExpired =
- (now - mLastInteractiveTimeElapsed) >= mStandbyTimeoutConfig;
+ (nowElapsed - mLastInteractiveTimeElapsed) >= mStandbyTimeoutConfig;
final boolean maintenanceMode = mIdleSinceNonInteractive && !mIsDeviceIdle;
final boolean newActive =
mForceActive || (mIsEnabled && !mIsInteractive && standbyTimeoutExpired
@@ -600,11 +615,11 @@
if (DEBUG) {
Slog.d(TAG, "onNonInteractive");
}
- final long now = mClock.elapsedRealtime();
+ final long nowElapsed = mClock.elapsedRealtime();
synchronized (mLock) {
mIsInteractive = false;
mIsDeviceIdle = false;
- mLastInteractiveTimeElapsed = now;
+ mLastInteractiveTimeElapsed = nowElapsed;
if (mStandbyTimeoutConfig > 0) {
scheduleStandbyTimeoutAlarmLocked();
@@ -630,7 +645,7 @@
@GuardedBy("mLock")
private void scheduleStandbyTimeoutAlarmLocked() {
- final long nextAlarmTime = SystemClock.elapsedRealtime() + mStandbyTimeoutConfig;
+ final long nextAlarmTime = mClock.elapsedRealtime() + mStandbyTimeoutConfig;
mAlarmManager.setExact(AlarmManager.ELAPSED_REALTIME_WAKEUP,
nextAlarmTime, "LowPowerStandbyController.StandbyTimeout",
mOnStandbyTimeoutExpired, mHandler);
@@ -748,9 +763,8 @@
@GuardedBy("mLock")
private void enqueueNotifyPolicyChangedLocked() {
- final long now = mClock.elapsedRealtime();
final Message msg = mHandler.obtainMessage(MSG_NOTIFY_POLICY_CHANGED, getPolicy());
- mHandler.sendMessageAtTime(msg, now);
+ mHandler.sendMessageAtTime(msg, mClock.uptimeMillis());
}
private void notifyPolicyChanged(LowPowerStandbyPolicy policy) {
@@ -775,9 +789,8 @@
@GuardedBy("mLock")
private void enqueueNotifyActiveChangedLocked() {
- final long now = mClock.elapsedRealtime();
final Message msg = mHandler.obtainMessage(MSG_NOTIFY_ACTIVE_CHANGED, mIsActive);
- mHandler.sendMessageAtTime(msg, now);
+ mHandler.sendMessageAtTime(msg, mClock.uptimeMillis());
}
/** Notify other system components about the updated Low Power Standby active state */
@@ -1308,7 +1321,6 @@
@GuardedBy("mLock")
private void enqueueNotifyAllowlistChangedLocked() {
- final long now = mClock.elapsedRealtime();
final int[] allowlistUids = getAllowlistUidsLocked();
if (DEBUG) {
@@ -1317,7 +1329,7 @@
}
final Message msg = mHandler.obtainMessage(MSG_NOTIFY_ALLOWLIST_CHANGED, allowlistUids);
- mHandler.sendMessageAtTime(msg, now);
+ mHandler.sendMessageAtTime(msg, mClock.uptimeMillis());
}
private void notifyAllowlistChanged(int[] allowlistUids) {
@@ -1334,14 +1346,12 @@
@GuardedBy("mLock")
private void enqueueNotifyStandbyPortsChangedLocked() {
- final long now = mClock.elapsedRealtime();
-
if (DEBUG) {
Slog.d(TAG, "enqueueNotifyStandbyPortsChangedLocked");
}
final Message msg = mHandler.obtainMessage(MSG_NOTIFY_STANDBY_PORTS_CHANGED);
- mHandler.sendMessageAtTime(msg, now);
+ mHandler.sendMessageAtTime(msg, mClock.uptimeMillis());
}
private void notifyStandbyPortsChanged() {
@@ -1448,12 +1458,11 @@
public void onForegroundStateChanged(IBinder serviceToken, String packageName,
int userId, boolean isForeground) {
try {
- final long now = mClock.elapsedRealtime();
final int uid = mContext.getPackageManager()
.getPackageUidAsUser(packageName, userId);
final Message message =
mHandler.obtainMessage(MSG_FOREGROUND_SERVICE_STATE_CHANGED, uid, 0);
- mHandler.sendMessageAtTime(message, now);
+ mHandler.sendMessageAtTime(message, mClock.uptimeMillis());
} catch (PackageManager.NameNotFoundException e) {
if (DEBUG) {
Slog.d(TAG, "onForegroundStateChanged: Unknown package: " + packageName
diff --git a/services/core/java/com/android/server/recoverysystem/hal/BootControlHIDL.java b/services/core/java/com/android/server/recoverysystem/hal/BootControlHIDL.java
index 65325c2..7c4d787 100644
--- a/services/core/java/com/android/server/recoverysystem/hal/BootControlHIDL.java
+++ b/services/core/java/com/android/server/recoverysystem/hal/BootControlHIDL.java
@@ -22,6 +22,8 @@
import android.os.RemoteException;
import android.util.Slog;
+import java.util.NoSuchElementException;
+
public class BootControlHIDL implements IBootControl {
private static final String TAG = "BootControlHIDL";
@@ -32,7 +34,7 @@
public static boolean isServicePresent() {
try {
android.hardware.boot.V1_0.IBootControl.getService(true);
- } catch (RemoteException e) {
+ } catch (RemoteException | NoSuchElementException e) {
return false;
}
return true;
@@ -41,7 +43,7 @@
public static boolean isV1_2ServicePresent() {
try {
android.hardware.boot.V1_2.IBootControl.getService(true);
- } catch (RemoteException e) {
+ } catch (RemoteException | NoSuchElementException e) {
return false;
}
return true;
diff --git a/services/core/java/com/android/server/security/FileIntegrityService.java b/services/core/java/com/android/server/security/FileIntegrityService.java
index 9529621..3aed6e3 100644
--- a/services/core/java/com/android/server/security/FileIntegrityService.java
+++ b/services/core/java/com/android/server/security/FileIntegrityService.java
@@ -27,10 +27,12 @@
import android.os.Environment;
import android.os.IBinder;
import android.os.ParcelFileDescriptor;
+import android.os.RemoteException;
import android.os.ResultReceiver;
import android.os.ShellCallback;
import android.os.ShellCommand;
import android.os.UserHandle;
+import android.os.storage.StorageManagerInternal;
import android.security.IFileIntegrityService;
import android.util.Slog;
@@ -54,6 +56,7 @@
import java.security.cert.CertificateFactory;
import java.security.cert.X509Certificate;
import java.util.ArrayList;
+import java.util.Objects;
/**
* A {@link SystemService} that provides file integrity related operations.
@@ -112,7 +115,7 @@
.exec(this, in, out, err, args, callback, resultReceiver);
}
- private void checkCallerPermission(String packageName) {
+ private void checkCallerPackageName(String packageName) {
final int callingUid = Binder.getCallingUid();
final int callingUserId = UserHandle.getUserId(callingUid);
final PackageManagerInternal packageManager =
@@ -123,7 +126,10 @@
throw new SecurityException(
"Calling uid " + callingUid + " does not own package " + packageName);
}
+ }
+ private void checkCallerPermission(String packageName) {
+ checkCallerPackageName(packageName);
if (getContext().checkCallingPermission(android.Manifest.permission.INSTALL_PACKAGES)
== PackageManager.PERMISSION_GRANTED) {
return;
@@ -131,12 +137,43 @@
final AppOpsManager appOpsManager = getContext().getSystemService(AppOpsManager.class);
final int mode = appOpsManager.checkOpNoThrow(
- AppOpsManager.OP_REQUEST_INSTALL_PACKAGES, callingUid, packageName);
+ AppOpsManager.OP_REQUEST_INSTALL_PACKAGES, Binder.getCallingUid(), packageName);
if (mode != AppOpsManager.MODE_ALLOWED) {
throw new SecurityException(
"Caller should have INSTALL_PACKAGES or REQUEST_INSTALL_PACKAGES");
}
}
+
+ @Override
+ public android.os.IInstalld.IFsveritySetupAuthToken createAuthToken(
+ ParcelFileDescriptor authFd) throws RemoteException {
+ Objects.requireNonNull(authFd);
+ try {
+ var authToken = getStorageManagerInternal().createFsveritySetupAuthToken(authFd,
+ Binder.getCallingUid(), Binder.getCallingUserHandle().getIdentifier());
+ // fs-verity setup requires no writable fd to the file. Release the dup now that
+ // it's passed.
+ authFd.close();
+ return authToken;
+ } catch (IOException e) {
+ throw new RemoteException(e);
+ }
+ }
+
+ @Override
+ public int setupFsverity(android.os.IInstalld.IFsveritySetupAuthToken authToken,
+ String filePath, String packageName) throws RemoteException {
+ Objects.requireNonNull(authToken);
+ Objects.requireNonNull(filePath);
+ Objects.requireNonNull(packageName);
+ checkCallerPackageName(packageName);
+
+ try {
+ return getStorageManagerInternal().enableFsverity(authToken, filePath, packageName);
+ } catch (IOException e) {
+ throw new RemoteException(e);
+ }
+ }
};
public FileIntegrityService(final Context context) {
@@ -146,9 +183,19 @@
} catch (CertificateException e) {
Slog.wtf(TAG, "Cannot get an instance of X.509 certificate factory");
}
+
LocalServices.addService(FileIntegrityService.class, this);
}
+ /**
+ * Returns StorageManagerInternal as a proxy to fs-verity related calls. This is to plumb
+ * the call through the canonical Installer instance in StorageManagerService, since the
+ * Installer instance isn't directly accessible.
+ */
+ private StorageManagerInternal getStorageManagerInternal() {
+ return LocalServices.getService(StorageManagerInternal.class);
+ }
+
@Override
public void onStart() {
loadAllCertificates();
diff --git a/services/core/java/com/android/server/sensorprivacy/CameraPrivacyLightController.java b/services/core/java/com/android/server/sensorprivacy/CameraPrivacyLightController.java
index f744d00..565eb6e 100644
--- a/services/core/java/com/android/server/sensorprivacy/CameraPrivacyLightController.java
+++ b/services/core/java/com/android/server/sensorprivacy/CameraPrivacyLightController.java
@@ -18,7 +18,6 @@
import static android.hardware.SensorManager.SENSOR_DELAY_NORMAL;
-import android.annotation.ColorInt;
import android.app.AppOpsManager;
import android.content.Context;
import android.hardware.Sensor;
@@ -39,6 +38,7 @@
import com.android.internal.R;
import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.util.ArrayUtils;
import com.android.server.FgThread;
import java.util.ArrayDeque;
@@ -48,12 +48,10 @@
import java.util.concurrent.Executor;
import java.util.concurrent.TimeUnit;
-
class CameraPrivacyLightController implements AppOpsManager.OnOpActiveChangedListener,
SensorEventListener {
- @VisibleForTesting
- static final double LIGHT_VALUE_MULTIPLIER = 1 / Math.log(1.1);
+ private static final double LIGHT_VALUE_MULTIPLIER = 1 / Math.log(1.1);
private final Handler mHandler;
private final Executor mExecutor;
@@ -69,11 +67,6 @@
private LightsManager.LightsSession mLightsSession = null;
- @ColorInt
- private final int mDayColor;
- @ColorInt
- private final int mNightColor;
-
private final Sensor mLightSensor;
private boolean mIsAmbientLightListenerRegistered = false;
@@ -81,7 +74,9 @@
/** When average of the time integral over the past {@link #mMovingAverageIntervalMillis}
* milliseconds of the log_1.1(lux(t)) is greater than this value, use the daytime brightness
* else use nighttime brightness. */
- private final long mNightThreshold;
+ private final long[] mThresholds;
+
+ private final int[] mColors;
private final ArrayDeque<Pair<Long, Integer>> mAmbientLightValues = new ArrayDeque<>();
/** Tracks the Riemann sum of {@link #mAmbientLightValues} to avoid O(n) operations when sum is
* needed */
@@ -101,6 +96,20 @@
@VisibleForTesting
CameraPrivacyLightController(Context context, Looper looper) {
+ mColors = context.getResources().getIntArray(R.array.config_cameraPrivacyLightColors);
+ if (ArrayUtils.isEmpty(mColors)) {
+ mHandler = null;
+ mExecutor = null;
+ mContext = null;
+ mAppOpsManager = null;
+ mLightsManager = null;
+ mSensorManager = null;
+ mLightSensor = null;
+ mMovingAverageIntervalMillis = 0;
+ mThresholds = null;
+ // Return here before this class starts interacting with other services.
+ return;
+ }
mContext = context;
mHandler = new Handler(looper);
@@ -109,14 +118,20 @@
mAppOpsManager = mContext.getSystemService(AppOpsManager.class);
mLightsManager = mContext.getSystemService(LightsManager.class);
mSensorManager = mContext.getSystemService(SensorManager.class);
-
- mDayColor = mContext.getColor(R.color.camera_privacy_light_day);
- mNightColor = mContext.getColor(R.color.camera_privacy_light_night);
mMovingAverageIntervalMillis = mContext.getResources()
.getInteger(R.integer.config_cameraPrivacyLightAlsAveragingIntervalMillis);
- mNightThreshold = (long) (Math.log(mContext.getResources()
- .getInteger(R.integer.config_cameraPrivacyLightAlsNightThreshold))
- * LIGHT_VALUE_MULTIPLIER);
+ int[] thresholdsLux = mContext.getResources().getIntArray(
+ R.array.config_cameraPrivacyLightAlsLuxThresholds);
+ if (thresholdsLux.length != mColors.length - 1) {
+ throw new IllegalStateException("There must be exactly one more color than thresholds."
+ + " Found " + mColors.length + " colors and " + thresholdsLux.length
+ + " thresholds.");
+ }
+ mThresholds = new long[thresholdsLux.length];
+ for (int i = 0; i < thresholdsLux.length; i++) {
+ int luxValue = thresholdsLux[i];
+ mThresholds[i] = (long) (Math.log(luxValue) * LIGHT_VALUE_MULTIPLIER);
+ }
List<Light> lights = mLightsManager.getLights();
for (int i = 0; i < lights.size(); i++) {
@@ -223,13 +238,8 @@
mLightsSession.close();
mLightsSession = null;
} else {
- int lightColor;
- if (mLightSensor != null && getLiveAmbientLightTotal()
- < getCurrentIntervalMillis() * mNightThreshold) {
- lightColor = mNightColor;
- } else {
- lightColor = mDayColor;
- }
+ int lightColor =
+ mLightSensor == null ? mColors[mColors.length - 1] : computeCurrentLightColor();
if (mLastLightColor == lightColor && mLightsSession != null) {
return;
@@ -252,6 +262,18 @@
}
}
+ private int computeCurrentLightColor() {
+ long liveAmbientLightTotal = getLiveAmbientLightTotal();
+ long currentInterval = getCurrentIntervalMillis();
+
+ for (int i = 0; i < mThresholds.length; i++) {
+ if (liveAmbientLightTotal < currentInterval * mThresholds[i]) {
+ return mColors[i];
+ }
+ }
+ return mColors[mColors.length - 1];
+ }
+
private void updateSensorListener(boolean shouldSessionEnd) {
if (shouldSessionEnd && mIsAmbientLightListenerRegistered) {
mSensorManager.unregisterListener(this);
diff --git a/services/core/java/com/android/server/trust/TrustAgentWrapper.java b/services/core/java/com/android/server/trust/TrustAgentWrapper.java
index 415d05e..0043122 100644
--- a/services/core/java/com/android/server/trust/TrustAgentWrapper.java
+++ b/services/core/java/com/android/server/trust/TrustAgentWrapper.java
@@ -120,9 +120,6 @@
private final BroadcastReceiver mTrustableDowngradeReceiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
- if (!TrustManagerService.ENABLE_ACTIVE_UNLOCK_FLAG) {
- return;
- }
// are these the broadcasts we want to listen to
if (Intent.ACTION_SCREEN_OFF.equals(intent.getAction())) {
downgradeToTrustable();
diff --git a/services/core/java/com/android/server/trust/TrustManagerService.java b/services/core/java/com/android/server/trust/TrustManagerService.java
index bed69fc..cc95da5 100644
--- a/services/core/java/com/android/server/trust/TrustManagerService.java
+++ b/services/core/java/com/android/server/trust/TrustManagerService.java
@@ -58,7 +58,6 @@
import android.os.PersistableBundle;
import android.os.RemoteException;
import android.os.SystemClock;
-import android.os.SystemProperties;
import android.os.UserHandle;
import android.os.UserManager;
import android.provider.Settings;
@@ -165,10 +164,6 @@
@GuardedBy("mUserIsTrusted")
private final SparseBooleanArray mUserIsTrusted = new SparseBooleanArray();
- //TODO(b/215724686): remove flag
- public static final boolean ENABLE_ACTIVE_UNLOCK_FLAG = SystemProperties.getBoolean(
- "fw.enable_active_unlock_flag", true);
-
private enum TrustState {
UNTRUSTED, // the phone is not unlocked by any trustagents
TRUSTABLE, // the phone is in a semi-locked state that can be unlocked if
@@ -569,69 +564,6 @@
int flags,
boolean isFromUnlock,
@Nullable AndroidFuture<GrantTrustResult> resultCallback) {
- if (ENABLE_ACTIVE_UNLOCK_FLAG) {
- updateTrustWithRenewableUnlock(userId, flags, isFromUnlock, resultCallback);
- } else {
- updateTrustWithNonrenewableTrust(userId, flags, isFromUnlock);
- }
- }
-
- private void updateTrustWithNonrenewableTrust(int userId, int flags, boolean isFromUnlock) {
- boolean managed = aggregateIsTrustManaged(userId);
- dispatchOnTrustManagedChanged(managed, userId);
- if (mStrongAuthTracker.isTrustAllowedForUser(userId)
- && isTrustUsuallyManagedInternal(userId) != managed) {
- updateTrustUsuallyManaged(userId, managed);
- }
-
- boolean trusted = aggregateIsTrusted(userId);
- IWindowManager wm = WindowManagerGlobal.getWindowManagerService();
- boolean showingKeyguard = true;
- try {
- showingKeyguard = wm.isKeyguardLocked();
- } catch (RemoteException e) {
- }
-
- boolean changed;
- synchronized (mUserIsTrusted) {
- if (mSettingsObserver.getTrustAgentsNonrenewableTrust()) {
- // For non-renewable trust agents can only set the device to trusted if it already
- // trusted or the device is unlocked. Attempting to set the device as trusted
- // when the device is locked will be ignored.
- changed = mUserIsTrusted.get(userId) != trusted;
- trusted = trusted
- && (!showingKeyguard || isFromUnlock || !changed)
- && userId == mCurrentUser;
- if (DEBUG) {
- Slog.d(TAG, "Extend unlock setting trusted as " + Boolean.toString(trusted)
- + " && " + Boolean.toString(!showingKeyguard)
- + " && " + Boolean.toString(userId == mCurrentUser));
- }
- }
- changed = mUserIsTrusted.get(userId) != trusted;
- mUserIsTrusted.put(userId, trusted);
- }
- dispatchOnTrustChanged(
- trusted,
- false /* newlyUnlocked */,
- userId,
- flags,
- getTrustGrantedMessages(userId));
- if (changed) {
- refreshDeviceLockedForUser(userId);
- if (!trusted) {
- maybeLockScreen(userId);
- } else {
- scheduleTrustTimeout(false /* override */, false /* isTrustableTimeout*/);
- }
- }
- }
-
- private void updateTrustWithRenewableUnlock(
- int userId,
- int flags,
- boolean isFromUnlock,
- @Nullable AndroidFuture<GrantTrustResult> resultCallback) {
boolean managed = aggregateIsTrustManaged(userId);
dispatchOnTrustManagedChanged(managed, userId);
if (mStrongAuthTracker.isTrustAllowedForUser(userId)
@@ -2265,16 +2197,11 @@
@Override
public void handleAlarm() {
- TrustableTimeoutAlarmListener otherAlarm;
- boolean otherAlarmPresent;
- if (ENABLE_ACTIVE_UNLOCK_FLAG) {
- otherAlarm = mTrustableTimeoutAlarmListenerForUser.get(mUserId);
- otherAlarmPresent = (otherAlarm != null) && otherAlarm.isQueued();
- if (otherAlarmPresent) {
- synchronized (mAlarmLock) {
- disableNonrenewableTrustWhileRenewableTrustIsPresent();
- }
- return;
+ TrustableTimeoutAlarmListener otherAlarm =
+ mTrustableTimeoutAlarmListenerForUser.get(mUserId);
+ if (otherAlarm != null && otherAlarm.isQueued()) {
+ synchronized (mAlarmLock) {
+ disableNonrenewableTrustWhileRenewableTrustIsPresent();
}
}
}
@@ -2299,17 +2226,11 @@
@Override
public void handleAlarm() {
- TrustedTimeoutAlarmListener otherAlarm;
- boolean otherAlarmPresent;
- if (ENABLE_ACTIVE_UNLOCK_FLAG) {
- cancelBothTrustableAlarms(mUserId);
- otherAlarm = mTrustTimeoutAlarmListenerForUser.get(mUserId);
- otherAlarmPresent = (otherAlarm != null) && otherAlarm.isQueued();
- if (otherAlarmPresent) {
- synchronized (mAlarmLock) {
- disableRenewableTrustWhileNonrenewableTrustIsPresent();
- }
- return;
+ cancelBothTrustableAlarms(mUserId);
+ TrustedTimeoutAlarmListener otherAlarm = mTrustTimeoutAlarmListenerForUser.get(mUserId);
+ if (otherAlarm != null && otherAlarm.isQueued()) {
+ synchronized (mAlarmLock) {
+ disableRenewableTrustWhileNonrenewableTrustIsPresent();
}
}
}
diff --git a/services/core/java/com/android/server/tv/TvInputManagerService.java b/services/core/java/com/android/server/tv/TvInputManagerService.java
index 7ea9870..234e3f4 100644
--- a/services/core/java/com/android/server/tv/TvInputManagerService.java
+++ b/services/core/java/com/android/server/tv/TvInputManagerService.java
@@ -41,6 +41,7 @@
import android.content.pm.ResolveInfo;
import android.content.pm.ServiceInfo;
import android.content.pm.UserInfo;
+import android.content.res.Resources;
import android.graphics.Rect;
import android.hardware.hdmi.HdmiControlManager;
import android.hardware.hdmi.HdmiDeviceInfo;
@@ -94,6 +95,7 @@
import android.view.InputChannel;
import android.view.Surface;
+import com.android.internal.R;
import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.content.PackageMonitor;
@@ -177,6 +179,11 @@
private final ActivityManager mActivityManager;
+ private boolean mExternalInputLoggingDisplayNameFilterEnabled = false;
+ private final HashSet<String> mExternalInputLoggingDeviceOnScreenDisplayNames =
+ new HashSet<String>();
+ private final List<String> mExternalInputLoggingDeviceBrandNames = new ArrayList<String>();
+
public TvInputManagerService(Context context) {
super(context);
@@ -192,6 +199,8 @@
synchronized (mLock) {
getOrCreateUserStateLocked(mCurrentUserId);
}
+
+ initExternalInputLoggingConfigs();
}
@Override
@@ -224,6 +233,21 @@
}
}
+ private void initExternalInputLoggingConfigs() {
+ mExternalInputLoggingDisplayNameFilterEnabled = mContext.getResources().getBoolean(
+ R.bool.config_tvExternalInputLoggingDisplayNameFilterEnabled);
+ if (!mExternalInputLoggingDisplayNameFilterEnabled) {
+ return;
+ }
+ final String[] deviceOnScreenDisplayNames = mContext.getResources().getStringArray(
+ R.array.config_tvExternalInputLoggingDeviceOnScreenDisplayNames);
+ final String[] deviceBrandNames = mContext.getResources().getStringArray(
+ R.array.config_tvExternalInputLoggingDeviceBrandNames);
+ mExternalInputLoggingDeviceOnScreenDisplayNames.addAll(
+ Arrays.asList(deviceOnScreenDisplayNames));
+ mExternalInputLoggingDeviceBrandNames.addAll(Arrays.asList(deviceBrandNames));
+ }
+
private void registerBroadcastReceivers() {
PackageMonitor monitor = new PackageMonitor() {
private void buildTvInputList(String[] packages) {
@@ -3073,13 +3097,32 @@
hdmiPort = hdmiDeviceInfo.getPortId();
if (hdmiDeviceInfo.isCecDevice()) {
displayName = hdmiDeviceInfo.getDisplayName();
+ if (mExternalInputLoggingDisplayNameFilterEnabled) {
+ displayName = filterExternalInputLoggingDisplayName(displayName);
+ }
vendorId = hdmiDeviceInfo.getVendorId();
}
}
}
FrameworkStatsLog.write(FrameworkStatsLog.EXTERNAL_TV_INPUT_EVENT, eventType, inputState,
- inputType, displayName, vendorId, hdmiPort, tifSessionId);
+ inputType, vendorId, hdmiPort, tifSessionId, displayName);
+ }
+
+ private String filterExternalInputLoggingDisplayName(String displayName) {
+ String nullDisplayName = "NULL_DISPLAY_NAME", filteredDisplayName = "FILTERED_DISPLAY_NAME";
+ if (displayName == null) {
+ return nullDisplayName;
+ }
+ if (mExternalInputLoggingDeviceOnScreenDisplayNames.contains(displayName)) {
+ return displayName;
+ }
+ for (String brandName : mExternalInputLoggingDeviceBrandNames) {
+ if (displayName.toUpperCase().contains(brandName.toUpperCase())) {
+ return brandName;
+ }
+ }
+ return filteredDisplayName;
}
private static final class UserState {
diff --git a/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java b/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java
index 598c1ae..ddc0519 100644
--- a/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java
+++ b/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java
@@ -2000,7 +2000,12 @@
WallpaperData wallpaper, IRemoteCallback reply, ServiceInfo serviceInfo) {
if (serviceInfo == null) {
- clearWallpaperLocked(wallpaper.mWhich, wallpaper.userId, reply);
+ if (wallpaper.mWhich == (FLAG_LOCK | FLAG_SYSTEM)) {
+ clearWallpaperLocked(FLAG_SYSTEM, wallpaper.userId, null);
+ clearWallpaperLocked(FLAG_LOCK, wallpaper.userId, reply);
+ } else {
+ clearWallpaperLocked(wallpaper.mWhich, wallpaper.userId, reply);
+ }
return;
}
Slog.w(TAG, "Wallpaper isn't direct boot aware; using fallback until unlocked");
@@ -2032,7 +2037,7 @@
WallpaperData data = null;
synchronized (mLock) {
if (mIsLockscreenLiveWallpaperEnabled) {
- clearWallpaperLocked(callingPackage, which, userId, null);
+ clearWallpaperLocked(callingPackage, which, userId);
} else {
clearWallpaperLocked(which, userId, null);
}
@@ -2052,8 +2057,7 @@
}
}
- private void clearWallpaperLocked(String callingPackage, int which, int userId,
- IRemoteCallback reply) {
+ private void clearWallpaperLocked(String callingPackage, int which, int userId) {
// Might need to bring it in the first time to establish our rewrite
if (!mWallpaperMap.contains(userId)) {
@@ -2092,8 +2096,8 @@
finalWhich = which;
}
- boolean success = withCleanCallingIdentity(() -> setWallpaperComponentInternal(
- component, callingPackage, finalWhich, userId, reply));
+ boolean success = withCleanCallingIdentity(() -> setWallpaperComponent(
+ component, callingPackage, finalWhich, userId));
if (success) return;
} catch (IllegalArgumentException e1) {
e = e1;
@@ -2105,23 +2109,10 @@
// wallpaper.
Slog.e(TAG, "Default wallpaper component not found!", e);
withCleanCallingIdentity(() -> clearWallpaperComponentLocked(wallpaper));
- if (reply != null) {
- try {
- reply.sendResult(null);
- } catch (RemoteException e1) {
- Slog.w(TAG, "Failed to notify callback after wallpaper clear", e1);
- }
- }
}
+ // TODO(b/266818039) remove this version of the method
private void clearWallpaperLocked(int which, int userId, IRemoteCallback reply) {
-
- if (mIsLockscreenLiveWallpaperEnabled) {
- String callingPackage = mPackageManagerInternal.getNameForUid(getCallingUid());
- clearWallpaperLocked(callingPackage, which, userId, reply);
- return;
- }
-
if (which != FLAG_SYSTEM && which != FLAG_LOCK) {
throw new IllegalArgumentException("Must specify exactly one kind of wallpaper to clear");
}
@@ -3293,7 +3284,7 @@
boolean setWallpaperComponent(ComponentName name, String callingPackage,
@SetWallpaperFlags int which, int userId) {
if (mIsLockscreenLiveWallpaperEnabled) {
- return setWallpaperComponentInternal(name, callingPackage, which, userId, null);
+ return setWallpaperComponentInternal(name, callingPackage, which, userId);
} else {
setWallpaperComponentInternalLegacy(name, callingPackage, which, userId);
return true;
@@ -3301,7 +3292,7 @@
}
private boolean setWallpaperComponentInternal(ComponentName name, String callingPackage,
- @SetWallpaperFlags int which, int userIdIn, IRemoteCallback reply) {
+ @SetWallpaperFlags int which, int userIdIn) {
if (DEBUG) {
Slog.v(TAG, "Setting new live wallpaper: which=" + which + ", component: " + name);
}
@@ -3350,7 +3341,6 @@
Slog.d(TAG, "publish system wallpaper changed!");
}
liveSync.complete();
- if (reply != null) reply.sendResult(null);
}
};
diff --git a/services/core/java/com/android/server/wm/ActivityClientController.java b/services/core/java/com/android/server/wm/ActivityClientController.java
index aafff2c..d430dda 100644
--- a/services/core/java/com/android/server/wm/ActivityClientController.java
+++ b/services/core/java/com/android/server/wm/ActivityClientController.java
@@ -615,7 +615,15 @@
@Override
public int getTaskForActivity(IBinder token, boolean onlyRoot) {
synchronized (mGlobalLock) {
- return ActivityRecord.getTaskForActivityLocked(token, onlyRoot);
+ final ActivityRecord r = ActivityRecord.forTokenLocked(token);
+ if (r == null) {
+ return INVALID_TASK_ID;
+ }
+ final Task task = r.getTask();
+ if (onlyRoot) {
+ return task.getRootActivity() == r ? task.mTaskId : INVALID_TASK_ID;
+ }
+ return task.mTaskId;
}
}
diff --git a/services/core/java/com/android/server/wm/ActivityRecord.java b/services/core/java/com/android/server/wm/ActivityRecord.java
index f776fac..6085488 100644
--- a/services/core/java/com/android/server/wm/ActivityRecord.java
+++ b/services/core/java/com/android/server/wm/ActivityRecord.java
@@ -225,7 +225,6 @@
import static com.android.server.wm.LetterboxConfiguration.DEFAULT_LETTERBOX_ASPECT_RATIO_FOR_MULTI_WINDOW;
import static com.android.server.wm.LetterboxConfiguration.MIN_FIXED_ORIENTATION_LETTERBOX_ASPECT_RATIO;
import static com.android.server.wm.StartingData.AFTER_TRANSACTION_COPY_TO_CLIENT;
-import static com.android.server.wm.StartingData.AFTER_TRANSACTION_IDLE;
import static com.android.server.wm.StartingData.AFTER_TRANSACTION_REMOVE_DIRECTLY;
import static com.android.server.wm.SurfaceAnimator.ANIMATION_TYPE_APP_TRANSITION;
import static com.android.server.wm.SurfaceAnimator.ANIMATION_TYPE_PREDICT_BACK;
@@ -584,20 +583,11 @@
// Tracking splash screen status from previous activity
boolean mSplashScreenStyleSolidColor = false;
- Drawable mEnterpriseThumbnailDrawable;
-
boolean mPauseSchedulePendingForPip = false;
// Gets set to indicate that the activity is currently being auto-pipped.
boolean mAutoEnteringPip = false;
- private void updateEnterpriseThumbnailDrawable(Context context) {
- DevicePolicyManager dpm = context.getSystemService(DevicePolicyManager.class);
- mEnterpriseThumbnailDrawable = dpm.getResources().getDrawable(
- WORK_PROFILE_ICON, OUTLINE, PROFILE_SWITCH_ANIMATION,
- () -> context.getDrawable(R.drawable.ic_corp_badge));
- }
-
static final int LAUNCH_SOURCE_TYPE_SYSTEM = 1;
static final int LAUNCH_SOURCE_TYPE_HOME = 2;
static final int LAUNCH_SOURCE_TYPE_SYSTEMUI = 3;
@@ -2212,8 +2202,6 @@
mActivityRecordInputSink = new ActivityRecordInputSink(this, sourceRecord);
- updateEnterpriseThumbnailDrawable(mAtmService.getUiContext());
-
boolean appActivityEmbeddingEnabled = false;
try {
appActivityEmbeddingEnabled = WindowManager.hasWindowExtensionsEnabled()
@@ -2709,7 +2697,8 @@
private void requestCopySplashScreen() {
mTransferringSplashScreenState = TRANSFER_SPLASH_SCREEN_COPYING;
- if (!mAtmService.mTaskOrganizerController.copySplashScreenView(getTask())) {
+ if (mStartingSurface == null || !mAtmService.mTaskOrganizerController.copySplashScreenView(
+ getTask(), mStartingSurface.mTaskOrganizer)) {
mTransferringSplashScreenState = TRANSFER_SPLASH_SCREEN_FINISH;
removeStartingWindow();
}
@@ -2722,12 +2711,14 @@
*/
void onCopySplashScreenFinish(SplashScreenViewParcelable parcelable) {
removeTransferSplashScreenTimeout();
- // unable to copy from shell, maybe it's not a splash screen. or something went wrong.
- // either way, abort and reset the sequence.
- if (parcelable == null
+ final SurfaceControl windowAnimationLeash = (parcelable == null
|| mTransferringSplashScreenState != TRANSFER_SPLASH_SCREEN_COPYING
|| mStartingWindow == null || mStartingWindow.mRemoved
- || finishing) {
+ || finishing) ? null
+ : TaskOrganizerController.applyStartingWindowAnimation(mStartingWindow);
+ if (windowAnimationLeash == null) {
+ // Unable to copy from shell, maybe it's not a splash screen, or something went wrong.
+ // Either way, abort and reset the sequence.
if (parcelable != null) {
parcelable.clearIfNeeded();
}
@@ -2735,9 +2726,6 @@
removeStartingWindow();
return;
}
- // schedule attach splashScreen to client
- final SurfaceControl windowAnimationLeash = TaskOrganizerController
- .applyStartingWindowAnimation(mStartingWindow);
try {
mTransferringSplashScreenState = TRANSFER_SPLASH_SCREEN_ATTACH_TO_CLIENT;
mAtmService.getLifecycleManager().scheduleTransaction(app.getThread(), token,
@@ -2777,7 +2765,8 @@
&& (mTransferringSplashScreenState == TRANSFER_SPLASH_SCREEN_FINISH
|| mTransferringSplashScreenState == TRANSFER_SPLASH_SCREEN_IDLE)) {
ProtoLog.v(WM_DEBUG_STARTING_WINDOW, "Cleaning splash screen token=%s", this);
- mAtmService.mTaskOrganizerController.onAppSplashScreenViewRemoved(getTask());
+ mAtmService.mTaskOrganizerController.onAppSplashScreenViewRemoved(getTask(),
+ mStartingSurface != null ? mStartingSurface.mTaskOrganizer : null);
}
}
@@ -2865,7 +2854,6 @@
} else if (lastData.mRemoveAfterTransaction == AFTER_TRANSACTION_COPY_TO_CLIENT) {
removeStartingWindow();
}
- lastData.mRemoveAfterTransaction = AFTER_TRANSACTION_IDLE;
}
void removeStartingWindowAnimation(boolean prepareAnimation) {
@@ -3397,7 +3385,7 @@
rootTask.moveToFront(reason, task);
// Report top activity change to tracking services and WM
- if (mRootWindowContainer.getTopResumedActivity() == this) {
+ if (mState == RESUMED && mRootWindowContainer.getTopResumedActivity() == this) {
mAtmService.setLastResumedActivityUncheckLocked(this, reason);
}
return true;
@@ -7122,15 +7110,18 @@
return mVisibleRequested || nowVisible || mState == PAUSING || mState == RESUMED;
}
+ /**
+ * Returns the task id of the activity token. If onlyRoot=true is specified, it will
+ * return a valid id only if the activity is root or the activity is immediately above
+ * the first non-relinquish-identity activity.
+ * TODO(b/297476786): Clarify the use cases about when should get the bottom activity
+ * or the first non-relinquish-identity activity from bottom.
+ */
static int getTaskForActivityLocked(IBinder token, boolean onlyRoot) {
final ActivityRecord r = ActivityRecord.forTokenLocked(token);
if (r == null || r.getParent() == null) {
return INVALID_TASK_ID;
}
- return getTaskForActivityLocked(r, onlyRoot);
- }
-
- static int getTaskForActivityLocked(ActivityRecord r, boolean onlyRoot) {
final Task task = r.task;
if (onlyRoot && r.compareTo(task.getRootActivity(
false /*ignoreRelinquishIdentity*/, true /*setToBottomIfNone*/)) > 0) {
@@ -7711,9 +7702,16 @@
return;
}
final Rect frame = win.getRelativeFrame();
- final Drawable thumbnailDrawable = task.mUserId == mWmService.mCurrentUserId
- ? mAtmService.getUiContext().getDrawable(R.drawable.ic_account_circle)
- : mEnterpriseThumbnailDrawable;
+ final Context context = mAtmService.getUiContext();
+ final Drawable thumbnailDrawable;
+ if (task.mUserId == mWmService.mCurrentUserId) {
+ thumbnailDrawable = context.getDrawable(R.drawable.ic_account_circle);
+ } else {
+ final DevicePolicyManager dpm = context.getSystemService(DevicePolicyManager.class);
+ thumbnailDrawable = dpm.getResources().getDrawable(
+ WORK_PROFILE_ICON, OUTLINE, PROFILE_SWITCH_ANIMATION,
+ () -> context.getDrawable(R.drawable.ic_corp_badge));
+ }
final HardwareBuffer thumbnail = getDisplayContent().mAppTransition
.createCrossProfileAppsThumbnail(thumbnailDrawable, frame);
if (thumbnail == null) {
diff --git a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
index fb62412..42c3630 100644
--- a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
+++ b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
@@ -4048,10 +4048,6 @@
mRecentTasks.notifyTaskPersisterLocked(task, flush);
}
- boolean isKeyguardLocked(int displayId) {
- return mKeyguardController.isKeyguardLocked(displayId);
- }
-
/**
* Clears launch params for the given package.
*
@@ -4588,6 +4584,20 @@
}
/**
+ * Updates the {@link ApplicationInfo}s of the package activities th that are attached in the
+ * WM hierarchy.
+ */
+ public void updateActivityApplicationInfo(int userId,
+ ArrayMap<String, ApplicationInfo> applicationInfoByPackage) {
+ synchronized (mGlobalLock) {
+ if (mRootWindowContainer != null) {
+ mRootWindowContainer.updateActivityApplicationInfo(userId,
+ applicationInfoByPackage);
+ }
+ }
+ }
+
+ /**
* Update the asset configuration and increase the assets sequence number.
* @param processes the processes that needs to update the asset configuration
*/
diff --git a/services/core/java/com/android/server/wm/ActivityTaskSupervisor.java b/services/core/java/com/android/server/wm/ActivityTaskSupervisor.java
index eb15b31..6eb9ed69 100644
--- a/services/core/java/com/android/server/wm/ActivityTaskSupervisor.java
+++ b/services/core/java/com/android/server/wm/ActivityTaskSupervisor.java
@@ -794,6 +794,14 @@
return false;
}
+ // Try pausing the existing resumed activity in the same TaskFragment if any.
+ final TaskFragment taskFragment = r.getTaskFragment();
+ if (taskFragment != null && taskFragment.getResumedActivity() != null) {
+ if (taskFragment.startPausing(mUserLeaving, false /* uiSleeping */, r, "realStart")) {
+ return false;
+ }
+ }
+
final Task task = r.getTask();
final Task rootTask = task.getRootTask();
diff --git a/services/core/java/com/android/server/wm/DesktopModeLaunchParamsModifier.java b/services/core/java/com/android/server/wm/DesktopModeLaunchParamsModifier.java
index 4180cd2..15a0445 100644
--- a/services/core/java/com/android/server/wm/DesktopModeLaunchParamsModifier.java
+++ b/services/core/java/com/android/server/wm/DesktopModeLaunchParamsModifier.java
@@ -40,8 +40,6 @@
private static final boolean DEBUG = false;
// Desktop mode feature flags.
- private static final boolean DESKTOP_MODE_PROTO1_SUPPORTED =
- SystemProperties.getBoolean("persist.wm.debug.desktop_mode", false);
private static final boolean DESKTOP_MODE_PROTO2_SUPPORTED =
SystemProperties.getBoolean("persist.wm.debug.desktop_mode_2", false);
// Override default freeform task width when desktop mode is enabled. In dips.
@@ -142,6 +140,6 @@
/** Whether desktop mode is supported. */
static boolean isDesktopModeSupported() {
- return DESKTOP_MODE_PROTO1_SUPPORTED || DESKTOP_MODE_PROTO2_SUPPORTED;
+ return DESKTOP_MODE_PROTO2_SUPPORTED;
}
}
diff --git a/services/core/java/com/android/server/wm/KeyguardController.java b/services/core/java/com/android/server/wm/KeyguardController.java
index 8c1d8fa..1a319ad 100644
--- a/services/core/java/com/android/server/wm/KeyguardController.java
+++ b/services/core/java/com/android/server/wm/KeyguardController.java
@@ -423,7 +423,7 @@
final TransitionController tc = mRootWindowContainer.mTransitionController;
- final boolean occluded = isDisplayOccluded(displayId);
+ final boolean occluded = getDisplayState(displayId).mOccluded;
final boolean performTransition = isKeyguardLocked(displayId);
final boolean executeTransition = performTransition && !tc.isCollecting();
@@ -500,15 +500,6 @@
}
}
- /**
- * Returns {@code true} if the top activity on the display can occlude keyguard or the device
- * is dreaming. Note that this method may return {@code true} even if the keyguard is disabled
- * or not showing.
- */
- boolean isDisplayOccluded(int displayId) {
- return getDisplayState(displayId).mOccluded;
- }
-
ActivityRecord getTopOccludingActivity(int displayId) {
return getDisplayState(displayId).mTopOccludesActivity;
}
@@ -601,6 +592,11 @@
private boolean mAodShowing;
private boolean mKeyguardGoingAway;
private boolean mDismissalRequested;
+
+ /**
+ * True if the top activity on the display can occlude keyguard or the device is dreaming.
+ * Note that this can be true even if the keyguard is disabled or not showing.
+ */
private boolean mOccluded;
private boolean mShowingDream;
diff --git a/services/core/java/com/android/server/wm/RootWindowContainer.java b/services/core/java/com/android/server/wm/RootWindowContainer.java
index 2eec58b..57f8268 100644
--- a/services/core/java/com/android/server/wm/RootWindowContainer.java
+++ b/services/core/java/com/android/server/wm/RootWindowContainer.java
@@ -2699,7 +2699,7 @@
// transition exists, so this affects only when no lock screen is set. Otherwise
// keyguard going away animation will be played.
// See also AppTransitionController#getTransitCompatType for more details.
- if ((!mTaskSupervisor.getKeyguardController().isDisplayOccluded(display.mDisplayId)
+ if ((!mTaskSupervisor.getKeyguardController().isKeyguardOccluded(display.mDisplayId)
&& token.mTag.equals(KEYGUARD_SLEEP_TOKEN_TAG))
|| token.mTag.equals(DISPLAY_OFF_SLEEP_TOKEN_TAG)) {
display.mSkipAppTransitionAnimation = true;
@@ -3172,6 +3172,20 @@
});
}
+ void updateActivityApplicationInfo(int userId,
+ ArrayMap<String, ApplicationInfo> applicationInfoByPackage) {
+ forAllActivities(r -> {
+ if (r.mUserId != userId) {
+ return;
+ }
+
+ final ApplicationInfo aInfo = applicationInfoByPackage.get(r.packageName);
+ if (aInfo != null) {
+ r.updateApplicationInfo(aInfo);
+ }
+ });
+ }
+
void finishVoiceTask(IVoiceInteractionSession session) {
final IBinder binder = session.asBinder();
forAllLeafTasks(t -> t.finishIfVoiceTask(binder), true /* traverseTopToBottom */);
diff --git a/services/core/java/com/android/server/wm/StartingData.java b/services/core/java/com/android/server/wm/StartingData.java
index a23547e..2d281c4 100644
--- a/services/core/java/com/android/server/wm/StartingData.java
+++ b/services/core/java/com/android/server/wm/StartingData.java
@@ -76,7 +76,7 @@
* This starting window should be removed after applying the start transaction of transition,
* which ensures the app window has shown.
*/
- @AfterTransaction int mRemoveAfterTransaction;
+ @AfterTransaction int mRemoveAfterTransaction = AFTER_TRANSACTION_IDLE;
/** Whether to prepare the removal animation. */
boolean mPrepareRemoveAnimation;
diff --git a/services/core/java/com/android/server/wm/StartingSurfaceController.java b/services/core/java/com/android/server/wm/StartingSurfaceController.java
index 0bb773a..a55c232 100644
--- a/services/core/java/com/android/server/wm/StartingSurfaceController.java
+++ b/services/core/java/com/android/server/wm/StartingSurfaceController.java
@@ -40,6 +40,7 @@
import android.content.pm.ApplicationInfo;
import android.os.UserHandle;
import android.util.Slog;
+import android.window.ITaskOrganizer;
import android.window.SplashScreenView;
import android.window.TaskSnapshot;
@@ -79,12 +80,13 @@
}
StartingSurface createSplashScreenStartingSurface(ActivityRecord activity, int theme) {
-
synchronized (mService.mGlobalLock) {
final Task task = activity.getTask();
- if (task != null && mService.mAtmService.mTaskOrganizerController.addStartingWindow(
- task, activity, theme, null /* taskSnapshot */)) {
- return new StartingSurface(task);
+ final TaskOrganizerController controller =
+ mService.mAtmService.mTaskOrganizerController;
+ if (task != null && controller.addStartingWindow(task, activity, theme,
+ null /* taskSnapshot */)) {
+ return new StartingSurface(task, controller.getTaskOrganizer());
}
}
return null;
@@ -166,9 +168,12 @@
activity.mDisplayContent.handleTopActivityLaunchingInDifferentOrientation(
activity, false /* checkOpening */);
}
- mService.mAtmService.mTaskOrganizerController.addStartingWindow(task,
- activity, 0 /* launchTheme */, taskSnapshot);
- return new StartingSurface(task);
+ final TaskOrganizerController controller =
+ mService.mAtmService.mTaskOrganizerController;
+ if (controller.addStartingWindow(task, activity, 0 /* launchTheme */, taskSnapshot)) {
+ return new StartingSurface(task, controller.getTaskOrganizer());
+ }
+ return null;
}
}
@@ -256,9 +261,12 @@
final class StartingSurface {
private final Task mTask;
+ // The task organizer which hold the client side reference of this surface.
+ final ITaskOrganizer mTaskOrganizer;
- StartingSurface(Task task) {
+ StartingSurface(Task task, ITaskOrganizer taskOrganizer) {
mTask = task;
+ mTaskOrganizer = taskOrganizer;
}
/**
@@ -268,7 +276,8 @@
*/
public void remove(boolean animate) {
synchronized (mService.mGlobalLock) {
- mService.mAtmService.mTaskOrganizerController.removeStartingWindow(mTask, animate);
+ mService.mAtmService.mTaskOrganizerController.removeStartingWindow(mTask,
+ mTaskOrganizer, animate);
}
}
}
diff --git a/services/core/java/com/android/server/wm/Task.java b/services/core/java/com/android/server/wm/Task.java
index f9bbc68..387a876 100644
--- a/services/core/java/com/android/server/wm/Task.java
+++ b/services/core/java/com/android/server/wm/Task.java
@@ -3579,7 +3579,7 @@
&& activity.info != info.taskInfo.topActivityInfo
? activity.info : null;
info.isKeyguardOccluded =
- mAtmService.mKeyguardController.isDisplayOccluded(DEFAULT_DISPLAY);
+ mAtmService.mKeyguardController.isKeyguardOccluded(info.taskInfo.displayId);
info.startingWindowTypeParameter = activity.mStartingData != null
? activity.mStartingData.mTypeParams
diff --git a/services/core/java/com/android/server/wm/TaskOrganizerController.java b/services/core/java/com/android/server/wm/TaskOrganizerController.java
index 3d01001..41e49b9 100644
--- a/services/core/java/com/android/server/wm/TaskOrganizerController.java
+++ b/services/core/java/com/android/server/wm/TaskOrganizerController.java
@@ -652,7 +652,7 @@
if (rootTask == null || activity.mStartingData == null) {
return false;
}
- final ITaskOrganizer lastOrganizer = mTaskOrganizers.peekLast();
+ final ITaskOrganizer lastOrganizer = getTaskOrganizer();
if (lastOrganizer == null) {
return false;
}
@@ -672,12 +672,13 @@
return true;
}
- void removeStartingWindow(Task task, boolean prepareAnimation) {
+ void removeStartingWindow(Task task, ITaskOrganizer taskOrganizer, boolean prepareAnimation) {
final Task rootTask = task.getRootTask();
if (rootTask == null) {
return;
}
- final ITaskOrganizer lastOrganizer = mTaskOrganizers.peekLast();
+ final ITaskOrganizer lastOrganizer = taskOrganizer != null ? taskOrganizer
+ : getTaskOrganizer();
if (lastOrganizer == null) {
return;
}
@@ -771,12 +772,13 @@
}
}
- boolean copySplashScreenView(Task task) {
+ boolean copySplashScreenView(Task task, ITaskOrganizer taskOrganizer) {
final Task rootTask = task.getRootTask();
if (rootTask == null) {
return false;
}
- final ITaskOrganizer lastOrganizer = mTaskOrganizers.peekLast();
+ final ITaskOrganizer lastOrganizer = taskOrganizer != null ? taskOrganizer
+ : getTaskOrganizer();
if (lastOrganizer == null) {
return false;
}
@@ -799,12 +801,12 @@
* @see com.android.wm.shell.ShellTaskOrganizer#onAppSplashScreenViewRemoved(int)
* @see SplashScreenView#remove()
*/
- public void onAppSplashScreenViewRemoved(Task task) {
+ public void onAppSplashScreenViewRemoved(Task task, ITaskOrganizer organizer) {
final Task rootTask = task.getRootTask();
if (rootTask == null) {
return;
}
- final ITaskOrganizer lastOrganizer = mTaskOrganizers.peekLast();
+ final ITaskOrganizer lastOrganizer = organizer != null ? organizer : getTaskOrganizer();
if (lastOrganizer == null) {
return;
}
diff --git a/services/core/java/com/android/server/wm/Transition.java b/services/core/java/com/android/server/wm/Transition.java
index e9af42b..81f91c7 100644
--- a/services/core/java/com/android/server/wm/Transition.java
+++ b/services/core/java/com/android/server/wm/Transition.java
@@ -1127,6 +1127,9 @@
// though, notify the controller to prevent degenerate cases.
if (!r.isVisibleRequested()) {
mController.mValidateCommitVis.add(r);
+ } else {
+ // Make sure onAppTransitionFinished can be notified.
+ mParticipants.add(r);
}
return;
}
diff --git a/services/core/java/com/android/server/wm/TransitionController.java b/services/core/java/com/android/server/wm/TransitionController.java
index 3c85f08..e4b9571 100644
--- a/services/core/java/com/android/server/wm/TransitionController.java
+++ b/services/core/java/com/android/server/wm/TransitionController.java
@@ -711,7 +711,7 @@
startTask.fillTaskInfo(info);
}
final TransitionRequestInfo request = new TransitionRequestInfo(
- transition.mType, info, remoteTransition, displayChange);
+ transition.mType, info, remoteTransition, displayChange, transition.getFlags());
transition.mLogger.mRequestTimeNs = SystemClock.elapsedRealtimeNanos();
transition.mLogger.mRequest = request;
mTransitionPlayer.requestStartTransition(transition.getToken(), request);
diff --git a/services/core/java/com/android/server/wm/WindowToken.java b/services/core/java/com/android/server/wm/WindowToken.java
index 31afcbf..4d73358 100644
--- a/services/core/java/com/android/server/wm/WindowToken.java
+++ b/services/core/java/com/android/server/wm/WindowToken.java
@@ -519,7 +519,8 @@
for (int i = mFixedRotationTransformState.mAssociatedTokens.size() - 1; i >= 0; i--) {
final ActivityRecord r =
mFixedRotationTransformState.mAssociatedTokens.get(i).asActivityRecord();
- if (r != null && r.isInTransition()) {
+ // Only care about the transition at Activity/Task level.
+ if (r != null && r.inTransitionSelfOrParent() && !r.mDisplayContent.inTransition()) {
return true;
}
}
diff --git a/services/core/jni/Android.bp b/services/core/jni/Android.bp
index 2f150a1..2a995b2 100644
--- a/services/core/jni/Android.bp
+++ b/services/core/jni/Android.bp
@@ -173,7 +173,6 @@
"android.hardware.graphics.bufferqueue@1.0",
"android.hardware.graphics.bufferqueue@2.0",
"android.hardware.graphics.common@1.2",
- "android.hardware.graphics.mapper@4.0",
"android.hardware.input.processor-V1-ndk",
"android.hardware.ir@1.0",
"android.hardware.light@2.0",
diff --git a/services/core/jni/com_android_server_input_InputManagerService.cpp b/services/core/jni/com_android_server_input_InputManagerService.cpp
index 5ab8d36..6e0d98c 100644
--- a/services/core/jni/com_android_server_input_InputManagerService.cpp
+++ b/services/core/jni/com_android_server_input_InputManagerService.cpp
@@ -262,7 +262,8 @@
class NativeInputManager : public virtual InputReaderPolicyInterface,
public virtual InputDispatcherPolicyInterface,
- public virtual PointerControllerPolicyInterface {
+ public virtual PointerControllerPolicyInterface,
+ public virtual PointerChoreographerPolicyInterface {
protected:
virtual ~NativeInputManager();
@@ -370,6 +371,9 @@
virtual PointerIconStyle getCustomPointerIconId();
virtual void onPointerDisplayIdChanged(int32_t displayId, const FloatPoint& position);
+ /* --- PointerControllerPolicyInterface implementation --- */
+ virtual std::shared_ptr<PointerControllerInterface> createPointerController() override;
+
private:
sp<InputManagerInterface> mInputManager;
@@ -402,8 +406,12 @@
// Sprite controller singleton, created on first use.
std::shared_ptr<SpriteController> spriteController{};
+ // TODO(b/293587049): Remove when the PointerChoreographer refactoring is complete.
// Pointer controller singleton, created and destroyed as needed.
- std::weak_ptr<PointerController> pointerController{};
+ std::weak_ptr<PointerController> legacyPointerController{};
+
+ // The list of PointerControllers created and managed by the PointerChoreographer.
+ std::list<std::weak_ptr<PointerController>> pointerControllers{};
// Input devices to be disabled
std::set<int32_t> disabledInputDevices{};
@@ -443,6 +451,9 @@
jmethodID method, const char* methodName,
std::function<T(std::string)> opOnValue = [](auto&& v) { return std::move(v); });
+ void forEachPointerControllerLocked(std::function<void(PointerController&)> apply)
+ REQUIRES(mLock);
+
static inline JNIEnv* jniEnv() { return AndroidRuntime::getJNIEnv(); }
};
@@ -452,7 +463,7 @@
mServiceObj = env->NewGlobalRef(serviceObj);
- InputManager* im = new InputManager(this, *this);
+ InputManager* im = new InputManager(this, *this, *this);
mInputManager = im;
defaultServiceManager()->addService(String16("inputflinger"), im);
}
@@ -465,10 +476,8 @@
void NativeInputManager::dump(std::string& dump) {
dump += "Input Manager State:\n";
- {
- dump += StringPrintf(INDENT "Interactive: %s\n", toString(mInteractive.load()));
- }
- {
+ dump += StringPrintf(INDENT "Interactive: %s\n", toString(mInteractive.load()));
+ { // acquire lock
std::scoped_lock _l(mLock);
dump += StringPrintf(INDENT "System UI Lights Out: %s\n",
toString(mLocked.systemUiLightsOut));
@@ -480,11 +489,8 @@
dump += StringPrintf(INDENT "Pointer Capture: %s, seq=%" PRIu32 "\n",
mLocked.pointerCaptureRequest.enable ? "Enabled" : "Disabled",
mLocked.pointerCaptureRequest.seq);
- auto pointerController = mLocked.pointerController.lock();
- if (pointerController != nullptr) {
- pointerController->dump(dump);
- }
- }
+ forEachPointerControllerLocked([&dump](PointerController& pc) { pc.dump(dump); });
+ } // release lock
dump += "\n";
mInputManager->dump(dump);
@@ -524,10 +530,9 @@
{ // acquire lock
std::scoped_lock _l(mLock);
mLocked.viewports = viewports;
- std::shared_ptr<PointerController> controller = mLocked.pointerController.lock();
- if (controller != nullptr) {
- controller->onDisplayViewportsUpdated(mLocked.viewports);
- }
+ forEachPointerControllerLocked([viewports = std::move(viewports)](PointerController& pc) {
+ pc.onDisplayViewportsUpdated(viewports);
+ });
} // release lock
mInputManager->getReader().requestRefreshConfiguration(
@@ -700,23 +705,57 @@
return map;
}
+void NativeInputManager::forEachPointerControllerLocked(
+ std::function<void(PointerController&)> apply) {
+ if (auto pc = mLocked.legacyPointerController.lock(); pc) {
+ apply(*pc);
+ }
+
+ auto it = mLocked.pointerControllers.begin();
+ while (it != mLocked.pointerControllers.end()) {
+ auto pc = it->lock();
+ if (!pc) {
+ it = mLocked.pointerControllers.erase(it);
+ continue;
+ }
+ apply(*pc);
+ }
+}
+
+// TODO(b/293587049): Remove the old way of obtaining PointerController when the
+// PointerChoreographer refactoring is complete.
std::shared_ptr<PointerControllerInterface> NativeInputManager::obtainPointerController(
int32_t /* deviceId */) {
ATRACE_CALL();
std::scoped_lock _l(mLock);
- std::shared_ptr<PointerController> controller = mLocked.pointerController.lock();
+ std::shared_ptr<PointerController> controller = mLocked.legacyPointerController.lock();
if (controller == nullptr) {
ensureSpriteControllerLocked();
- controller = PointerController::create(this, mLooper, *mLocked.spriteController);
- mLocked.pointerController = controller;
+ static const bool ENABLE_POINTER_CHOREOGRAPHER =
+ sysprop::InputProperties::enable_pointer_choreographer().value_or(false);
+
+ // Disable the functionality of the legacy PointerController if PointerChoreographer is
+ // enabled.
+ controller = PointerController::create(this, mLooper, *mLocked.spriteController,
+ /*enabled=*/!ENABLE_POINTER_CHOREOGRAPHER);
+ mLocked.legacyPointerController = controller;
updateInactivityTimeoutLocked();
}
return controller;
}
+std::shared_ptr<PointerControllerInterface> NativeInputManager::createPointerController() {
+ std::scoped_lock _l(mLock);
+ ensureSpriteControllerLocked();
+ std::shared_ptr<PointerController> pc =
+ PointerController::create(this, mLooper, *mLocked.spriteController, /*enabled=*/true);
+ mLocked.pointerControllers.emplace_back(pc);
+ return pc;
+}
+
void NativeInputManager::onPointerDisplayIdChanged(int32_t pointerDisplayId,
const FloatPoint& position) {
JNIEnv* env = jniEnv();
@@ -1071,13 +1110,9 @@
}
void NativeInputManager::updateInactivityTimeoutLocked() REQUIRES(mLock) {
- std::shared_ptr<PointerController> controller = mLocked.pointerController.lock();
- if (controller == nullptr) {
- return;
- }
-
- controller->setInactivityTimeout(mLocked.systemUiLightsOut ? InactivityTimeout::SHORT
- : InactivityTimeout::NORMAL);
+ forEachPointerControllerLocked([lightsOut = mLocked.systemUiLightsOut](PointerController& pc) {
+ pc.setInactivityTimeout(lightsOut ? InactivityTimeout::SHORT : InactivityTimeout::NORMAL);
+ });
}
void NativeInputManager::setPointerDisplayId(int32_t displayId) {
@@ -1247,7 +1282,7 @@
void NativeInputManager::setPointerIconType(PointerIconStyle iconId) {
std::scoped_lock _l(mLock);
- std::shared_ptr<PointerController> controller = mLocked.pointerController.lock();
+ std::shared_ptr<PointerController> controller = mLocked.legacyPointerController.lock();
if (controller != nullptr) {
controller->updatePointerIcon(iconId);
}
@@ -1255,15 +1290,12 @@
void NativeInputManager::reloadPointerIcons() {
std::scoped_lock _l(mLock);
- std::shared_ptr<PointerController> controller = mLocked.pointerController.lock();
- if (controller != nullptr) {
- controller->reloadPointerResources();
- }
+ forEachPointerControllerLocked([](PointerController& pc) { pc.reloadPointerResources(); });
}
void NativeInputManager::setCustomPointerIcon(const SpriteIcon& icon) {
std::scoped_lock _l(mLock);
- std::shared_ptr<PointerController> controller = mLocked.pointerController.lock();
+ std::shared_ptr<PointerController> controller = mLocked.legacyPointerController.lock();
if (controller != nullptr) {
controller->setCustomPointerIcon(icon);
}
@@ -1656,7 +1688,7 @@
FloatPoint NativeInputManager::getMouseCursorPosition() {
std::scoped_lock _l(mLock);
- const auto pc = mLocked.pointerController.lock();
+ const auto pc = mLocked.legacyPointerController.lock();
if (!pc) return {AMOTION_EVENT_INVALID_CURSOR_POSITION, AMOTION_EVENT_INVALID_CURSOR_POSITION};
return pc->getPosition();
diff --git a/services/core/jni/gnss/GnssVisibilityControlCallback.cpp b/services/core/jni/gnss/GnssVisibilityControlCallback.cpp
index ec215f1..bc57c1d 100644
--- a/services/core/jni/gnss/GnssVisibilityControlCallback.cpp
+++ b/services/core/jni/gnss/GnssVisibilityControlCallback.cpp
@@ -73,7 +73,7 @@
template <>
jstring ToJstring(JNIEnv* env, const String16& value) {
- const char16_t* str = value.string();
+ const char16_t* str = value.c_str();
size_t len = value.size();
return env->NewString(reinterpret_cast<const jchar*>(str), len);
}
diff --git a/services/net/java/android/net/util/NetworkConstants.java b/services/net/java/android/net/util/NetworkConstants.java
index ea5ce65..49962ea 100644
--- a/services/net/java/android/net/util/NetworkConstants.java
+++ b/services/net/java/android/net/util/NetworkConstants.java
@@ -52,7 +52,6 @@
public static final int IPV6_ADDR_BITS = 128;
public static final int IPV6_ADDR_LEN = 16;
public static final int IPV6_MIN_MTU = 1280;
- public static final int RFC7421_PREFIX_LENGTH = 64;
/**
* ICMP common (v4/v6) constants.
diff --git a/services/permission/TEST_MAPPING b/services/permission/TEST_MAPPING
index b2dcf37..24323c8 100644
--- a/services/permission/TEST_MAPPING
+++ b/services/permission/TEST_MAPPING
@@ -4,7 +4,7 @@
"name": "CtsPermissionTestCases",
"options": [
{
- "exclude-annotation": "android.platform.test.annotations.FlakyTest"
+ "exclude-annotation": "androidx.test.filters.FlakyTest"
},
{
"include-filter": "android.permission.cts.BackgroundPermissionsTest"
@@ -32,7 +32,7 @@
"name": "CtsPermissionPolicyTestCases",
"options": [
{
- "exclude-annotation": "android.platform.test.annotations.FlakyTest"
+ "exclude-annotation": "androidx.test.filters.FlakyTest"
},
{
"include-filter": "android.permissionpolicy.cts.RestrictedPermissionsTest"
diff --git a/services/permission/java/com/android/server/permission/access/AccessPolicy.kt b/services/permission/java/com/android/server/permission/access/AccessPolicy.kt
index 6a349e2..17474fb 100644
--- a/services/permission/java/com/android/server/permission/access/AccessPolicy.kt
+++ b/services/permission/java/com/android/server/permission/access/AccessPolicy.kt
@@ -26,7 +26,6 @@
import com.android.server.permission.access.immutable.* // ktlint-disable no-wildcard-imports
import com.android.server.permission.access.immutable.IndexedMap
import com.android.server.permission.access.permission.AppIdPermissionPolicy
-import com.android.server.permission.access.permission.DevicePermissionPolicy
import com.android.server.permission.access.util.attributeInt
import com.android.server.permission.access.util.attributeInterned
import com.android.server.permission.access.util.forEachTag
@@ -47,7 +46,6 @@
getOrPut(policy.subjectScheme) { MutableIndexedMap() }[policy.objectScheme] = policy
}
addPolicy(AppIdPermissionPolicy())
- addPolicy(DevicePermissionPolicy())
addPolicy(AppIdAppOpPolicy())
addPolicy(PackageAppOpPolicy())
} as IndexedMap<String, IndexedMap<String, SchemePolicy>>
diff --git a/services/permission/java/com/android/server/permission/access/AccessState.kt b/services/permission/java/com/android/server/permission/access/AccessState.kt
index 94c878a..4ec32ea 100644
--- a/services/permission/java/com/android/server/permission/access/AccessState.kt
+++ b/services/permission/java/com/android/server/permission/access/AccessState.kt
@@ -329,18 +329,6 @@
private typealias AppIdPermissionFlagsReference =
MutableReference<AppIdPermissionFlags, MutableAppIdPermissionFlags>
-
-typealias DevicePermissionFlags =
- IndexedReferenceMap<String, IndexedMap<String, Int>, MutableIndexedMap<String, Int>>
-typealias MutableDevicePermissionFlags =
- MutableIndexedReferenceMap<String, IndexedMap<String, Int>, MutableIndexedMap<String, Int>>
-typealias AppIdDevicePermissionFlags =
- IntReferenceMap<DevicePermissionFlags, MutableDevicePermissionFlags>
-typealias MutableAppIdDevicePermissionFlags =
- MutableIntReferenceMap<DevicePermissionFlags, MutableDevicePermissionFlags>
-private typealias AppIdDevicePermissionFlagsReference =
- MutableReference<AppIdDevicePermissionFlags, MutableAppIdDevicePermissionFlags>
-
typealias AppIdAppOpModes =
IntReferenceMap<IndexedMap<String, Int>, MutableIndexedMap<String, Int>>
typealias MutableAppIdAppOpModes =
@@ -358,7 +346,6 @@
sealed class UserState(
internal val packageVersionsReference: PackageVersionsReference,
internal val appIdPermissionFlagsReference: AppIdPermissionFlagsReference,
- internal val appIdDevicePermissionFlagsReference: AppIdDevicePermissionFlagsReference,
internal val appIdAppOpModesReference: AppIdAppOpModesReference,
internal val packageAppOpModesReference: PackageAppOpModesReference,
defaultPermissionGrantFingerprint: String?,
@@ -370,9 +357,6 @@
val appIdPermissionFlags: AppIdPermissionFlags
get() = appIdPermissionFlagsReference.get()
- val appIdDevicePermissionFlags: AppIdDevicePermissionFlags
- get() = appIdDevicePermissionFlagsReference.get()
-
val appIdAppOpModes: AppIdAppOpModes
get() = appIdAppOpModesReference.get()
@@ -391,7 +375,6 @@
class MutableUserState private constructor(
packageVersionsReference: PackageVersionsReference,
appIdPermissionFlagsReference: AppIdPermissionFlagsReference,
- appIdDevicePermissionFlagsReference: AppIdDevicePermissionFlagsReference,
appIdAppOpModesReference: AppIdAppOpModesReference,
packageAppOpModesReference: PackageAppOpModesReference,
defaultPermissionGrantFingerprint: String?,
@@ -399,7 +382,6 @@
) : UserState(
packageVersionsReference,
appIdPermissionFlagsReference,
- appIdDevicePermissionFlagsReference,
appIdAppOpModesReference,
packageAppOpModesReference,
defaultPermissionGrantFingerprint,
@@ -408,7 +390,6 @@
constructor() : this(
PackageVersionsReference(MutableIndexedMap<String, Int>()),
AppIdPermissionFlagsReference(MutableAppIdPermissionFlags()),
- AppIdDevicePermissionFlagsReference(MutableAppIdDevicePermissionFlags()),
AppIdAppOpModesReference(MutableAppIdAppOpModes()),
PackageAppOpModesReference(MutablePackageAppOpModes()),
null,
@@ -418,7 +399,6 @@
internal constructor(userState: UserState) : this(
userState.packageVersionsReference.toImmutable(),
userState.appIdPermissionFlagsReference.toImmutable(),
- userState.appIdDevicePermissionFlagsReference.toImmutable(),
userState.appIdAppOpModesReference.toImmutable(),
userState.packageAppOpModesReference.toImmutable(),
userState.defaultPermissionGrantFingerprint,
@@ -430,9 +410,6 @@
fun mutateAppIdPermissionFlags(): MutableAppIdPermissionFlags =
appIdPermissionFlagsReference.mutate()
- fun mutateAppIdDevicePermissionFlags(): MutableAppIdDevicePermissionFlags =
- appIdDevicePermissionFlagsReference.mutate()
-
fun mutateAppIdAppOpModes(): MutableAppIdAppOpModes = appIdAppOpModesReference.mutate()
fun mutatePackageAppOpModes(): MutablePackageAppOpModes = packageAppOpModesReference.mutate()
diff --git a/services/permission/java/com/android/server/permission/access/AccessUri.kt b/services/permission/java/com/android/server/permission/access/AccessUri.kt
index 1d46ca7..d1abc04 100644
--- a/services/permission/java/com/android/server/permission/access/AccessUri.kt
+++ b/services/permission/java/com/android/server/permission/access/AccessUri.kt
@@ -65,17 +65,6 @@
}
}
-data class DevicePermissionUri(
- val permissionName: String,
- val deviceId: Int
-) : AccessUri(SCHEME) {
- override fun toString(): String = "$scheme:///$permissionName/$deviceId"
-
- companion object {
- const val SCHEME = "device-permission"
- }
-}
-
data class UidUri(
val uid: Int
) : AccessUri(SCHEME) {
diff --git a/services/permission/java/com/android/server/permission/access/permission/DevicePermissionPersistence.kt b/services/permission/java/com/android/server/permission/access/permission/DevicePermissionPersistence.kt
deleted file mode 100644
index 37a4a90..0000000
--- a/services/permission/java/com/android/server/permission/access/permission/DevicePermissionPersistence.kt
+++ /dev/null
@@ -1,169 +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.server.permission.access.permission
-
-import android.util.Slog
-import com.android.modules.utils.BinaryXmlPullParser
-import com.android.modules.utils.BinaryXmlSerializer
-import com.android.server.permission.access.AccessState
-import com.android.server.permission.access.DevicePermissionFlags
-import com.android.server.permission.access.MutableAccessState
-import com.android.server.permission.access.MutableAppIdDevicePermissionFlags
-import com.android.server.permission.access.MutableDevicePermissionFlags
-import com.android.server.permission.access.WriteMode
-import com.android.server.permission.access.immutable.IndexedMap
-import com.android.server.permission.access.immutable.MutableIndexedMap
-import com.android.server.permission.access.immutable.forEachIndexed
-import com.android.server.permission.access.immutable.forEachReversedIndexed
-import com.android.server.permission.access.immutable.set
-import com.android.server.permission.access.util.andInv
-import com.android.server.permission.access.util.attributeInt
-import com.android.server.permission.access.util.attributeInterned
-import com.android.server.permission.access.util.forEachTag
-import com.android.server.permission.access.util.getAttributeIntOrThrow
-import com.android.server.permission.access.util.getAttributeValueOrThrow
-import com.android.server.permission.access.util.hasBits
-import com.android.server.permission.access.util.tag
-import com.android.server.permission.access.util.tagName
-
-class DevicePermissionPersistence {
- fun BinaryXmlPullParser.parseUserState(state: MutableAccessState, userId: Int) {
- when (tagName) {
- TAG_APP_ID_DEVICE_PERMISSIONS -> parseAppIdDevicePermissions(state, userId)
- else -> {}
- }
- }
-
- private fun BinaryXmlPullParser.parseAppIdDevicePermissions(
- state: MutableAccessState,
- userId: Int
- ) {
- val userState = state.mutateUserState(userId, WriteMode.NONE)!!
- val appIdDevicePermissionFlags = userState.mutateAppIdDevicePermissionFlags()
- forEachTag {
- when (tagName) {
- TAG_APP_ID -> parseAppId(appIdDevicePermissionFlags)
- else -> Slog.w(LOG_TAG, "Ignoring unknown tag $name when parsing permission state")
- }
- }
-
- appIdDevicePermissionFlags.forEachReversedIndexed { appIdIndex, appId, _ ->
- if (appId !in state.externalState.appIdPackageNames) {
- Slog.w(LOG_TAG, "Dropping unknown app ID $appId when parsing permission state")
- appIdDevicePermissionFlags.removeAt(appIdIndex)
- userState.requestWriteMode(WriteMode.ASYNCHRONOUS)
- }
- }
- }
-
- private fun BinaryXmlPullParser.parseAppId(
- appIdPermissionFlags: MutableAppIdDevicePermissionFlags
- ) {
- val appId = getAttributeIntOrThrow(ATTR_ID)
- val devicePermissionFlags = MutableDevicePermissionFlags()
- appIdPermissionFlags[appId] = devicePermissionFlags
- forEachTag {
- when (tagName) {
- TAG_DEVICE -> parseDevice(devicePermissionFlags)
- else -> {
- Slog.w(LOG_TAG, "Ignoring unknown tag $name when parsing permission state")
- }
- }
- }
- }
-
- private fun BinaryXmlPullParser.parseDevice(
- deviceIdPermissionFlags: MutableDevicePermissionFlags
- ) {
- val deviceId = getAttributeValueOrThrow(ATTR_ID)
- val permissionFlags = MutableIndexedMap<String, Int>()
- deviceIdPermissionFlags.put(deviceId, permissionFlags)
- forEachTag {
- when (tagName) {
- TAG_PERMISSION -> parsePermission(permissionFlags)
- else -> Slog.w(LOG_TAG, "Ignoring unknown tag $name when parsing permission state")
- }
- }
- }
-
- private fun BinaryXmlPullParser.parsePermission(
- permissionFlags: MutableIndexedMap<String, Int>
- ) {
- val name = getAttributeValueOrThrow(ATTR_NAME).intern()
- val flags = getAttributeIntOrThrow(ATTR_FLAGS)
- permissionFlags[name] = flags
- }
-
- fun BinaryXmlSerializer.serializeUserState(state: AccessState, userId: Int) {
- val appIdDevicePermissionFlags = state.userStates[userId]!!.appIdDevicePermissionFlags
- tag(TAG_APP_ID_DEVICE_PERMISSIONS) {
- appIdDevicePermissionFlags.forEachIndexed { _, appId, devicePermissionFlags ->
- serializeAppId(appId, devicePermissionFlags)
- }
- }
- }
-
- private fun BinaryXmlSerializer.serializeAppId(
- appId: Int,
- devicePermissionFlags: DevicePermissionFlags
- ) {
- tag(TAG_APP_ID) {
- attributeInt(ATTR_ID, appId)
- devicePermissionFlags.forEachIndexed { _, deviceId, permissionFlags ->
- serializeDevice(deviceId, permissionFlags)
- }
- }
- }
-
- private fun BinaryXmlSerializer.serializeDevice(
- deviceId: String,
- permissionFlags: IndexedMap<String, Int>
- ) {
- tag(TAG_DEVICE) {
- attributeInterned(ATTR_ID, deviceId)
- permissionFlags.forEachIndexed { _, name, flags ->
- serializePermission(name, flags)
- }
- }
- }
-
- private fun BinaryXmlSerializer.serializePermission(name: String, flags: Int) {
- tag(TAG_PERMISSION) {
- attributeInterned(ATTR_NAME, name)
- // Never serialize one-time permissions as granted.
- val serializedFlags = if (flags.hasBits(PermissionFlags.ONE_TIME)) {
- flags andInv PermissionFlags.RUNTIME_GRANTED
- } else {
- flags
- }
- attributeInt(ATTR_FLAGS, serializedFlags)
- }
- }
-
- companion object {
- private val LOG_TAG = DevicePermissionPersistence::class.java.simpleName
-
- private const val TAG_APP_ID_DEVICE_PERMISSIONS = "app-id-device-permissions"
- private const val TAG_APP_ID = "app-id"
- private const val TAG_DEVICE = "device"
- private const val TAG_PERMISSION = "permission"
-
- private const val ATTR_ID = "id"
- private const val ATTR_NAME = "name"
- private const val ATTR_FLAGS = "flags"
- }
-}
\ No newline at end of file
diff --git a/services/permission/java/com/android/server/permission/access/permission/DevicePermissionPolicy.kt b/services/permission/java/com/android/server/permission/access/permission/DevicePermissionPolicy.kt
deleted file mode 100644
index c0d7546..0000000
--- a/services/permission/java/com/android/server/permission/access/permission/DevicePermissionPolicy.kt
+++ /dev/null
@@ -1,276 +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.server.permission.access.permission
-
-import android.util.Slog
-import com.android.modules.utils.BinaryXmlPullParser
-import com.android.modules.utils.BinaryXmlSerializer
-import com.android.server.permission.access.AccessState
-import com.android.server.permission.access.DevicePermissionUri
-import com.android.server.permission.access.GetStateScope
-import com.android.server.permission.access.MutableAccessState
-import com.android.server.permission.access.MutateStateScope
-import com.android.server.permission.access.SchemePolicy
-import com.android.server.permission.access.UidUri
-import com.android.server.permission.access.collection.* // ktlint-disable no-wildcard-imports
-import com.android.server.permission.access.immutable.* // ktlint-disable no-wildcard-imports
-import com.android.server.permission.access.util.andInv
-import com.android.server.pm.pkg.PackageState
-
-class DevicePermissionPolicy : SchemePolicy() {
- private val persistence = DevicePermissionPersistence()
-
- @Volatile
- private var listeners: IndexedListSet<OnDevicePermissionFlagsChangedListener> =
- MutableIndexedListSet()
- private val listenersLock = Any()
-
- override val subjectScheme: String
- get() = UidUri.SCHEME
-
- override val objectScheme: String
- get() = DevicePermissionUri.SCHEME
-
- override fun GetStateScope.onStateMutated() {
- listeners.forEachIndexed { _, it -> it.onStateMutated() }
- }
-
- override fun MutateStateScope.onAppIdRemoved(appId: Int) {
- newState.userStates.forEachIndexed { userStateIndex, _, userState ->
- if (appId in userState.appIdDevicePermissionFlags) {
- newState.mutateUserStateAt(userStateIndex)
- .mutateAppIdDevicePermissionFlags() -= appId
- }
- }
- }
-
- override fun MutateStateScope.onStorageVolumeMounted(
- volumeUuid: String?,
- packageNames: List<String>,
- isSystemUpdated: Boolean
- ) {
- packageNames.forEachIndexed { _, packageName ->
- val packageState = newState.externalState.packageStates[packageName]!!
- trimPermissionStates(packageState.appId)
- }
- }
-
- override fun MutateStateScope.onPackageAdded(packageState: PackageState) {
- trimPermissionStates(packageState.appId)
- }
-
- override fun MutateStateScope.onPackageRemoved(packageName: String, appId: Int) {
- if (appId in newState.externalState.appIdPackageNames) {
- trimPermissionStates(appId)
- }
- }
-
- override fun MutateStateScope.onPackageUninstalled(
- packageName: String,
- appId: Int,
- userId: Int
- ) {
- resetPermissionStates(packageName, userId)
- }
-
- private fun MutateStateScope.resetPermissionStates(packageName: String, userId: Int) {
- // It's okay to skip resetting permissions for packages that are removed,
- // because their states will be trimmed in onPackageRemoved()/onAppIdRemoved()
- val packageState = newState.externalState.packageStates[packageName] ?: return
- val androidPackage = packageState.androidPackage ?: return
- val appId = packageState.appId
- val appIdPermissionFlags = newState.userStates[userId]!!.appIdDevicePermissionFlags
- androidPackage.requestedPermissions.forEach { permissionName ->
- val isRequestedByOtherPackages = anyPackageInAppId(appId) {
- it.packageName != packageName &&
- permissionName in it.androidPackage!!.requestedPermissions
- }
- if (isRequestedByOtherPackages) {
- return@forEach
- }
- appIdPermissionFlags[appId]?.forEachIndexed { _, deviceId, _ ->
- setPermissionFlags(appId, deviceId, userId, permissionName, 0)
- }
- }
- }
-
- private fun MutateStateScope.trimPermissionStates(appId: Int) {
- val requestedPermissions = MutableIndexedSet<String>()
- forEachPackageInAppId(appId) {
- requestedPermissions += it.androidPackage!!.requestedPermissions
- }
- newState.userStates.forEachIndexed { _, userId, userState ->
- userState.appIdDevicePermissionFlags[appId]?.forEachReversedIndexed {
- _, deviceId, permissionFlags ->
- permissionFlags.forEachReversedIndexed { _, permissionName, _ ->
- if (permissionName !in requestedPermissions) {
- setPermissionFlags(appId, deviceId, userId, permissionName, 0)
- }
- }
- }
- }
- }
-
- private inline fun MutateStateScope.anyPackageInAppId(
- appId: Int,
- state: AccessState = newState,
- predicate: (PackageState) -> Boolean
- ): Boolean {
- val packageNames = state.externalState.appIdPackageNames[appId]!!
- return packageNames.anyIndexed { _, packageName ->
- val packageState = state.externalState.packageStates[packageName]!!
- packageState.androidPackage != null && predicate(packageState)
- }
- }
-
- private inline fun MutateStateScope.forEachPackageInAppId(
- appId: Int,
- state: AccessState = newState,
- action: (PackageState) -> Unit
- ) {
- val packageNames = state.externalState.appIdPackageNames[appId]!!
- packageNames.forEachIndexed { _, packageName ->
- val packageState = state.externalState.packageStates[packageName]!!
- if (packageState.androidPackage != null) {
- action(packageState)
- }
- }
- }
-
- override fun BinaryXmlPullParser.parseUserState(state: MutableAccessState, userId: Int) {
- with(persistence) { this@parseUserState.parseUserState(state, userId) }
- }
-
- override fun BinaryXmlSerializer.serializeUserState(state: AccessState, userId: Int) {
- with(persistence) { this@serializeUserState.serializeUserState(state, userId) }
- }
-
- fun GetStateScope.getPermissionFlags(
- appId: Int,
- deviceId: String,
- userId: Int,
- permissionName: String
- ): Int =
- state.userStates[userId]?.appIdDevicePermissionFlags?.get(appId)?.get(deviceId)
- ?.getWithDefault(permissionName, 0) ?: 0
-
- fun MutateStateScope.setPermissionFlags(
- appId: Int,
- deviceId: String,
- userId: Int,
- permissionName: String,
- flags: Int
- ): Boolean =
- updatePermissionFlags(
- appId, deviceId, userId, permissionName, PermissionFlags.MASK_ALL, flags
- )
-
- private fun MutateStateScope.updatePermissionFlags(
- appId: Int,
- deviceId: String,
- userId: Int,
- permissionName: String,
- flagMask: Int,
- flagValues: Int
- ): Boolean {
- if (!isDeviceAwarePermission(permissionName)) {
- Slog.w(LOG_TAG, "$permissionName is not a device aware permission.")
- return false
- }
- val oldFlags = newState.userStates[userId]!!.appIdDevicePermissionFlags[appId]
- ?.get(deviceId).getWithDefault(permissionName, 0)
- val newFlags = (oldFlags andInv flagMask) or (flagValues and flagMask)
- if (oldFlags == newFlags) {
- return false
- }
- val appIdDevicePermissionFlags =
- newState.mutateUserState(userId)!!.mutateAppIdDevicePermissionFlags()
- val devicePermissionFlags = appIdDevicePermissionFlags.mutateOrPut(appId) {
- MutableIndexedReferenceMap()
- }
- val permissionFlags = devicePermissionFlags.mutateOrPut(deviceId) { MutableIndexedMap() }
- permissionFlags.putWithDefault(permissionName, newFlags, 0)
- if (permissionFlags.isEmpty()) {
- devicePermissionFlags -= deviceId
- if (devicePermissionFlags.isEmpty()) {
- appIdDevicePermissionFlags -= appId
- }
- }
- listeners.forEachIndexed { _, it ->
- it.onDevicePermissionFlagsChanged(
- appId, userId, deviceId, permissionName, oldFlags, newFlags
- )
- }
- return true
- }
-
- fun addOnPermissionFlagsChangedListener(listener: OnDevicePermissionFlagsChangedListener) {
- synchronized(listenersLock) {
- listeners = listeners + listener
- }
- }
-
- fun removeOnPermissionFlagsChangedListener(listener: OnDevicePermissionFlagsChangedListener) {
- synchronized(listenersLock) {
- listeners = listeners - listener
- }
- }
-
- private fun isDeviceAwarePermission(permissionName: String): Boolean =
- DEVICE_SUPPORTED_PERMISSIONS.contains(permissionName)
-
- companion object {
- private val LOG_TAG = DevicePermissionPolicy::class.java.simpleName
-
- /**
- * These permissions are supported for virtual devices.
- */
- private val DEVICE_SUPPORTED_PERMISSIONS = indexedSetOf(
- android.Manifest.permission.CAMERA,
- android.Manifest.permission.RECORD_AUDIO
- )
- }
-
- /**
- * TODO: b/289355341 - implement listener for permission changes
- * Listener for permission flags changes.
- */
- abstract class OnDevicePermissionFlagsChangedListener {
- /**
- * Called when a permission flags change has been made to the upcoming new state.
- *
- * Implementations should keep this method fast to avoid stalling the locked state mutation,
- * and only call external code after [onStateMutated] when the new state has actually become
- * the current state visible to external code.
- */
- abstract fun onDevicePermissionFlagsChanged(
- appId: Int,
- userId: Int,
- deviceId: String,
- permissionName: String,
- oldFlags: Int,
- newFlags: Int
- )
-
- /**
- * Called when the upcoming new state has become the current state.
- *
- * Implementations should keep this method fast to avoid stalling the locked state mutation.
- */
- abstract fun onStateMutated()
- }
-}
\ No newline at end of file
diff --git a/services/permission/java/com/android/server/permission/access/permission/PermissionService.kt b/services/permission/java/com/android/server/permission/access/permission/PermissionService.kt
index f0705ed..b797492 100644
--- a/services/permission/java/com/android/server/permission/access/permission/PermissionService.kt
+++ b/services/permission/java/com/android/server/permission/access/permission/PermissionService.kt
@@ -64,11 +64,9 @@
import com.android.server.PermissionThread
import com.android.server.ServiceThread
import com.android.server.SystemConfig
-import com.android.server.companion.virtual.VirtualDeviceManagerInternal
import com.android.server.permission.access.AccessCheckingService
import com.android.server.permission.access.AccessState
import com.android.server.permission.access.AppOpUri
-import com.android.server.permission.access.DevicePermissionUri
import com.android.server.permission.access.GetStateScope
import com.android.server.permission.access.MutateStateScope
import com.android.server.permission.access.PermissionUri
@@ -112,9 +110,6 @@
private val policy =
service.getSchemePolicy(UidUri.SCHEME, PermissionUri.SCHEME) as AppIdPermissionPolicy
- private val devicePolicy =
- service.getSchemePolicy(UidUri.SCHEME, DevicePermissionUri.SCHEME) as DevicePermissionPolicy
-
private val context = service.context
private lateinit var metricsLogger: MetricsLogger
private lateinit var packageManagerInternal: PackageManagerInternal
@@ -135,8 +130,6 @@
@GuardedBy("storageVolumeLock")
private val storageVolumePackageNames = ArrayMap<String?, MutableList<String>>()
- private var virtualDeviceManagerInternal: VirtualDeviceManagerInternal? = null
-
private lateinit var permissionControllerManager: PermissionControllerManager
/**
@@ -159,6 +152,7 @@
systemConfig = SystemConfig.getInstance()
userManagerInternal = LocalServices.getService(UserManagerInternal::class.java)
userManagerService = UserManagerService.getInstance()
+
// The package info cache is the cache for package and permission information.
// Disable the package info and package permission caches locally but leave the
// checkPermission cache active.
@@ -466,7 +460,7 @@
return size
}
- override fun checkUidPermission(uid: Int, permissionName: String, deviceId: Int): Int {
+ override fun checkUidPermission(uid: Int, permissionName: String): Int {
val userId = UserHandle.getUserId(uid)
if (!userManagerInternal.exists(userId)) {
return PackageManager.PERMISSION_DENIED
@@ -488,7 +482,7 @@
return PackageManager.PERMISSION_DENIED
}
val isPermissionGranted = service.getState {
- isPermissionGranted(packageState, userId, permissionName, deviceId)
+ isPermissionGranted(packageState, userId, permissionName)
}
return if (isPermissionGranted) {
PackageManager.PERMISSION_GRANTED
@@ -521,12 +515,7 @@
return false
}
- override fun checkPermission(
- packageName: String,
- permissionName: String,
- deviceId: Int,
- userId: Int
- ): Int {
+ override fun checkPermission(packageName: String, permissionName: String, userId: Int): Int {
if (!userManagerInternal.exists(userId)) {
return PackageManager.PERMISSION_DENIED
}
@@ -535,7 +524,7 @@
.use { it.getPackageState(packageName) } ?: return PackageManager.PERMISSION_DENIED
val isPermissionGranted = service.getState {
- isPermissionGranted(packageState, userId, permissionName, deviceId)
+ isPermissionGranted(packageState, userId, permissionName)
}
return if (isPermissionGranted) {
PackageManager.PERMISSION_GRANTED
@@ -553,21 +542,19 @@
private fun GetStateScope.isPermissionGranted(
packageState: PackageState,
userId: Int,
- permissionName: String,
- deviceId: Int
+ permissionName: String
): Boolean {
val appId = packageState.appId
// Note that instant apps can't have shared UIDs, so we only need to check the current
// package state.
val isInstantApp = packageState.getUserStateOrDefault(userId).isInstantApp
- if (isSinglePermissionGranted(appId, userId, isInstantApp, permissionName, deviceId)) {
+ if (isSinglePermissionGranted(appId, userId, isInstantApp, permissionName)) {
return true
}
val fullerPermissionName = FULLER_PERMISSIONS[permissionName]
if (fullerPermissionName != null &&
- isSinglePermissionGranted(appId, userId, isInstantApp, fullerPermissionName, deviceId)
- ) {
+ isSinglePermissionGranted(appId, userId, isInstantApp, fullerPermissionName)) {
return true
}
@@ -581,10 +568,9 @@
appId: Int,
userId: Int,
isInstantApp: Boolean,
- permissionName: String,
- deviceId: Int,
+ permissionName: String
): Boolean {
- val flags = getPermissionFlagsWithPolicy(appId, userId, permissionName, deviceId)
+ val flags = with(policy) { getPermissionFlags(appId, userId, permissionName) }
if (!PermissionFlags.isPermissionGranted(flags)) {
return false
}
@@ -615,8 +601,7 @@
?: return emptySet()
return permissionFlags.mapNotNullIndexedTo(ArraySet()) { _, permissionName, _ ->
- if (isPermissionGranted(
- packageState, userId, permissionName, Context.DEVICE_ID_DEFAULT)) {
+ if (isPermissionGranted(packageState, userId, permissionName)) {
permissionName
} else {
null
@@ -655,26 +640,18 @@
}
}
- override fun grantRuntimePermission(
- packageName: String,
- permissionName: String,
- deviceId: Int,
- userId: Int
- ) {
- setRuntimePermissionGranted(
- packageName, userId, permissionName, deviceId, isGranted = true
- )
+ override fun grantRuntimePermission(packageName: String, permissionName: String, userId: Int) {
+ setRuntimePermissionGranted(packageName, userId, permissionName, isGranted = true)
}
override fun revokeRuntimePermission(
packageName: String,
permissionName: String,
- deviceId: Int,
userId: Int,
reason: String?
) {
setRuntimePermissionGranted(
- packageName, userId, permissionName, deviceId, isGranted = false, revokeReason = reason
+ packageName, userId, permissionName, isGranted = false, revokeReason = reason
)
}
@@ -683,8 +660,8 @@
userId: Int
) {
setRuntimePermissionGranted(
- packageName, userId, Manifest.permission.POST_NOTIFICATIONS, Context.DEVICE_ID_DEFAULT,
- isGranted = false, skipKillUid = true
+ packageName, userId, Manifest.permission.POST_NOTIFICATIONS, isGranted = false,
+ skipKillUid = true
)
}
@@ -696,7 +673,6 @@
packageName: String,
userId: Int,
permissionName: String,
- deviceId: Int,
isGranted: Boolean,
skipKillUid: Boolean = false,
revokeReason: String? = null
@@ -772,7 +748,7 @@
}
setRuntimePermissionGranted(
- packageState, userId, permissionName, deviceId, isGranted, canManageRolePermission,
+ packageState, userId, permissionName, isGranted, canManageRolePermission,
overridePolicyFixed, reportError = true, methodName
)
}
@@ -806,16 +782,14 @@
if (permissionState ==
PackageInstaller.SessionParams.PERMISSION_STATE_GRANTED) {
setRuntimePermissionGranted(
- packageState, userId, permissionName, Context.DEVICE_ID_DEFAULT,
- isGranted = true, canManageRolePermission = false,
- overridePolicyFixed = false, reportError = false,
- "setRequestedPermissionStates"
+ packageState, userId, permissionName, isGranted = true,
+ canManageRolePermission = false, overridePolicyFixed = false,
+ reportError = false, "setRequestedPermissionStates"
)
updatePermissionFlags(
packageState.appId, userId, permissionName,
- Context.DEVICE_ID_DEFAULT,
PackageManager.FLAG_PERMISSION_REVIEW_REQUIRED or
- PackageManager.FLAG_PERMISSION_REVOKED_COMPAT, 0,
+ PackageManager.FLAG_PERMISSION_REVOKED_COMPAT, 0,
canUpdateSystemFlags = false,
reportErrorForUnknownPermission = false,
isPermissionRequested = true, "setRequestedPermissionStates",
@@ -842,7 +816,6 @@
packageState: PackageState,
userId: Int,
permissionName: String,
- deviceId: Int,
isGranted: Boolean,
canManageRolePermission: Boolean,
overridePolicyFixed: Boolean,
@@ -898,7 +871,7 @@
}
val appId = packageState.appId
- val oldFlags = getPermissionFlagsWithPolicy(appId, userId, permissionName, deviceId)
+ val oldFlags = with(policy) { getPermissionFlags(appId, userId, permissionName) }
if (permissionName !in androidPackage.requestedPermissions && oldFlags == 0) {
if (reportError) {
@@ -961,7 +934,7 @@
return
}
- setPermissionFlagsWithPolicy(appId, userId, permissionName, deviceId, newFlags)
+ with(policy) { setPermissionFlags(appId, userId, permissionName, newFlags) }
if (permission.isRuntime) {
val action = if (isGranted) {
@@ -990,12 +963,7 @@
with(appOpPolicy) { setAppOpMode(packageState.appId, userId, appOpName, mode) }
}
- override fun getPermissionFlags(
- packageName: String,
- permissionName: String,
- deviceId: Int,
- userId: Int,
- ): Int {
+ override fun getPermissionFlags(packageName: String, permissionName: String, userId: Int): Int {
if (!userManagerInternal.exists(userId)) {
Slog.w(LOG_TAG, "getPermissionFlags: Unknown user $userId")
return 0
@@ -1026,8 +994,7 @@
}
val flags =
- getPermissionFlagsWithPolicy(packageState.appId, userId, permissionName, deviceId)
-
+ with(policy) { getPermissionFlags(packageState.appId, userId, permissionName) }
return PermissionFlags.toApiFlags(flags)
}
}
@@ -1035,7 +1002,6 @@
override fun isPermissionRevokedByPolicy(
packageName: String,
permissionName: String,
- deviceId: Int,
userId: Int
): Boolean {
if (!userManagerInternal.exists(userId)) {
@@ -1052,13 +1018,13 @@
.use { it.getPackageState(packageName) } ?: return false
service.getState {
- if (isPermissionGranted(packageState, userId, permissionName, deviceId)) {
+ if (isPermissionGranted(packageState, userId, permissionName)) {
return false
}
- val flags =
- getPermissionFlagsWithPolicy(packageState.appId, userId, permissionName, deviceId)
-
+ val flags = with(policy) {
+ getPermissionFlags(packageState.appId, userId, permissionName)
+ }
return flags.hasBits(PermissionFlags.POLICY_FIXED)
}
}
@@ -1080,8 +1046,7 @@
override fun shouldShowRequestPermissionRationale(
packageName: String,
permissionName: String,
- deviceId: Int,
- userId: Int,
+ userId: Int
): Boolean {
if (!userManagerInternal.exists(userId)) {
Slog.w(LOG_TAG, "shouldShowRequestPermissionRationale: Unknown user $userId")
@@ -1103,11 +1068,11 @@
val flags: Int
service.getState {
- if (isPermissionGranted(packageState, userId, permissionName, deviceId)) {
+ if (isPermissionGranted(packageState, userId, permissionName)) {
return false
}
- flags = getPermissionFlagsWithPolicy(appId, userId, permissionName, deviceId)
+ flags = with(policy) { getPermissionFlags(appId, userId, permissionName) }
}
if (flags.hasAnyBit(UNREQUESTABLE_MASK)) {
return false
@@ -1139,7 +1104,6 @@
flagMask: Int,
flagValues: Int,
enforceAdjustPolicyPermission: Boolean,
- deviceId: Int,
userId: Int
) {
val callingUid = Binder.getCallingUid()
@@ -1235,7 +1199,7 @@
val appId = packageState.appId
service.mutateState {
updatePermissionFlags(
- appId, userId, permissionName, deviceId, flagMask, flagValues, canUpdateSystemFlags,
+ appId, userId, permissionName, flagMask, flagValues, canUpdateSystemFlags,
reportErrorForUnknownPermission = true, isPermissionRequested,
"updatePermissionFlags", packageName
)
@@ -1284,9 +1248,8 @@
val androidPackage = packageState.androidPackage ?: return@forEach
androidPackage.requestedPermissions.forEach { permissionName ->
updatePermissionFlags(
- packageState.appId, userId, permissionName, Context.DEVICE_ID_DEFAULT,
- flagMask, flagValues, canUpdateSystemFlags,
- reportErrorForUnknownPermission = false,
+ packageState.appId, userId, permissionName, flagMask, flagValues,
+ canUpdateSystemFlags, reportErrorForUnknownPermission = false,
isPermissionRequested = true, "updatePermissionFlagsForAllApps", packageName
)
}
@@ -1301,7 +1264,6 @@
appId: Int,
userId: Int,
permissionName: String,
- deviceId: Int,
flagMask: Int,
flagValues: Int,
canUpdateSystemFlags: Boolean,
@@ -1336,7 +1298,7 @@
return
}
- val oldFlags = getPermissionFlagsWithPolicy(appId, userId, permissionName, deviceId)
+ val oldFlags = with(policy) { getPermissionFlags(appId, userId, permissionName) }
if (!isPermissionRequested && oldFlags == 0) {
Slog.w(
LOG_TAG, "$methodName: Permission $permissionName isn't requested by package" +
@@ -1346,7 +1308,7 @@
}
val newFlags = PermissionFlags.updateFlags(permission, oldFlags, flagMask, flagValues)
- setPermissionFlagsWithPolicy(appId, userId, permissionName, deviceId, newFlags)
+ with(policy) { setPermissionFlags(appId, userId, permissionName, newFlags) }
}
override fun getAllowlistedRestrictedPermissions(
@@ -1403,63 +1365,6 @@
)
}
- private fun GetStateScope.getPermissionFlagsWithPolicy(
- appId: Int,
- userId: Int,
- permissionName: String,
- deviceId: Int,
- ): Int {
- return if (deviceId == Context.DEVICE_ID_DEFAULT) {
- with(policy) { getPermissionFlags(appId, userId, permissionName) }
- } else {
- val virtualDeviceManagerInternal = virtualDeviceManagerInternal
- if (virtualDeviceManagerInternal == null) {
- Slog.e(LOG_TAG, "Virtual device manager service is not available.")
- return 0
- }
- val persistentDeviceId =
- virtualDeviceManagerInternal.getPersistentIdForDevice(deviceId)
- if (persistentDeviceId != null) {
- with(devicePolicy) {
- getPermissionFlags(appId, persistentDeviceId, userId, permissionName)
- }
- } else {
- Slog.e(LOG_TAG, "Invalid device ID $deviceId.")
- 0
- }
- }
- }
-
- private fun MutateStateScope.setPermissionFlagsWithPolicy(
- appId: Int,
- userId: Int,
- permissionName: String,
- deviceId: Int,
- flags: Int
- ): Boolean {
- return if (deviceId == Context.DEVICE_ID_DEFAULT) {
- with(policy) {
- setPermissionFlags(appId, userId, permissionName, flags)
- }
- } else {
- val virtualDeviceManagerInternal = virtualDeviceManagerInternal
- if (virtualDeviceManagerInternal == null) {
- Slog.e(LOG_TAG, "Virtual device manager service is not available.")
- return false
- }
- val persistentDeviceId =
- virtualDeviceManagerInternal.getPersistentIdForDevice(deviceId)
- if (persistentDeviceId != null) {
- with(devicePolicy) {
- setPermissionFlags(appId, persistentDeviceId, userId, permissionName, flags)
- }
- } else {
- Slog.e(LOG_TAG, "Invalid device ID $deviceId.")
- false
- }
- }
- }
-
/**
* This method does not enforce checks on the caller, should only be called after
* required checks.
@@ -1634,7 +1539,8 @@
) {
service.mutateState {
with(policy) {
- val permissionsFlags = getUidPermissionFlags(appId, userId) ?: return@mutateState
+ val permissionsFlags =
+ getUidPermissionFlags(appId, userId) ?: return@mutateState
val permissions = getPermissions()
androidPackage.requestedPermissions.forEachIndexed { _, requestedPermission ->
@@ -1755,6 +1661,8 @@
)
}
+
+
override fun getAppOpPermissionPackages(permissionName: String): Array<String> {
requireNotNull(permissionName) { "permissionName cannot be null" }
val packageNames = ArraySet<String>()
@@ -1971,7 +1879,7 @@
println("Permissions:")
withIndent {
userState.appIdPermissionFlags[appId]?.forEachIndexed {
- _, permissionName, flags ->
+ _, permissionName, flags ->
val isGranted = PermissionFlags.isPermissionGranted(flags)
println(
"$permissionName: granted=$isGranted, flags=" +
@@ -1980,20 +1888,6 @@
}
}
- userState.appIdDevicePermissionFlags[appId]?.forEachIndexed {
- _, deviceId, devicePermissionFlags ->
- println("Permissions (Device $deviceId):")
- withIndent {
- devicePermissionFlags.forEachIndexed { _, permissionName, flags ->
- val isGranted = PermissionFlags.isPermissionGranted(flags)
- println(
- "$permissionName: granted=$isGranted, flags=" +
- PermissionFlags.toString(flags)
- )
- }
- }
- }
-
println("App ops:")
withIndent {
userState.appIdAppOpModes[appId]?.forEachIndexed {_, appOpName, appOpMode ->
@@ -2109,8 +2003,6 @@
override fun onSystemReady() {
service.onSystemReady()
- virtualDeviceManagerInternal =
- LocalServices.getService(VirtualDeviceManagerInternal::class.java)
permissionControllerManager = PermissionControllerManager(
context, PermissionThread.getHandler()
)
@@ -2520,7 +2412,7 @@
}
private fun isAppBackupAndRestoreRunning(uid: Int): Boolean {
- if (checkUidPermission(uid, Manifest.permission.BACKUP, Context.DEVICE_ID_DEFAULT) !=
+ if (checkUidPermission(uid, Manifest.permission.BACKUP) !=
PackageManager.PERMISSION_GRANTED) {
return false
}
diff --git a/services/tests/PackageManagerServiceTests/server/src/com/android/server/pm/ScanTests.java b/services/tests/PackageManagerServiceTests/server/src/com/android/server/pm/ScanTests.java
index e2939c1..98c6c42 100644
--- a/services/tests/PackageManagerServiceTests/server/src/com/android/server/pm/ScanTests.java
+++ b/services/tests/PackageManagerServiceTests/server/src/com/android/server/pm/ScanTests.java
@@ -612,6 +612,9 @@
final PackageSetting pkgSetting = scanResult.mPkgSetting;
assertBasicPackageSetting(scanResult, packageName, isInstant, pkgSetting);
+ // pretend that the data dir has been set up already, so that the generated applicationInfo
+ // includes the expected data dir string
+ pkgSetting.setCeDataInode(/* ceDataInode= */100, /* userId= */0);
final ApplicationInfo applicationInfo = PackageInfoUtils.generateApplicationInfo(
pkgSetting.getPkg(), 0, pkgSetting.getUserStateOrDefault(0), 0, pkgSetting);
diff --git a/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/PackageManagerLocalSnapshotTest.kt b/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/PackageManagerLocalSnapshotTest.kt
index 5f26d6f..cd37674 100644
--- a/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/PackageManagerLocalSnapshotTest.kt
+++ b/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/PackageManagerLocalSnapshotTest.kt
@@ -31,6 +31,7 @@
import org.mockito.ArgumentMatchers.any
import org.mockito.ArgumentMatchers.anyInt
import org.mockito.ArgumentMatchers.anyString
+import org.mockito.Mockito.doReturn
import kotlin.test.assertFailsWith
class PackageManagerLocalSnapshotTest {
@@ -154,7 +155,7 @@
put(packageStateUser0.packageName, packageStateUser0)
put(packageStateUser10.packageName, packageStateUser10)
}
- whenever(this.packageStates) { packageStates }
+ doReturn(packageStates).whenever(this).packageStates
whenever(getPackageStateFiltered(anyString(), anyInt(), anyInt())) {
packageStates[arguments[0]]?.takeUnless {
shouldFilterApplication(it, arguments[1] as Int, arguments[2] as Int)
diff --git a/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/verify/domain/DomainVerificationEnforcerTest.kt b/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/verify/domain/DomainVerificationEnforcerTest.kt
index 55645d7..9fbf86e 100644
--- a/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/verify/domain/DomainVerificationEnforcerTest.kt
+++ b/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/verify/domain/DomainVerificationEnforcerTest.kt
@@ -48,6 +48,7 @@
import org.mockito.Mockito.anyInt
import org.mockito.Mockito.anyLong
import org.mockito.Mockito.anyString
+import org.mockito.Mockito.doReturn
import org.mockito.Mockito.eq
import org.mockito.Mockito.mock
import org.mockito.Mockito.verifyNoMoreInteractions
@@ -351,12 +352,12 @@
whenever(this.domainSetId) { domainSetId }
whenever(getUserStateOrDefault(0)) { PackageUserStateInternal.DEFAULT }
whenever(getUserStateOrDefault(1)) { PackageUserStateInternal.DEFAULT }
- whenever(userStates) {
+ doReturn(
SparseArray<PackageUserStateInternal>().apply {
this[0] = PackageUserStateInternal.DEFAULT
this[1] = PackageUserStateInternal.DEFAULT
}
- }
+ ).whenever(this).userStates
whenever(isSystem) { false }
whenever(signingDetails) { SigningDetails.UNKNOWN }
}
diff --git a/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/verify/domain/DomainVerificationManagerApiTest.kt b/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/verify/domain/DomainVerificationManagerApiTest.kt
index 86c4335..47d9196 100644
--- a/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/verify/domain/DomainVerificationManagerApiTest.kt
+++ b/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/verify/domain/DomainVerificationManagerApiTest.kt
@@ -44,6 +44,7 @@
import org.mockito.ArgumentMatchers.anyInt
import org.mockito.ArgumentMatchers.anyLong
import org.mockito.ArgumentMatchers.anyString
+import org.mockito.Mockito.doReturn
import java.util.UUID
import java.util.concurrent.atomic.AtomicBoolean
import kotlin.test.assertFailsWith
@@ -555,12 +556,12 @@
whenever(this.domainSetId) { domainSetId }
whenever(getUserStateOrDefault(0)) { pkgUserState0() }
whenever(getUserStateOrDefault(1)) { pkgUserState1() }
- whenever(userStates) {
+ doReturn(
SparseArray<PackageUserStateInternal>().apply {
this[0] = pkgUserState0()
this[1] = pkgUserState1()
}
- }
+ ).whenever(this).userStates
whenever(isSystem) { false }
}
diff --git a/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/verify/domain/DomainVerificationPackageTest.kt b/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/verify/domain/DomainVerificationPackageTest.kt
index e55ff3b..98d7801 100644
--- a/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/verify/domain/DomainVerificationPackageTest.kt
+++ b/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/verify/domain/DomainVerificationPackageTest.kt
@@ -1084,12 +1084,12 @@
whenever(this.domainSetId) { domainSetId }
whenever(getUserStateOrDefault(0)) { PackageUserStateInternal.DEFAULT }
whenever(getUserStateOrDefault(10)) { PackageUserStateInternal.DEFAULT }
- whenever(userStates) {
+ doReturn(
SparseArray<PackageUserStateInternal>().apply {
this[0] = PackageUserStateInternal.DEFAULT
this[1] = PackageUserStateInternal.DEFAULT
}
- }
+ ).whenever(this).userStates
whenever(isSystem) { isSystemApp }
val mockSigningDetails = SigningDetails(arrayOf(spy(Signature(signature)) {
diff --git a/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/verify/domain/DomainVerificationSettingsMutationTest.kt b/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/verify/domain/DomainVerificationSettingsMutationTest.kt
index 427b5b3..4a211df 100644
--- a/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/verify/domain/DomainVerificationSettingsMutationTest.kt
+++ b/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/verify/domain/DomainVerificationSettingsMutationTest.kt
@@ -41,6 +41,7 @@
import org.mockito.Mockito.anyInt
import org.mockito.Mockito.anyLong
import org.mockito.Mockito.anyString
+import org.mockito.Mockito.doReturn
import org.mockito.Mockito.eq
import org.mockito.Mockito.verify
import java.util.UUID
@@ -218,12 +219,12 @@
whenever(domainSetId) { TEST_UUID }
whenever(getUserStateOrDefault(0)) { PackageUserStateInternal.DEFAULT }
whenever(getUserStateOrDefault(10)) { PackageUserStateInternal.DEFAULT }
- whenever(userStates) {
+ doReturn(
SparseArray<PackageUserStateInternal>().apply {
this[0] = PackageUserStateInternal.DEFAULT
this[1] = PackageUserStateInternal.DEFAULT
}
- }
+ ).whenever(this).userStates
whenever(isSystem) { false }
}
}
diff --git a/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/verify/domain/DomainVerificationUserSelectionOverrideTest.kt b/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/verify/domain/DomainVerificationUserSelectionOverrideTest.kt
index 6bb5f39..d54d608 100644
--- a/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/verify/domain/DomainVerificationUserSelectionOverrideTest.kt
+++ b/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/verify/domain/DomainVerificationUserSelectionOverrideTest.kt
@@ -41,6 +41,8 @@
import org.mockito.ArgumentMatchers.anyInt
import org.mockito.ArgumentMatchers.anyLong
import org.mockito.ArgumentMatchers.anyString
+import org.mockito.Mockito.doReturn
+
import java.util.UUID
class DomainVerificationUserStateOverrideTest {
@@ -155,12 +157,12 @@
whenever(this.domainSetId) { domainSetId }
whenever(getUserStateOrDefault(0)) { PackageUserStateInternal.DEFAULT }
whenever(getUserStateOrDefault(1)) { PackageUserStateInternal.DEFAULT }
- whenever(userStates) {
+ doReturn(
SparseArray<PackageUserStateInternal>().apply {
this[0] = PackageUserStateInternal.DEFAULT
this[1] = PackageUserStateInternal.DEFAULT
}
- }
+ ).whenever(this).userStates
whenever(isSystem) { false }
}
diff --git a/services/tests/displayservicetests/Android.bp b/services/tests/displayservicetests/Android.bp
index 4f555d9..fb14419 100644
--- a/services/tests/displayservicetests/Android.bp
+++ b/services/tests/displayservicetests/Android.bp
@@ -28,6 +28,7 @@
static_libs: [
"androidx.test.ext.junit",
+ "androidx.test.rules",
"frameworks-base-testutils",
"junit",
"junit-params",
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 979676e..d099693 100644
--- a/services/tests/displayservicetests/src/com/android/server/display/DisplayManagerServiceTest.java
+++ b/services/tests/displayservicetests/src/com/android/server/display/DisplayManagerServiceTest.java
@@ -18,6 +18,7 @@
import static android.Manifest.permission.ADD_ALWAYS_UNLOCKED_DISPLAY;
import static android.Manifest.permission.ADD_TRUSTED_DISPLAY;
+import static android.Manifest.permission.MANAGE_DISPLAYS;
import static android.hardware.display.DisplayManager.VIRTUAL_DISPLAY_FLAG_ALWAYS_UNLOCKED;
import static android.hardware.display.DisplayManager.VIRTUAL_DISPLAY_FLAG_OWN_CONTENT_ONLY;
import static android.hardware.display.DisplayManager.VIRTUAL_DISPLAY_FLAG_OWN_DISPLAY_GROUP;
@@ -42,7 +43,9 @@
import static org.mockito.ArgumentMatchers.nullable;
import static org.mockito.Mockito.any;
import static org.mockito.Mockito.atLeastOnce;
+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;
@@ -104,11 +107,14 @@
import com.android.server.companion.virtual.VirtualDeviceManagerInternal;
import com.android.server.display.DisplayManagerService.DeviceStateListener;
import com.android.server.display.DisplayManagerService.SyncRoot;
+import com.android.server.display.feature.DisplayManagerFlags;
import com.android.server.input.InputManagerInternal;
import com.android.server.lights.LightsManager;
import com.android.server.sensors.SensorManagerInternal;
import com.android.server.wm.WindowManagerInternal;
+import com.google.common.truth.Expect;
+
import libcore.junit.util.compat.CoreCompatChangeRule.DisableCompatChanges;
import libcore.junit.util.compat.CoreCompatChangeRule.EnableCompatChanges;
@@ -125,6 +131,7 @@
import org.mockito.MockitoAnnotations;
import java.time.Duration;
+import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
@@ -133,6 +140,7 @@
import java.util.concurrent.TimeUnit;
import java.util.stream.LongStream;
+// TODO(b/297170420) Parameterize the test.
@SmallTest
@RunWith(AndroidJUnit4.class)
public class DisplayManagerServiceTest {
@@ -146,12 +154,31 @@
private static final long STANDARD_DISPLAY_EVENTS = DisplayManager.EVENT_FLAG_DISPLAY_ADDED
| DisplayManager.EVENT_FLAG_DISPLAY_CHANGED
| DisplayManager.EVENT_FLAG_DISPLAY_REMOVED;
+ private static final long STANDARD_AND_CONNECTION_DISPLAY_EVENTS =
+ STANDARD_DISPLAY_EVENTS | DisplayManager.EVENT_FLAG_DISPLAY_CONNECTION_CHANGED;
- @Rule
+ private static final String EVENT_DISPLAY_ADDED = "EVENT_DISPLAY_ADDED";
+ private static final String EVENT_DISPLAY_REMOVED = "EVENT_DISPLAY_REMOVED";
+ private static final String EVENT_DISPLAY_CHANGED = "EVENT_DISPLAY_CHANGED";
+ private static final String EVENT_DISPLAY_BRIGHTNESS_CHANGED =
+ "EVENT_DISPLAY_BRIGHTNESS_CHANGED";
+ private static final String EVENT_DISPLAY_HDR_SDR_RATIO_CHANGED =
+ "EVENT_DISPLAY_HDR_SDR_RATIO_CHANGED";
+ private static final String EVENT_DISPLAY_CONNECTED = "EVENT_DISPLAY_CONNECTED";
+ private static final String EVENT_DISPLAY_DISCONNECTED = "EVENT_DISPLAY_DISCONNECTED";
+ private static final String DISPLAY_GROUP_EVENT_ADDED = "DISPLAY_GROUP_EVENT_ADDED";
+ private static final String DISPLAY_GROUP_EVENT_REMOVED = "DISPLAY_GROUP_EVENT_REMOVED";
+ private static final String DISPLAY_GROUP_EVENT_CHANGED = "DISPLAY_GROUP_EVENT_CHANGED";
+
+ @Rule(order = 0)
public TestRule compatChangeRule = new PlatformCompatChangeRule();
+ @Rule(order = 1)
+ public Expect expect = Expect.create();
private Context mContext;
+ private Resources mResources;
+
private int mHdrConversionMode;
private int mPreferredHdrOutputType;
@@ -189,6 +216,11 @@
}
@Override
+ DisplayManagerFlags getFlags() {
+ return mMockFlags;
+ }
+
+ @Override
VirtualDisplayAdapter getVirtualDisplayAdapter(SyncRoot syncRoot, Context context,
Handler handler, DisplayAdapter.Listener displayAdapterListener) {
return new VirtualDisplayAdapter(syncRoot, context, handler, displayAdapterListener,
@@ -208,8 +240,8 @@
@Override
LocalDisplayAdapter getLocalDisplayAdapter(SyncRoot syncRoot, Context context,
Handler handler, DisplayAdapter.Listener displayAdapterListener) {
- return new LocalDisplayAdapter(syncRoot, context, handler,
- displayAdapterListener, new LocalDisplayAdapter.Injector() {
+ return new LocalDisplayAdapter(syncRoot, context, handler, displayAdapterListener,
+ new LocalDisplayAdapter.Injector() {
@Override
public LocalDisplayAdapter.SurfaceControlProxy getSurfaceControlProxy() {
return mSurfaceControlProxy;
@@ -262,10 +294,12 @@
@Mock DisplayDeviceConfig mMockDisplayDeviceConfig;
@Captor ArgumentCaptor<ContentRecordingSession> mContentRecordingSessionCaptor;
+ @Mock DisplayManagerFlags mMockFlags;
@Before
public void setUp() throws Exception {
MockitoAnnotations.initMocks(this);
+ when(mMockFlags.isConnectedDisplayManagementEnabled()).thenReturn(false);
LocalServices.removeServiceForTest(InputManagerInternal.class);
LocalServices.addService(InputManagerInternal.class, mMockInputManagerInternal);
@@ -280,6 +314,9 @@
VirtualDeviceManagerInternal.class, mMockVirtualDeviceManagerInternal);
// TODO: b/287945043
mContext = spy(new ContextWrapper(ApplicationProvider.getApplicationContext()));
+ mResources = Mockito.spy(mContext.getResources());
+ manageDisplaysPermission(/* granted= */ false);
+ when(mContext.getResources()).thenReturn(mResources);
VirtualDeviceManager vdm = new VirtualDeviceManager(mIVirtualDeviceManager, mContext);
when(mContext.getSystemService(VirtualDeviceManager.class)).thenReturn(vdm);
@@ -315,7 +352,7 @@
// This is to update the display device config such that DisplayManagerService can ignore
// the usage of SensorManager, which is available only after the PowerManagerService
// is ready.
- resetConfigToIgnoreSensorManager(mContext);
+ resetConfigToIgnoreSensorManager();
DisplayManagerService displayManager = new DisplayManagerService(mContext, mBasicInjector);
registerDefaultDisplays(displayManager);
displayManager.systemReady(false /* safeMode */);
@@ -392,7 +429,7 @@
// This is to update the display device config such that DisplayManagerService can ignore
// the usage of SensorManager, which is available only after the PowerManagerService
// is ready.
- resetConfigToIgnoreSensorManager(mContext);
+ resetConfigToIgnoreSensorManager();
DisplayManagerService displayManager = new DisplayManagerService(mContext, mBasicInjector);
registerDefaultDisplays(displayManager);
displayManager.systemReady(false /* safeMode */);
@@ -670,7 +707,7 @@
Handler handler = displayManager.getDisplayHandler();
waitForIdleHandler(handler);
- assertTrue(callback.mDisplayChangedCalled);
+ assertThat(callback.receivedEvents()).containsExactly(EVENT_DISPLAY_CHANGED);
}
/**
@@ -696,7 +733,7 @@
* Tests that we get a Runtime exception when we cannot initialize the virtual display.
*/
@Test
- public void testStartVirtualDisplayWithDefDisplay_NoVirtualDisplayAdapter() throws Exception {
+ public void testStartVirtualDisplayWithDefDisplay_NoVirtualDisplayAdapter() {
DisplayManagerService displayManager = new DisplayManagerService(mContext,
new DisplayManagerService.Injector() {
@Override
@@ -1487,7 +1524,7 @@
* list is updated.
*/
@Test
- public void testShouldNotifyChangeWhenDisplayInfoFrameRateOverrideChanged() throws Exception {
+ public void testShouldNotifyChangeWhenDisplayInfoFrameRateOverrideChanged() {
DisplayManagerService displayManager =
new DisplayManagerService(mContext, mShortMockedInjector);
DisplayManagerService.BinderService displayManagerBinderService =
@@ -1504,7 +1541,8 @@
new DisplayEventReceiver.FrameRateOverride[]{
new DisplayEventReceiver.FrameRateOverride(myUid, 30f),
});
- assertTrue(callback.mDisplayChangedCalled);
+ waitForIdleHandler(displayManager.getDisplayHandler());
+ assertThat(callback.receivedEvents()).contains(EVENT_DISPLAY_CHANGED);
callback.clear();
updateFrameRateOverride(displayManager, displayDevice,
@@ -1512,7 +1550,8 @@
new DisplayEventReceiver.FrameRateOverride(myUid, 30f),
new DisplayEventReceiver.FrameRateOverride(1234, 30f),
});
- assertFalse(callback.mDisplayChangedCalled);
+ waitForIdleHandler(displayManager.getDisplayHandler());
+ assertThat(callback.receivedEvents()).doesNotContain(EVENT_DISPLAY_CHANGED);
updateFrameRateOverride(displayManager, displayDevice,
new DisplayEventReceiver.FrameRateOverride[]{
@@ -1520,7 +1559,8 @@
new DisplayEventReceiver.FrameRateOverride(1234, 30f),
new DisplayEventReceiver.FrameRateOverride(5678, 30f),
});
- assertTrue(callback.mDisplayChangedCalled);
+ waitForIdleHandler(displayManager.getDisplayHandler());
+ assertThat(callback.receivedEvents()).contains(EVENT_DISPLAY_CHANGED);
callback.clear();
updateFrameRateOverride(displayManager, displayDevice,
@@ -1528,21 +1568,23 @@
new DisplayEventReceiver.FrameRateOverride(1234, 30f),
new DisplayEventReceiver.FrameRateOverride(5678, 30f),
});
- assertTrue(callback.mDisplayChangedCalled);
+ waitForIdleHandler(displayManager.getDisplayHandler());
+ assertThat(callback.receivedEvents()).contains(EVENT_DISPLAY_CHANGED);
callback.clear();
updateFrameRateOverride(displayManager, displayDevice,
new DisplayEventReceiver.FrameRateOverride[]{
new DisplayEventReceiver.FrameRateOverride(5678, 30f),
});
- assertFalse(callback.mDisplayChangedCalled);
+ waitForIdleHandler(displayManager.getDisplayHandler());
+ assertThat(callback.receivedEvents()).doesNotContain(EVENT_DISPLAY_CHANGED);
}
/**
* Tests that the DisplayInfo is updated correctly with a frame rate override
*/
@Test
- public void testDisplayInfoFrameRateOverride() throws Exception {
+ public void testDisplayInfoFrameRateOverride() {
DisplayManagerService displayManager =
new DisplayManagerService(mContext, mShortMockedInjector);
DisplayManagerService.BinderService displayManagerBinderService =
@@ -1579,7 +1621,7 @@
* DisplayInfo#getRefreshRate
*/
@Test
- public void testDisplayInfoNonNativeFrameRateOverride() throws Exception {
+ public void testDisplayInfoNonNativeFrameRateOverride() {
DisplayManagerService displayManager =
new DisplayManagerService(mContext, mBasicInjector);
DisplayManagerService.BinderService displayManagerBinderService =
@@ -1627,7 +1669,7 @@
*/
@Test
@DisableCompatChanges({DisplayManagerService.DISPLAY_MODE_RETURNS_PHYSICAL_REFRESH_RATE})
- public void testDisplayInfoNonNativeFrameRateOverrideModeCompat() throws Exception {
+ public void testDisplayInfoNonNativeFrameRateOverrideModeCompat() {
testDisplayInfoNonNativeFrameRateOverrideMode(/*compatChangeEnabled*/ false);
}
@@ -1636,7 +1678,7 @@
*/
@Test
@EnableCompatChanges({DisplayManagerService.DISPLAY_MODE_RETURNS_PHYSICAL_REFRESH_RATE})
- public void testDisplayInfoNonNativeFrameRateOverrideMode() throws Exception {
+ public void testDisplayInfoNonNativeFrameRateOverrideMode() {
testDisplayInfoNonNativeFrameRateOverrideMode(/*compatChangeEnabled*/ true);
}
@@ -1644,7 +1686,7 @@
* Tests that there is a display change notification if the render frame rate is updated
*/
@Test
- public void testShouldNotifyChangeWhenDisplayInfoRenderFrameRateChanged() throws Exception {
+ public void testShouldNotifyChangeWhenDisplayInfoRenderFrameRateChanged() {
DisplayManagerService displayManager =
new DisplayManagerService(mContext, mShortMockedInjector);
DisplayManagerService.BinderService displayManagerBinderService =
@@ -1657,14 +1699,17 @@
displayManagerBinderService, displayDevice);
updateRenderFrameRate(displayManager, displayDevice, 30f);
- assertTrue(callback.mDisplayChangedCalled);
+ waitForIdleHandler(displayManager.getDisplayHandler());
+ assertThat(callback.receivedEvents()).contains(EVENT_DISPLAY_CHANGED);
callback.clear();
updateRenderFrameRate(displayManager, displayDevice, 30f);
- assertFalse(callback.mDisplayChangedCalled);
+ waitForIdleHandler(displayManager.getDisplayHandler());
+ assertThat(callback.receivedEvents()).doesNotContain(EVENT_DISPLAY_CHANGED);
updateRenderFrameRate(displayManager, displayDevice, 20f);
- assertTrue(callback.mDisplayChangedCalled);
+ waitForIdleHandler(displayManager.getDisplayHandler());
+ assertThat(callback.receivedEvents()).contains(EVENT_DISPLAY_CHANGED);
callback.clear();
}
@@ -1672,7 +1717,7 @@
* Tests that the DisplayInfo is updated correctly with a render frame rate
*/
@Test
- public void testDisplayInfoRenderFrameRate() throws Exception {
+ public void testDisplayInfoRenderFrameRate() {
DisplayManagerService displayManager =
new DisplayManagerService(mContext, mShortMockedInjector);
DisplayManagerService.BinderService displayManagerBinderService =
@@ -1734,9 +1779,7 @@
waitForIdleHandler(handler);
- assertFalse(callback.mDisplayChangedCalled);
- assertFalse(callback.mDisplayRemovedCalled);
- assertTrue(callback.mDisplayAddedCalled);
+ assertThat(callback.receivedEvents()).containsExactly(EVENT_DISPLAY_ADDED);
}
/**
@@ -1766,9 +1809,7 @@
waitForIdleHandler(handler);
- assertFalse(callback.mDisplayChangedCalled);
- assertFalse(callback.mDisplayRemovedCalled);
- assertFalse(callback.mDisplayAddedCalled);
+ assertThat(callback.receivedEvents()).isEmpty();
}
/**
@@ -1780,12 +1821,15 @@
new DisplayManagerService(mContext, mShortMockedInjector);
DisplayManagerService.BinderService displayManagerBinderService =
displayManager.new BinderService();
+ LogicalDisplayMapper logicalDisplayMapper = displayManager.getLogicalDisplayMapper();
Handler handler = displayManager.getDisplayHandler();
waitForIdleHandler(handler);
FakeDisplayDevice displayDevice = createFakeDisplayDevice(displayManager,
new float[]{60f});
+ LogicalDisplay display =
+ logicalDisplayMapper.getDisplayLocked(displayDevice, /* includeDisabled= */ true);
waitForIdleHandler(handler);
@@ -1794,14 +1838,12 @@
waitForIdleHandler(handler);
+ display.setPrimaryDisplayDeviceLocked(null);
displayManager.getDisplayDeviceRepository()
.onDisplayDeviceEvent(displayDevice, DisplayAdapter.DISPLAY_DEVICE_EVENT_REMOVED);
-
waitForIdleHandler(handler);
- assertFalse(callback.mDisplayChangedCalled);
- assertTrue(callback.mDisplayRemovedCalled);
- assertFalse(callback.mDisplayAddedCalled);
+ assertThat(callback.receivedEvents()).containsExactly(EVENT_DISPLAY_REMOVED);
}
/**
@@ -1814,12 +1856,15 @@
new DisplayManagerService(mContext, mShortMockedInjector);
DisplayManagerService.BinderService displayManagerBinderService =
displayManager.new BinderService();
+ LogicalDisplayMapper logicalDisplayMapper = displayManager.getLogicalDisplayMapper();
Handler handler = displayManager.getDisplayHandler();
waitForIdleHandler(handler);
FakeDisplayDevice displayDevice = createFakeDisplayDevice(displayManager,
new float[]{60f});
+ LogicalDisplay display =
+ logicalDisplayMapper.getDisplayLocked(displayDevice, /* includeDisabled= */ true);
waitForIdleHandler(handler);
@@ -1831,14 +1876,12 @@
waitForIdleHandler(handler);
+ display.setPrimaryDisplayDeviceLocked(null);
displayManager.getDisplayDeviceRepository()
.onDisplayDeviceEvent(displayDevice, DisplayAdapter.DISPLAY_DEVICE_EVENT_REMOVED);
-
waitForIdleHandler(handler);
- assertFalse(callback.mDisplayChangedCalled);
- assertFalse(callback.mDisplayRemovedCalled);
- assertFalse(callback.mDisplayAddedCalled);
+ assertThat(callback.receivedEvents()).isEmpty();
}
@@ -2052,8 +2095,315 @@
assertNull(result);
}
- private void testDisplayInfoFrameRateOverrideModeCompat(boolean compatChangeEnabled)
- throws Exception {
+ @Test
+ public void testConnectExternalDisplay_withoutDisplayManagement_shouldAddDisplay() {
+ when(mMockFlags.isConnectedDisplayManagementEnabled()).thenReturn(false);
+ manageDisplaysPermission(/* granted= */ true);
+ DisplayManagerService displayManager = new DisplayManagerService(mContext, mBasicInjector);
+ DisplayManagerService.BinderService bs = displayManager.new BinderService();
+ LogicalDisplayMapper logicalDisplayMapper = displayManager.getLogicalDisplayMapper();
+ FakeDisplayManagerCallback callback = new FakeDisplayManagerCallback();
+ bs.registerCallbackWithEventMask(callback, STANDARD_AND_CONNECTION_DISPLAY_EVENTS);
+
+ FakeDisplayDevice displayDevice =
+ createFakeDisplayDevice(displayManager, new float[]{60f}, Display.TYPE_EXTERNAL);
+ waitForIdleHandler(displayManager.getDisplayHandler());
+
+ LogicalDisplay display =
+ logicalDisplayMapper.getDisplayLocked(displayDevice, /* includeDisabled= */ true);
+ assertThat(display.isEnabledLocked()).isTrue();
+ assertThat(callback.receivedEvents()).containsExactly(EVENT_DISPLAY_ADDED);
+
+ }
+
+ @Test
+ public void testConnectExternalDisplay_withDisplayManagement_shouldDisableDisplay() {
+ when(mMockFlags.isConnectedDisplayManagementEnabled()).thenReturn(true);
+ manageDisplaysPermission(/* granted= */ true);
+ DisplayManagerService displayManager = new DisplayManagerService(mContext, mBasicInjector);
+ DisplayManagerInternal localService = displayManager.new LocalService();
+ DisplayManagerService.BinderService bs = displayManager.new BinderService();
+ LogicalDisplayMapper logicalDisplayMapper = displayManager.getLogicalDisplayMapper();
+ // Create default display device
+ createFakeDisplayDevice(displayManager, new float[]{60f}, Display.TYPE_INTERNAL);
+ FakeDisplayManagerCallback callback = new FakeDisplayManagerCallback();
+
+ bs.registerCallbackWithEventMask(callback, STANDARD_AND_CONNECTION_DISPLAY_EVENTS);
+ localService.registerDisplayGroupListener(callback);
+
+ FakeDisplayDevice displayDevice =
+ createFakeDisplayDevice(displayManager, new float[]{60f}, Display.TYPE_EXTERNAL);
+ waitForIdleHandler(displayManager.getDisplayHandler());
+
+ LogicalDisplay display =
+ logicalDisplayMapper.getDisplayLocked(displayDevice, /* includeDisabled= */ true);
+ assertThat(display.isEnabledLocked()).isFalse();
+ assertThat(callback.receivedEvents()).containsExactly(DISPLAY_GROUP_EVENT_ADDED,
+ EVENT_DISPLAY_CONNECTED).inOrder();
+ }
+
+ @Test
+ public void testConnectInternalDisplay_withDisplayManagement_shouldConnectAndAddDisplay() {
+ when(mMockFlags.isConnectedDisplayManagementEnabled()).thenReturn(true);
+ manageDisplaysPermission(/* granted= */ true);
+ DisplayManagerService displayManager = new DisplayManagerService(mContext, mBasicInjector);
+ DisplayManagerService.BinderService bs = displayManager.new BinderService();
+ FakeDisplayManagerCallback callback = new FakeDisplayManagerCallback();
+ bs.registerCallbackWithEventMask(callback, STANDARD_AND_CONNECTION_DISPLAY_EVENTS);
+
+ createFakeDisplayDevice(displayManager, new float[]{60f}, Display.TYPE_INTERNAL);
+ waitForIdleHandler(displayManager.getDisplayHandler());
+
+ assertThat(callback.receivedEvents()).containsExactly(EVENT_DISPLAY_CONNECTED,
+ EVENT_DISPLAY_ADDED).inOrder();
+ }
+
+ @Test
+ public void testEnableExternalDisplay_withDisplayManagement_shouldSignalDisplayAdded() {
+ when(mMockFlags.isConnectedDisplayManagementEnabled()).thenReturn(true);
+ manageDisplaysPermission(/* granted= */ true);
+ DisplayManagerService displayManager = new DisplayManagerService(mContext, mBasicInjector);
+ DisplayManagerService.BinderService bs = displayManager.new BinderService();
+ LogicalDisplayMapper logicalDisplayMapper = displayManager.getLogicalDisplayMapper();
+ FakeDisplayManagerCallback callback = new FakeDisplayManagerCallback();
+ // Create default display device
+ createFakeDisplayDevice(displayManager, new float[]{60f}, Display.TYPE_INTERNAL);
+ bs.registerCallbackWithEventMask(callback, STANDARD_DISPLAY_EVENTS);
+ FakeDisplayDevice displayDevice =
+ createFakeDisplayDevice(displayManager, new float[]{60f}, Display.TYPE_EXTERNAL);
+ waitForIdleHandler(displayManager.getDisplayHandler());
+ callback.clear();
+
+ LogicalDisplay display =
+ logicalDisplayMapper.getDisplayLocked(displayDevice, /* includeDisabled= */ true);
+ displayManager.enableConnectedDisplay(display.getDisplayIdLocked(), /* enabled= */ true);
+ waitForIdleHandler(displayManager.getDisplayHandler());
+
+ assertThat(display.isEnabledLocked()).isTrue();
+ assertThat(callback.receivedEvents()).containsExactly(EVENT_DISPLAY_ADDED).inOrder();
+ }
+
+ @Test
+ public void testEnableExternalDisplay_withoutPermission_shouldThrowException() {
+ when(mMockFlags.isConnectedDisplayManagementEnabled()).thenReturn(true);
+ DisplayManagerService displayManager = new DisplayManagerService(mContext, mBasicInjector);
+ DisplayManagerService.BinderService bs = displayManager.new BinderService();
+ LogicalDisplayMapper logicalDisplayMapper = displayManager.getLogicalDisplayMapper();
+ FakeDisplayManagerCallback callback = new FakeDisplayManagerCallback();
+ // Create default display device
+ createFakeDisplayDevice(displayManager, new float[]{60f}, Display.TYPE_INTERNAL);
+ bs.registerCallbackWithEventMask(callback, STANDARD_DISPLAY_EVENTS);
+ FakeDisplayDevice displayDevice =
+ createFakeDisplayDevice(displayManager, new float[]{60f}, Display.TYPE_EXTERNAL);
+ waitForIdleHandler(displayManager.getDisplayHandler());
+ callback.clear();
+ LogicalDisplay display =
+ logicalDisplayMapper.getDisplayLocked(displayDevice, /* includeDisabled= */ true);
+ int displayId = display.getDisplayIdLocked();
+
+ assertThrows(SecurityException.class, () -> bs.enableConnectedDisplay(displayId));
+ }
+
+ @Test
+ public void testEnableInternalDisplay_withManageDisplays_shouldSignalAdded() {
+ when(mMockFlags.isConnectedDisplayManagementEnabled()).thenReturn(true);
+ DisplayManagerService displayManager = new DisplayManagerService(mContext, mBasicInjector);
+ DisplayManagerService.BinderService bs = displayManager.new BinderService();
+ LogicalDisplayMapper logicalDisplayMapper = displayManager.getLogicalDisplayMapper();
+ FakeDisplayManagerCallback callback = new FakeDisplayManagerCallback();
+ bs.registerCallbackWithEventMask(callback, STANDARD_DISPLAY_EVENTS);
+ FakeDisplayDevice displayDevice =
+ createFakeDisplayDevice(displayManager, new float[]{60f}, Display.TYPE_INTERNAL);
+ waitForIdleHandler(displayManager.getDisplayHandler());
+ LogicalDisplay display =
+ logicalDisplayMapper.getDisplayLocked(displayDevice, /* includeDisabled= */ true);
+ logicalDisplayMapper.setEnabledLocked(display, /* isEnabled= */ false);
+ logicalDisplayMapper.updateLogicalDisplays();
+ waitForIdleHandler(displayManager.getDisplayHandler());
+ callback.clear();
+
+ logicalDisplayMapper.setEnabledLocked(display, /* isEnabled= */ true);
+ logicalDisplayMapper.updateLogicalDisplays();
+ waitForIdleHandler(displayManager.getDisplayHandler());
+
+ assertThat(callback.receivedEvents()).containsExactly(EVENT_DISPLAY_ADDED);
+ }
+
+ @Test
+ public void testDisableInternalDisplay_withDisplayManagement_shouldSignalRemove() {
+ when(mMockFlags.isConnectedDisplayManagementEnabled()).thenReturn(true);
+ DisplayManagerService displayManager = new DisplayManagerService(mContext, mBasicInjector);
+ DisplayManagerService.BinderService bs = displayManager.new BinderService();
+ LogicalDisplayMapper logicalDisplayMapper = displayManager.getLogicalDisplayMapper();
+ FakeDisplayManagerCallback callback = new FakeDisplayManagerCallback();
+ bs.registerCallbackWithEventMask(callback, STANDARD_DISPLAY_EVENTS);
+ FakeDisplayDevice displayDevice =
+ createFakeDisplayDevice(displayManager, new float[]{60f}, Display.TYPE_INTERNAL);
+ waitForIdleHandler(displayManager.getDisplayHandler());
+ callback.clear();
+ LogicalDisplay display =
+ logicalDisplayMapper.getDisplayLocked(displayDevice, /* includeDisabled= */ true);
+
+ logicalDisplayMapper.setEnabledLocked(display, /* isEnabled= */ false);
+ logicalDisplayMapper.updateLogicalDisplays();
+ waitForIdleHandler(displayManager.getDisplayHandler());
+
+ assertThat(callback.receivedEvents()).containsExactly(EVENT_DISPLAY_REMOVED);
+ }
+
+ @Test
+ public void testDisableExternalDisplay_shouldSignalDisplayRemoved() {
+ when(mMockFlags.isConnectedDisplayManagementEnabled()).thenReturn(true);
+ DisplayManagerService displayManager = new DisplayManagerService(mContext, mBasicInjector);
+ DisplayManagerService.BinderService bs = displayManager.new BinderService();
+ LogicalDisplayMapper logicalDisplayMapper = displayManager.getLogicalDisplayMapper();
+ DisplayManagerInternal localService = displayManager.new LocalService();
+ FakeDisplayManagerCallback callback = new FakeDisplayManagerCallback();
+ // Create default display device
+ createFakeDisplayDevice(displayManager, new float[]{60f}, Display.TYPE_INTERNAL);
+ bs.registerCallbackWithEventMask(callback, STANDARD_DISPLAY_EVENTS);
+ localService.registerDisplayGroupListener(callback);
+ FakeDisplayDevice displayDevice =
+ createFakeDisplayDevice(displayManager, new float[]{60f}, Display.TYPE_EXTERNAL);
+ waitForIdleHandler(displayManager.getDisplayHandler());
+ LogicalDisplay display =
+ logicalDisplayMapper.getDisplayLocked(displayDevice, /* includeDisabled= */ true);
+ logicalDisplayMapper.setEnabledLocked(display, /* isEnabled= */ true);
+ logicalDisplayMapper.updateLogicalDisplays();
+ waitForIdleHandler(displayManager.getDisplayHandler());
+ callback.clear();
+
+ logicalDisplayMapper.setEnabledLocked(display, /* isEnabled= */ false);
+ logicalDisplayMapper.updateLogicalDisplays();
+ waitForIdleHandler(displayManager.getDisplayHandler());
+
+ assertThat(display.isEnabledLocked()).isFalse();
+ assertThat(callback.receivedEvents()).containsExactly(EVENT_DISPLAY_REMOVED);
+ }
+
+ @Test
+ public void testDisableExternalDisplay_withoutPermission_shouldThrowException() {
+ when(mMockFlags.isConnectedDisplayManagementEnabled()).thenReturn(true);
+ DisplayManagerService displayManager = new DisplayManagerService(mContext, mBasicInjector);
+ DisplayManagerService.BinderService bs = displayManager.new BinderService();
+ LogicalDisplayMapper logicalDisplayMapper = displayManager.getLogicalDisplayMapper();
+ FakeDisplayManagerCallback callback = new FakeDisplayManagerCallback();
+ // Create default display device
+ createFakeDisplayDevice(displayManager, new float[]{60f}, Display.TYPE_INTERNAL);
+ bs.registerCallbackWithEventMask(callback, STANDARD_DISPLAY_EVENTS);
+ FakeDisplayDevice displayDevice =
+ createFakeDisplayDevice(displayManager, new float[]{60f}, Display.TYPE_EXTERNAL);
+ waitForIdleHandler(displayManager.getDisplayHandler());
+ LogicalDisplay display =
+ logicalDisplayMapper.getDisplayLocked(displayDevice, /* includeDisabled= */ true);
+ int displayId = display.getDisplayIdLocked();
+ logicalDisplayMapper.setEnabledLocked(display, /* isEnabled= */ true);
+ logicalDisplayMapper.updateLogicalDisplays();
+ waitForIdleHandler(displayManager.getDisplayHandler());
+ callback.clear();
+
+ assertThrows(SecurityException.class, () -> bs.disableConnectedDisplay(displayId));
+ }
+
+ @Test
+ public void testRemoveExternalDisplay_whenDisabled_shouldSignalDisconnected() {
+ when(mMockFlags.isConnectedDisplayManagementEnabled()).thenReturn(true);
+ manageDisplaysPermission(/* granted= */ true);
+ DisplayManagerService displayManager = new DisplayManagerService(mContext, mBasicInjector);
+ DisplayManagerService.BinderService bs = displayManager.new BinderService();
+ DisplayManagerInternal localService = displayManager.new LocalService();
+ LogicalDisplayMapper logicalDisplayMapper = displayManager.getLogicalDisplayMapper();
+ FakeDisplayManagerCallback callback = new FakeDisplayManagerCallback();
+ // Create default display device
+ createFakeDisplayDevice(displayManager, new float[]{60f}, Display.TYPE_INTERNAL);
+ bs.registerCallbackWithEventMask(callback, STANDARD_AND_CONNECTION_DISPLAY_EVENTS);
+ localService.registerDisplayGroupListener(callback);
+ FakeDisplayDevice displayDevice =
+ createFakeDisplayDevice(displayManager, new float[]{60f}, Display.TYPE_EXTERNAL);
+ waitForIdleHandler(displayManager.getDisplayHandler());
+ callback.clear();
+ LogicalDisplay display = logicalDisplayMapper.getDisplayLocked(displayDevice);
+ int groupId = display.getDisplayInfoLocked().displayGroupId;
+ DisplayGroup group = logicalDisplayMapper.getDisplayGroupLocked(groupId);
+ assertThat(group.getSizeLocked()).isEqualTo(1);
+
+ display.setPrimaryDisplayDeviceLocked(null);
+ displayManager.getDisplayDeviceRepository()
+ .onDisplayDeviceEvent(displayDevice, DisplayAdapter.DISPLAY_DEVICE_EVENT_REMOVED);
+ waitForIdleHandler(displayManager.getDisplayHandler());
+
+ assertThat(group.getSizeLocked()).isEqualTo(0);
+ assertThat(callback.receivedEvents()).containsExactly(EVENT_DISPLAY_DISCONNECTED,
+ DISPLAY_GROUP_EVENT_REMOVED);
+ }
+
+ @Test
+ public void testRegisterCallback_withoutPermission_shouldThrow() {
+ when(mMockFlags.isConnectedDisplayManagementEnabled()).thenReturn(true);
+ DisplayManagerService displayManager = new DisplayManagerService(mContext, mBasicInjector);
+ DisplayManagerService.BinderService bs = displayManager.new BinderService();
+ FakeDisplayManagerCallback callback = new FakeDisplayManagerCallback();
+
+ assertThrows(SecurityException.class, () -> bs.registerCallbackWithEventMask(callback,
+ STANDARD_AND_CONNECTION_DISPLAY_EVENTS));
+ }
+
+ @Test
+ public void testRemoveExternalDisplay_whenEnabled_shouldSignalRemovedAndDisconnected() {
+ when(mMockFlags.isConnectedDisplayManagementEnabled()).thenReturn(true);
+ manageDisplaysPermission(/* granted= */ true);
+ DisplayManagerService displayManager = new DisplayManagerService(mContext, mBasicInjector);
+ DisplayManagerService.BinderService bs = displayManager.new BinderService();
+ LogicalDisplayMapper logicalDisplayMapper = displayManager.getLogicalDisplayMapper();
+ FakeDisplayManagerCallback callback = new FakeDisplayManagerCallback();
+ bs.registerCallbackWithEventMask(callback, STANDARD_AND_CONNECTION_DISPLAY_EVENTS);
+ FakeDisplayDevice displayDevice =
+ createFakeDisplayDevice(displayManager, new float[]{60f}, Display.TYPE_EXTERNAL);
+ waitForIdleHandler(displayManager.getDisplayHandler());
+ LogicalDisplay display =
+ logicalDisplayMapper.getDisplayLocked(displayDevice, /* includeDisabled= */ true);
+ int displayId = display.getDisplayIdLocked();
+ displayManager.enableConnectedDisplay(displayId, /* enabled= */ true);
+ waitForIdleHandler(displayManager.getDisplayHandler());
+ callback.clear();
+
+ display.setPrimaryDisplayDeviceLocked(null);
+ displayManager.getDisplayDeviceRepository()
+ .onDisplayDeviceEvent(displayDevice, DisplayAdapter.DISPLAY_DEVICE_EVENT_REMOVED);
+ waitForIdleHandler(displayManager.getDisplayHandler());
+
+ assertThat(logicalDisplayMapper.getDisplayLocked(displayId, true)).isNull();
+ assertThat(callback.receivedEvents()).containsExactly(EVENT_DISPLAY_REMOVED,
+ EVENT_DISPLAY_DISCONNECTED).inOrder();
+ }
+
+ @Test
+ public void testRemoveInternalDisplay_whenEnabled_shouldSignalRemovedAndDisconnected() {
+ when(mMockFlags.isConnectedDisplayManagementEnabled()).thenReturn(true);
+ manageDisplaysPermission(/* granted= */ true);
+ DisplayManagerService displayManager = new DisplayManagerService(mContext, mBasicInjector);
+ DisplayManagerService.BinderService bs = displayManager.new BinderService();
+ LogicalDisplayMapper logicalDisplayMapper = displayManager.getLogicalDisplayMapper();
+ FakeDisplayManagerCallback callback = new FakeDisplayManagerCallback();
+ bs.registerCallbackWithEventMask(callback, STANDARD_AND_CONNECTION_DISPLAY_EVENTS);
+ FakeDisplayDevice displayDevice =
+ createFakeDisplayDevice(displayManager, new float[]{60f}, Display.TYPE_INTERNAL);
+ LogicalDisplay display =
+ logicalDisplayMapper.getDisplayLocked(displayDevice, /* includeDisabled= */ true);
+ waitForIdleHandler(displayManager.getDisplayHandler());
+ callback.clear();
+
+ display.setPrimaryDisplayDeviceLocked(null);
+ displayManager.getDisplayDeviceRepository()
+ .onDisplayDeviceEvent(displayDevice, DisplayAdapter.DISPLAY_DEVICE_EVENT_REMOVED);
+ waitForIdleHandler(displayManager.getDisplayHandler());
+
+ assertThat(logicalDisplayMapper.getDisplayLocked(displayDevice,
+ /* includeDisabled= */ true)).isNull();
+ assertThat(callback.receivedEvents()).containsExactly(EVENT_DISPLAY_REMOVED,
+ EVENT_DISPLAY_DISCONNECTED);
+ }
+ private void testDisplayInfoFrameRateOverrideModeCompat(boolean compatChangeEnabled) {
DisplayManagerService displayManager =
new DisplayManagerService(mContext, mShortMockedInjector);
DisplayManagerService.BinderService displayManagerBinderService =
@@ -2086,8 +2436,7 @@
assertEquals(expectedMode, displayInfo.getMode());
}
- private void testDisplayInfoRenderFrameRateModeCompat(boolean compatChangeEnabled)
- throws Exception {
+ private void testDisplayInfoRenderFrameRateModeCompat(boolean compatChangeEnabled) {
DisplayManagerService displayManager =
new DisplayManagerService(mContext, mShortMockedInjector);
DisplayManagerService.BinderService displayManagerBinderService =
@@ -2214,6 +2563,15 @@
DisplayManagerService displayManager,
DisplayManagerService.BinderService displayManagerBinderService,
FakeDisplayDevice displayDevice) {
+ return registerDisplayListenerCallback(displayManager, displayManagerBinderService,
+ displayDevice, STANDARD_DISPLAY_EVENTS);
+ }
+
+ private FakeDisplayManagerCallback registerDisplayListenerCallback(
+ DisplayManagerService displayManager,
+ DisplayManagerService.BinderService displayManagerBinderService,
+ FakeDisplayDevice displayDevice,
+ long displayEventsMask) {
// Find the display id of the added FakeDisplayDevice
int displayId = getDisplayIdForDisplayDevice(displayManager, displayManagerBinderService,
displayDevice);
@@ -2224,12 +2582,18 @@
// register display listener callback
FakeDisplayManagerCallback callback = new FakeDisplayManagerCallback(displayId);
displayManagerBinderService.registerCallbackWithEventMask(
- callback, STANDARD_DISPLAY_EVENTS);
+ callback, displayEventsMask);
return callback;
}
private FakeDisplayDevice createFakeDisplayDevice(DisplayManagerService displayManager,
- float[] refreshRates) {
+ float[] refreshRates) {
+ return createFakeDisplayDevice(displayManager, refreshRates, Display.TYPE_UNKNOWN);
+ }
+
+ private FakeDisplayDevice createFakeDisplayDevice(DisplayManagerService displayManager,
+ float[] refreshRates,
+ int displayType) {
FakeDisplayDevice displayDevice = new FakeDisplayDevice();
DisplayDeviceInfo displayDeviceInfo = new DisplayDeviceInfo();
int width = 100;
@@ -2240,6 +2604,7 @@
new Display.Mode(i + 1, width, height, refreshRates[i]);
}
displayDeviceInfo.modeId = 1;
+ displayDeviceInfo.type = displayType;
displayDeviceInfo.renderFrameRate = displayDeviceInfo.supportedModes[0].getRefreshRate();
displayDeviceInfo.width = width;
displayDeviceInfo.height = height;
@@ -2248,6 +2613,9 @@
Insets.of(0, 10, 0, 0),
zeroRect, new Rect(0, 0, 10, 10), zeroRect, zeroRect);
displayDeviceInfo.flags = DisplayDeviceInfo.FLAG_ALLOWED_TO_BE_DEFAULT_DISPLAY;
+ if (displayType == Display.TYPE_EXTERNAL) {
+ displayDeviceInfo.flags |= DisplayDeviceInfo.FLAG_OWN_DISPLAY_GROUP;
+ }
displayDeviceInfo.address = new TestUtils.TestDisplayAddress();
displayDevice.setDisplayDeviceInfo(displayDeviceInfo);
displayManager.getDisplayDeviceRepository()
@@ -2282,25 +2650,30 @@
}
}
- private void resetConfigToIgnoreSensorManager(Context context) {
- final Resources res = Mockito.spy(context.getResources());
- doReturn(new int[]{-1}).when(res).getIntArray(R.array
+ private void resetConfigToIgnoreSensorManager() {
+ doReturn(new int[]{-1}).when(mResources).getIntArray(R.array
.config_ambientThresholdsOfPeakRefreshRate);
- doReturn(new int[]{-1}).when(res).getIntArray(R.array
+ doReturn(new int[]{-1}).when(mResources).getIntArray(R.array
.config_brightnessThresholdsOfPeakRefreshRate);
- doReturn(new int[]{-1}).when(res).getIntArray(R.array
+ doReturn(new int[]{-1}).when(mResources).getIntArray(R.array
.config_highDisplayBrightnessThresholdsOfFixedRefreshRate);
- doReturn(new int[]{-1}).when(res).getIntArray(R.array
+ doReturn(new int[]{-1}).when(mResources).getIntArray(R.array
.config_highAmbientBrightnessThresholdsOfFixedRefreshRate);
-
- when(context.getResources()).thenReturn(res);
}
- private class FakeDisplayManagerCallback extends IDisplayManagerCallback.Stub {
+ private void manageDisplaysPermission(boolean granted) {
+ if (granted) {
+ doNothing().when(mContext).enforceCallingOrSelfPermission(eq(MANAGE_DISPLAYS), any());
+ } else {
+ doThrow(new SecurityException("MANAGE_DISPLAYS permission denied")).when(mContext)
+ .enforceCallingOrSelfPermission(eq(MANAGE_DISPLAYS), any());
+ }
+ }
+
+ private static class FakeDisplayManagerCallback extends IDisplayManagerCallback.Stub
+ implements DisplayManagerInternal.DisplayGroupListener {
int mDisplayId;
- boolean mDisplayAddedCalled = false;
- boolean mDisplayChangedCalled = false;
- boolean mDisplayRemovedCalled = false;
+ List<String> mReceivedEvents = new ArrayList<>();
FakeDisplayManagerCallback(int displayId) {
mDisplayId = displayId;
@@ -2316,23 +2689,55 @@
return;
}
- if (event == DisplayManagerGlobal.EVENT_DISPLAY_ADDED) {
- mDisplayAddedCalled = true;
- }
+ // We convert the event to a string for two reasons:
+ // 1 - The error produced is a lot easier to read
+ // 2 - The values used for display and group events are the same, strings are used to
+ // differentiate them easily.
+ mReceivedEvents.add(eventTypeToString(event));
+ }
- if (event == DisplayManagerGlobal.EVENT_DISPLAY_CHANGED) {
- mDisplayChangedCalled = true;
- }
+ @Override
+ public void onDisplayGroupAdded(int groupId) {
+ mReceivedEvents.add(DISPLAY_GROUP_EVENT_ADDED);
+ }
- if (event == DisplayManagerGlobal.EVENT_DISPLAY_REMOVED) {
- mDisplayRemovedCalled = true;
- }
+ @Override
+ public void onDisplayGroupRemoved(int groupId) {
+ mReceivedEvents.add(DISPLAY_GROUP_EVENT_REMOVED);
+ }
+
+ @Override
+ public void onDisplayGroupChanged(int groupId) {
+ mReceivedEvents.add(DISPLAY_GROUP_EVENT_CHANGED);
}
public void clear() {
- mDisplayAddedCalled = false;
- mDisplayChangedCalled = false;
- mDisplayRemovedCalled = false;
+ mReceivedEvents.clear();
+ }
+
+ private String eventTypeToString(int eventType) {
+ switch (eventType) {
+ case DisplayManagerGlobal.EVENT_DISPLAY_ADDED:
+ return EVENT_DISPLAY_ADDED;
+ case DisplayManagerGlobal.EVENT_DISPLAY_REMOVED:
+ return EVENT_DISPLAY_REMOVED;
+ case DisplayManagerGlobal.EVENT_DISPLAY_CHANGED:
+ return EVENT_DISPLAY_CHANGED;
+ case DisplayManagerGlobal.EVENT_DISPLAY_BRIGHTNESS_CHANGED:
+ return EVENT_DISPLAY_BRIGHTNESS_CHANGED;
+ case DisplayManagerGlobal.EVENT_DISPLAY_HDR_SDR_RATIO_CHANGED:
+ return EVENT_DISPLAY_HDR_SDR_RATIO_CHANGED;
+ case DisplayManagerGlobal.EVENT_DISPLAY_CONNECTED:
+ return EVENT_DISPLAY_CONNECTED;
+ case DisplayManagerGlobal.EVENT_DISPLAY_DISCONNECTED:
+ return EVENT_DISPLAY_DISCONNECTED;
+ default:
+ return "UNKNOWN: " + eventType;
+ }
+ }
+
+ List<String> receivedEvents() {
+ return mReceivedEvents;
}
}
diff --git a/services/tests/displayservicetests/src/com/android/server/display/HighBrightnessModeControllerTest.java b/services/tests/displayservicetests/src/com/android/server/display/HighBrightnessModeControllerTest.java
index 76e6ec7..8e01a11 100644
--- a/services/tests/displayservicetests/src/com/android/server/display/HighBrightnessModeControllerTest.java
+++ b/services/tests/displayservicetests/src/com/android/server/display/HighBrightnessModeControllerTest.java
@@ -32,8 +32,10 @@
import static org.mockito.Mockito.anyFloat;
import static org.mockito.Mockito.anyInt;
import static org.mockito.Mockito.eq;
+import static org.mockito.Mockito.mock;
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.when;
@@ -390,6 +392,35 @@
assertEquals(Float.POSITIVE_INFINITY, hbmc.getHdrBrightnessValue(), 0.0);
}
+ @Test
+ public void testHdrRespectsChangingDesiredHdrSdrRatio() {
+ final Runnable hbmChangedCallback = mock(Runnable.class);
+ final HighBrightnessModeController hbmc = new TestHbmBuilder()
+ .setClock(new OffsettableClock())
+ .setHdrBrightnessConfig(mHdrBrightnessDeviceConfigMock)
+ .setHbmChangedCallback(hbmChangedCallback)
+ .build();
+
+ // Passthrough return the max desired hdr/sdr ratio
+ when(mHdrBrightnessDeviceConfigMock.getHdrBrightnessFromSdr(anyFloat(), anyFloat()))
+ .thenAnswer(i -> i.getArgument(1));
+
+ hbmc.getHdrListener().onHdrInfoChanged(null /*displayToken*/, 1 /*numberOfHdrLayers*/,
+ DISPLAY_WIDTH, DISPLAY_HEIGHT, 0 /*flags*/, 2.0f /*maxDesiredHdrSdrRatio*/);
+ advanceTime(0);
+ assertEquals(2.0f, hbmc.getHdrBrightnessValue(), EPSILON);
+ verify(hbmChangedCallback, times(1)).run();
+
+ // Verify that a change in only the desired hdrSdrRatio still results in the changed
+ // callback being invoked
+ hbmc.getHdrListener().onHdrInfoChanged(null /*displayToken*/, 1 /*numberOfHdrLayers*/,
+ DISPLAY_WIDTH, DISPLAY_HEIGHT, 0 /*flags*/,
+ 3.0f /*maxDesiredHdrSdrRatio*/);
+ advanceTime(0);
+ assertEquals(3.0f, hbmc.getHdrBrightnessValue(), 0.0);
+ verify(hbmChangedCallback, times(2)).run();
+ }
+
@Test
public void testHdrTrumpsSunlight() {
@@ -698,6 +729,7 @@
private class TestHbmBuilder {
OffsettableClock mClock;
HighBrightnessModeController.HdrBrightnessDeviceConfig mHdrBrightnessCfg;
+ Runnable mHdrChangedCallback = () -> {};
TestHbmBuilder setClock(OffsettableClock clock) {
mClock = clock;
@@ -711,6 +743,11 @@
return this;
}
+ TestHbmBuilder setHbmChangedCallback(Runnable runnable) {
+ mHdrChangedCallback = runnable;
+ return this;
+ }
+
HighBrightnessModeController build() {
initHandler(mClock);
if (mHighBrightnessModeMetadata == null) {
@@ -718,8 +755,8 @@
}
return new HighBrightnessModeController(mInjectorMock, mHandler, DISPLAY_WIDTH,
DISPLAY_HEIGHT, mDisplayToken, mDisplayUniqueId, DEFAULT_MIN, DEFAULT_MAX,
- DEFAULT_HBM_DATA, mHdrBrightnessCfg, () -> {}, mHighBrightnessModeMetadata,
- mContextSpy);
+ DEFAULT_HBM_DATA, mHdrBrightnessCfg, mHdrChangedCallback,
+ mHighBrightnessModeMetadata, mContextSpy);
}
}
diff --git a/services/tests/displayservicetests/src/com/android/server/display/LocalDisplayAdapterTest.java b/services/tests/displayservicetests/src/com/android/server/display/LocalDisplayAdapterTest.java
index aa0a2fe..6cde5e3 100644
--- a/services/tests/displayservicetests/src/com/android/server/display/LocalDisplayAdapterTest.java
+++ b/services/tests/displayservicetests/src/com/android/server/display/LocalDisplayAdapterTest.java
@@ -107,7 +107,6 @@
private LightsManager mMockedLightsManager;
@Mock
private LogicalLight mMockedBacklight;
-
private Handler mHandler;
private TestListener mListener = new TestListener();
@@ -1063,6 +1062,53 @@
assertThat(info.roundedCorners).isNull();
}
+ @Test
+ public void test_createLocalExternalDisplay_displayManagementEnabled_shouldHaveDefaultGroup()
+ throws Exception {
+ FakeDisplay display = new FakeDisplay(PORT_A);
+ display.info.isInternal = false;
+ setUpDisplay(display);
+ updateAvailableDisplays();
+ mAdapter.registerLocked();
+ waitForHandlerToComplete(mHandler, HANDLER_WAIT_MS);
+ assertThat(mListener.addedDisplays.size()).isEqualTo(1);
+ DisplayDevice displayDevice = mListener.addedDisplays.get(0);
+
+ // Turn on / initialize
+ Runnable changeStateRunnable = displayDevice.requestDisplayStateLocked(Display.STATE_ON, 0,
+ 0);
+ changeStateRunnable.run();
+ waitForHandlerToComplete(mHandler, HANDLER_WAIT_MS);
+ mListener.changedDisplays.clear();
+
+ DisplayDeviceInfo info = displayDevice.getDisplayDeviceInfoLocked();
+
+ assertThat(info.flags & DisplayDeviceInfo.FLAG_OWN_DISPLAY_GROUP).isEqualTo(0);
+ }
+ @Test
+ public void test_createLocalExternalDisplay_displayManagementDisabled_shouldNotHaveOwnGroup()
+ throws Exception {
+ FakeDisplay display = new FakeDisplay(PORT_A);
+ display.info.isInternal = false;
+ setUpDisplay(display);
+ updateAvailableDisplays();
+ mAdapter.registerLocked();
+ waitForHandlerToComplete(mHandler, HANDLER_WAIT_MS);
+ assertThat(mListener.addedDisplays.size()).isEqualTo(1);
+ DisplayDevice displayDevice = mListener.addedDisplays.get(0);
+
+ // Turn on / initialize
+ Runnable changeStateRunnable = displayDevice.requestDisplayStateLocked(Display.STATE_ON, 0,
+ 0);
+ changeStateRunnable.run();
+ waitForHandlerToComplete(mHandler, HANDLER_WAIT_MS);
+ mListener.changedDisplays.clear();
+
+ DisplayDeviceInfo info = displayDevice.getDisplayDeviceInfoLocked();
+
+ assertThat(info.flags & DisplayDeviceInfo.FLAG_OWN_DISPLAY_GROUP).isEqualTo(0);
+ }
+
private void setupCutoutAndRoundedCorners() {
String sampleCutout = "M 507,66\n"
+ "a 33,33 0 1 0 66,0 33,33 0 1 0 -66,0\n"
diff --git a/services/tests/displayservicetests/src/com/android/server/display/LogicalDisplayMapperTest.java b/services/tests/displayservicetests/src/com/android/server/display/LogicalDisplayMapperTest.java
index 0fe6e64..6954435 100644
--- a/services/tests/displayservicetests/src/com/android/server/display/LogicalDisplayMapperTest.java
+++ b/services/tests/displayservicetests/src/com/android/server/display/LogicalDisplayMapperTest.java
@@ -30,6 +30,8 @@
import static com.android.server.display.DisplayAdapter.DISPLAY_DEVICE_EVENT_REMOVED;
import static com.android.server.display.DisplayDeviceInfo.FLAG_ALLOWED_TO_BE_DEFAULT_DISPLAY;
import static com.android.server.display.LogicalDisplayMapper.LOGICAL_DISPLAY_EVENT_ADDED;
+import static com.android.server.display.LogicalDisplayMapper.LOGICAL_DISPLAY_EVENT_CONNECTED;
+import static com.android.server.display.LogicalDisplayMapper.LOGICAL_DISPLAY_EVENT_DISCONNECTED;
import static com.android.server.display.LogicalDisplayMapper.LOGICAL_DISPLAY_EVENT_REMOVED;
import static com.android.server.display.layout.Layout.Display.POSITION_REAR;
import static com.android.server.display.layout.Layout.Display.POSITION_UNKNOWN;
@@ -44,6 +46,7 @@
import static org.junit.Assert.assertTrue;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.clearInvocations;
+import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
@@ -63,6 +66,7 @@
import androidx.test.ext.junit.runners.AndroidJUnit4;
import androidx.test.filters.SmallTest;
+import com.android.server.display.feature.DisplayManagerFlags;
import com.android.server.display.layout.DisplayIdProducer;
import com.android.server.display.layout.Layout;
import com.android.server.utils.FoldSettingWrapper;
@@ -79,7 +83,9 @@
import java.io.File;
import java.io.InputStream;
import java.io.OutputStream;
+import java.util.ArrayList;
import java.util.Arrays;
+import java.util.List;
@SmallTest
@RunWith(AndroidJUnit4.class)
@@ -107,8 +113,11 @@
@Mock IThermalService mIThermalServiceMock;
@Spy DeviceStateToLayoutMap mDeviceStateToLayoutMapSpy =
new DeviceStateToLayoutMap(mIdProducer, NON_EXISTING_FILE);
+ @Mock
+ DisplayManagerFlags mFlagsMock;
@Captor ArgumentCaptor<LogicalDisplay> mDisplayCaptor;
+ @Captor ArgumentCaptor<Integer> mDisplayEventCaptor;
@Before
public void setUp() {
@@ -154,11 +163,12 @@
com.android.internal.R.array.config_deviceStatesOnWhichToSleep))
.thenReturn(new int[]{0});
+ when(mFlagsMock.isConnectedDisplayManagementEnabled()).thenReturn(false);
mLooper = new TestLooper();
mHandler = new Handler(mLooper.getLooper());
mLogicalDisplayMapper = new LogicalDisplayMapper(mContextMock, mDisplayDeviceRepo,
mListenerMock, new DisplayManagerService.SyncRoot(), mHandler,
- mDeviceStateToLayoutMapSpy, mFoldSettingWrapperMock);
+ mDeviceStateToLayoutMapSpy, mFoldSettingWrapperMock, mFlagsMock);
}
@@ -279,6 +289,67 @@
}
@Test
+ public void testDisplayDeviceAddAndRemove_withDisplayManagement() {
+ when(mFlagsMock.isConnectedDisplayManagementEnabled()).thenReturn(true);
+ DisplayDevice device = createDisplayDevice(TYPE_INTERNAL, 600, 800,
+ FLAG_ALLOWED_TO_BE_DEFAULT_DISPLAY);
+
+ // add
+ mDisplayDeviceRepo.onDisplayDeviceEvent(device, DISPLAY_DEVICE_EVENT_ADDED);
+
+ verify(mListenerMock, times(2)).onLogicalDisplayEventLocked(
+ mDisplayCaptor.capture(), mDisplayEventCaptor.capture());
+ LogicalDisplay added = mDisplayCaptor.getAllValues().get(0);
+ assertThat(mDisplayCaptor.getAllValues().get(1)).isEqualTo(added);
+ LogicalDisplay displayAdded = add(device);
+ assertThat(info(displayAdded).address).isEqualTo(info(device).address);
+ assertThat(id(displayAdded)).isEqualTo(DEFAULT_DISPLAY);
+ assertThat(mDisplayEventCaptor.getAllValues()).containsExactly(
+ LOGICAL_DISPLAY_EVENT_CONNECTED, LOGICAL_DISPLAY_EVENT_ADDED).inOrder();
+ clearInvocations(mListenerMock);
+
+ // remove
+ mDisplayDeviceRepo.onDisplayDeviceEvent(device, DISPLAY_DEVICE_EVENT_REMOVED);
+ verify(mListenerMock, times(2)).onLogicalDisplayEventLocked(
+ mDisplayCaptor.capture(), mDisplayEventCaptor.capture());
+ List<Integer> allEvents = mDisplayEventCaptor.getAllValues();
+ int numEvents = allEvents.size();
+ // Only extract the last two events
+ List<Integer> events = new ArrayList(2);
+ events.add(allEvents.get(numEvents - 2));
+ events.add(allEvents.get(numEvents - 1));
+ assertThat(events).containsExactly(
+ LOGICAL_DISPLAY_EVENT_REMOVED, LOGICAL_DISPLAY_EVENT_DISCONNECTED).inOrder();
+ List<LogicalDisplay> displays = mDisplayCaptor.getAllValues();
+ LogicalDisplay displayRemoved = displays.get(numEvents - 2);
+ assertThat(displays.get(numEvents - 1)).isEqualTo(displayRemoved);
+ assertThat(id(displayRemoved)).isEqualTo(DEFAULT_DISPLAY);
+ assertThat(displayRemoved).isEqualTo(displayAdded);
+ }
+
+ @Test
+ public void testDisplayDisableEnable_withDisplayManagement() {
+ when(mFlagsMock.isConnectedDisplayManagementEnabled()).thenReturn(true);
+ DisplayDevice device = createDisplayDevice(TYPE_INTERNAL, 600, 800,
+ FLAG_ALLOWED_TO_BE_DEFAULT_DISPLAY);
+ LogicalDisplay displayAdded = add(device);
+ assertThat(displayAdded.isEnabledLocked()).isTrue();
+
+ // Disable device
+ mLogicalDisplayMapper.setDisplayEnabledLocked(
+ displayAdded.getDisplayIdLocked(), /* isEnabled= */ false);
+ verify(mListenerMock).onLogicalDisplayEventLocked(mDisplayCaptor.capture(),
+ eq(LOGICAL_DISPLAY_EVENT_REMOVED));
+ clearInvocations(mListenerMock);
+
+ // Enable device
+ mLogicalDisplayMapper.setDisplayEnabledLocked(
+ displayAdded.getDisplayIdLocked(), /* isEnabled= */ true);
+ verify(mListenerMock).onLogicalDisplayEventLocked(mDisplayCaptor.capture(),
+ eq(LOGICAL_DISPLAY_EVENT_ADDED));
+ }
+
+ @Test
public void testGetDisplayIdsLocked() {
add(createDisplayDevice(TYPE_INTERNAL, 600, 800,
FLAG_ALLOWED_TO_BE_DEFAULT_DISPLAY));
diff --git a/services/tests/mockingservicestests/src/com/android/server/job/controllers/QuotaControllerTest.java b/services/tests/mockingservicestests/src/com/android/server/job/controllers/QuotaControllerTest.java
index dce162c..552b59c 100644
--- a/services/tests/mockingservicestests/src/com/android/server/job/controllers/QuotaControllerTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/job/controllers/QuotaControllerTest.java
@@ -188,6 +188,7 @@
.when(() -> LocalServices.getService(BatteryManagerInternal.class));
doReturn(mUsageStatsManager)
.when(() -> LocalServices.getService(UsageStatsManagerInternal.class));
+ JobSchedulerService.sUsageStatsManagerInternal = mUsageStatsManager;
doReturn(mPowerAllowlistInternal)
.when(() -> LocalServices.getService(PowerAllowlistInternal.class));
// Used in JobStatus.
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 3c75332..e578ea3 100644
--- a/services/tests/mockingservicestests/src/com/android/server/pm/MockSystem.kt
+++ b/services/tests/mockingservicestests/src/com/android/server/pm/MockSystem.kt
@@ -567,9 +567,8 @@
@Throws(Exception::class)
private fun stageInstantAppResolverScan() {
- whenever(mocks.resources.getStringArray(R.array.config_ephemeralResolverPackage)) {
- arrayOf("com.android.test.ephemeral.resolver")
- }
+ doReturn(arrayOf("com.android.test.ephemeral.resolver"))
+ .whenever(mocks.resources).getStringArray(R.array.config_ephemeralResolverPackage)
stageScanNewPackage("com.android.test.ephemeral.resolver",
1L, getPartitionFromFlag(PackageManagerService.SCAN_AS_PRODUCT).privAppFolder,
withPackage = { pkg: PackageImpl ->
diff --git a/services/tests/mockingservicestests/src/com/android/server/pm/ArchiveManagerTest.java b/services/tests/mockingservicestests/src/com/android/server/pm/PackageArchiverServiceTest.java
similarity index 74%
rename from services/tests/mockingservicestests/src/com/android/server/pm/ArchiveManagerTest.java
rename to services/tests/mockingservicestests/src/com/android/server/pm/PackageArchiverServiceTest.java
index a8b0a7b..c7e1bda 100644
--- a/services/tests/mockingservicestests/src/com/android/server/pm/ArchiveManagerTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/pm/PackageArchiverServiceTest.java
@@ -37,10 +37,9 @@
import android.content.pm.VersionedPackage;
import android.os.Binder;
import android.os.Build;
-import android.os.Process;
+import android.os.ParcelableException;
import android.os.UserHandle;
import android.platform.test.annotations.Presubmit;
-import android.text.TextUtils;
import androidx.test.filters.SmallTest;
import androidx.test.runner.AndroidJUnit4;
@@ -62,7 +61,7 @@
@SmallTest
@Presubmit
@RunWith(AndroidJUnit4.class)
-public class ArchiveManagerTest {
+public class PackageArchiverServiceTest {
private static final String PACKAGE = "com.example";
private static final String CALLER_PACKAGE = "com.vending";
@@ -95,10 +94,11 @@
private final List<LauncherActivityInfo> mLauncherActivityInfos = createLauncherActivities();
+ private final int mUserId = UserHandle.CURRENT.getIdentifier();
+
private PackageSetting mPackageSetting;
- private PackageManagerService mPm;
- private ArchiveManager mArchiveManager;
+ private PackageArchiverService mArchiveService;
@Before
public void setUp() throws Exception {
@@ -106,7 +106,7 @@
mMockSystem.system().stageNominalSystemState();
when(mMockSystem.mocks().getInjector().getPackageInstallerService()).thenReturn(
mInstallerService);
- mPm = spy(new PackageManagerService(mMockSystem.mocks().getInjector(),
+ PackageManagerService pm = spy(new PackageManagerService(mMockSystem.mocks().getInjector(),
/* factoryTest= */false,
MockSystem.Companion.getDEFAULT_VERSION_INFO().fingerprint,
/* isEngBuild= */ false,
@@ -124,37 +124,42 @@
when(mContext.getSystemService(LauncherApps.class)).thenReturn(mLauncherApps);
when(mLauncherApps.getActivityList(eq(PACKAGE), eq(UserHandle.CURRENT))).thenReturn(
mLauncherActivityInfos);
- doReturn(mComputer).when(mPm).snapshotComputer();
- when(mComputer.getNameForUid(eq(Binder.getCallingUid()))).thenReturn(CALLER_PACKAGE);
- mArchiveManager = new ArchiveManager(mContext, mPm);
+ doReturn(mComputer).when(pm).snapshotComputer();
+ when(mComputer.getPackageUid(eq(CALLER_PACKAGE), eq(0L), eq(mUserId))).thenReturn(
+ Binder.getCallingUid());
+ mArchiveService = new PackageArchiverService(mContext, pm);
}
@Test
public void archiveApp_callerPackageNameIncorrect() {
Exception e = assertThrows(
SecurityException.class,
- () -> mArchiveManager.archiveApp(PACKAGE, "different", UserHandle.CURRENT,
- mIntentSender)
+ () -> mArchiveService.requestArchive(PACKAGE, "different", mIntentSender,
+ UserHandle.CURRENT
+ )
);
assertThat(e).hasMessageThat().isEqualTo(
String.format(
- "The callerPackageName %s set by the caller doesn't match the "
- + "caller's own package name %s.",
- "different",
- CALLER_PACKAGE));
+ "The UID %s of callerPackageName set by the caller doesn't match the "
+ + "caller's actual UID %s.",
+ 0,
+ Binder.getCallingUid()));
}
@Test
public void archiveApp_packageNotInstalled() {
- when(mMockSystem.mocks().getSettings().getPackageLPr(eq(PACKAGE))).thenReturn(
+ when(mComputer.getPackageStateFiltered(eq(PACKAGE), anyInt(), anyInt())).thenReturn(
null);
Exception e = assertThrows(
- PackageManager.NameNotFoundException.class,
- () -> mArchiveManager.archiveApp(PACKAGE, CALLER_PACKAGE, UserHandle.CURRENT,
- mIntentSender)
+ ParcelableException.class,
+ () -> mArchiveService.requestArchive(PACKAGE, CALLER_PACKAGE, mIntentSender,
+ UserHandle.CURRENT
+ )
);
- assertThat(e).hasMessageThat().isEqualTo(String.format("Package %s not found.", PACKAGE));
+ assertThat(e.getCause()).isInstanceOf(PackageManager.NameNotFoundException.class);
+ assertThat(e.getCause()).hasMessageThat().isEqualTo(
+ String.format("Package %s not found.", PACKAGE));
}
@Test
@@ -162,11 +167,14 @@
mPackageSetting.modifyUserState(UserHandle.CURRENT.getIdentifier()).setInstalled(false);
Exception e = assertThrows(
- PackageManager.NameNotFoundException.class,
- () -> mArchiveManager.archiveApp(PACKAGE, CALLER_PACKAGE, UserHandle.CURRENT,
- mIntentSender)
+ ParcelableException.class,
+ () -> mArchiveService.requestArchive(PACKAGE, CALLER_PACKAGE, mIntentSender,
+ UserHandle.CURRENT
+ )
);
- assertThat(e).hasMessageThat().isEqualTo(String.format("Package %s not found.", PACKAGE));
+ assertThat(e.getCause()).isInstanceOf(PackageManager.NameNotFoundException.class);
+ assertThat(e.getCause()).hasMessageThat().isEqualTo(
+ String.format("Package %s not found.", PACKAGE));
}
@Test
@@ -183,17 +191,18 @@
when(mPackageState.getInstallSource()).thenReturn(otherInstallSource);
Exception e = assertThrows(
- SecurityException.class,
- () -> mArchiveManager.archiveApp(PACKAGE, CALLER_PACKAGE, Process.myUserHandle(),
- mIntentSender)
+ ParcelableException.class,
+ () -> mArchiveService.requestArchive(PACKAGE, CALLER_PACKAGE, mIntentSender,
+ UserHandle.CURRENT
+ )
);
- assertThat(e).hasMessageThat().isEqualTo(
- TextUtils.formatSimple("No installer found to archive app %s.",
- PACKAGE));
+ assertThat(e.getCause()).isInstanceOf(PackageManager.NameNotFoundException.class);
+ assertThat(e.getCause()).hasMessageThat().isEqualTo(
+ String.format("No installer found to archive app %s.", PACKAGE));
}
@Test
- public void archiveApp_success() throws PackageManager.NameNotFoundException {
+ public void archiveApp_success() {
List<ArchiveState.ArchiveActivityInfo> activityInfos = new ArrayList<>();
for (LauncherActivityInfo mainActivity : createLauncherActivities()) {
// TODO(b/278553670) Extract and store launcher icons
@@ -203,7 +212,8 @@
activityInfos.add(activityInfo);
}
- mArchiveManager.archiveApp(PACKAGE, CALLER_PACKAGE, UserHandle.CURRENT, mIntentSender);
+ mArchiveService.requestArchive(PACKAGE, CALLER_PACKAGE, mIntentSender, UserHandle.CURRENT);
+
verify(mInstallerService).uninstall(
eq(new VersionedPackage(PACKAGE, PackageManager.VERSION_CODE_HIGHEST)),
eq(CALLER_PACKAGE), eq(DELETE_KEEP_DATA), eq(mIntentSender),
diff --git a/services/tests/mockingservicestests/src/com/android/server/pm/PackageFreezerTest.kt b/services/tests/mockingservicestests/src/com/android/server/pm/PackageFreezerTest.kt
index d99af77..e5fae49 100644
--- a/services/tests/mockingservicestests/src/com/android/server/pm/PackageFreezerTest.kt
+++ b/services/tests/mockingservicestests/src/com/android/server/pm/PackageFreezerTest.kt
@@ -88,7 +88,8 @@
@Test
fun freezePackage() {
- val freezer = PackageFreezer(TEST_PACKAGE, TEST_USER_ID, TEST_REASON, pms, TEST_EXIT_REASON)
+ val freezer = PackageFreezer(TEST_PACKAGE, TEST_USER_ID, TEST_REASON, pms,
+ TEST_EXIT_REASON, null /* request */)
verify(pms, times(1))
.killApplication(eq(TEST_PACKAGE), any(), eq(TEST_USER_ID), eq(TEST_REASON),
eq(TEST_EXIT_REASON))
@@ -104,9 +105,9 @@
@Test
fun freezePackage_twice() {
val freezer1 = PackageFreezer(TEST_PACKAGE, TEST_USER_ID, TEST_REASON, pms,
- TEST_EXIT_REASON)
+ TEST_EXIT_REASON, null /* request */)
val freezer2 = PackageFreezer(TEST_PACKAGE, TEST_USER_ID, TEST_REASON, pms,
- TEST_EXIT_REASON)
+ TEST_EXIT_REASON, null /* request */)
verify(pms, times(2))
.killApplication(eq(TEST_PACKAGE), any(), eq(TEST_USER_ID), eq(TEST_REASON),
eq(TEST_EXIT_REASON))
@@ -127,7 +128,7 @@
@Test
fun freezePackage_withoutClosing() {
var freezer: PackageFreezer? = PackageFreezer(TEST_PACKAGE, TEST_USER_ID, TEST_REASON, pms,
- TEST_EXIT_REASON)
+ TEST_EXIT_REASON, null /* request */)
verify(pms, times(1))
.killApplication(eq(TEST_PACKAGE), any(), eq(TEST_USER_ID), eq(TEST_REASON),
eq(TEST_EXIT_REASON))
diff --git a/services/tests/mockingservicestests/src/com/android/server/sensorprivacy/CameraPrivacyLightControllerTest.java b/services/tests/mockingservicestests/src/com/android/server/sensorprivacy/CameraPrivacyLightControllerTest.java
index 20cfd59..dc04b6a 100644
--- a/services/tests/mockingservicestests/src/com/android/server/sensorprivacy/CameraPrivacyLightControllerTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/sensorprivacy/CameraPrivacyLightControllerTest.java
@@ -24,10 +24,12 @@
import static org.junit.Assert.assertEquals;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.atLeastOnce;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verifyZeroInteractions;
import android.app.AppOpsManager;
import android.content.Context;
@@ -43,14 +45,13 @@
import android.os.Handler;
import android.os.Looper;
import android.permission.PermissionManager;
-import android.util.ArraySet;
+import android.testing.TestableLooper;
import com.android.dx.mockito.inline.extended.ExtendedMockito;
import com.android.internal.R;
import org.junit.After;
import org.junit.Before;
-import org.junit.Ignore;
import org.junit.Test;
import org.mockito.ArgumentCaptor;
import org.mockito.Mock;
@@ -62,24 +63,19 @@
import java.util.List;
import java.util.Random;
import java.util.Set;
+import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;
public class CameraPrivacyLightControllerTest {
+ private int[] mDefaultColors = {0, 1, 2};
+ private int[] mDefaultAlsThresholdsLux = {10, 50};
+ private int mDefaultAlsAveragingIntervalMillis = 5000;
- private int mDayColor = 1;
- private int mNightColor = 0;
- private int mCameraPrivacyLightAlsAveragingIntervalMillis = 5000;
- private int mCameraPrivacyLightAlsNightThreshold = (int) getLightSensorValue(15);
+ private TestableLooper mTestableLooper;
private MockitoSession mMockitoSession;
@Mock
- private Context mContext;
-
- @Mock
- private Resources mResources;
-
- @Mock
private LightsManager mLightsManager;
@Mock
@@ -103,11 +99,64 @@
private ArgumentCaptor<SensorEventListener> mLightSensorListenerCaptor =
ArgumentCaptor.forClass(SensorEventListener.class);
- private Set<String> mExemptedPackages = new ArraySet<>();
- private List<Light> mLights = new ArrayList<>();
+ private Set<String> mExemptedPackages;
+ private List<Light> mLights;
private int mNextLightId = 1;
+ public CameraPrivacyLightController prepareDefaultCameraPrivacyLightController() {
+ return prepareDefaultCameraPrivacyLightController(List.of(getNextLight(true)));
+ }
+
+ public CameraPrivacyLightController prepareDefaultCameraPrivacyLightController(
+ List<Light> lights) {
+ return prepareCameraPrivacyLightController(lights, Set.of(), true, mDefaultColors,
+ mDefaultAlsThresholdsLux, mDefaultAlsAveragingIntervalMillis);
+ }
+
+ public CameraPrivacyLightController prepareCameraPrivacyLightController(List<Light> lights,
+ Set<String> exemptedPackages, boolean hasLightSensor, int[] lightColors,
+ int[] alsThresholds, int averagingInterval) {
+ Looper looper = Looper.myLooper();
+ if (looper == null) {
+ Looper.prepare();
+ looper = Looper.myLooper();
+ }
+ if (mTestableLooper == null) {
+ try {
+ mTestableLooper = new TestableLooper(looper);
+ } catch (Exception e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ Context context = mock(Context.class);
+ Resources resources = mock(Resources.class);
+ doReturn(resources).when(context).getResources();
+ doReturn(lightColors).when(resources).getIntArray(R.array.config_cameraPrivacyLightColors);
+ doReturn(alsThresholds).when(resources)
+ .getIntArray(R.array.config_cameraPrivacyLightAlsLuxThresholds);
+ doReturn(averagingInterval).when(resources)
+ .getInteger(R.integer.config_cameraPrivacyLightAlsAveragingIntervalMillis);
+
+ doReturn(mLightsManager).when(context).getSystemService(LightsManager.class);
+ doReturn(mAppOpsManager).when(context).getSystemService(AppOpsManager.class);
+ doReturn(mSensorManager).when(context).getSystemService(SensorManager.class);
+
+ mLights = lights;
+ mExemptedPackages = exemptedPackages;
+ doReturn(mLights).when(mLightsManager).getLights();
+ doReturn(mLightsSession).when(mLightsManager).openSession(anyInt());
+ if (!hasLightSensor) {
+ mLightSensor = null;
+ }
+ doReturn(mLightSensor).when(mSensorManager).getDefaultSensor(Sensor.TYPE_LIGHT);
+ doReturn(exemptedPackages)
+ .when(() -> PermissionManager.getIndicatorExemptedPackages(any()));
+
+ return new CameraPrivacyLightController(context, looper);
+ }
+
@Before
public void setUp() {
mMockitoSession = ExtendedMockito.mockitoSession()
@@ -115,49 +164,33 @@
.strictness(Strictness.WARN)
.spyStatic(PermissionManager.class)
.startMocking();
-
- doReturn(mDayColor).when(mContext).getColor(R.color.camera_privacy_light_day);
- doReturn(mNightColor).when(mContext).getColor(R.color.camera_privacy_light_night);
-
- doReturn(mResources).when(mContext).getResources();
- doReturn(mCameraPrivacyLightAlsAveragingIntervalMillis).when(mResources)
- .getInteger(R.integer.config_cameraPrivacyLightAlsAveragingIntervalMillis);
- doReturn(mCameraPrivacyLightAlsNightThreshold).when(mResources)
- .getInteger(R.integer.config_cameraPrivacyLightAlsNightThreshold);
-
- doReturn(mLightsManager).when(mContext).getSystemService(LightsManager.class);
- doReturn(mAppOpsManager).when(mContext).getSystemService(AppOpsManager.class);
- doReturn(mSensorManager).when(mContext).getSystemService(SensorManager.class);
-
- doReturn(mLights).when(mLightsManager).getLights();
- doReturn(mLightsSession).when(mLightsManager).openSession(anyInt());
- doReturn(mLightSensor).when(mSensorManager).getDefaultSensor(Sensor.TYPE_LIGHT);
-
- doReturn(mExemptedPackages)
- .when(() -> PermissionManager.getIndicatorExemptedPackages(any()));
}
@After
public void tearDown() {
- mExemptedPackages.clear();
- mLights.clear();
-
mMockitoSession.finishMocking();
}
@Test
+ public void testNoInteractionsWithServicesIfNoColorsSpecified() {
+ prepareCameraPrivacyLightController(List.of(getNextLight(true)), Collections.EMPTY_SET,
+ true, new int[0], mDefaultAlsThresholdsLux, mDefaultAlsAveragingIntervalMillis);
+
+ verifyZeroInteractions(mLightsManager);
+ verifyZeroInteractions(mAppOpsManager);
+ verifyZeroInteractions(mSensorManager);
+ }
+
+ @Test
public void testAppsOpsListenerNotRegisteredWithoutCameraLights() {
- mLights.add(getNextLight(false));
- createCameraPrivacyLightController();
+ prepareDefaultCameraPrivacyLightController(List.of(getNextLight(false)));
verify(mAppOpsManager, times(0)).startWatchingActive(any(), any(), any());
}
@Test
public void testAppsOpsListenerRegisteredWithCameraLight() {
- mLights.add(getNextLight(true));
-
- createCameraPrivacyLightController();
+ prepareDefaultCameraPrivacyLightController();
verify(mAppOpsManager, times(1)).startWatchingActive(any(), any(), any());
}
@@ -165,11 +198,12 @@
@Test
public void testAllCameraLightsAreRequestedOnOpActive() {
Random r = new Random(0);
+ List<Light> lights = new ArrayList<>();
for (int i = 0; i < 30; i++) {
- mLights.add(getNextLight(r.nextBoolean()));
+ lights.add(getNextLight(r.nextBoolean()));
}
- createCameraPrivacyLightController();
+ prepareDefaultCameraPrivacyLightController(lights);
// Verify no session has been opened at this point.
verify(mLightsManager, times(0)).openSession(anyInt());
@@ -181,8 +215,6 @@
verify(mLightsManager, times(1)).openSession(anyInt());
verify(mLightsSession).requestLights(mLightsRequestCaptor.capture());
- assertEquals("requestLights() not invoked exactly once",
- 1, mLightsRequestCaptor.getAllValues().size());
List<Integer> expectedCameraLightIds = mLights.stream()
.filter(l -> l.getType() == Light.LIGHT_TYPE_CAMERA)
@@ -199,40 +231,25 @@
@Test
public void testWillOnlyOpenOnceWhenTwoPackagesStartOp() {
- mLights.add(getNextLight(true));
-
- createCameraPrivacyLightController();
-
- verify(mAppOpsManager).startWatchingActive(any(), any(), mAppOpsListenerCaptor.capture());
-
- AppOpsManager.OnOpActiveChangedListener listener = mAppOpsListenerCaptor.getValue();
- listener.onOpActiveChanged(OPSTR_CAMERA, 10101, "pkg1", true);
+ prepareDefaultCameraPrivacyLightController();
+ notifyCamOpChanged(10101, "pkg1", true);
verify(mLightsManager, times(1)).openSession(anyInt());
- listener.onOpActiveChanged(OPSTR_CAMERA, 10102, "pkg2", true);
+ notifyCamOpChanged(10102, "pkg2", true);
verify(mLightsManager, times(1)).openSession(anyInt());
}
@Test
public void testWillCloseOnFinishOp() {
- mLights.add(getNextLight(true));
-
- createCameraPrivacyLightController();
-
- verify(mAppOpsManager).startWatchingActive(any(), any(), mAppOpsListenerCaptor.capture());
-
- AppOpsManager.OnOpActiveChangedListener listener = mAppOpsListenerCaptor.getValue();
- listener.onOpActiveChanged(OPSTR_CAMERA, 10101, "pkg1", true);
-
+ prepareDefaultCameraPrivacyLightController();
+ notifyCamOpChanged(10101, "pkg1", true);
verify(mLightsSession, times(0)).close();
- listener.onOpActiveChanged(OPSTR_CAMERA, 10101, "pkg1", false);
+ notifyCamOpChanged(10101, "pkg1", false);
verify(mLightsSession, times(1)).close();
}
@Test
public void testWillCloseOnFinishOpForAllPackages() {
- mLights.add(getNextLight(true));
-
- createCameraPrivacyLightController();
+ prepareDefaultCameraPrivacyLightController();
int numUids = 100;
List<Integer> uids = new ArrayList<>(numUids);
@@ -240,64 +257,52 @@
uids.add(10001 + i);
}
- verify(mAppOpsManager).startWatchingActive(any(), any(), mAppOpsListenerCaptor.capture());
-
- AppOpsManager.OnOpActiveChangedListener listener = mAppOpsListenerCaptor.getValue();
-
for (int i = 0; i < numUids; i++) {
- listener.onOpActiveChanged(OPSTR_CAMERA, uids.get(i), "pkg" + (int) uids.get(i), true);
+ notifyCamOpChanged(uids.get(i), "pkg" + (int) uids.get(i), true);
}
// Change the order which their ops are finished
Collections.shuffle(uids, new Random(0));
for (int i = 0; i < numUids - 1; i++) {
- listener.onOpActiveChanged(OPSTR_CAMERA, uids.get(i), "pkg" + (int) uids.get(i), false);
+ notifyCamOpChanged(uids.get(i), "pkg" + (int) uids.get(i), false);
}
verify(mLightsSession, times(0)).close();
int lastUid = uids.get(numUids - 1);
- listener.onOpActiveChanged(OPSTR_CAMERA, lastUid, "pkg" + lastUid, false);
+ notifyCamOpChanged(lastUid, "pkg" + lastUid, false);
verify(mLightsSession, times(1)).close();
}
@Test
public void testWontOpenForExemptedPackage() {
- mLights.add(getNextLight(true));
- mExemptedPackages.add("pkg1");
+ String exemptPackage = "pkg1";
+ prepareCameraPrivacyLightController(List.of(getNextLight(true)),
+ Set.of(exemptPackage), true, mDefaultColors, mDefaultAlsThresholdsLux,
+ mDefaultAlsAveragingIntervalMillis);
- createCameraPrivacyLightController();
-
- verify(mAppOpsManager).startWatchingActive(any(), any(), mAppOpsListenerCaptor.capture());
-
- AppOpsManager.OnOpActiveChangedListener listener = mAppOpsListenerCaptor.getValue();
- listener.onOpActiveChanged(OPSTR_CAMERA, 10101, "pkg1", true);
+ notifyCamOpChanged(10101, exemptPackage, true);
verify(mLightsManager, times(0)).openSession(anyInt());
}
@Test
public void testNoLightSensor() {
- mLights.add(getNextLight(true));
- doReturn(null).when(mSensorManager).getDefaultSensor(Sensor.TYPE_LIGHT);
-
- createCameraPrivacyLightController();
+ prepareCameraPrivacyLightController(List.of(getNextLight(true)),
+ Set.of(), true, mDefaultColors, mDefaultAlsThresholdsLux,
+ mDefaultAlsAveragingIntervalMillis);
openCamera();
verify(mLightsSession).requestLights(mLightsRequestCaptor.capture());
LightsRequest lightsRequest = mLightsRequestCaptor.getValue();
for (LightState lightState : lightsRequest.getLightStates()) {
- assertEquals(mDayColor, lightState.getColor());
+ assertEquals(mDefaultColors[mDefaultColors.length - 1], lightState.getColor());
}
}
@Test
public void testALSListenerNotRegisteredUntilCameraIsOpened() {
- mLights.add(getNextLight(true));
- Sensor sensor = mock(Sensor.class);
- doReturn(sensor).when(mSensorManager).getDefaultSensor(Sensor.TYPE_LIGHT);
-
- CameraPrivacyLightController cplc = createCameraPrivacyLightController();
+ prepareDefaultCameraPrivacyLightController();
verify(mSensorManager, never()).registerListener(any(SensorEventListener.class),
any(Sensor.class), anyInt(), any(Handler.class));
@@ -307,113 +312,44 @@
verify(mSensorManager, times(1)).registerListener(mLightSensorListenerCaptor.capture(),
any(Sensor.class), anyInt(), any(Handler.class));
- mAppOpsListenerCaptor.getValue().onOpActiveChanged(OPSTR_CAMERA, 10001, "pkg", false);
+ notifyCamOpChanged(10001, "pkg", false);
verify(mSensorManager, times(1)).unregisterListener(mLightSensorListenerCaptor.getValue());
}
- @Ignore
@Test
- public void testDayColor() {
- testBrightnessToColor(20, mDayColor);
- }
-
- @Ignore
- @Test
- public void testNightColor() {
- testBrightnessToColor(10, mNightColor);
- }
-
- private void testBrightnessToColor(int brightnessValue, int color) {
- mLights.add(getNextLight(true));
- Sensor sensor = mock(Sensor.class);
- doReturn(sensor).when(mSensorManager).getDefaultSensor(Sensor.TYPE_LIGHT);
-
- CameraPrivacyLightController cplc = createCameraPrivacyLightController();
+ public void testAlsThresholds() {
+ CameraPrivacyLightController cplc = prepareDefaultCameraPrivacyLightController();
+ long elapsedTime = 0;
cplc.setElapsedRealTime(0);
-
openCamera();
+ for (int i = 0; i < mDefaultColors.length; i++) {
+ int expectedColor = mDefaultColors[i];
+ int alsLuxValue = i
+ == mDefaultAlsThresholdsLux.length
+ ? mDefaultAlsThresholdsLux[i - 1] : mDefaultAlsThresholdsLux[i] - 1;
- verify(mSensorManager).registerListener(mLightSensorListenerCaptor.capture(),
- any(Sensor.class), anyInt(), any(Handler.class));
- SensorEventListener sensorListener = mLightSensorListenerCaptor.getValue();
- float[] sensorEventValues = new float[1];
- SensorEvent sensorEvent = new SensorEvent(sensor, 0, 0, sensorEventValues);
+ notifySensorEvent(cplc, elapsedTime, alsLuxValue);
+ elapsedTime += mDefaultAlsAveragingIntervalMillis + 1;
+ notifySensorEvent(cplc, elapsedTime, alsLuxValue);
- sensorEventValues[0] = getLightSensorValue(brightnessValue);
- sensorListener.onSensorChanged(sensorEvent);
-
- verify(mLightsSession, atLeastOnce()).requestLights(mLightsRequestCaptor.capture());
- for (LightState lightState : mLightsRequestCaptor.getValue().getLightStates()) {
- assertEquals(color, lightState.getColor());
+ verify(mLightsSession, atLeastOnce()).requestLights(mLightsRequestCaptor.capture());
+ for (LightState lightState : mLightsRequestCaptor.getValue().getLightStates()) {
+ assertEquals(expectedColor, lightState.getColor());
+ }
}
}
- @Ignore
- @Test
- public void testDayToNightTransistion() {
- mLights.add(getNextLight(true));
- Sensor sensor = mock(Sensor.class);
- doReturn(sensor).when(mSensorManager).getDefaultSensor(Sensor.TYPE_LIGHT);
-
- CameraPrivacyLightController cplc = createCameraPrivacyLightController();
- cplc.setElapsedRealTime(0);
-
- openCamera();
- // There will be an initial call at brightness 0
- verify(mLightsSession, times(1)).requestLights(any(LightsRequest.class));
-
- verify(mSensorManager).registerListener(mLightSensorListenerCaptor.capture(),
- any(Sensor.class), anyInt(), any(Handler.class));
- SensorEventListener sensorListener = mLightSensorListenerCaptor.getValue();
-
- onSensorEvent(cplc, sensorListener, sensor, 0, 20);
-
- // 5 sec avg = 20
- onSensorEvent(cplc, sensorListener, sensor, 5000, 30);
-
- verify(mLightsSession, times(2)).requestLights(mLightsRequestCaptor.capture());
- for (LightState lightState : mLightsRequestCaptor.getValue().getLightStates()) {
- assertEquals(mDayColor, lightState.getColor());
- }
-
- // 5 sec avg = 22
-
- onSensorEvent(cplc, sensorListener, sensor, 6000, 10);
-
- // 5 sec avg = 18
-
- onSensorEvent(cplc, sensorListener, sensor, 8000, 5);
-
- // Should have always been day
- verify(mLightsSession, times(2)).requestLights(mLightsRequestCaptor.capture());
- for (LightState lightState : mLightsRequestCaptor.getValue().getLightStates()) {
- assertEquals(mDayColor, lightState.getColor());
- }
-
- // 5 sec avg = 12
-
- onSensorEvent(cplc, sensorListener, sensor, 10000, 5);
-
- // Should now be night
- verify(mLightsSession, times(3)).requestLights(mLightsRequestCaptor.capture());
- for (LightState lightState : mLightsRequestCaptor.getValue().getLightStates()) {
- assertEquals(mNightColor, lightState.getColor());
- }
+ private void notifyCamOpChanged(int uid, String pkg, boolean active) {
+ verify(mAppOpsManager).startWatchingActive(any(), any(), mAppOpsListenerCaptor.capture());
+ mAppOpsListenerCaptor.getValue().onOpActiveChanged(OPSTR_CAMERA, uid, pkg, active);
}
- private void onSensorEvent(CameraPrivacyLightController cplc,
- SensorEventListener sensorListener, Sensor sensor, long timestamp, int value) {
+ private void notifySensorEvent(CameraPrivacyLightController cplc, long timestamp, int value) {
cplc.setElapsedRealTime(timestamp);
- sensorListener.onSensorChanged(new SensorEvent(sensor, 0, timestamp,
- new float[] {getLightSensorValue(value)}));
- }
-
- // Use the test thread so that the test is deterministic
- private CameraPrivacyLightController createCameraPrivacyLightController() {
- if (Looper.myLooper() == null) {
- Looper.prepare();
- }
- return new CameraPrivacyLightController(mContext, Looper.myLooper());
+ verify(mSensorManager, atLeastOnce()).registerListener(mLightSensorListenerCaptor.capture(),
+ eq(mLightSensor), anyInt(), any());
+ mLightSensorListenerCaptor.getValue().onSensorChanged(new SensorEvent(mLightSensor, 0,
+ TimeUnit.MILLISECONDS.toNanos(timestamp), new float[] {value}));
}
private Light getNextLight(boolean cameraType) {
@@ -427,10 +363,6 @@
return light;
}
- private float getLightSensorValue(int i) {
- return (float) Math.exp(i / CameraPrivacyLightController.LIGHT_VALUE_MULTIPLIER);
- }
-
private void openCamera() {
verify(mAppOpsManager).startWatchingActive(any(), any(), mAppOpsListenerCaptor.capture());
mAppOpsListenerCaptor.getValue().onOpActiveChanged(OPSTR_CAMERA, 10001, "pkg", true);
diff --git a/services/tests/powerservicetests/src/com/android/server/power/LowPowerStandbyControllerTest.java b/services/tests/powerservicetests/src/com/android/server/power/LowPowerStandbyControllerTest.java
index d3c0e35..8e328ca 100644
--- a/services/tests/powerservicetests/src/com/android/server/power/LowPowerStandbyControllerTest.java
+++ b/services/tests/powerservicetests/src/com/android/server/power/LowPowerStandbyControllerTest.java
@@ -191,7 +191,17 @@
mTestPolicyFile = new File(mContextSpy.getCacheDir(), "lps_policy.xml");
mController = new LowPowerStandbyController(mContextSpy, mTestLooper.getLooper(),
- () -> mClock.now(), mDeviceConfigWrapperMock, () -> mIActivityManagerMock,
+ new LowPowerStandbyController.Clock() {
+ @Override
+ public long elapsedRealtime() {
+ return mClock.now();
+ }
+
+ @Override
+ public long uptimeMillis() {
+ return mClock.now();
+ }
+ }, mDeviceConfigWrapperMock, () -> mIActivityManagerMock,
mTestPolicyFile);
}
diff --git a/services/tests/servicestests/src/com/android/server/accessibility/magnification/FullScreenMagnificationGestureHandlerTest.java b/services/tests/servicestests/src/com/android/server/accessibility/magnification/FullScreenMagnificationGestureHandlerTest.java
index b79c7be..349a597 100644
--- a/services/tests/servicestests/src/com/android/server/accessibility/magnification/FullScreenMagnificationGestureHandlerTest.java
+++ b/services/tests/servicestests/src/com/android/server/accessibility/magnification/FullScreenMagnificationGestureHandlerTest.java
@@ -303,6 +303,7 @@
});
}
+ @FlakyTest(bugId = 297879316)
@Test
public void testStates_areMutuallyExclusive() {
forEachState(state1 -> {
@@ -523,6 +524,7 @@
});
}
+ @FlakyTest(bugId = 297879316)
@Test
public void testTwoFingersOneTap_activatedState_dispatchMotionEvents() {
goFromStateIdleTo(STATE_ACTIVATED);
@@ -583,6 +585,7 @@
returnToNormalFrom(STATE_ACTIVATED);
}
+ @FlakyTest(bugId = 297879316)
@Test
public void testFirstFingerSwipe_twoPointerDownAndActivatedState_panningState() {
goFromStateIdleTo(STATE_ACTIVATED);
diff --git a/services/tests/servicestests/src/com/android/server/accessibility/magnification/WindowMagnificationManagerTest.java b/services/tests/servicestests/src/com/android/server/accessibility/magnification/WindowMagnificationManagerTest.java
index bbbab21..a0bca3b 100644
--- a/services/tests/servicestests/src/com/android/server/accessibility/magnification/WindowMagnificationManagerTest.java
+++ b/services/tests/servicestests/src/com/android/server/accessibility/magnification/WindowMagnificationManagerTest.java
@@ -50,6 +50,7 @@
import android.os.RemoteException;
import android.os.SystemClock;
import android.os.UserHandle;
+import android.platform.test.annotations.FlakyTest;
import android.provider.Settings;
import android.test.mock.MockContentResolver;
import android.view.InputDevice;
@@ -307,6 +308,7 @@
MagnificationScaleProvider.MAX_SCALE);
}
+ @FlakyTest(bugId = 297879435)
@Test
public void logTrackingTypingFocus_processScroll_logDuration() {
WindowMagnificationManager spyWindowMagnificationManager = spy(mWindowMagnificationManager);
diff --git a/services/tests/servicestests/src/com/android/server/audio/AudioDeviceBrokerTest.java b/services/tests/servicestests/src/com/android/server/audio/AudioDeviceBrokerTest.java
index aba24fb..7f8ad45 100644
--- a/services/tests/servicestests/src/com/android/server/audio/AudioDeviceBrokerTest.java
+++ b/services/tests/servicestests/src/com/android/server/audio/AudioDeviceBrokerTest.java
@@ -261,7 +261,8 @@
// Verify disconnection has been cancelled and we're seeing two connections attempts,
// with the device connected at the end of the test
verify(mSpyDevInventory, times(2)).onSetBtActiveDevice(
- any(AudioDeviceBroker.BtDeviceInfo.class), anyInt());
+ any(AudioDeviceBroker.BtDeviceInfo.class), anyInt() /*codec*/,
+ anyInt() /*streamType*/);
Assert.assertTrue("Mock device not connected",
mSpyDevInventory.isA2dpDeviceConnected(mFakeBtDevice));
diff --git a/services/tests/servicestests/src/com/android/server/biometrics/AuthenticationStatsCollectorTest.java b/services/tests/servicestests/src/com/android/server/biometrics/AuthenticationStatsCollectorTest.java
index 64e776e..a621c0c 100644
--- a/services/tests/servicestests/src/com/android/server/biometrics/AuthenticationStatsCollectorTest.java
+++ b/services/tests/servicestests/src/com/android/server/biometrics/AuthenticationStatsCollectorTest.java
@@ -181,6 +181,7 @@
.getAuthenticationStatsForUser(USER_ID_1);
assertThat(authenticationStats.getTotalAttempts()).isEqualTo(0);
assertThat(authenticationStats.getRejectedAttempts()).isEqualTo(0);
+ assertThat(authenticationStats.getEnrollmentNotifications()).isEqualTo(0);
assertThat(authenticationStats.getFrr()).isWithin(0f).of(-1.0f);
}
@@ -203,6 +204,8 @@
.getAuthenticationStatsForUser(USER_ID_1);
assertThat(authenticationStats.getTotalAttempts()).isEqualTo(500);
assertThat(authenticationStats.getRejectedAttempts()).isEqualTo(400);
+ assertThat(authenticationStats.getEnrollmentNotifications())
+ .isEqualTo(MAXIMUM_ENROLLMENT_NOTIFICATIONS);
assertThat(authenticationStats.getFrr()).isWithin(0f).of(0.8f);
}
@@ -230,6 +233,7 @@
.getAuthenticationStatsForUser(USER_ID_1);
assertThat(authenticationStats.getTotalAttempts()).isEqualTo(0);
assertThat(authenticationStats.getRejectedAttempts()).isEqualTo(0);
+ assertThat(authenticationStats.getEnrollmentNotifications()).isEqualTo(0);
assertThat(authenticationStats.getFrr()).isWithin(0f).of(-1.0f);
}
@@ -256,6 +260,7 @@
.getAuthenticationStatsForUser(USER_ID_1);
assertThat(authenticationStats.getTotalAttempts()).isEqualTo(0);
assertThat(authenticationStats.getRejectedAttempts()).isEqualTo(0);
+ assertThat(authenticationStats.getEnrollmentNotifications()).isEqualTo(0);
assertThat(authenticationStats.getFrr()).isWithin(0f).of(-1.0f);
}
@@ -284,6 +289,8 @@
assertThat(authenticationStats.getTotalAttempts()).isEqualTo(0);
assertThat(authenticationStats.getRejectedAttempts()).isEqualTo(0);
assertThat(authenticationStats.getFrr()).isWithin(0f).of(-1.0f);
+ // Assert that notification count has been updated.
+ assertThat(authenticationStats.getEnrollmentNotifications()).isEqualTo(1);
}
@Test
@@ -311,5 +318,7 @@
assertThat(authenticationStats.getTotalAttempts()).isEqualTo(0);
assertThat(authenticationStats.getRejectedAttempts()).isEqualTo(0);
assertThat(authenticationStats.getFrr()).isWithin(0f).of(-1.0f);
+ // Assert that notification count has been updated.
+ assertThat(authenticationStats.getEnrollmentNotifications()).isEqualTo(1);
}
}
diff --git a/services/tests/servicestests/src/com/android/server/biometrics/AuthenticationStatsPersisterTest.java b/services/tests/servicestests/src/com/android/server/biometrics/AuthenticationStatsPersisterTest.java
index dde2a3c..0c0d47a 100644
--- a/services/tests/servicestests/src/com/android/server/biometrics/AuthenticationStatsPersisterTest.java
+++ b/services/tests/servicestests/src/com/android/server/biometrics/AuthenticationStatsPersisterTest.java
@@ -217,6 +217,31 @@
}
@Test
+ public void persistFrrStats_multiUser_newUser_shouldUpdateRecord() throws JSONException {
+ AuthenticationStats authenticationStats1 = new AuthenticationStats(USER_ID_1,
+ 300 /* totalAttempts */, 10 /* rejectedAttempts */,
+ 0 /* enrollmentNotifications */, BiometricsProtoEnums.MODALITY_FACE);
+ AuthenticationStats authenticationStats2 = new AuthenticationStats(USER_ID_2,
+ 100 /* totalAttempts */, 5 /* rejectedAttempts */,
+ 1 /* enrollmentNotifications */, BiometricsProtoEnums.MODALITY_FINGERPRINT);
+
+ // Sets up the shared preference with user 1 only.
+ when(mSharedPreferences.getStringSet(eq(KEY), anySet())).thenReturn(
+ Set.of(buildFrrStats(authenticationStats1)));
+
+ // Add data for user 2.
+ mAuthenticationStatsPersister.persistFrrStats(authenticationStats2.getUserId(),
+ authenticationStats2.getTotalAttempts(),
+ authenticationStats2.getRejectedAttempts(),
+ authenticationStats2.getEnrollmentNotifications(),
+ authenticationStats2.getModality());
+
+ verify(mEditor).putStringSet(eq(KEY), mStringSetArgumentCaptor.capture());
+ assertThat(mStringSetArgumentCaptor.getValue())
+ .contains(buildFrrStats(authenticationStats2));
+ }
+
+ @Test
public void removeFrrStats_existingUser_shouldUpdateRecord() throws JSONException {
AuthenticationStats authenticationStats = new AuthenticationStats(USER_ID_1,
300 /* totalAttempts */, 10 /* rejectedAttempts */,
diff --git a/services/tests/uiservicestests/Android.bp b/services/tests/uiservicestests/Android.bp
index 4b65895..2dacda0 100644
--- a/services/tests/uiservicestests/Android.bp
+++ b/services/tests/uiservicestests/Android.bp
@@ -34,6 +34,7 @@
"platform-test-annotations",
"platformprotosnano",
"statsdprotolite",
+ "StatsdTestUtils",
"hamcrest-library",
"servicestests-utils",
"testables",
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 37f4983..70e5c2e1 100755
--- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
@@ -83,7 +83,6 @@
import static com.android.internal.config.sysui.SystemUiSystemPropertiesFlags.NotificationFlags.FSI_FORCE_DEMOTE;
import static com.android.internal.config.sysui.SystemUiSystemPropertiesFlags.NotificationFlags.SHOW_STICKY_HUN_FOR_DENIED_FSI;
-import static com.android.internal.config.sysui.SystemUiSystemPropertiesFlags.NotificationFlags.WAKE_LOCK_FOR_POSTING_NOTIFICATION;
import static com.android.internal.widget.LockPatternUtils.StrongAuthTracker.STRONG_AUTH_REQUIRED_AFTER_USER_LOCKDOWN;
import static com.android.server.am.PendingIntentRecord.FLAG_ACTIVITY_SENDER;
import static com.android.server.am.PendingIntentRecord.FLAG_BROADCAST_SENDER;
@@ -128,7 +127,6 @@
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;
import static java.util.Collections.emptyList;
@@ -596,9 +594,6 @@
mAcquiredWakeLocks.add(wl);
return wl;
});
- mTestFlagResolver.setFlagOverride(WAKE_LOCK_FOR_POSTING_NOTIFICATION, true);
- DeviceConfig.setProperty(DeviceConfig.NAMESPACE_SYSTEMUI,
- SystemUiDeviceConfigFlags.NOTIFY_WAKELOCK, "true", false);
// apps allowed as convos
mService.setStringArrayResourceValue(PKG_O);
@@ -1964,34 +1959,6 @@
}
@Test
- public void enqueueNotification_wakeLockSystemPropertyOff_noWakeLock() throws Exception {
- mTestFlagResolver.setFlagOverride(WAKE_LOCK_FOR_POSTING_NOTIFICATION, false);
- DeviceConfig.setProperty(DeviceConfig.NAMESPACE_SYSTEMUI,
- SystemUiDeviceConfigFlags.NOTIFY_WAKELOCK, "true", false);
-
- mBinderService.enqueueNotificationWithTag(PKG, PKG,
- "enqueueNotification_setsWakeLockWorkSource", 0,
- generateNotificationRecord(null).getNotification(), 0);
- waitForIdle();
-
- verifyZeroInteractions(mPowerManager);
- }
-
- @Test
- public void enqueueNotification_wakeLockDeviceConfigOff_noWakeLock() throws Exception {
- mTestFlagResolver.setFlagOverride(WAKE_LOCK_FOR_POSTING_NOTIFICATION, true);
- DeviceConfig.setProperty(DeviceConfig.NAMESPACE_SYSTEMUI,
- SystemUiDeviceConfigFlags.NOTIFY_WAKELOCK, "false", false);
-
- mBinderService.enqueueNotificationWithTag(PKG, PKG,
- "enqueueNotification_setsWakeLockWorkSource", 0,
- generateNotificationRecord(null).getNotification(), 0);
- waitForIdle();
-
- verifyZeroInteractions(mPowerManager);
- }
-
- @Test
public void testCancelNonexistentNotification() throws Exception {
mBinderService.cancelNotificationWithTag(PKG, PKG,
"testCancelNonexistentNotification", 0, 0);
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/PreferencesHelperTest.java b/services/tests/uiservicestests/src/com/android/server/notification/PreferencesHelperTest.java
index 9b745f5..020afdb 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/PreferencesHelperTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/PreferencesHelperTest.java
@@ -44,22 +44,11 @@
import static android.media.AudioAttributes.CONTENT_TYPE_SONIFICATION;
import static android.media.AudioAttributes.USAGE_NOTIFICATION;
import static android.os.UserHandle.USER_SYSTEM;
-import static android.util.StatsLog.ANNOTATION_ID_IS_UID;
import static com.android.internal.config.sysui.SystemUiSystemPropertiesFlags.NotificationFlags.PROPAGATE_CHANNEL_UPDATES_TO_CONVERSATIONS;
-import static com.android.internal.util.FrameworkStatsLog.PACKAGE_NOTIFICATION_CHANNEL_PREFERENCES;
-import static com.android.internal.util.FrameworkStatsLog.PACKAGE_NOTIFICATION_PREFERENCES;
import static com.android.internal.util.FrameworkStatsLog.PACKAGE_NOTIFICATION_PREFERENCES__FSI_STATE__DENIED;
import static com.android.internal.util.FrameworkStatsLog.PACKAGE_NOTIFICATION_PREFERENCES__FSI_STATE__GRANTED;
import static com.android.internal.util.FrameworkStatsLog.PACKAGE_NOTIFICATION_PREFERENCES__FSI_STATE__NOT_REQUESTED;
-import static com.android.os.AtomsProto.PackageNotificationChannelPreferences.CHANNEL_ID_FIELD_NUMBER;
-import static com.android.os.AtomsProto.PackageNotificationChannelPreferences.CHANNEL_NAME_FIELD_NUMBER;
-import static com.android.os.AtomsProto.PackageNotificationChannelPreferences.IMPORTANCE_FIELD_NUMBER;
-import static com.android.os.AtomsProto.PackageNotificationChannelPreferences.IS_CONVERSATION_FIELD_NUMBER;
-import static com.android.os.AtomsProto.PackageNotificationChannelPreferences.IS_DELETED_FIELD_NUMBER;
-import static com.android.os.AtomsProto.PackageNotificationChannelPreferences.IS_DEMOTED_CONVERSATION_FIELD_NUMBER;
-import static com.android.os.AtomsProto.PackageNotificationChannelPreferences.IS_IMPORTANT_CONVERSATION_FIELD_NUMBER;
-import static com.android.os.AtomsProto.PackageNotificationChannelPreferences.UID_FIELD_NUMBER;
import static com.android.server.notification.NotificationChannelLogger.NotificationChannelEvent.NOTIFICATION_CHANNEL_UPDATED_BY_USER;
import static com.android.server.notification.PreferencesHelper.DEFAULT_BUBBLE_PREFERENCE;
import static com.android.server.notification.PreferencesHelper.NOTIFICATION_CHANNEL_COUNT_LIMIT;
@@ -134,6 +123,7 @@
import android.util.IntArray;
import android.util.Pair;
import android.util.StatsEvent;
+import android.util.StatsEventTestUtils;
import android.util.Xml;
import android.util.proto.ProtoOutputStream;
@@ -144,11 +134,14 @@
import com.android.internal.config.sysui.TestableFlagResolver;
import com.android.modules.utils.TypedXmlPullParser;
import com.android.modules.utils.TypedXmlSerializer;
+import com.android.os.AtomsProto;
+import com.android.os.AtomsProto.PackageNotificationChannelPreferences;
import com.android.os.AtomsProto.PackageNotificationPreferences;
import com.android.server.UiServiceTestCase;
import com.android.server.notification.PermissionHelper.PackagePermission;
import com.google.common.collect.ImmutableList;
+import com.google.protobuf.InvalidProtocolBufferException;
import org.json.JSONArray;
import org.json.JSONObject;
@@ -218,7 +211,6 @@
private PreferencesHelper mHelper;
private AudioAttributes mAudioAttributes;
private NotificationChannelLoggerFake mLogger = new NotificationChannelLoggerFake();
- private WrappedSysUiStatsEvent.WrappedBuilderFactory mStatsEventBuilderFactory;
@Before
public void setUp() throws Exception {
@@ -331,11 +323,9 @@
when(mUserProfiles.getCurrentProfileIds()).thenReturn(IntArray.wrap(new int[] {0}));
- mStatsEventBuilderFactory = new WrappedSysUiStatsEvent.WrappedBuilderFactory();
-
mHelper = new PreferencesHelper(getContext(), mPm, mHandler, mMockZenModeHelper,
mPermissionHelper, mPermissionManager, mLogger, mAppOpsManager, mUserProfiles,
- mStatsEventBuilderFactory, false);
+ false);
resetZenModeHelper();
mAudioAttributes = new AudioAttributes.Builder()
@@ -683,7 +673,7 @@
public void testReadXml_oldXml_migrates() throws Exception {
mHelper = new PreferencesHelper(getContext(), mPm, mHandler, mMockZenModeHelper,
mPermissionHelper, mPermissionManager, mLogger, mAppOpsManager, mUserProfiles,
- mStatsEventBuilderFactory, /* showReviewPermissionsNotification= */ true);
+ /* showReviewPermissionsNotification= */ true);
String xml = "<ranking version=\"2\">\n"
+ "<package name=\"" + PKG_N_MR1 + "\" uid=\"" + UID_N_MR1
@@ -825,7 +815,7 @@
public void testReadXml_newXml_noMigration_showPermissionNotification() throws Exception {
mHelper = new PreferencesHelper(getContext(), mPm, mHandler, mMockZenModeHelper,
mPermissionHelper, mPermissionManager, mLogger, mAppOpsManager, mUserProfiles,
- mStatsEventBuilderFactory, /* showReviewPermissionsNotification= */ true);
+ /* showReviewPermissionsNotification= */ true);
String xml = "<ranking version=\"3\">\n"
+ "<package name=\"" + PKG_N_MR1 + "\" show_badge=\"true\">\n"
@@ -883,7 +873,7 @@
public void testReadXml_newXml_permissionNotificationOff() throws Exception {
mHelper = new PreferencesHelper(getContext(), mPm, mHandler, mMockZenModeHelper,
mPermissionHelper, mPermissionManager, mLogger, mAppOpsManager, mUserProfiles,
- mStatsEventBuilderFactory, /* showReviewPermissionsNotification= */ false);
+ /* showReviewPermissionsNotification= */ false);
String xml = "<ranking version=\"3\">\n"
+ "<package name=\"" + PKG_N_MR1 + "\" show_badge=\"true\">\n"
@@ -941,7 +931,7 @@
public void testReadXml_newXml_noMigration_noPermissionNotification() throws Exception {
mHelper = new PreferencesHelper(getContext(), mPm, mHandler, mMockZenModeHelper,
mPermissionHelper, mPermissionManager, mLogger, mAppOpsManager, mUserProfiles,
- mStatsEventBuilderFactory, /* showReviewPermissionsNotification= */ true);
+ /* showReviewPermissionsNotification= */ true);
String xml = "<ranking version=\"4\">\n"
+ "<package name=\"" + PKG_N_MR1 + "\" show_badge=\"true\">\n"
@@ -1521,7 +1511,7 @@
mHelper = new PreferencesHelper(mContext, mPm, mHandler, mMockZenModeHelper,
mPermissionHelper, mPermissionManager, mLogger, mAppOpsManager, mUserProfiles,
- mStatsEventBuilderFactory, false);
+ false);
NotificationChannel channel =
new NotificationChannel("id", "name", IMPORTANCE_LOW);
@@ -5392,7 +5382,7 @@
}
@Test
- public void testPullPackageChannelPreferencesStats() {
+ public void testPullPackageChannelPreferencesStats() throws InvalidProtocolBufferException {
String channelId = "parent";
String name = "messages";
NotificationChannel fodderA = new NotificationChannel("a", "a", IMPORTANCE_LOW);
@@ -5406,25 +5396,40 @@
ArrayList<StatsEvent> events = new ArrayList<>();
mHelper.pullPackageChannelPreferencesStats(events);
- int found = 0;
- for (WrappedSysUiStatsEvent.WrappedBuilder builder : mStatsEventBuilderFactory.builders) {
- if (builder.getAtomId() == PACKAGE_NOTIFICATION_CHANNEL_PREFERENCES
- && channelId.equals(builder.getValue(CHANNEL_ID_FIELD_NUMBER))) {
- ++found;
- assertEquals("uid", UID_O, builder.getValue(UID_FIELD_NUMBER));
- assertTrue("uid annotation", builder.getBooleanAnnotation(
- UID_FIELD_NUMBER, ANNOTATION_ID_IS_UID));
- assertEquals("importance", IMPORTANCE_DEFAULT, builder.getValue(
- IMPORTANCE_FIELD_NUMBER));
- assertEquals("name", name, builder.getValue(CHANNEL_NAME_FIELD_NUMBER));
- assertFalse("isconv", builder.getBoolean(IS_CONVERSATION_FIELD_NUMBER));
- assertFalse("deleted", builder.getBoolean(IS_DELETED_FIELD_NUMBER));
+ // number of channels with preferences should be 3 total
+ assertEquals("expected number of events", 3, events.size());
+ for (StatsEvent ev : events) {
+ // all of these events should be of PackageNotificationChannelPreferences type,
+ // and therefore we expect the atom to have this field.
+ AtomsProto.Atom atom = StatsEventTestUtils.convertToAtom(ev);
+ assertTrue(atom.hasPackageNotificationChannelPreferences());
+ PackageNotificationChannelPreferences p =
+ atom.getPackageNotificationChannelPreferences();
+
+ // uid is shared across all channels; conversation & deleted are not set in any of
+ // these channels; beyond that check individual channel properties
+ assertEquals("uid", UID_O, p.getUid());
+ assertFalse("is conversation", p.getIsConversation());
+ assertFalse("is deleted", p.getIsDeleted());
+
+ String eventChannelId = p.getChannelId();
+ if (eventChannelId.equals(channelId)) {
+ assertEquals("channel name", name, p.getChannelName());
+ assertEquals("importance", IMPORTANCE_DEFAULT, p.getImportance());
+ assertFalse("is conversation", p.getIsConversation());
+ } else if (eventChannelId.equals("a")) {
+ assertEquals("channel name", "a", p.getChannelName());
+ assertEquals("importance", IMPORTANCE_LOW, p.getImportance());
+ } else { // b
+ assertEquals("channel name", "b", p.getChannelName());
+ assertEquals("importance", IMPORTANCE_HIGH, p.getImportance());
}
}
}
@Test
- public void testPullPackageChannelPreferencesStats_one_to_one() {
+ public void testPullPackageChannelPreferencesStats_one_to_one()
+ throws InvalidProtocolBufferException {
NotificationChannel channelA = new NotificationChannel("a", "a", IMPORTANCE_LOW);
mHelper.createNotificationChannel(PKG_O, UID_O, channelA, true, false, UID_O, false);
NotificationChannel channelB = new NotificationChannel("b", "b", IMPORTANCE_LOW);
@@ -5437,19 +5442,22 @@
ArrayList<StatsEvent> events = new ArrayList<>();
mHelper.pullPackageChannelPreferencesStats(events);
- int found = 0;
- for (WrappedSysUiStatsEvent.WrappedBuilder builder : mStatsEventBuilderFactory.builders) {
- if (builder.getAtomId() == PACKAGE_NOTIFICATION_CHANNEL_PREFERENCES) {
- Object id = builder.getValue(CHANNEL_ID_FIELD_NUMBER);
- assertTrue("missing channel in the output", channels.contains(id));
- channels.remove(id);
- }
+ assertEquals("total events", 3, events.size());
+ for (StatsEvent ev : events) {
+ AtomsProto.Atom atom = StatsEventTestUtils.convertToAtom(ev);
+ assertTrue(atom.hasPackageNotificationChannelPreferences());
+ PackageNotificationChannelPreferences p =
+ atom.getPackageNotificationChannelPreferences();
+ String id = p.getChannelId();
+ assertTrue("missing channel in the output", channels.contains(id));
+ channels.remove(id);
}
assertTrue("unexpected channel in output", channels.isEmpty());
}
@Test
- public void testPullPackageChannelPreferencesStats_conversation() {
+ public void testPullPackageChannelPreferencesStats_conversation()
+ throws InvalidProtocolBufferException {
String conversationId = "friend";
NotificationChannel parent =
@@ -5467,21 +5475,25 @@
ArrayList<StatsEvent> events = new ArrayList<>();
mHelper.pullPackageChannelPreferencesStats(events);
- for (WrappedSysUiStatsEvent.WrappedBuilder builder : mStatsEventBuilderFactory.builders) {
- if (builder.getAtomId() == PACKAGE_NOTIFICATION_CHANNEL_PREFERENCES
- && channelId.equals(builder.getValue(CHANNEL_ID_FIELD_NUMBER))) {
- assertTrue("isConveration should be true", builder.getBoolean(
- IS_CONVERSATION_FIELD_NUMBER));
- assertFalse("not demoted", builder.getBoolean(
- IS_DEMOTED_CONVERSATION_FIELD_NUMBER));
- assertFalse("not important", builder.getBoolean(
- IS_IMPORTANT_CONVERSATION_FIELD_NUMBER));
+ // In this case, we want to check the properties of the conversation channel (not parent)
+ assertEquals("total events", 2, events.size());
+ for (StatsEvent ev : events) {
+ AtomsProto.Atom atom = StatsEventTestUtils.convertToAtom(ev);
+ assertTrue(atom.hasPackageNotificationChannelPreferences());
+ PackageNotificationChannelPreferences p =
+ atom.getPackageNotificationChannelPreferences();
+
+ if (channelId.equals(p.getChannelId())) {
+ assertTrue("isConversation should be true", p.getIsConversation());
+ assertFalse("not demoted", p.getIsDemotedConversation());
+ assertFalse("not important", p.getIsImportantConversation());
}
}
}
@Test
- public void testPullPackageChannelPreferencesStats_conversation_demoted() {
+ public void testPullPackageChannelPreferencesStats_conversation_demoted()
+ throws InvalidProtocolBufferException {
NotificationChannel parent =
new NotificationChannel("parent", "messages", IMPORTANCE_DEFAULT);
mHelper.createNotificationChannel(PKG_O, UID_O, parent, true, false, UID_O, false);
@@ -5496,21 +5508,23 @@
ArrayList<StatsEvent> events = new ArrayList<>();
mHelper.pullPackageChannelPreferencesStats(events);
- for (WrappedSysUiStatsEvent.WrappedBuilder builder : mStatsEventBuilderFactory.builders) {
- if (builder.getAtomId() == PACKAGE_NOTIFICATION_CHANNEL_PREFERENCES
- && channelId.equals(builder.getValue(CHANNEL_ID_FIELD_NUMBER))) {
- assertTrue("isConveration should be true", builder.getBoolean(
- IS_CONVERSATION_FIELD_NUMBER));
- assertTrue("is demoted", builder.getBoolean(
- IS_DEMOTED_CONVERSATION_FIELD_NUMBER));
- assertFalse("not important", builder.getBoolean(
- IS_IMPORTANT_CONVERSATION_FIELD_NUMBER));
+ assertEquals("total events", 2, events.size());
+ for (StatsEvent ev : events) {
+ AtomsProto.Atom atom = StatsEventTestUtils.convertToAtom(ev);
+ assertTrue(atom.hasPackageNotificationChannelPreferences());
+ PackageNotificationChannelPreferences p =
+ atom.getPackageNotificationChannelPreferences();
+ if (channelId.equals(p.getChannelId())) {
+ assertTrue("isConversation should be true", p.getIsConversation());
+ assertTrue("is demoted", p.getIsDemotedConversation());
+ assertFalse("not important", p.getIsImportantConversation());
}
}
}
@Test
- public void testPullPackageChannelPreferencesStats_conversation_priority() {
+ public void testPullPackageChannelPreferencesStats_conversation_priority()
+ throws InvalidProtocolBufferException {
NotificationChannel parent =
new NotificationChannel("parent", "messages", IMPORTANCE_DEFAULT);
mHelper.createNotificationChannel(PKG_O, UID_O, parent, true, false, UID_O, false);
@@ -5525,21 +5539,23 @@
ArrayList<StatsEvent> events = new ArrayList<>();
mHelper.pullPackageChannelPreferencesStats(events);
- for (WrappedSysUiStatsEvent.WrappedBuilder builder : mStatsEventBuilderFactory.builders) {
- if (builder.getAtomId() == PACKAGE_NOTIFICATION_CHANNEL_PREFERENCES
- && channelId.equals(builder.getValue(CHANNEL_ID_FIELD_NUMBER))) {
- assertTrue("isConveration should be true", builder.getBoolean(
- IS_CONVERSATION_FIELD_NUMBER));
- assertFalse("not demoted", builder.getBoolean(
- IS_DEMOTED_CONVERSATION_FIELD_NUMBER));
- assertTrue("is important", builder.getBoolean(
- IS_IMPORTANT_CONVERSATION_FIELD_NUMBER));
+ assertEquals("total events", 2, events.size());
+ for (StatsEvent ev : events) {
+ AtomsProto.Atom atom = StatsEventTestUtils.convertToAtom(ev);
+ assertTrue(atom.hasPackageNotificationChannelPreferences());
+ PackageNotificationChannelPreferences p =
+ atom.getPackageNotificationChannelPreferences();
+ if (channelId.equals(p.getChannelId())) {
+ assertTrue("isConversation should be true", p.getIsConversation());
+ assertFalse("not demoted", p.getIsDemotedConversation());
+ assertTrue("is important", p.getIsImportantConversation());
}
}
}
@Test
- public void testPullPackagePreferencesStats_postPermissionMigration() {
+ public void testPullPackagePreferencesStats_postPermissionMigration()
+ throws InvalidProtocolBufferException {
// make sure there's at least one channel for each package we want to test
NotificationChannel channelA = new NotificationChannel("a", "a", IMPORTANCE_DEFAULT);
mHelper.createNotificationChannel(PKG_N_MR1, UID_N_MR1, channelA, true, false,
@@ -5568,23 +5584,18 @@
ArrayList<StatsEvent> events = new ArrayList<>();
mHelper.pullPackagePreferencesStats(events, appPermissions);
- int found = 0;
- for (WrappedSysUiStatsEvent.WrappedBuilder builder : mStatsEventBuilderFactory.builders) {
- if (builder.getAtomId() == PACKAGE_NOTIFICATION_PREFERENCES) {
- ++found;
- int uid = builder.getInt(PackageNotificationPreferences.UID_FIELD_NUMBER);
- boolean userSet = builder.getBoolean(
- PackageNotificationPreferences.USER_SET_IMPORTANCE_FIELD_NUMBER);
+ assertEquals("total number of packages", 3, events.size());
+ for (StatsEvent ev : events) {
+ AtomsProto.Atom atom = StatsEventTestUtils.convertToAtom(ev);
+ assertTrue(atom.hasPackageNotificationPreferences());
+ PackageNotificationPreferences p = atom.getPackageNotificationPreferences();
+ int uid = p.getUid();
- // if it's one of the expected ids, then make sure the importance matches
- assertTrue(expected.containsKey(uid));
- assertThat(expected.get(uid).first).isEqualTo(
- builder.getInt(PackageNotificationPreferences.IMPORTANCE_FIELD_NUMBER));
- assertThat(expected.get(uid).second).isEqualTo(userSet);
- }
+ // if it's one of the expected ids, then make sure the importance matches
+ assertTrue(expected.containsKey(uid));
+ assertThat(expected.get(uid).first).isEqualTo(p.getImportance());
+ assertThat(expected.get(uid).second).isEqualTo(p.getUserSetImportance());
}
- // should have at least one entry for each of the packages we expected to see
- assertThat(found).isAtLeast(3);
}
@Test
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/WrappedSysUiStatsEvent.java b/services/tests/uiservicestests/src/com/android/server/notification/WrappedSysUiStatsEvent.java
deleted file mode 100644
index 89adc72..0000000
--- a/services/tests/uiservicestests/src/com/android/server/notification/WrappedSysUiStatsEvent.java
+++ /dev/null
@@ -1,143 +0,0 @@
-/*
- * Copyright (C) 2020 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.notification;
-
-import android.util.StatsEvent;
-
-import com.android.server.notification.SysUiStatsEvent.Builder;
-
-import java.util.ArrayList;
-import java.util.HashMap;
-import java.util.List;
-
-/**
- * Wrapper for SysUiStatsEvent that implements validation.
- */
-public class WrappedSysUiStatsEvent {
-
- static class WrappedBuilder extends Builder {
- private ArrayList<Object> mValues;
- private HashMap<Integer, HashMap<Byte, Object>> mAnnotations;
- private int mAtomId;
- private int mLastIndex;
- private boolean mBuilt;
-
- WrappedBuilder(StatsEvent.Builder builder) {
- super(builder);
- mValues = new ArrayList<>();
- mAnnotations = new HashMap<>();
- mValues.add(0); // proto fields are 1-based
- }
-
- @Override
- public Builder setAtomId(int atomId) {
- mAtomId = atomId;
- super.setAtomId(atomId);
- return this;
- }
-
- @Override
- public Builder writeInt(int value) {
- addValue(Integer.valueOf(value));
- super.writeInt(value);
- return this;
- }
-
- @Override
- public Builder addBooleanAnnotation(byte annotation, boolean value) {
- addAnnotation(annotation, Boolean.valueOf(value));
- super.addBooleanAnnotation(annotation, value);
- return this;
- }
-
- @Override
- public Builder writeString(String value) {
- addValue(value);
- super.writeString(value);
- return this;
- }
-
- @Override
- public Builder writeBoolean(boolean value) {
- addValue(Boolean.valueOf(value));
- super.writeBoolean(value);
- return this;
- }
-
- @Override
- public StatsEvent build() {
- mBuilt = true;
- return super.build();
- }
-
- public Object getValue(int index) {
- return index < mValues.size() ? mValues.get(index) : null;
- }
-
- /** useful to make assertTrue() statements more readable. */
- public boolean getBoolean(int index) {
- return (Boolean) mValues.get(index);
- }
-
- /** useful to make assertTrue() statements more readable. */
- public int getInt(int index) {
- return (Integer) mValues.get(index);
- }
-
- /** useful to make assertTrue() statements more readable. */
- public String getString(int index) {
- return (String) mValues.get(index);
- }
-
- private void addValue(Object value) {
- mLastIndex = mValues.size();
- mValues.add(value);
- }
-
- private void addAnnotation(byte annotation, Object value) {
- Integer key = Integer.valueOf(mLastIndex);
- if (!mAnnotations.containsKey(key)) {
- mAnnotations.put(key, new HashMap<>());
- }
- mAnnotations.get(key).put(Byte.valueOf(annotation), value);
- }
-
- public boolean getBooleanAnnotation(int i, byte a) {
- return ((Boolean) mAnnotations.get(Integer.valueOf(i)).get(Byte.valueOf(a)))
- .booleanValue();
- }
-
- public int getAtomId() {
- return mAtomId;
- }
- }
-
- static class WrappedBuilderFactory extends SysUiStatsEvent.BuilderFactory {
- public List<WrappedBuilder> builders;
-
- WrappedBuilderFactory() {
- builders = new ArrayList<>();
- }
-
- @Override
- Builder newBuilder() {
- WrappedBuilder b = new WrappedBuilder(StatsEvent.newBuilder());
- builders.add(b);
- return b;
- }
- }
-}
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/ZenModeHelperTest.java b/services/tests/uiservicestests/src/com/android/server/notification/ZenModeHelperTest.java
index 3ee75de..e22c104 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/ZenModeHelperTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/ZenModeHelperTest.java
@@ -33,18 +33,12 @@
import static android.app.NotificationManager.Policy.SUPPRESSED_EFFECT_FULL_SCREEN_INTENT;
import static android.app.NotificationManager.Policy.SUPPRESSED_EFFECT_LIGHTS;
import static android.app.NotificationManager.Policy.SUPPRESSED_EFFECT_PEEK;
+import static android.provider.Settings.Global.ZEN_MODE_ALARMS;
import static android.provider.Settings.Global.ZEN_MODE_IMPORTANT_INTERRUPTIONS;
import static android.service.notification.Condition.STATE_FALSE;
import static android.service.notification.Condition.STATE_TRUE;
-import static android.util.StatsLog.ANNOTATION_ID_IS_UID;
import static com.android.internal.config.sysui.SystemUiSystemPropertiesFlags.NotificationFlags.LOG_DND_STATE_EVENTS;
-import static com.android.internal.util.FrameworkStatsLog.DND_MODE_RULE;
-import static com.android.os.dnd.DNDModeProto.CHANNELS_BYPASSING_FIELD_NUMBER;
-import static com.android.os.dnd.DNDModeProto.ENABLED_FIELD_NUMBER;
-import static com.android.os.dnd.DNDModeProto.ID_FIELD_NUMBER;
-import static com.android.os.dnd.DNDModeProto.UID_FIELD_NUMBER;
-import static com.android.os.dnd.DNDModeProto.ZEN_MODE_FIELD_NUMBER;
import static com.android.os.dnd.DNDProtoEnums.PEOPLE_STARRED;
import static com.android.os.dnd.DNDProtoEnums.ROOT_CONFIG;
import static com.android.os.dnd.DNDProtoEnums.STATE_ALLOW;
@@ -73,6 +67,7 @@
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
+import android.annotation.SuppressLint;
import android.app.AppGlobals;
import android.app.AppOpsManager;
import android.app.AutomaticZenRule;
@@ -106,6 +101,7 @@
import android.util.ArrayMap;
import android.util.Log;
import android.util.StatsEvent;
+import android.util.StatsEventTestUtils;
import android.util.Xml;
import com.android.internal.R;
@@ -113,12 +109,15 @@
import com.android.internal.messages.nano.SystemMessageProto.SystemMessage;
import com.android.modules.utils.TypedXmlPullParser;
import com.android.modules.utils.TypedXmlSerializer;
+import com.android.os.AtomsProto;
+import com.android.os.dnd.DNDModeProto;
import com.android.os.dnd.DNDPolicyProto;
import com.android.os.dnd.DNDProtoEnums;
import com.android.server.UiServiceTestCase;
import com.android.server.notification.ManagedServices.UserProfiles;
import com.google.common.collect.ImmutableList;
+import com.google.protobuf.InvalidProtocolBufferException;
import org.junit.Before;
import org.junit.Test;
@@ -140,13 +139,13 @@
import java.util.Objects;
@SmallTest
+@SuppressLint("GuardedBy") // It's ok for this test to access guarded methods from the service.
@RunWith(AndroidTestingRunner.class)
@TestableLooper.RunWithLooper
public class ZenModeHelperTest extends UiServiceTestCase {
private static final String EVENTS_DEFAULT_RULE_ID = "EVENTS_DEFAULT_RULE";
private static final String SCHEDULE_DEFAULT_RULE_ID = "EVERY_NIGHT_DEFAULT_RULE";
- private static final int ZEN_MODE_FOR_TESTING = 99;
private static final String CUSTOM_PKG_NAME = "not.android";
private static final int CUSTOM_PKG_UID = 1;
private static final String CUSTOM_RULE_ID = "custom_rule";
@@ -159,7 +158,6 @@
private ZenModeHelper mZenModeHelper;
private ContentResolver mContentResolver;
@Mock AppOpsManager mAppOps;
- private WrappedSysUiStatsEvent.WrappedBuilderFactory mStatsEventBuilderFactory;
TestableFlagResolver mTestFlagResolver = new TestableFlagResolver();
ZenModeEventLoggerFake mZenModeEventLogger;
@@ -178,7 +176,6 @@
Log.d("ZenModeHelperTest", "Couldn't mock default zen mode config xml file err=" +
e.toString());
}
- mStatsEventBuilderFactory = new WrappedSysUiStatsEvent.WrappedBuilderFactory();
when(mContext.getSystemService(AppOpsManager.class)).thenReturn(mAppOps);
when(mContext.getSystemService(NotificationManager.class)).thenReturn(mNotificationManager);
@@ -188,8 +185,7 @@
mConditionProviders.addSystemProvider(new ScheduleConditionProvider());
mZenModeEventLogger = new ZenModeEventLoggerFake(mPackageManager);
mZenModeHelper = new ZenModeHelper(mContext, mTestableLooper.getLooper(),
- mConditionProviders, mStatsEventBuilderFactory, mTestFlagResolver,
- mZenModeEventLogger);
+ mConditionProviders, mTestFlagResolver, mZenModeEventLogger);
ResolveInfo ri = new ResolveInfo();
ri.activityInfo = new ActivityInfo();
@@ -911,37 +907,35 @@
}
@Test
- public void testProto() {
+ public void testProto() throws InvalidProtocolBufferException {
mZenModeHelper.mZenMode = ZEN_MODE_IMPORTANT_INTERRUPTIONS;
// existence of manual rule means it should be in output
mZenModeHelper.mConfig.manualRule = new ZenModeConfig.ZenRule();
mZenModeHelper.mConfig.manualRule.pkg = "android"; // system
+ mZenModeHelper.mConfig.automaticRules = new ArrayMap<>(); // no automatic rules
- int n = mZenModeHelper.mConfig.automaticRules.size();
- List<String> ids = new ArrayList<>(n);
- for (ZenModeConfig.ZenRule rule : mZenModeHelper.mConfig.automaticRules.values()) {
- ids.add(rule.id);
- }
+ List<String> ids = new ArrayList<>();
ids.add(ZenModeConfig.MANUAL_RULE_ID);
ids.add(""); // for ROOT_CONFIG, logged with empty string as id
List<StatsEvent> events = new LinkedList<>();
mZenModeHelper.pullRules(events);
- assertEquals(n + 2, events.size()); // automatic rules + manual rule + root config
- for (WrappedSysUiStatsEvent.WrappedBuilder builder : mStatsEventBuilderFactory.builders) {
- if (builder.getAtomId() == DND_MODE_RULE) {
- if (builder.getInt(ZEN_MODE_FIELD_NUMBER) == ROOT_CONFIG) {
- assertTrue(builder.getBoolean(ENABLED_FIELD_NUMBER));
- assertFalse(builder.getBoolean(CHANNELS_BYPASSING_FIELD_NUMBER));
- }
- assertEquals(Process.SYSTEM_UID, builder.getInt(UID_FIELD_NUMBER));
- assertTrue(builder.getBooleanAnnotation(UID_FIELD_NUMBER, ANNOTATION_ID_IS_UID));
- String name = (String) builder.getValue(ID_FIELD_NUMBER);
- assertTrue("unexpected rule id", ids.contains(name));
- ids.remove(name);
- } else {
- fail("unexpected atom id: " + builder.getAtomId());
+ assertEquals(2, events.size()); // manual rule + root config
+ for (StatsEvent ev : events) {
+ AtomsProto.Atom atom = StatsEventTestUtils.convertToAtom(ev);
+ assertTrue(atom.hasDndModeRule());
+ DNDModeProto cfg = atom.getDndModeRule();
+ // Additional check for ID to clearly identify the root config because there's some
+ // odd behavior in the test util around enum value of 0 (the usual default, but not in
+ // this case).
+ if (cfg.getZenMode().getNumber() == ROOT_CONFIG && cfg.getId().equals("")) {
+ assertTrue(cfg.getEnabled());
+ assertFalse(cfg.getChannelsBypassing());
}
+ assertEquals(Process.SYSTEM_UID, cfg.getUid());
+ String name = cfg.getId();
+ assertTrue("unexpected rule id", ids.contains(name));
+ ids.remove(name);
}
assertEquals("extra rule in output", 0, ids.size());
}
@@ -949,28 +943,61 @@
@Test
public void testProtoWithAutoRule() throws Exception {
setupZenConfig();
- // one enabled automatic rule
- mZenModeHelper.mConfig.automaticRules = getCustomAutomaticRules(ZEN_MODE_FOR_TESTING);
+ // one enabled automatic rule. we use a non-usual zen mode value (though it has to be
+ // a real one in the enum because non-valid enum values are reverted to default).
+ mZenModeHelper.mConfig.automaticRules = getCustomAutomaticRules(ZEN_MODE_ALARMS);
List<StatsEvent> events = new LinkedList<>();
mZenModeHelper.pullRules(events);
boolean foundCustomEvent = false;
- for (WrappedSysUiStatsEvent.WrappedBuilder builder : mStatsEventBuilderFactory.builders) {
- if (builder.getAtomId() == DND_MODE_RULE) {
- if (ZEN_MODE_FOR_TESTING == builder.getInt(ZEN_MODE_FIELD_NUMBER)) {
- foundCustomEvent = true;
- assertEquals(CUSTOM_PKG_UID, builder.getInt(UID_FIELD_NUMBER));
- assertTrue(builder.getBoolean(ENABLED_FIELD_NUMBER));
- }
- } else {
- fail("unexpected atom id: " + builder.getAtomId());
+ for (StatsEvent ev : events) {
+ AtomsProto.Atom atom = StatsEventTestUtils.convertToAtom(ev);
+ assertTrue(atom.hasDndModeRule());
+ DNDModeProto cfg = atom.getDndModeRule();
+ if (cfg.getZenMode().getNumber() == ZEN_MODE_ALARMS) {
+ foundCustomEvent = true;
+ assertEquals(CUSTOM_PKG_UID, cfg.getUid());
+ assertTrue(cfg.getEnabled());
}
}
assertTrue("couldn't find custom rule", foundCustomEvent);
}
@Test
+ public void testProtoWithDefaultAutoRules() throws Exception {
+ setupZenConfig();
+ // clear the automatic rules so we can reset to only the default rules
+ mZenModeHelper.mConfig.automaticRules = new ArrayMap<>();
+
+ // read in XML to restore the default rules
+ ByteArrayOutputStream baos = writeXmlAndPurge(5);
+ TypedXmlPullParser parser = Xml.newFastPullParser();
+ parser.setInput(new BufferedInputStream(
+ new ByteArrayInputStream(baos.toByteArray())), null);
+ parser.nextTag();
+ mZenModeHelper.readXml(parser, false, UserHandle.USER_ALL);
+ List<StatsEvent> events = new LinkedList<>();
+ mZenModeHelper.pullRules(events);
+
+ // list for tracking which ids we've seen in the pulled atom output
+ List<String> ids = new ArrayList<>();
+ ids.addAll(ZenModeConfig.DEFAULT_RULE_IDS);
+ ids.add(""); // empty string for root config
+
+ for (StatsEvent ev : events) {
+ AtomsProto.Atom atom = StatsEventTestUtils.convertToAtom(ev);
+ assertTrue(atom.hasDndModeRule());
+ DNDModeProto cfg = atom.getDndModeRule();
+ if (!ids.contains(cfg.getId())) {
+ fail("unexpected ID found: " + cfg.getId());
+ }
+ ids.remove(cfg.getId());
+ }
+ assertEquals("default ID(s) not found", 0, ids.size());
+ }
+
+ @Test
public void ruleUidsCached() throws Exception {
setupZenConfig();
// one enabled automatic rule
@@ -1019,10 +1046,11 @@
List<StatsEvent> events = new LinkedList<>();
mZenModeHelper.pullRules(events);
- boolean foundCustomEvent = false;
- for (WrappedSysUiStatsEvent.WrappedBuilder builder : mStatsEventBuilderFactory.builders) {
- if (builder.getAtomId() == DND_MODE_RULE
- && "customRule".equals(builder.getString(ID_FIELD_NUMBER))) {
+ for (StatsEvent ev : events) {
+ AtomsProto.Atom atom = StatsEventTestUtils.convertToAtom(ev);
+ assertTrue(atom.hasDndModeRule());
+ DNDModeProto cfg = atom.getDndModeRule();
+ if ("customRule".equals(cfg.getId())) {
fail("non-default IDs should be redacted");
}
}
@@ -1039,14 +1067,17 @@
mZenModeHelper.pullRules(events);
boolean foundManualRule = false;
- for (WrappedSysUiStatsEvent.WrappedBuilder builder : mStatsEventBuilderFactory.builders) {
- if (builder.getAtomId() == DND_MODE_RULE
- && ZenModeConfig.MANUAL_RULE_ID.equals(builder.getString(ID_FIELD_NUMBER))) {
- assertEquals(0, builder.getInt(UID_FIELD_NUMBER));
+ for (StatsEvent ev : events) {
+ AtomsProto.Atom atom = StatsEventTestUtils.convertToAtom(ev);
+ assertTrue(atom.hasDndModeRule());
+ DNDModeProto cfg = atom.getDndModeRule();
+ if (ZenModeConfig.MANUAL_RULE_ID.equals(cfg.getId())) {
+ assertEquals(0, cfg.getUid());
foundManualRule = true;
}
}
- assertTrue("couldn't find manual rule", foundManualRule); }
+ assertTrue("couldn't find manual rule", foundManualRule);
+ }
@Test
public void testWriteXml_onlyBackupsTargetUser() throws Exception {
@@ -2439,6 +2470,143 @@
assertEquals(12345, mZenModeEventLogger.getPackageUid(4));
}
+ @Test
+ public void testUpdateConsolidatedPolicy_defaultRulesOnly() {
+ setupZenConfig();
+
+ // When there's one automatic rule active and it doesn't specify a policy, test that the
+ // resulting consolidated policy is one that matches the default rule settings.
+ AutomaticZenRule zenRule = new AutomaticZenRule("name",
+ null,
+ new ComponentName(CUSTOM_PKG_NAME, "ScheduleConditionProvider"),
+ ZenModeConfig.toScheduleConditionId(new ScheduleInfo()),
+ null,
+ NotificationManager.INTERRUPTION_FILTER_PRIORITY, true);
+ String id = mZenModeHelper.addAutomaticZenRule(mContext.getPackageName(), zenRule, "test",
+ Process.SYSTEM_UID, true);
+
+ // enable the rule
+ mZenModeHelper.setAutomaticZenRuleState(id,
+ new Condition(zenRule.getConditionId(), "", STATE_TRUE),
+ Process.SYSTEM_UID, true);
+
+ // inspect the consolidated policy. Based on setupZenConfig() values.
+ assertFalse(mZenModeHelper.mConsolidatedPolicy.allowAlarms());
+ assertFalse(mZenModeHelper.mConsolidatedPolicy.allowMedia());
+ assertFalse(mZenModeHelper.mConsolidatedPolicy.allowSystem());
+ assertTrue(mZenModeHelper.mConsolidatedPolicy.allowReminders());
+ assertTrue(mZenModeHelper.mConsolidatedPolicy.allowCalls());
+ assertEquals(PRIORITY_SENDERS_STARRED, mZenModeHelper.mConsolidatedPolicy.allowCallsFrom());
+ assertTrue(mZenModeHelper.mConsolidatedPolicy.allowMessages());
+ assertTrue(mZenModeHelper.mConsolidatedPolicy.allowConversations());
+ assertTrue(mZenModeHelper.mConsolidatedPolicy.allowRepeatCallers());
+ assertFalse(mZenModeHelper.mConsolidatedPolicy.showBadges());
+ }
+
+ @Test
+ public void testUpdateConsolidatedPolicy_customPolicyOnly() {
+ setupZenConfig();
+
+ // when there's only one automatic rule active and it has a custom policy, make sure that's
+ // what the consolidated policy reflects whether or not it's stricter than what the default
+ // would specify.
+ ZenPolicy customPolicy = new ZenPolicy.Builder()
+ .allowAlarms(true) // more lenient than default
+ .allowMedia(true) // more lenient than default
+ .allowRepeatCallers(false) // more restrictive than default
+ .allowCalls(ZenPolicy.PEOPLE_TYPE_NONE) // more restrictive than default
+ .showBadges(true) // more lenient
+ .showPeeking(false) // more restrictive
+ .build();
+
+ AutomaticZenRule zenRule = new AutomaticZenRule("name",
+ null,
+ new ComponentName(CUSTOM_PKG_NAME, "ScheduleConditionProvider"),
+ ZenModeConfig.toScheduleConditionId(new ScheduleInfo()),
+ customPolicy,
+ NotificationManager.INTERRUPTION_FILTER_PRIORITY, true);
+ String id = mZenModeHelper.addAutomaticZenRule(mContext.getPackageName(), zenRule, "test",
+ Process.SYSTEM_UID, true);
+
+ // enable the rule; this will update the consolidated policy
+ mZenModeHelper.setAutomaticZenRuleState(id,
+ new Condition(zenRule.getConditionId(), "", STATE_TRUE),
+ Process.SYSTEM_UID, true);
+
+ // since this is the only active rule, the consolidated policy should match the custom
+ // policy for every field specified, and take default values for unspecified things
+ assertTrue(mZenModeHelper.mConsolidatedPolicy.allowAlarms()); // custom
+ assertTrue(mZenModeHelper.mConsolidatedPolicy.allowMedia()); // custom
+ assertFalse(mZenModeHelper.mConsolidatedPolicy.allowSystem()); // default
+ assertTrue(mZenModeHelper.mConsolidatedPolicy.allowReminders()); // default
+ assertFalse(mZenModeHelper.mConsolidatedPolicy.allowCalls()); // custom
+ assertTrue(mZenModeHelper.mConsolidatedPolicy.allowMessages()); // default
+ assertTrue(mZenModeHelper.mConsolidatedPolicy.allowConversations()); // default
+ assertFalse(mZenModeHelper.mConsolidatedPolicy.allowRepeatCallers()); // custom
+ assertTrue(mZenModeHelper.mConsolidatedPolicy.showBadges()); // custom
+ assertFalse(mZenModeHelper.mConsolidatedPolicy.showPeeking()); // custom
+ }
+
+ @Test
+ public void testUpdateConsolidatedPolicy_defaultAndCustomActive() {
+ setupZenConfig();
+
+ // when there are two rules active, one inheriting the default policy and one setting its
+ // own custom policy, they should be merged to form the most restrictive combination.
+
+ // rule 1: no custom policy
+ AutomaticZenRule zenRule = new AutomaticZenRule("name",
+ null,
+ new ComponentName(CUSTOM_PKG_NAME, "ScheduleConditionProvider"),
+ ZenModeConfig.toScheduleConditionId(new ScheduleInfo()),
+ null,
+ NotificationManager.INTERRUPTION_FILTER_PRIORITY, true);
+ String id = mZenModeHelper.addAutomaticZenRule(mContext.getPackageName(), zenRule, "test",
+ Process.SYSTEM_UID, true);
+
+ // enable rule 1
+ mZenModeHelper.setAutomaticZenRuleState(id,
+ new Condition(zenRule.getConditionId(), "", STATE_TRUE),
+ Process.SYSTEM_UID, true);
+
+ // custom policy for rule 2
+ ZenPolicy customPolicy = new ZenPolicy.Builder()
+ .allowAlarms(true) // more lenient than default
+ .allowMedia(true) // more lenient than default
+ .allowRepeatCallers(false) // more restrictive than default
+ .allowCalls(ZenPolicy.PEOPLE_TYPE_NONE) // more restrictive than default
+ .showBadges(true) // more lenient
+ .showPeeking(false) // more restrictive
+ .build();
+
+ AutomaticZenRule zenRule2 = new AutomaticZenRule("name2",
+ null,
+ new ComponentName(CUSTOM_PKG_NAME, "ScheduleConditionProvider"),
+ ZenModeConfig.toScheduleConditionId(new ScheduleInfo()),
+ customPolicy,
+ NotificationManager.INTERRUPTION_FILTER_PRIORITY, true);
+ String id2 = mZenModeHelper.addAutomaticZenRule(mContext.getPackageName(), zenRule2,
+ "test", Process.SYSTEM_UID, true);
+
+ // enable rule 2; this will update the consolidated policy
+ mZenModeHelper.setAutomaticZenRuleState(id2,
+ new Condition(zenRule2.getConditionId(), "", STATE_TRUE),
+ Process.SYSTEM_UID, true);
+
+ // now both rules should be on, and the consolidated policy should reflect the most
+ // restrictive option of each of the two
+ assertFalse(mZenModeHelper.mConsolidatedPolicy.allowAlarms()); // default stricter
+ assertFalse(mZenModeHelper.mConsolidatedPolicy.allowMedia()); // default stricter
+ assertFalse(mZenModeHelper.mConsolidatedPolicy.allowSystem()); // default, unset in custom
+ assertTrue(mZenModeHelper.mConsolidatedPolicy.allowReminders()); // default
+ assertFalse(mZenModeHelper.mConsolidatedPolicy.allowCalls()); // custom stricter
+ assertTrue(mZenModeHelper.mConsolidatedPolicy.allowMessages()); // default, unset in custom
+ assertTrue(mZenModeHelper.mConsolidatedPolicy.allowConversations()); // default
+ assertFalse(mZenModeHelper.mConsolidatedPolicy.allowRepeatCallers()); // custom stricter
+ assertFalse(mZenModeHelper.mConsolidatedPolicy.showBadges()); // default stricter
+ assertFalse(mZenModeHelper.mConsolidatedPolicy.showPeeking()); // custom stricter
+ }
+
private void setupZenConfig() {
mZenModeHelper.mZenMode = Global.ZEN_MODE_OFF;
mZenModeHelper.mConfig.allowAlarms = false;
diff --git a/services/tests/vibrator/src/com/android/server/vibrator/VibrationThreadTest.java b/services/tests/vibrator/src/com/android/server/vibrator/VibrationThreadTest.java
index 44cf333..085241f 100644
--- a/services/tests/vibrator/src/com/android/server/vibrator/VibrationThreadTest.java
+++ b/services/tests/vibrator/src/com/android/server/vibrator/VibrationThreadTest.java
@@ -63,7 +63,6 @@
import android.util.SparseArray;
import androidx.test.InstrumentationRegistry;
-import androidx.test.filters.FlakyTest;
import androidx.test.filters.LargeTest;
import com.android.server.LocalServices;
@@ -1005,7 +1004,6 @@
mVibratorProviders.get(3).getEffectSegments(vibrationId));
}
- @FlakyTest
@Test
public void vibrate_multipleSyncedCallbackTriggered_finishSteps() throws Exception {
int[] vibratorIds = new int[]{1, 2};
diff --git a/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java b/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java
index 302ad7f..31682bc 100644
--- a/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java
@@ -1541,7 +1541,8 @@
// Make keyguard locked and set the top activity show-when-locked.
KeyguardController keyguardController = activity.mTaskSupervisor.getKeyguardController();
int displayId = activity.getDisplayId();
- doReturn(true).when(keyguardController).isKeyguardLocked(eq(displayId));
+ keyguardController.setKeyguardShown(displayId, true /* keyguardShowing */,
+ false /* aodShowing */);
final ActivityRecord topActivity = new ActivityBuilder(mAtm).setTask(task).build();
topActivity.setVisibleRequested(true);
topActivity.nowVisible = true;
@@ -1553,7 +1554,7 @@
// Verify the stack-top activity is occluded keyguard.
assertEquals(topActivity, task.topRunningActivity());
- assertTrue(keyguardController.isDisplayOccluded(DEFAULT_DISPLAY));
+ assertTrue(keyguardController.isKeyguardOccluded(displayId));
// Finish the top activity
topActivity.setState(PAUSED, "true");
@@ -1562,7 +1563,7 @@
// Verify new top activity does not occlude keyguard.
assertEquals(activity, task.topRunningActivity());
- assertFalse(keyguardController.isDisplayOccluded(DEFAULT_DISPLAY));
+ assertFalse(keyguardController.isKeyguardOccluded(displayId));
}
/**
diff --git a/services/tests/wmtests/src/com/android/server/wm/ActivityTaskManagerServiceTests.java b/services/tests/wmtests/src/com/android/server/wm/ActivityTaskManagerServiceTests.java
index d169a58..5341588 100644
--- a/services/tests/wmtests/src/com/android/server/wm/ActivityTaskManagerServiceTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/ActivityTaskManagerServiceTests.java
@@ -24,7 +24,6 @@
import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.mock;
-import static com.android.dx.mockito.inline.extended.ExtendedMockito.mockitoSession;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.verify;
import static com.android.server.wm.ActivityInterceptorCallback.MAINLINE_FIRST_ORDERED_ID;
import static com.android.server.wm.ActivityInterceptorCallback.SYSTEM_FIRST_ORDERED_ID;
@@ -41,11 +40,9 @@
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;
import static org.mockito.ArgumentMatchers.anyBoolean;
-import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.Mockito.atLeast;
import static org.mockito.Mockito.doCallRealMethod;
-import static org.mockito.Mockito.when;
import android.annotation.NonNull;
import android.annotation.Nullable;
@@ -60,7 +57,6 @@
import android.content.res.Configuration;
import android.graphics.Rect;
import android.os.Binder;
-import android.os.IBinder;
import android.os.LocaleList;
import android.os.RemoteException;
import android.platform.test.annotations.Presubmit;
@@ -76,7 +72,6 @@
import org.junit.runner.RunWith;
import org.mockito.ArgumentCaptor;
import org.mockito.Mockito;
-import org.mockito.MockitoSession;
import java.util.ArrayList;
import java.util.List;
@@ -304,18 +299,11 @@
*/
@Test
public void testEnterPipModeWhenRecordParentChangesToNull() {
- MockitoSession mockSession = mockitoSession()
- .initMocks(this)
- .mockStatic(ActivityRecord.class)
- .startMocking();
-
- ActivityRecord record = mock(ActivityRecord.class);
- IBinder token = mock(IBinder.class);
+ final ActivityRecord record = new ActivityBuilder(mAtm).setCreateTask(true).build();
PictureInPictureParams params = mock(PictureInPictureParams.class);
record.pictureInPictureArgs = params;
//mock operations in private method ensureValidPictureInPictureActivityParamsLocked()
- when(ActivityRecord.forTokenLocked(token)).thenReturn(record);
doReturn(true).when(record).supportsPictureInPicture();
doReturn(false).when(params).hasSetAspectRatio();
@@ -323,15 +311,13 @@
doReturn(true).when(record)
.checkEnterPictureInPictureState("enterPictureInPictureMode", false);
doReturn(false).when(record).inPinnedWindowingMode();
- doReturn(false).when(mAtm).isKeyguardLocked(anyInt());
+ doReturn(false).when(record).isKeyguardLocked();
//to simulate NPE
doReturn(null).when(record).getParent();
- mAtm.mActivityClientController.enterPictureInPictureMode(token, params);
+ mAtm.mActivityClientController.enterPictureInPictureMode(record.token, params);
//if record's null parent is not handled gracefully, test will fail with NPE
-
- mockSession.finishMocking();
}
@Test
diff --git a/services/tests/wmtests/src/com/android/server/wm/ActivityTaskSupervisorTests.java b/services/tests/wmtests/src/com/android/server/wm/ActivityTaskSupervisorTests.java
index bb20244..b44d52e 100644
--- a/services/tests/wmtests/src/com/android/server/wm/ActivityTaskSupervisorTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/ActivityTaskSupervisorTests.java
@@ -296,7 +296,7 @@
.setWindowingMode(WINDOWING_MODE_MULTI_WINDOW).build();
final ActivityRecord activity1 = new ActivityBuilder(mAtm)
.setTask(task1).setUid(ActivityBuilder.DEFAULT_FAKE_UID + 1).build();
- task1.setResumedActivity(activity1, "test");
+ activity1.setState(ActivityRecord.State.RESUMED, "test");
final ActivityRecord activity2 = new TaskBuilder(mSupervisor)
.setWindowingMode(WINDOWING_MODE_MULTI_WINDOW)
diff --git a/services/tests/wmtests/src/com/android/server/wm/ContentRecordingControllerTests.java b/services/tests/wmtests/src/com/android/server/wm/ContentRecordingControllerTests.java
index 4473a31..c84fe08 100644
--- a/services/tests/wmtests/src/com/android/server/wm/ContentRecordingControllerTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/ContentRecordingControllerTests.java
@@ -123,7 +123,7 @@
controller.setContentRecordingSessionLocked(mWaitingDisplaySession, mWm);
verify(mVirtualDisplayContent, atLeastOnce()).setContentRecordingSession(
mWaitingDisplaySession);
- verify(mVirtualDisplayContent).updateRecording();
+ verify(mVirtualDisplayContent, atLeastOnce()).updateRecording();
// WHEN updating the session on the same display, so no longer waiting to record.
ContentRecordingSession sessionUpdate = ContentRecordingSession.createTaskSession(
diff --git a/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java b/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java
index a2b7da3..ae4ebc1 100644
--- a/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java
@@ -1698,14 +1698,17 @@
final Task task = app.getTask();
final ActivityRecord app2 = new ActivityBuilder(mWm.mAtmService).setTask(task).build();
mDisplayContent.setFixedRotationLaunchingApp(app2, (mDisplayContent.getRotation() + 1) % 4);
- doReturn(true).when(app).isInTransition();
+ doReturn(true).when(app).inTransitionSelfOrParent();
// If the task contains a transition, this should be no-op.
mDisplayContent.mFixedRotationTransitionListener.onAppTransitionFinishedLocked(app.token);
assertTrue(app2.hasFixedRotationTransform());
assertTrue(mDisplayContent.hasTopFixedRotationLaunchingApp());
- doReturn(false).when(app).isInTransition();
+ // The display should be unlikely to be in transition, but if it happens, the fixed
+ // rotation should proceed to finish because the activity/task level transition is finished.
+ doReturn(true).when(mDisplayContent).inTransition();
+ doReturn(false).when(app).inTransitionSelfOrParent();
// Although this notifies app instead of app2 that uses the fixed rotation, app2 should
// still finish the transform because there is no more transition event.
mDisplayContent.mFixedRotationTransitionListener.onAppTransitionFinishedLocked(app.token);
diff --git a/services/tests/wmtests/src/com/android/server/wm/InputMethodDialogWindowContextTest.java b/services/tests/wmtests/src/com/android/server/wm/InputMethodDialogWindowContextTest.java
index c3db241..4165911 100644
--- a/services/tests/wmtests/src/com/android/server/wm/InputMethodDialogWindowContextTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/InputMethodDialogWindowContextTest.java
@@ -26,7 +26,6 @@
import static com.google.common.truth.Truth.assertThat;
-import static org.junit.Assert.assertNotNull;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.eq;
@@ -35,9 +34,14 @@
import static org.mockito.Mockito.timeout;
import static org.mockito.Mockito.verify;
+import static java.util.Objects.requireNonNull;
+
import android.app.ActivityThread;
import android.app.IApplicationThread;
+import android.app.WindowConfiguration;
+import android.content.ComponentCallbacks;
import android.content.Context;
+import android.content.res.Configuration;
import android.content.res.Resources;
import android.graphics.Rect;
import android.hardware.display.DisplayManagerGlobal;
@@ -50,6 +54,8 @@
import android.window.WindowContextInfo;
import android.window.WindowTokenClient;
+import androidx.annotation.NonNull;
+
import com.android.server.inputmethod.InputMethodDialogWindowContext;
import org.junit.After;
@@ -58,6 +64,9 @@
import org.junit.runner.RunWith;
import org.mockito.Mockito;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
+
// TODO(b/157888351): Move the test to inputmethod package once we find the way to test the
// scenario there.
/**
@@ -138,44 +147,96 @@
@Test
public void testGetSettingsContextOnDualDisplayContent() {
final Context context = mWindowContext.get(mSecondaryDisplay.getDisplayId());
+ final MaxBoundsVerifier maxBoundsVerifier = new MaxBoundsVerifier();
+ context.registerComponentCallbacks(maxBoundsVerifier);
+
final WindowTokenClient tokenClient = (WindowTokenClient) context.getWindowContextToken();
- assertNotNull(tokenClient);
- spyOn(tokenClient);
+ spyOn(requireNonNull(tokenClient));
final DisplayArea.Tokens imeContainer = mSecondaryDisplay.getImeContainer();
spyOn(imeContainer);
assertThat(imeContainer.getRootDisplayArea()).isEqualTo(mSecondaryDisplay);
- mSecondaryDisplay.mFirstRoot.placeImeContainer(imeContainer);
+ final DisplayAreaGroup firstDaGroup = mSecondaryDisplay.mFirstRoot;
+ maxBoundsVerifier.setMaxBounds(firstDaGroup.getMaxBounds());
+
+ firstDaGroup.placeImeContainer(imeContainer);
verify(imeContainer, timeout(WAIT_TIMEOUT_MS)).onConfigurationChanged(
- eq(mSecondaryDisplay.mFirstRoot.getConfiguration()));
+ eq(firstDaGroup.getConfiguration()));
verify(tokenClient, timeout(WAIT_TIMEOUT_MS)).onConfigurationChanged(
- eq(mSecondaryDisplay.mFirstRoot.getConfiguration()),
+ eq(firstDaGroup.getConfiguration()),
eq(mSecondaryDisplay.mDisplayId));
- assertThat(imeContainer.getRootDisplayArea()).isEqualTo(mSecondaryDisplay.mFirstRoot);
+ assertThat(imeContainer.getRootDisplayArea()).isEqualTo(firstDaGroup);
+ maxBoundsVerifier.waitAndAssertMaxMetricsMatches();
assertImeSwitchContextMetricsValidity(context, mSecondaryDisplay);
// Clear the previous invocation histories in case we may count the previous
// onConfigurationChanged invocation into the next verification.
clearInvocations(tokenClient, imeContainer);
- mSecondaryDisplay.mSecondRoot.placeImeContainer(imeContainer);
+ final DisplayAreaGroup secondDaGroup = mSecondaryDisplay.mSecondRoot;
+ maxBoundsVerifier.setMaxBounds(secondDaGroup.getMaxBounds());
+
+ secondDaGroup.placeImeContainer(imeContainer);
verify(imeContainer, timeout(WAIT_TIMEOUT_MS)).onConfigurationChanged(
- eq(mSecondaryDisplay.mSecondRoot.getConfiguration()));
+ eq(secondDaGroup.getConfiguration()));
verify(tokenClient, timeout(WAIT_TIMEOUT_MS)).onConfigurationChanged(
- eq(mSecondaryDisplay.mSecondRoot.getConfiguration()),
+ eq(secondDaGroup.getConfiguration()),
eq(mSecondaryDisplay.mDisplayId));
- assertThat(imeContainer.getRootDisplayArea()).isEqualTo(mSecondaryDisplay.mSecondRoot);
+ assertThat(imeContainer.getRootDisplayArea()).isEqualTo(secondDaGroup);
+ maxBoundsVerifier.waitAndAssertMaxMetricsMatches();
assertImeSwitchContextMetricsValidity(context, mSecondaryDisplay);
}
private void assertImeSwitchContextMetricsValidity(Context context, DisplayContent dc) {
assertThat(context.getDisplayId()).isEqualTo(dc.getDisplayId());
+ final Rect imeContainerBounds = dc.getImeContainer().getBounds();
final Rect contextBounds = context.getSystemService(WindowManager.class)
.getMaximumWindowMetrics().getBounds();
- final Rect imeContainerBounds = dc.getImeContainer().getBounds();
assertThat(contextBounds).isEqualTo(imeContainerBounds);
}
+
+ private static final class MaxBoundsVerifier implements ComponentCallbacks {
+
+ private CountDownLatch mLatch;
+
+ private Rect mMaxBounds;
+
+ /**
+ * Sets max bounds to verify whether it matches the
+ * {@link WindowConfiguration#getMaxBounds()} reported from
+ * {@link #onConfigurationChanged(Configuration)} callback, and also resets the count down
+ * latch.
+ *
+ * @param maxBounds max bounds to verify
+ */
+ private void setMaxBounds(@NonNull Rect maxBounds) {
+ mMaxBounds = maxBounds;
+ mLatch = new CountDownLatch(1);
+ }
+
+ /**
+ * Waits for the {@link #onConfigurationChanged(Configuration)} callback whose the reported
+ * {@link WindowConfiguration#getMaxBounds()} matches {@link #mMaxBounds}.
+ */
+ private void waitAndAssertMaxMetricsMatches() {
+ try {
+ assertThat(mLatch.await(WAIT_TIMEOUT_MS, TimeUnit.MILLISECONDS)).isTrue();
+ } catch (InterruptedException e) {
+ throw new RuntimeException("Test failed because of " + e);
+ }
+ }
+
+ @Override
+ public void onConfigurationChanged(@NonNull Configuration newConfig) {
+ if (newConfig.windowConfiguration.getMaxBounds().equals(mMaxBounds)) {
+ mLatch.countDown();
+ }
+ }
+
+ @Override
+ public void onLowMemory() {}
+ }
}
diff --git a/services/tests/wmtests/src/com/android/server/wm/TaskTests.java b/services/tests/wmtests/src/com/android/server/wm/TaskTests.java
index f332b69..1dd71e0 100644
--- a/services/tests/wmtests/src/com/android/server/wm/TaskTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/TaskTests.java
@@ -1093,10 +1093,16 @@
final ActivityRecord activity0 = task.getBottomMostActivity();
activity0.info.flags |= FLAG_RELINQUISH_TASK_IDENTITY;
// Add an extra activity on top of the root one.
- new ActivityBuilder(mAtm).setTask(task).build();
+ final ActivityRecord activity1 = new ActivityBuilder(mAtm).setTask(task).build();
assertEquals("The root activity in the task must be reported.",
task.getBottomMostActivity(), task.getRootActivity());
+ assertEquals("The task id of root activity must be reported.",
+ task.mTaskId, mAtm.mActivityClientController.getTaskForActivity(
+ activity0.token, true /* onlyRoot */));
+ assertEquals("No task must be reported for non root activity if onlyRoot.",
+ INVALID_TASK_ID, mAtm.mActivityClientController.getTaskForActivity(
+ activity1.token, true /* onlyRoot */));
}
/**
@@ -1572,8 +1578,7 @@
}
private Task getTestTask() {
- final Task task = new TaskBuilder(mSupervisor).setCreateActivity(true).build();
- return task.getBottomMostTask();
+ return new TaskBuilder(mSupervisor).setCreateActivity(true).build();
}
private void testRootTaskBoundsConfiguration(int windowingMode, Rect parentBounds, Rect bounds,
diff --git a/services/usage/java/com/android/server/usage/UsageStatsService.java b/services/usage/java/com/android/server/usage/UsageStatsService.java
index e738d29..90b798c 100644
--- a/services/usage/java/com/android/server/usage/UsageStatsService.java
+++ b/services/usage/java/com/android/server/usage/UsageStatsService.java
@@ -2061,6 +2061,15 @@
return mode == AppOpsManager.MODE_ALLOWED;
}
+ private boolean canReportUsageStats() {
+ if (isCallingUidSystem()) {
+ return true; // System UID can always report UsageStats
+ }
+
+ return getContext().checkCallingPermission(Manifest.permission.REPORT_USAGE_STATS)
+ == PackageManager.PERMISSION_GRANTED;
+ }
+
private boolean hasObserverPermission() {
final int callingUid = Binder.getCallingUid();
DevicePolicyManagerInternal dpmInternal = getDpmInternal();
@@ -2541,14 +2550,19 @@
@Override
public void reportChooserSelection(@NonNull String packageName, int userId,
@NonNull String contentType, String[] annotations, @NonNull String action) {
- if (packageName == null) {
- throw new IllegalArgumentException("Package selection must not be null.");
- }
- // A valid contentType and action must be provided for chooser selection events.
- if (contentType == null || contentType.isBlank()
- || action == null || action.isBlank()) {
+ // A valid package name, content type, and action must be provided for these events
+ Objects.requireNonNull(packageName);
+ Objects.requireNonNull(contentType);
+ Objects.requireNonNull(action);
+ if (contentType.isBlank() || action.isBlank()) {
return;
}
+
+ if (!canReportUsageStats()) {
+ throw new SecurityException("Only the system or holders of the REPORT_USAGE_STATS"
+ + " permission are allowed to call reportChooserSelection");
+ }
+
// Verify if this package exists before reporting an event for it.
if (mPackageManagerInternal.getPackageUid(packageName, 0, userId) < 0) {
Slog.w(TAG, "Event report user selecting an invalid package");
@@ -2566,9 +2580,11 @@
@Override
public void reportUserInteraction(String packageName, int userId) {
Objects.requireNonNull(packageName);
- if (!isCallingUidSystem()) {
- throw new SecurityException("Only system is allowed to call reportUserInteraction");
+ if (!canReportUsageStats()) {
+ throw new SecurityException("Only the system or holders of the REPORT_USAGE_STATS"
+ + " permission are allowed to call reportUserInteraction");
}
+
final Event event = new Event(USER_INTERACTION, SystemClock.elapsedRealtime());
event.mPackage = packageName;
reportEventOrAddToQueue(userId, event);
diff --git a/telephony/java/android/telephony/CarrierConfigManager.java b/telephony/java/android/telephony/CarrierConfigManager.java
index 13e5ff1..0bb75d8 100644
--- a/telephony/java/android/telephony/CarrierConfigManager.java
+++ b/telephony/java/android/telephony/CarrierConfigManager.java
@@ -1999,7 +1999,7 @@
"lte_plus_threshold_bandwidth_khz_int";
/**
- * The combined channel bandwidth threshold (non-inclusive) in KHz required to display the
+ * The combined channel bandwidth threshold (inclusive) in KHz required to display the
* NR advanced (i.e. 5G+) data icon. It is 0 by default, meaning minimum bandwidth check is
* not enabled. Other factors like bands or frequency can also determine whether the NR
* advanced data icon is shown or not.
diff --git a/tests/Camera2Tests/SmartCamera/SimpleCamera/src/androidx/media/filterfw/BackingStore.java b/tests/Camera2Tests/SmartCamera/SimpleCamera/src/androidx/media/filterfw/BackingStore.java
index 216e743..df140b9 100644
--- a/tests/Camera2Tests/SmartCamera/SimpleCamera/src/androidx/media/filterfw/BackingStore.java
+++ b/tests/Camera2Tests/SmartCamera/SimpleCamera/src/androidx/media/filterfw/BackingStore.java
@@ -14,17 +14,10 @@
package androidx.media.filterfw;
-import android.annotation.TargetApi;
import android.graphics.Bitmap;
-import android.os.Build;
-import android.renderscript.Allocation;
-import android.renderscript.Element;
-import android.renderscript.RenderScript;
-import android.renderscript.Type;
import android.util.Log;
import java.nio.ByteBuffer;
-import java.nio.ByteOrder;
import java.util.Arrays;
import java.util.Vector;
@@ -42,14 +35,11 @@
static final int ACCESS_OBJECT = 0x08;
/** Access mode Bitmap: Frame data will be accessed as a Bitmap. */
static final int ACCESS_BITMAP = 0x10;
- /** Access mode Allocation: Frame data will be accessed as a RenderScript Allocation. */
- static final int ACCESS_ALLOCATION = 0x20;
private static final int BACKING_BYTEBUFFER = 1;
private static final int BACKING_TEXTURE = 2;
private static final int BACKING_OBJECT = 3;
private static final int BACKING_BITMAP = 4;
- private static final int BACKING_ALLOCATION = 5;
private final FrameType mType;
private int[] mDimensions;
@@ -243,14 +233,6 @@
case ACCESS_BITMAP:
backing = new BitmapBacking();
break;
- case ACCESS_ALLOCATION:
- if (!AllocationBacking.isSupported()) {
- throw new RuntimeException(
- "Attempted to create an AllocationBacking in context that does " +
- "not support RenderScript!");
- }
- backing = new AllocationBacking(mFrameManager.getContext().getRenderScript());
- break;
}
if (backing == null) {
throw new RuntimeException(
@@ -518,9 +500,6 @@
RenderTarget renderTarget = (RenderTarget) backing.lock(ACCESS_RENDERTARGET);
mBitmap.copyPixelsFromBuffer(
renderTarget.getPixelData(mDimensions[0], mDimensions[1]));
- } else if ((access & ACCESS_ALLOCATION) != 0 && AllocationBacking.isSupported()) {
- createBitmap();
- syncToAllocationBacking(backing);
} else {
throw new RuntimeException("Cannot sync bytebuffer backing!");
}
@@ -528,12 +507,6 @@
mIsDirty = false;
}
- @TargetApi(11)
- private void syncToAllocationBacking(Backing backing) {
- Allocation allocation = (Allocation) backing.lock(ACCESS_ALLOCATION);
- allocation.copyTo(mBitmap);
- }
-
@Override
public Object lock(int accessType) {
return mBitmap;
@@ -612,8 +585,6 @@
int w = mDimensions[0];
int h = mDimensions[1];
ImageShader.renderTextureToTarget(texture, getRenderTarget(), w, h);
- } else if ((access & ACCESS_ALLOCATION) != 0 && AllocationBacking.isSupported()) {
- syncToAllocationBacking(backing);
} else {
throw new RuntimeException("Cannot sync bytebuffer backing!");
}
@@ -621,14 +592,6 @@
mIsDirty = false;
}
- @TargetApi(11)
- private void syncToAllocationBacking(Backing backing) {
- Allocation allocation = (Allocation) backing.lock(ACCESS_ALLOCATION);
- ByteBuffer pixels = ByteBuffer.allocateDirect(getSize());
- allocation.copyTo(pixels.array());
- mTexture.allocateWithPixels(pixels, mDimensions[0], mDimensions[1]);
- }
-
@Override
public Object lock(int accessType) {
switch (accessType) {
@@ -733,8 +696,6 @@
ByteBuffer otherBuffer = (ByteBuffer) backing.lock(ACCESS_BYTES);
mBuffer.put(otherBuffer);
otherBuffer.rewind();
- } else if ((access & ACCESS_ALLOCATION) != 0 && AllocationBacking.isSupported()) {
- syncToAllocationBacking(backing);
} else {
throw new RuntimeException("Cannot sync bytebuffer backing!");
}
@@ -743,23 +704,6 @@
mIsDirty = false;
}
- @TargetApi(11)
- private void syncToAllocationBacking(Backing backing) {
- Allocation allocation = (Allocation) backing.lock(ACCESS_ALLOCATION);
- if (getElementId() == FrameType.ELEMENT_RGBA8888) {
- byte[] bytes = mBuffer.array();
- allocation.copyTo(bytes);
- } else if (getElementId() == FrameType.ELEMENT_FLOAT32) {
- float[] floats = new float[getSize() / 4];
- allocation.copyTo(floats);
- mBuffer.asFloatBuffer().put(floats);
- } else {
- throw new RuntimeException(
- "Trying to sync to an allocation with an unsupported element id: "
- + getElementId());
- }
- }
-
@Override
public Object lock(int accessType) {
return mBuffer.rewind();
@@ -791,139 +735,4 @@
}
}
-
- @TargetApi(11)
- static class AllocationBacking extends Backing {
-
- private final RenderScript mRenderScript;
- private Allocation mAllocation = null;
-
- public AllocationBacking(RenderScript renderScript) {
- mRenderScript = renderScript;
- }
-
- @Override
- public void allocate(FrameType frameType) {
- assertCompatible(frameType);
-
- Element element = null;
- switch (frameType.getElementId()) {
- case FrameType.ELEMENT_RGBA8888:
- element = Element.RGBA_8888(mRenderScript);
- break;
- case FrameType.ELEMENT_FLOAT32:
- element = Element.F32(mRenderScript);
- break;
- }
- Type.Builder imageTypeBuilder = new Type.Builder(mRenderScript, element);
- imageTypeBuilder.setX(mDimensions.length >= 1 ? mDimensions[0] : 1);
- imageTypeBuilder.setY(mDimensions.length == 2 ? mDimensions[1] : 1);
- Type imageType = imageTypeBuilder.create();
-
- mAllocation = Allocation.createTyped(mRenderScript, imageType);
- }
-
- @Override
- public int readAccess() {
- return ACCESS_ALLOCATION;
- }
-
- @Override
- public int writeAccess() {
- return ACCESS_ALLOCATION;
- }
-
- @Override
- public boolean requiresGpu() {
- return false;
- }
-
- @Override
- public void syncTo(Backing backing) {
- int access = backing.readAccess();
- if ((access & ACCESS_TEXTURE) != 0) {
- RenderTarget target = (RenderTarget) backing.lock(ACCESS_RENDERTARGET);
- ByteBuffer pixels = ByteBuffer.allocateDirect(getSize());
- GLToolbox.readTarget(target, pixels, mDimensions[0], mDimensions[1]);
- mAllocation.copyFrom(pixels.array());
- } else if ((access & ACCESS_BITMAP) != 0) {
- Bitmap bitmap = (Bitmap) backing.lock(ACCESS_BITMAP);
- mAllocation.copyFrom(bitmap);
- } else if ((access & ACCESS_BYTES) != 0) {
- ByteBuffer buffer = (ByteBuffer) backing.lock(ACCESS_BYTES);
- if (buffer.order() != ByteOrder.nativeOrder()) {
- throw new RuntimeException(
- "Trying to sync to the ByteBufferBacking with non-native byte order!");
- }
- byte[] bytes;
- if (buffer.hasArray()) {
- bytes = buffer.array();
- } else {
- bytes = new byte[getSize()];
- buffer.get(bytes);
- buffer.rewind();
- }
- mAllocation.copyFromUnchecked(bytes);
- } else {
- throw new RuntimeException("Cannot sync allocation backing!");
- }
- backing.unlock();
- mIsDirty = false;
- }
-
- @Override
- public Object lock(int accessType) {
- return mAllocation;
- }
-
- @Override
- public void unlock() {
- }
-
- @Override
- public int getType() {
- return BACKING_ALLOCATION;
- }
-
- @Override
- public boolean shouldCache() {
- return true;
- }
-
- @Override
- public void destroy() {
- if (mAllocation != null) {
- mAllocation.destroy();
- mAllocation = null;
- }
- }
-
- @Override
- public int getSize() {
- int elementCount = 1;
- for (int dim : mDimensions) {
- elementCount *= dim;
- }
- return getElementSize() * elementCount;
- }
-
- public static boolean isSupported() {
- return Build.VERSION.SDK_INT >= 11;
- }
-
- private void assertCompatible(FrameType type) {
- // TODO: consider adding support for other data types.
- if (type.getElementId() != FrameType.ELEMENT_RGBA8888
- && type.getElementId() != FrameType.ELEMENT_FLOAT32) {
- throw new RuntimeException(
- "Cannot allocate allocation with a non-RGBA or non-float data type!");
- }
- if (mDimensions == null || mDimensions.length > 2) {
- throw new RuntimeException(
- "Cannot create an allocation with more than 2 dimensions!");
- }
- }
-
- }
-
}
diff --git a/tests/Camera2Tests/SmartCamera/SimpleCamera/src/androidx/media/filterfw/FrameBuffer1D.java b/tests/Camera2Tests/SmartCamera/SimpleCamera/src/androidx/media/filterfw/FrameBuffer1D.java
index 0e24f5b..20cc1bf 100644
--- a/tests/Camera2Tests/SmartCamera/SimpleCamera/src/androidx/media/filterfw/FrameBuffer1D.java
+++ b/tests/Camera2Tests/SmartCamera/SimpleCamera/src/androidx/media/filterfw/FrameBuffer1D.java
@@ -16,9 +16,6 @@
package androidx.media.filterfw;
-import android.annotation.TargetApi;
-import android.renderscript.Allocation;
-
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
@@ -40,19 +37,6 @@
return (ByteBuffer)mBackingStore.lockData(mode, BackingStore.ACCESS_BYTES);
}
- /**
- * Access frame's data using a RenderScript {@link Allocation}.
- * This is a convenience method and is equivalent to calling {@code lockData} with an
- * {@code accessFormat} of {@code ACCESS_ALLOCATION}.
- *
- * @return The Allocation instance holding the Frame's data.
- */
- @TargetApi(11)
- public Allocation lockAllocation(int mode) {
- assertAccessible(mode);
- return (Allocation) mBackingStore.lockData(mode, BackingStore.ACCESS_ALLOCATION);
- }
-
public int getLength() {
return mLength;
}
diff --git a/tests/Camera2Tests/SmartCamera/SimpleCamera/src/androidx/media/filterfw/MffContext.java b/tests/Camera2Tests/SmartCamera/SimpleCamera/src/androidx/media/filterfw/MffContext.java
index 6bd6c18..8fd44d2 100644
--- a/tests/Camera2Tests/SmartCamera/SimpleCamera/src/androidx/media/filterfw/MffContext.java
+++ b/tests/Camera2Tests/SmartCamera/SimpleCamera/src/androidx/media/filterfw/MffContext.java
@@ -16,15 +16,12 @@
package androidx.media.filterfw;
-import android.annotation.TargetApi;
import android.app.Activity;
import android.app.ActivityManager;
import android.content.Context;
import android.content.pm.ConfigurationInfo;
-import android.os.Build;
import android.os.Handler;
import android.os.Looper;
-import android.renderscript.RenderScript;
import android.util.Log;
import android.view.SurfaceHolder;
import android.view.SurfaceView;
@@ -116,9 +113,6 @@
/** Flag whether camera streaming is supported in this context. */
private boolean mCameraStreamingSupport;
- /** RenderScript base master class. */
- private RenderScript mRenderScript;
-
/**
* Creates a new MffContext with the default configuration.
*
@@ -200,9 +194,7 @@
mCameraStreamer.stop();
mCameraStreamer.tearDown();
}
- if (Build.VERSION.SDK_INT >= 11) {
- maybeDestroyRenderScript();
- }
+
stopRunners(false);
waitUntilStopped();
tearDown();
@@ -301,14 +293,6 @@
return mCameraStreamingSupport;
}
- @TargetApi(11)
- public final RenderScript getRenderScript() {
- if (mRenderScript == null) {
- mRenderScript = RenderScript.create(mApplicationContext);
- }
- return mRenderScript;
- }
-
final void assertOpenGLSupported() {
if (!isOpenGLSupported()) {
throw new RuntimeException("Attempting to use OpenGL ES 2 in a context that does not "
@@ -459,12 +443,4 @@
return (context instanceof Activity) ? (Activity) context : null;
}
- @TargetApi(11)
- private void maybeDestroyRenderScript() {
- if (mRenderScript != null) {
- mRenderScript.destroy();
- mRenderScript = null;
- }
- }
-
}
diff --git a/tools/aapt/AaptAssets.cpp b/tools/aapt/AaptAssets.cpp
index b94d14f..0fc2617 100644
--- a/tools/aapt/AaptAssets.cpp
+++ b/tools/aapt/AaptAssets.cpp
@@ -1540,7 +1540,7 @@
}
const String8& featureOfBase = bundle->getFeatureOfPackage();
- if (!featureOfBase.isEmpty()) {
+ if (!featureOfBase.empty()) {
if (bundle->getVerbose()) {
printf("Including base feature resources from package: %s\n",
featureOfBase.c_str());
diff --git a/tools/aapt/Command.cpp b/tools/aapt/Command.cpp
index 5a06b10..60f3f27 100644
--- a/tools/aapt/Command.cpp
+++ b/tools/aapt/Command.cpp
@@ -1133,7 +1133,7 @@
if (code == ResXMLTree::END_TAG) {
depth--;
if (depth < 2) {
- if (withinSupportsInput && !supportedInput.isEmpty()) {
+ if (withinSupportsInput && !supportedInput.empty()) {
printf("supports-input: '");
const size_t N = supportedInput.size();
for (size_t i=0; i<N; i++) {
@@ -1300,7 +1300,7 @@
ResTable::normalizeForOutput(versionName.c_str()).c_str());
String8 splitName = AaptXml::getAttribute(tree, NULL, "split");
- if (!splitName.isEmpty()) {
+ if (!splitName.empty()) {
printf(" split='%s'", ResTable::normalizeForOutput(
splitName.c_str()).c_str());
}
diff --git a/tools/aapt/Resource.cpp b/tools/aapt/Resource.cpp
index 9c944e0..4a360ed 100644
--- a/tools/aapt/Resource.cpp
+++ b/tools/aapt/Resource.cpp
@@ -1285,7 +1285,7 @@
packageType = ResourceTable::SharedLibrary;
} else if (bundle->getExtending()) {
packageType = ResourceTable::System;
- } else if (!bundle->getFeatureOfPackage().isEmpty()) {
+ } else if (!bundle->getFeatureOfPackage().empty()) {
packageType = ResourceTable::AppFeature;
}
@@ -3144,7 +3144,7 @@
tree.restart();
- if (!startTags.isEmpty()) {
+ if (!startTags.empty()) {
bool haveStart = false;
while ((code=tree.next()) != ResXMLTree::END_DOCUMENT && code != ResXMLTree::BAD_DOCUMENT) {
if (code != ResXMLTree::START_TAG) {
diff --git a/tools/aapt/ResourceTable.cpp b/tools/aapt/ResourceTable.cpp
index 449e080..bc252cf 100644
--- a/tools/aapt/ResourceTable.cpp
+++ b/tools/aapt/ResourceTable.cpp
@@ -1813,7 +1813,7 @@
mTypeIdOffset = findLargestTypeIdForPackage(assets->getIncludedResources(), mAssetsPackage);
const String8& featureAfter = bundle->getFeatureAfterPackage();
- if (!featureAfter.isEmpty()) {
+ if (!featureAfter.empty()) {
AssetManager featureAssetManager;
if (!featureAssetManager.addAssetPath(featureAfter, NULL)) {
fprintf(stderr, "ERROR: Feature package '%s' not found.\n",
@@ -1823,7 +1823,7 @@
const ResTable& featureTable = featureAssetManager.getResources(false);
mTypeIdOffset = std::max(mTypeIdOffset,
- findLargestTypeIdForPackage(featureTable, mAssetsPackage));
+ findLargestTypeIdForPackage(featureTable, mAssetsPackage));
}
return NO_ERROR;
@@ -3252,7 +3252,7 @@
// If we're building splits, then each invocation of the flattening
// step will have 'missing' entries. Don't warn/error for this case.
- if (bundle->getSplitConfigurations().isEmpty()) {
+ if (bundle->getSplitConfigurations().empty()) {
bool missing_entry = false;
const char* log_prefix = bundle->getErrorOnMissingConfigEntry() ?
"error" : "warning";
@@ -4858,7 +4858,7 @@
Vector<sp<XMLNode> > nodesToVisit;
nodesToVisit.push(root);
- while (!nodesToVisit.isEmpty()) {
+ while (!nodesToVisit.empty()) {
sp<XMLNode> node = nodesToVisit.top();
nodesToVisit.pop();
diff --git a/tools/aapt/SourcePos.cpp b/tools/aapt/SourcePos.cpp
index e130286..354a65c 100644
--- a/tools/aapt/SourcePos.cpp
+++ b/tools/aapt/SourcePos.cpp
@@ -78,7 +78,7 @@
break;
}
- if (!this->file.isEmpty()) {
+ if (!this->file.empty()) {
if (this->line >= 0) {
fprintf(to, "%s:%d: %s%s\n", this->file.c_str(), this->line, type, this->error.c_str());
} else {
diff --git a/tools/aapt2/DominatorTree_test.cpp b/tools/aapt2/DominatorTree_test.cpp
index 52949da..a0679a6 100644
--- a/tools/aapt2/DominatorTree_test.cpp
+++ b/tools/aapt2/DominatorTree_test.cpp
@@ -50,8 +50,7 @@
private:
void VisitConfig(const DominatorTree::Node* node, const int indent) {
auto config_string = node->value()->config.toString();
- buffer_ << std::string(indent, ' ')
- << (config_string.isEmpty() ? "<default>" : config_string)
+ buffer_ << std::string(indent, ' ') << (config_string.empty() ? "<default>" : config_string)
<< std::endl;
}
diff --git a/tools/obbtool/Main.cpp b/tools/obbtool/Main.cpp
index 64808c0..7014068 100644
--- a/tools/obbtool/Main.cpp
+++ b/tools/obbtool/Main.cpp
@@ -135,7 +135,7 @@
}
printf("OBB info for '%s':\n", filename);
- printf("Package name: %s\n", obb->getPackageName().string());
+ printf("Package name: %s\n", obb->getPackageName().c_str());
printf(" Version: %d\n", obb->getVersion());
printf(" Flags: 0x%08x\n", obb->getFlags());
printf(" Overlay: %s\n", obb->isOverlay() ? "true" : "false");
diff --git a/tools/split-select/SplitDescription.cpp b/tools/split-select/SplitDescription.cpp
index 4e2b48e..7150008 100644
--- a/tools/split-select/SplitDescription.cpp
+++ b/tools/split-select/SplitDescription.cpp
@@ -70,7 +70,7 @@
String8 SplitDescription::toString() const {
String8 extension;
if (abi != abi::Variant_none) {
- if (extension.isEmpty()) {
+ if (extension.empty()) {
extension.append(":");
} else {
extension.append("-");